mirror of
https://github.com/bitwarden/ios.git
synced 2025-12-11 13:54:06 -06:00
132 lines
4.4 KiB
Swift
132 lines
4.4 KiB
Swift
import CoreData
|
|
import Foundation
|
|
|
|
// Based on https://medium.com/swlh/using-core-data-in-your-swiftui-app-with-combine-mvvm-and-protocols-4577f44d240d
|
|
|
|
class CoreDataHelper: DBHelperProtocol {
|
|
static let shared = CoreDataHelper()
|
|
|
|
typealias ObjectType = NSManagedObject
|
|
typealias PredicateType = NSPredicate
|
|
|
|
var context: NSManagedObjectContext { persistentContainer.viewContext }
|
|
|
|
// MARK: - Core Data
|
|
|
|
lazy var persistentContainer: NSPersistentContainer = {
|
|
StringEncryptionTransformer.register()
|
|
let container = NSPersistentContainer(name: "BitwardenDB")
|
|
|
|
container.loadPersistentStores(completionHandler: { _, error in
|
|
if let error = error as NSError? {
|
|
fatalError("Unresolved error \(error), \(error.userInfo)")
|
|
}
|
|
})
|
|
return container
|
|
}()
|
|
|
|
func saveContext() {
|
|
let context = persistentContainer.viewContext
|
|
if context.hasChanges {
|
|
do {
|
|
try context.save()
|
|
} catch {
|
|
let nserror = error as NSError
|
|
fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - DBHelper Protocol
|
|
|
|
func create(_: NSManagedObject) {
|
|
do {
|
|
try context.save()
|
|
} catch {
|
|
fatalError("error saving context while creating an object")
|
|
}
|
|
}
|
|
|
|
func fetch<T: NSManagedObject>(_: T.Type, _ entityName: String, predicate: NSPredicate? = nil,
|
|
limit: Int? = nil) -> Result<[T], Error> {
|
|
let request = NSFetchRequest<T>(entityName: entityName)
|
|
request.predicate = predicate
|
|
if let limit {
|
|
request.fetchLimit = limit
|
|
}
|
|
do {
|
|
let result = try context.fetch(request)
|
|
return .success(result as [T])
|
|
} catch {
|
|
return .failure(error)
|
|
}
|
|
}
|
|
|
|
func fetchFirst<T: NSManagedObject>(_ objectType: T.Type, predicate: NSPredicate?) -> Result<T?, Error> {
|
|
let result = fetch(objectType, predicate: predicate, limit: 1)
|
|
switch result {
|
|
case let .success(todos):
|
|
return .success(todos.first as? T)
|
|
case let .failure(error):
|
|
return .failure(error)
|
|
}
|
|
}
|
|
|
|
func update(_: NSManagedObject) {
|
|
do {
|
|
try context.save()
|
|
} catch {
|
|
fatalError("error saving context while updating an object")
|
|
}
|
|
}
|
|
|
|
func delete(_ object: NSManagedObject) {
|
|
context.delete(object)
|
|
}
|
|
|
|
func insertBatch(
|
|
_ entityName: String,
|
|
items: [Any],
|
|
itemMapper: @escaping (Any, NSManagedObjectContext) -> [String: Any],
|
|
completionHandler: @escaping () -> Void,
|
|
) {
|
|
persistentContainer.performBackgroundTask { context in
|
|
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
|
|
let objects = items.map { item in
|
|
itemMapper(item, context)
|
|
}
|
|
let batchInsert = NSBatchInsertRequest(entityName: entityName, objects: objects)
|
|
batchInsert.resultType = NSBatchInsertRequestResultType.objectIDs
|
|
do {
|
|
let result = try context.execute(batchInsert) as! NSBatchInsertResult
|
|
if let objectIDs = result.result as? [NSManagedObjectID], !objectIDs.isEmpty {
|
|
let save = [NSInsertedObjectsKey: objectIDs]
|
|
NSManagedObjectContext.mergeChanges(fromRemoteContextSave: save, into: [self.context])
|
|
}
|
|
} catch let nsError as NSError {
|
|
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
|
|
}
|
|
DispatchQueue.main.async {
|
|
completionHandler()
|
|
}
|
|
}
|
|
}
|
|
|
|
func deleteAll(_ entityName: String, predicate: NSPredicate? = nil, completionHandler: @escaping () -> Void) {
|
|
let fetchRequest: NSFetchRequest<NSFetchRequestResult> = NSFetchRequest(entityName: entityName)
|
|
fetchRequest.predicate = predicate
|
|
let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
|
|
deleteRequest.resultType = .resultTypeObjectIDs
|
|
|
|
persistentContainer.performBackgroundTask { context in
|
|
do {
|
|
try context.execute(deleteRequest)
|
|
} catch let nsError as NSError {
|
|
Log.e("Unresolved error \(nsError), \(nsError.userInfo)")
|
|
}
|
|
|
|
completionHandler()
|
|
}
|
|
}
|
|
}
|