Search code examples
swiftcore-datansmanagedobjectcontext

Getting nil managedObjectContext in CoreData with Swift working from a Library


I'm developing a library for iOS using Swift 5, and I want this library to use CoreData independent of the application which consumes that library and this is what I've done so far:

  1. Created the entities with their respective data types
  2. Created the .xcdatamodeld file, which contains the entities
  3. Created a CoreDataManager which looks like this:
// MARK: - CoreDataManager
final class CoreDataManager {
    static let shared = CoreDataManager()
    private static let defaultObject = NSManagedObject.init()
    
    lazy var persistentContainer: NSPersistentContainer = {
        let container = NSPersistentContainer(name: "Audit")
        container.loadPersistentStores(completionHandler: { (storeDescription, error) in
            if let error = error as NSError? {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
        return container
    }()
}

And the trouble is I'm trying to get the context by:

let coreDataManager = CoreDataManager.shared
let context = coreDataManager.persistentContainer.viewContext

and context is returning nil

please help


Solution

  • I solved it, and apparently the trouble was that the ios application where I wanted to use my library wasn't finding the .xcdatamodeld file which resulted in a useless NSPersistentContainer object, which also meant that let context = persistentContainer.viewContext was nil.

    In order to avoid this kind of troubles in the future, I'll left a list of important considerations when working with CoreData and swift libraries.

    Key things to consider

    1. Make sure the app that is consuming your library knows exactly where to look for it. Might want to take a look at this article for details.
    2. If you are working with cocoapods for distributing your library, make sure to add the following to your .podspec: s.resources = "path/to/model.xcdatamodeld" This will produce a folder named "Resources" in your Pods target:

    enter image description here

    1. Make sure your model file name matches the NSPersistentContainer name.
    2. (NOT SURE ABOUT THIS) I changed the class definition of my NSManagedObjects from
    class Audit: NSManagedObject {}
    

    to

    public class Audit: NSManagedObject {}
    

    And even when I'm not sure if that makes sense, It could work for you.

    Finally I'll leave the code that worked for me

    // MARK: - CoreDataManager
    final class CoreDataManager {
        static let shared = CoreDataManager()
        private static let defaultObject = NSManagedObject.init()
        
        lazy var persistentContainer: NSPersistentContainer? = {
            let modelURL = Bundle(for: Audit.self).url(forResource: "Audit", withExtension: "momd")
            
            guard let model = modelURL.flatMap(NSManagedObjectModel.init) else {
                print("Fail to load the trigger model!")
                return nil
            }
            
            let container = NSPersistentContainer(name: "Audit", managedObjectModel: model)
            container.loadPersistentStores(completionHandler: { (storeDescription, error) in
                if let error = error as NSError? {
                    fatalError("Unresolved error \(error), \(error.userInfo)")
                }
            })
            
            return container
        }()
    }
    

    And to get the context outside

    let coreDataManager = CoreDataManager.shared
    
    guard
        let context = coreDataManager.persistentContainer?.viewContext
    else {
        print("Nil context case")
        return
    }
    

    Hope you guys find it helpful!!