Last active
November 17, 2018 15:50
-
-
Save Lavmint/b4d33ce328f48cee6eb89cbb5afde133 to your computer and use it in GitHub Desktop.
Nice CoreData usage
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import CoreData | |
enum db { | |
static let todoist: DAO = { | |
let container = NSPersistentContainer(name: "Todoist") | |
container.loadPersistentStores(completionHandler: { (storeDescription, error) in | |
guard let err = error else { return } | |
fatalError(err.localizedDescription) | |
}) | |
return DAO(container: container) | |
}() | |
} | |
extension ProjectMO: ManagedObject { | |
public static func primaryKey() -> String { | |
return #keyPath(ProjectMO.id) | |
} | |
} | |
public class DAO { | |
private let container: NSPersistentContainer | |
public init(container: NSPersistentContainer) { | |
self.container = container | |
} | |
public func transaction(_ block: ((_ w: Transaction) throws -> Void)) throws { | |
try block(Transaction(ctx: container.viewContext)) | |
} | |
public func backgroundTransaction(_ block: @escaping ((_ w: Transaction) throws -> Void)) throws { | |
container.performBackgroundTask { (ctx) in | |
do { | |
try block(Transaction(ctx: ctx)) | |
} catch { | |
print(error) | |
} | |
} | |
} | |
} | |
public class Transaction { | |
fileprivate let ctx: NSManagedObjectContext | |
internal init(ctx: NSManagedObjectContext) { | |
self.ctx = ctx | |
} | |
public func objects<T: NSManagedObject>(_ type: T.Type) -> RequestBuilder<T, T> { | |
return RequestBuilder<T, T>(ctx: ctx) | |
} | |
public func dictionaries<T: NSManagedObject>(_ type: T.Type) -> RequestBuilder<T, NSDictionary> { | |
return RequestBuilder<T, NSDictionary>(ctx: ctx) | |
} | |
} | |
//MARK: - Write | |
public extension Transaction { | |
public func write(_ block: ((_ w: WriteTransaction) throws -> Void)) throws { | |
try block(WriteTransaction(ctx: ctx)) | |
if ctx.hasChanges { | |
do { | |
try ctx.save() | |
} catch { | |
ctx.rollback() | |
throw error | |
} | |
} | |
} | |
} | |
public class WriteTransaction: Transaction { | |
@discardableResult | |
public func create<T: NSManagedObject & ManagedObject>(_ type: T.Type, id: T.PrimaryKey) throws -> T { | |
var obj = try objects(T.self).find(id: id) ?? T(context: ctx) | |
obj.id = id | |
return obj | |
} | |
public func update<T: NSManagedObject & ManagedObject>(_ type: T.Type, id: T.PrimaryKey, kyedValues: [String: Any], createIfNotExists: Bool = false) throws { | |
if createIfNotExists { | |
let obj = try create(type, id: id) | |
obj.setValuesForKeys(kyedValues) | |
} else { | |
guard let obj = try objects(T.self).find(id: id) else { return } | |
obj.setValuesForKeys(kyedValues) | |
} | |
} | |
} | |
public class RequestBuilder<Object: NSManagedObject & ManagedObject, Result: NSFetchRequestResult> { | |
public private(set) var request: NSFetchRequest<Result> | |
private let ctx: NSManagedObjectContext | |
internal init(ctx: NSManagedObjectContext) { | |
self.ctx = ctx | |
request = NSFetchRequest<Result>() | |
request.entity = Object.entity() | |
if Result.self == NSDictionary.self { | |
request.resultType = .dictionaryResultType | |
} | |
} | |
public func find(id: Object.PrimaryKey) throws -> Result? { | |
let p = id is NSNumber ? "%d" : "%@" | |
_ = filter(predicate: NSPredicate(format: "\(Object.primaryKey()) == \(p)", id)) | |
request.fetchLimit = 1 | |
return try ctx.fetch(request).first | |
} | |
public func filter(predicate: NSPredicate) -> Self { | |
request.predicate = predicate | |
return self | |
} | |
public func filter(_ query: String) -> Self { | |
return filter(predicate: NSPredicate(format: query)) | |
} | |
public func sort(by descriptors: [NSSortDescriptor]) -> Self { | |
request.sortDescriptors? = descriptors | |
return self | |
} | |
public func sort(by descriptor: NSSortDescriptor) -> Self { | |
return sort(by: [descriptor]) | |
} | |
public func frc(sectionNameKeyPath: String? = nil, cacheName: String? = nil) -> NSFetchedResultsController<Result> { | |
return NSFetchedResultsController(fetchRequest: request, managedObjectContext: ctx, sectionNameKeyPath: sectionNameKeyPath, cacheName: cacheName) | |
} | |
} | |
public extension RequestBuilder where Result: NSDictionary { | |
public func fetch<T: Decodable>(_ decodable: T.Type) throws -> [T] { | |
let dicts = try ctx.fetch(request) | |
let data = try JSONSerialization.data(withJSONObject: dicts) | |
let decoder = JSONDecoder() | |
return try decoder.decode([T].self, from: data) | |
} | |
} | |
public extension RequestBuilder where Result: NSManagedObject { | |
public func fetch() throws -> [Result] { | |
return try ctx.fetch(request) | |
} | |
} | |
public protocol ManagedObject { | |
associatedtype PrimaryKey: CVarArg | |
static func primaryKey() -> String | |
var id: PrimaryKey { get set } | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment