[TIL Tips] Convert Local JSON to CoreData using Sync

Posted on Dec 17, 2018
tl;dr:

Sync is a Swift library that uses a convention-over-configuration paradigm to facilitate JSON-2-CoreData operations.

Sync uses a wrapper for CoreData called DataStack which allows us to skip dealing with NSPersistentStoreCoordinator and NSManageObjectContext.

First, we need to create a custom Fetcher class to fetch data from our JSON file and populate the DB.

import CoreData
import Sync

class Fetcher {
    private let dataStack: DataStack

    init() {
        self.dataStack = DataStack(modelName: "ENTITY")
    }
}

Moving forward, replace all instances of ENTITY with the name of your entity class.

Now, let’s create a fetchLocalEntities() function that processes the NSFetchRequest and sorts our info by a given KEY

    func fetchLocalEntities() -> [ENTITY] {
        let request: NSFetchRequest<ENTITY> = ENTITY.fetchRequest()
        let sortDescriptor = NSSortDescriptor(key: "KEY", ascending: true)
        let sortDescriptors = [sortDescriptor]
        request.sortDescriptors = sortDescriptors

        return try! self.dataStack.viewContext.fetch(request)
    }

Now the fun part. Let’s add a syncUsingLocalJSON() function that parses our JSON file. The code goes as follows:

    func syncUsingLocalJSON(completion: @escaping (_ result: VoidResult) -> ()) {
        let fileName = "JSON-FILE"
        guard let url = URL(string: fileName) else { return }
        guard let filePath = Bundle.main.path(forResource: url.deletingPathExtension().absoluteString, ofType: url.pathExtension) else { return }
        guard let data = try? Data(contentsOf: URL(fileURLWithPath: filePath)) else { return }
        guard let json = try! JSONSerialization.jsonObject(with: data, options: []) as? [[String: Any]] else { return }
        print("Syncing")
        self.dataStack.sync(json, inEntityNamed: EntityClass.entity().name!) { error in
            completion(.success)
        }
    }

Remember to replace JSON-FILE with the name of your .json file.

Lastly, add an enumerator that returns the correct case for our functions.

enum VoidResult {
    case success
    case failure(NSError)
}