Skip to content

Instantly share code, notes, and snippets.

@JasonCanCode
Last active August 3, 2018 15:45
Show Gist options
  • Save JasonCanCode/bcc447b0d64b3e7fecd4d6f561572783 to your computer and use it in GitHub Desktop.
Save JasonCanCode/bcc447b0d64b3e7fecd4d6f561572783 to your computer and use it in GitHub Desktop.
Make CoreData model objects easier to interact with
import Foundation
protocol JSONUpdatable {
func update(with json: JSON)
}
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