带有核心数据的 iOS Today 扩展
Posted
技术标签:
【中文标题】带有核心数据的 iOS Today 扩展【英文标题】:iOS today extension with core data 【发布时间】:2020-03-01 14:15:52 【问题描述】:我正在尝试为我的 ios 应用程序进行今天的扩展。 今天的扩展将根据与核心数据一起保存的数据显示“下一个”课程。 我一直在做一些研究,我知道我必须与 appGroup 共享我的 persistentContainer。 所以我做到了:
为 ios 应用和今天的扩展目标启用了 appGroups。 编写了一个函数来分享它: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)
然后在我的核心数据堆栈中:
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)")
文件和 xcdatamodel 在两个目标之间共享。 在这一点上,我认为我可以从我的扩展中访问我的核心数据,但是当我执行获取请求时,我没有得到任何结果。 controllerDidChangeContent 永远不会被执行。
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")
我也尝试直接进行获取请求,但结果是一个空数组。 为什么我没有得到任何结果?
另一件事:每次从 ios 应用程序更新数据时,我使用 nsfetchedResult 控制器刷新视图数据,这样可以吗,还是应该使用 widgetPerformUpdate 方法?在那种情况下,我们不知道今天的扩展什么时候会被刷新,它可能会显示过时的数据。
使用的主要文档:https://www.avanderlee.com/swift/core-data-app-extension-data-sharing/
【问题讨论】:
您是否能够弄清楚如何将您的数据连接到您今天的扩展程序?我目前遇到了同样的问题 @fphhelp 你在使用 cloudKit 容器吗? 是的,我在用户打开 iCloud 时使用 CloudKit 容器,在关闭时使用常规 NSPersistentContainer 两者都可以使用云容器,尝试添加 yourStoreDescription.cloudKitContainerOptions = NSPersistentCloudKitContainerOptions(containerIdentifier: "yourContainerID") 【参考方案1】:您应该像下面这样子类化 NSPersistentCloudKitContainer,返回 defaultDirectoryURL()
的 App Group URL。然后在您的 CoreDataStack 中,使用 let container = GroupedPersistentCloudKitContainer(name: "SchoolCompanion")
。同时删除您对addToAppGroup(...)
的呼叫。您需要在 App 和 Extension 中实例化 GroupedPersistentCloudKitContainer,您还需要确保 GroupedPersistentCloudKitContainer 链接到两个 Target。
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!
...
【讨论】:
我在defaultDirectoryURL()
函数行上收到此错误:Overriding non-@objc declarations from extensions is not supported
。我完全使用了你写的东西,所以我不确定为什么会这样
@fphelp 我没有看到那个错误,但听起来你在扩展中声明了这个类。在您的应用程序中声明它,并通过将目标设置为两者来确保它在您的扩展程序中可访问。
我从小部件中休息了一下,因为它太难了,但现在我又回到了它,不得不重新编码所有东西。我刚刚将 GroupedPersistentCloudKitContainer 添加到我的代码中,但是当我尝试运行该应用程序时,我在 if !FileManager.default.fileExists(atPath: url!.path)
行收到一个错误,说它在展开 url 时不会出错。我相信这意味着网址为零。我进行了三次检查,我为URLStrings.group.rawValue
使用了正确的字符串。猜猜为什么会这样?
我在这里添加了一个新问题,这样更容易获得积分:***.com/questions/61845865/…【参考方案2】:
您的应用组的网址在这里:
let url = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: "SchoolCompanion)
sqlite 文件在那里。您不需要云容器。
【讨论】:
我的容器是 NSPersistentCloudKit 容器,这意味着它已同步到 iCloud。 在我帖子的链接中不是那样的。在这里,您将容器名称作为参数传递,但这应该是 appGroup 标识符,如所述以上是关于带有核心数据的 iOS Today 扩展的主要内容,如果未能解决你的问题,请参考以下文章
适用于 iOS Today 扩展的 Firebase 分析(小部件扩展)