ioscore-dataios8-today-widget

coredata - move to app group target


I am new to the Today extension and using an embedded framework.

Our app currently uses core data backed by sqlite. If I want to share this between the app and the today extension, should I move this to a framework to be shared by both?

How can I migrate our current version in the app store to be able to upgrade to a new structure?


Solution

  • In case someone wants the solution in swift just add below function in didFinishLaunchingWithOptions.

     func migratePersistentStore(){
    
        let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
        var storeOptions = [AnyHashable : Any]()
        storeOptions[NSMigratePersistentStoresAutomaticallyOption] = true
        storeOptions[NSInferMappingModelAutomaticallyOption] = true
        let oldStoreUrl = self.applicationDocumentsDirectory.appendingPathComponent("YourApp.sqlite")!
        let newStoreUrl = self.applicationGroupDirectory.appendingPathComponent("YourApp.sqlite")!
        var targetUrl : URL? = nil
        var needMigrate = false
        var needDeleteOld = false
    
        if FileManager.default.fileExists(atPath: oldStoreUrl.path){
            needMigrate = true
            targetUrl = oldStoreUrl
        }
    
        if FileManager.default.fileExists(atPath: newStoreUrl.path){
            needMigrate = false
            targetUrl = newStoreUrl
    
            if FileManager.default.fileExists(atPath: oldStoreUrl.path){
                needDeleteOld = true
            }
        }
        if targetUrl == nil {
            targetUrl = newStoreUrl
        }
        if needMigrate {
            do {
                try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: targetUrl!, options: storeOptions)
                if let store = coordinator.persistentStore(for: targetUrl!) 
                 {
                    do {
                        try coordinator.migratePersistentStore(store, to: newStoreUrl, options: storeOptions, withType: NSSQLiteStoreType)
    
                    } catch let error {
                        print("migrate failed with error : \(error)")
                    }
                }
            } catch let error {
                CrashlyticsHelper.reportCrash(err: error as NSError, strMethodName: "migrateStore")
            }
        }
      if needDeleteOld {
            DBHelper.deleteDocumentAtUrl(url: oldStoreUrl)
            guard let shmDocumentUrl = self.applicationDocumentsDirectory.appendingPathComponent("NoddApp.sqlite-shm") else { return }
            DBHelper.deleteDocumentAtUrl(url: shmDocumentUrl)
            guard let walDocumentUrl = self.applicationDocumentsDirectory.appendingPathComponent("NoddApp.sqlite-wal") else { return }
            DBHelper.deleteDocumentAtUrl(url: walDocumentUrl)
        }
    }
    

    My PersistentStoreCoordinator Looks like this:

    lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator = {
        let coordinator = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)
        let url = self.applicationGroupDirectory.appendingPathComponent("YourApp.sqlite")
        var storeOptions = [AnyHashable : Any]()
        storeOptions[NSMigratePersistentStoresAutomaticallyOption] = true
        storeOptions[NSInferMappingModelAutomaticallyOption] = true
        var failureReason = "There was an error creating or loading the application's saved data."
        do {
            try coordinator.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options:storeOptions)
        } catch {
            // Report any error we got.
            var dict = [String: AnyObject]()
            dict[NSLocalizedDescriptionKey] = "Failed to initialize the application's saved data" as AnyObject?
            dict[NSLocalizedFailureReasonErrorKey] = failureReason as AnyObject?
    
            dict[NSUnderlyingErrorKey] = error as NSError
            let wrappedError = NSError(domain: "YOUR_ERROR_DOMAIN", code: 9999, userInfo: dict)
            NSLog("Unresolved error \(wrappedError), \(wrappedError.userInfo)")
            abort()
        }
        return coordinator
    }()
    

    This is the case when there is already an app in appstore and you want to migrate the coreData persistent store file from your default store location to your App Group Location.

    Edit : For Deleting the file from the old location it is recommended that we use NSFileCoordinator to perform the task.

    static func deleteDocumentAtUrl(url: URL){
        let fileCoordinator = NSFileCoordinator(filePresenter: nil)
        fileCoordinator.coordinate(writingItemAt: url, options: .forDeleting, error: nil, byAccessor: {
            (urlForModifying) -> Void in
            do {
                try FileManager.default.removeItem(at: urlForModifying)
            }catch let error {
                print("Failed to remove item with error: \(error.localizedDescription)")
            }
        })
    }
    

    Please note the reason why NSFileCoordinator is used to delete the file is because NSFileCoordinator allows us to ensure that file related tasks such as opening reading writing are done in such a way that wont interfere with anyother task on the system trying to work with the same file.Eg if you want to open a file and at the same time it gets deleted ,you dont want both the actions to happen at the same time.

    Please call the above function after the store is successfully migrated.