Last active
August 3, 2018 15:45
-
-
Save JasonCanCode/bcc447b0d64b3e7fecd4d6f561572783 to your computer and use it in GitHub Desktop.
Make CoreData model objects easier to interact with
This file contains hidden or 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 Foundation | |
protocol JSONUpdatable { | |
func update(with json: JSON) | |
} |
This file contains hidden or 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 | |
typealias JSON = [String: Any] | |
protocol ManagedMappable: class { | |
var id: Int64 { get } | |
static var uniqueIdentifier: String { get } | |
static func entityPredicate(withIdentifier identifier: Any) -> NSPredicate | |
} | |
extension ManagedMappable where Self: NSManagedObject { | |
private static var context: NSManagedObjectContext? { | |
return DatabaseManager.managedObjectContext | |
} | |
private static var className: String { | |
return String(describing: self) | |
} | |
static var uniqueIdentifier: String { | |
return "id" | |
} | |
static func entityPredicate(withIdentifier identifier: Any) -> NSPredicate { | |
guard let id = identifier as? Int64 else { | |
return NSPredicate() | |
} | |
return NSPredicate(format: "id == %d", id) | |
} | |
// MARK: Create/Update | |
static func saveChanges() { | |
guard let context = context else { | |
return | |
} | |
context.performAndWait { | |
if context.hasChanges == true { | |
do { | |
try context.save() | |
} catch { | |
print("ERROR SAVING TO MANAGED CONTEXT:\n", error) | |
} | |
} | |
} | |
} | |
static func newEntity() -> Self? { | |
guard let context = context else { | |
return nil | |
} | |
return NSEntityDescription.insertNewObject(forEntityName: className, into: context) as? Self | |
} | |
static func entity(withIdentifier id: Any) -> Self? { | |
return Self.firstEntity(predicate: entityPredicate(withIdentifier: id)) | |
} | |
private static func fetchOrInsertEntity(withId id: Any) -> Self? { | |
if let existingEntity = entity(withIdentifier: id) { | |
return existingEntity | |
} else if let newEntity = Self.newEntity() { | |
return newEntity | |
} else { | |
return nil | |
} | |
} | |
static func deleteObsoleteObjects(entities updatedEntities: [Self], existingEntities: [Self] = Self.entities()) { | |
var entitiesForDelete: [Self] = [] | |
for existingEntity in existingEntities { | |
let isExistingEntityInUpdatedEntities = updatedEntities.lazy.filter({ $0.id == existingEntity.id }).first != nil | |
if !isExistingEntityInUpdatedEntities { | |
entitiesForDelete.append(existingEntity) | |
} | |
} | |
for entityForDelete in entitiesForDelete { | |
print("deleting id: " + String(entityForDelete.id)) | |
Self.deleteInstance(entityForDelete) | |
} | |
} | |
// MARK: Read | |
static func firstEntity(predicate: NSPredicate? = nil, sortDescriptors: [NSSortDescriptor]? = nil) -> Self? { | |
return entities(fetchLimit: 1, predicate: predicate, sortDescriptors: sortDescriptors).first | |
} | |
static func entities(fetchLimit: Int = 0, predicate: NSPredicate? = nil, sortDescriptors: [NSSortDescriptor]? = nil) -> [Self] { | |
guard let context = context, | |
let entityDescription = NSEntityDescription.entity(forEntityName: className, in: context) else { | |
return [] | |
} | |
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: className) | |
fetchRequest.entity = entityDescription | |
fetchRequest.predicate = predicate | |
fetchRequest.sortDescriptors = sortDescriptors | |
fetchRequest.resultType = NSFetchRequestResultType() | |
if fetchLimit > 0 { | |
fetchRequest.fetchLimit = fetchLimit | |
} | |
do { | |
let fetchedObjects = try context.fetch(fetchRequest) | |
return fetchedObjects as? [Self] ?? [] | |
} catch { | |
print("error by execute requests") | |
return [] | |
} | |
} | |
static func entityCount(predicate: NSPredicate? = nil) -> Int { | |
guard let context = context, | |
let entityDescription = NSEntityDescription.entity(forEntityName: className, in: context) else { | |
return 0 | |
} | |
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: className) | |
fetchRequest.entity = entityDescription | |
fetchRequest.predicate = predicate | |
fetchRequest.resultType = NSFetchRequestResultType.countResultType | |
do { | |
return try context.count(for: fetchRequest) | |
} catch { | |
print("error by execute requests") | |
return 0 | |
} | |
} | |
// MARK: Delete | |
static func deleteInstance(_ object: NSManagedObject) { | |
context?.delete(object) | |
saveChanges() | |
} | |
static func deleteAll(matchingPredicate predicate: NSPredicate? = nil) { | |
let allEntities = self.entities(predicate: predicate) | |
for entity in allEntities { | |
deleteInstance(entity) | |
} | |
} | |
} | |
extension ManagedMappable where Self: NSManagedObject, Self: JSONUpdatable { | |
@discardableResult static func createOrUpdateEntities(withJSONArray jsonArray: [JSON]) -> [Self] { | |
return jsonArray.compactMap { json in | |
self.createOrUpdateEntity(withJSON: json) | |
} | |
} | |
@discardableResult static func createOrUpdateEntity(withJSON json: JSON) -> Self? { | |
guard let entityId = json[uniqueIdentifier], | |
let entity: Self = fetchOrInsertEntity(withId: entityId) else { | |
return nil | |
} | |
entity.update(with: json) | |
return entity | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment