iOS today extension with core data

佐手、 提交于 2020-05-23 10:07:05

问题


I am trying to make a today extension for my ios app. That today extension will display the 'next' Course based on data saved with core data. I've been doing some research and I understand I have to share my persistentContainer with an appGroup. So I did :

  • enabled appGroups for both ios app and today extension targets.
  • coded a function to share it :
public extension NSPersistentContainer {
    func addToAppGroup(id: String) {
        guard let fileContainer = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: id) else {
            fatalError("Shared file container could not be created.")
        }
        let storeURL = fileContainer.appendingPathComponent("\(self.name).sqlite")
        let storeDescription = NSPersistentStoreDescription(url: storeURL)
        self.persistentStoreDescriptions.append(storeDescription)
    }
}

then in my core data stack :

internal class CoreDataContainer {

    static var persistentContainer: NSPersistentContainer = {
        let container = NSPersistentCloudKitContainer(name: "SchoolCompanion")
        container.addToAppGroup(id: "group.com.Ce-dricLoneux.School-Companion")
        container.loadPersistentStores(completionHandler: { (_, error) in
            if let error = error as NSError? {
                fatalError("Unresolved error \(error), \(error.userInfo)")
            }
        })
        return container
    }()

    // MARK: - Core Data Saving support

    func saveContext () {
        let context = CoreDataContainer.persistentContainer.viewContext
        if context.hasChanges {
            do {
                try context.save()
            } catch {
                let nserror = error as NSError
                fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
            }
        }
    }
}

that files and the xcdatamodel are shared between both targets. At this point I thought I could access my core data from my extension but when i do a fetch request I don't get any result. The controllerDidChangeContent is never executed.

class TodayViewController: UIViewController, NCWidgetProviding {
private func configureFetchedResultsController() {
        let request: NSFetchRequest<Course> = Course.fetchRequest()
        request.sortDescriptors =  []
        self.fetchedResultsController = NSFetchedResultsController<Course>(fetchRequest: request, managedObjectContext: CoreDataContainer.persistentContainer.viewContext, sectionNameKeyPath: nil, cacheName: nil)
        self.fetchedResultsController.delegate = self
        do {
            try self.fetchedResultsController.performFetch()
        } catch {
            print(error.localizedDescription)
        }
    }

override func viewDidLoad() {
        super.viewDidLoad()

        self.extensionContext?.widgetLargestAvailableDisplayMode = .compact
        self.configureFetchedResultsController()

    }

func widgetPerformUpdate(completionHandler: (@escaping (NCUpdateResult) -> Void)) {
        // Perform any setup necessary in order to update the view.

        // If an error is encountered, use NCUpdateResult.Failed
        // If there's no update required, use NCUpdateResult.NoData
        // If there's an update, use NCUpdateResult.NewData

        completionHandler(NCUpdateResult.newData)
    }
}

// MARK: - FetchedResultsController Delegate

extension TodayViewController: NSFetchedResultsControllerDelegate {
    func controllerDidChangeContent(_ controller: NSFetchedResultsController<NSFetchRequestResult>) {
        print("content did change")
    }
}

I tried to make a fetch request direclty too, buti get an empty array as result. Why does I don't get any result ?

Another thing : i used nsfetchedResult controller in order to refresh the view data each time the data is updated from the ios app so is that ok or should I use the widgetPerformUpdate method ? In that case we don't know when the today extension will be refreshed and it may dispay outdated data.

main documentation used : https://www.avanderlee.com/swift/core-data-app-extension-data-sharing/


回答1:


You should subclass NSPersistentCloudKitContainer like below, returning the App Group URL for defaultDirectoryURL(). Then in your CoreDataStack, use let container = GroupedPersistentCloudKitContainer(name: "SchoolCompanion"). Also remove, your call to addToAppGroup(...). You will need to instantiate the GroupedPersistentCloudKitContainer in both the App and the Extension, you will also need to make sure the GroupedPersistentCloudKitContainer is linked to both Targets.

class GroupedPersistentCloudKitContainer: NSPersistentCloudKitContainer {

    enum URLStrings: String {
        case group = "group.com.yourCompany.yourApp"
    }


    override class func defaultDirectoryURL() -> URL {
        let url = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: URLStrings.group.rawValue)

        if !FileManager.default.fileExists(atPath: url!.path) {
            try? FileManager.default.createDirectory(at: url!, withIntermediateDirectories: true, attributes: nil)
        }
        return url!
    }
    ...
}



回答2:


The url for your app group is here:

let url = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "SchoolCompanion)

The sqlite-file is there. You don't need Cloud container.



来源:https://stackoverflow.com/questions/60476017/ios-today-extension-with-core-data

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!