0

I have two read-only databases that I update occasionally during new app releases. Rather that do some type of version control updating of the sqlite by copying it from the app bundle to the app documents directory I would like to simply point the persistent container to the app bundle and read it. This works fine for one database but not for the other and I can't figure out why. (FYI both work in simulator, but one crashes on device)

I use identical persistent container code, the one that does not crash is like this:

    lazy var strengthPersistentContainer: NSPersistentContainer =
    {
        let container = NSPersistentContainer(name: "Favs")

        let sqlFileName: String = kStrengthShortFileName
        let sqlFileExtension: String = "sqlite"

        let fileUrl: URL = Bundle.main.url(forResource: sqlFileName, withExtension: sqlFileExtension)!

        let description = NSPersistentStoreDescription(url: fileUrl)
        description.isReadOnly = true
        container.persistentStoreDescriptions = [description]
        container.loadPersistentStores
        {
            _, error in
            if let error = error as NSError?
            {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        }
        return container
    }()

The one that crashes, is the same:

    lazy var everKeneticPersistentContainer: NSPersistentContainer =
    {
        let container = NSPersistentContainer(name: "EverKenetic")

        let sqlFileName: String = kEverKineticFileName
        let sqlFileExtension: String = "sqlite"

        let fileUrl: URL = Bundle.main.url(forResource: sqlFileName, withExtension: sqlFileExtension)!

        let description = NSPersistentStoreDescription(url: fileUrl)
        description.isReadOnly = true
        container.persistentStoreDescriptions = [description]
        container.loadPersistentStores
        {
            _, error in
            if let error = error as NSError?
            {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        }
        return container
    }()

The main difference is I am using a singleton and a simple context fetch that crashes whereas the other uses a FetchedResultsController fetch which works fine.

Here is the singleton code that crashes:

class EverKenetic: NSObject
{
    private static var manager: EverKenetic = EverKenetic()
    var everKineticContext: NSManagedObjectContext?

    override init()
    {
        if let appDelegate = UIApplication.shared.delegate as? AppDelegate
        {
            let theContext = appDelegate.everKeneticPersistentContainer.viewContext
            everKineticContext = theContext
        }
    }

 class func exerciseInfo(_ everKeneticName: String) -> EverKeneticExercise!
    {
        let fetchRequest: NSFetchRequest<EverKeneticExercise> = EverKeneticExercise.fetchRequest()

        if let context: NSManagedObjectContext = manager.everKineticContext
        {
            let entity = NSEntityDescription.entity(forEntityName: "EverKeneticExercise", in: context)
            fetchRequest.entity = entity

            fetchRequest.fetchBatchSize = 2

            let predicate: NSPredicate = NSPredicate(format: "name = %@", everKeneticName)
            fetchRequest.predicate = predicate

            let sortDescriptorRoutine = NSSortDescriptor(key: "name", ascending: false)
            let sortDescriptors = [sortDescriptorRoutine]
            fetchRequest.sortDescriptors = sortDescriptors

            fetchRequest.returnsObjectsAsFaults = false

            var fetch: [EverKeneticExercise]?
            do
            {
///////////// CRASHES HERE /////////////////
                fetch = try context.fetch(fetchRequest)
            }
            catch _ as NSError
            {
                fetch = nil
            }
            if let result = fetch
            {
                if !result.isEmpty
                {
                    return result[0]
                }
            }
        }
        return nil
    }

It crashes with: Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Can't create support directory (can't create directory)' terminating with uncaught exception of type NSException

It's really hard to find code examples for how to do read-only app bundle databases. I would appreciate any help or direction.

Greg Robertson
  • 2,317
  • 1
  • 16
  • 30

1 Answers1

0

Hopefully this helps someone else .... turns out it was not in the code but in a core data setting. I eventually found this thread: Did iOS10 remove the ability to read a SQLite database from the bundle?

I removed the binary fetch indexes and now it works fine.

If anyone finds official documentation on this related to core data read only app bundles, post it and I will change answer to that.

Greg Robertson
  • 2,317
  • 1
  • 16
  • 30
  • Good to hear that your code is working, however that answer is not about binary fetch indexes. It's about binary attributes with external storage, which is something different. – Tom Harrington Mar 04 '21 at 18:26
  • Tom ... this issue is confusing me as to why one of my databases works and the other does not .... I added the folders as per the other thread and it is working now but I feel like it is a hack as I really don't know why it needs to applied. Can you explain? – Greg Robertson Mar 06 '21 at 11:48