Skip to content

Decodable CoreData work-in-progress

A proof-of-concept for using CoreData and the Codable (Encodable/Decodable) protocol for working with JSON in offline-first iOS apps. POC also includes an approach for easily working with CoreData entities with generics:

Generic CoreDataRepository

swift
import CoreData
import Foundation

/// Helper for working with CoreData entities
class CoreDataRepository<Entity: NSManagedObject> {
    private let context: NSManagedObjectContext

    init(context: NSManagedObjectContext) {
        self.context = context
    }

    func fetch(sortDescriptors: [NSSortDescriptor] = [],
               predicate: NSPredicate? = nil) -> Result<[Entity], Error>
    {
        let request = Entity.fetchRequest()
        request.sortDescriptors = sortDescriptors
        request.predicate = predicate

        do {
            let results = try context.fetch(request) as! [Entity]
            return .success(results)
        } catch {
            return .failure(error)
        }
    }

    func getById(_ id: NSManagedObjectID) -> Result<Entity, Error> {
        guard let entity = try? context.existingObject(with: id) as? Entity else {
            return .failure(Errors.objectNotFound)
        }

        return .success(entity)
    }

    func create(_ body: @escaping (inout Entity) -> Void) -> Result<Entity, Error> {
        var entity = Entity(context: context)
        body(&entity)
        do {
            try context.save()
            return .success(entity)
        } catch {
            return .failure(error)
        }
    }

    func update(_ entity: Entity) -> Result<Entity, Error> {
        do {
            try context.save()
            return .success(entity)
        } catch {
            return .failure(error)
        }
    }

    func delete(_ entity: Entity) -> Result<Void, Error> {
        do {
            context.delete(entity)
            try context.save()
            return .success(())
        } catch {
            return .failure(error)
        }
    }

    enum Errors: Error {
        case objectNotFound
    }
}

Usage

swift
let repo = CoreDataRepository<ItemEntity>(context: PersistenceController.shared.container.viewContext)

func create() {
    do {
        let result = repo.create { item in
            item.id = UUID()
            item.title = self.newItemTitle
            item.subtitle = self.newItemSubtitle
        }
        let newItem = try result.get()
        print(newItem)
    } catch {
        print(error)
    }
}

See the code.