在iOS 8扩展中访问核心数据SQL数据库(在应用程序和小部件扩展之间共享数据)

34

问题:

无法从Today View小部件中访问应用程序的Core Data数据库。

在iOS 8下,应用程序本身能够像往常一样读写数据库,但扩展程序将无法创建存储,并显示错误“无法写入文件”。

日志如下:

Error Domain=NSCocoaErrorDomain Code=512 "The operation couldn’t be completed. (Cocoa error 512.)"

reason = "Failed to create file; code = 2
4个回答

68

小部件无法访问NSDocuments目录,这通常是存储数据库的位置。

解决方法是首先创建一个应用程序组。

前往: 项目 - 目标 - 应用程序组 - 添加新容器

为容器命名,例如:'group.mycontainer'。

为小部件的目标重复此过程,使用相同的容器名称。

然后将您的数据库写入您的组容器中。

因此:

NSURL *storeURL = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory  inDomains:NSAllDomainsMask] lastObject];
storeURL = [storeURL URLByAppendingPathComponent:@"db.sqlite"];

成为:

NSURL *storeURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.mycontainer"];
storeURL = [storeURL URLByAppendingPathComponent:@"db.sqlite"];

初始化存储应该像这样:

NSURL *storeURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.mycontainer"];
storeURL = [storeURL URLByAppendingPathComponent:@"db.sqlite"];

NSPersistentStore *store = nil;
store = [coordinator addPersistentStoreWithType:NSSQLiteStoreType
                                  configuration:nil
                                            URL:storeURL
                                        options:nil
                                          error:&error]

2
但是我该如何获取协调器呢?我需要模型(momd)吗?如果是,我该如何与扩展共享模型? - Ben
1
@BenjaminHerzog,你可以分享momd文件,只需将其添加到扩展目标和容器应用程序目标中即可。 - mokagio
我有点困惑,怎样才能让这个在应用程序中运行起来?我在扩展中可以用NSFileManager这种方式,但在主应用程序中就不行了。这会返回nil,而扩展却不会。 - Phong Le
只有在调试器上运行时,它才能在我的设备上正常工作。添加钩子构建会立即崩溃。 - Awesome-o
7
@Mark 数据迁移场景如何工作?想象一下,在 iOS 8 之前,应用程序将数据存储在原始的 storeURL 中,现在一个新版本必须首先在新的 storeURL 中创建新文件,然后再将原始数据迁移到新的 store。 - Sean Dong
显示剩余6条评论

7

刚刚发现,使用标准iOS备份程序时,不会备份应用组文件。

请记住,如果你将持久存储保留在应用组容器中,则用户在恢复iOS后可能会丢失所有应用数据。

更新

rdar://18750178

更新

看起来在iOS 8.1中已经修复了,苹果的工作人员给我发消息要求我在iOS 8.1中检查问题是否已经解决(相当无礼,不是吗?)。我还没有测试过,所以请注意。无论如何,在支持有缺陷的iOS 8.0的情况下,将存储保留在AppGroups中是一个不好的想法。


这个有文档记录吗? - Awesome-o
你所说的标准备份程序是指通过iTunes进行备份,对吗? - Awesome-o
@Awesome-o 可以通过iTunes和iCloud进行备份。似乎没有文档记录。找不到任何相关信息。 - kas-kad
1
这对我来说似乎是一个错误,请提交错误报告。 - Awesome-o
有没有一个RDAR ID可以复制? - choise
一些最近的消息在答案里。 - kas-kad

5

更改

[MagicalRecord setupCoreDataStackWithStoreNamed:@"Database"];

为了

 - (void)setupCoreDataStack
{
     if ([NSPersistentStoreCoordinator MR_defaultStoreCoordinator] != nil)
     {
        return;
    }

    NSManagedObjectModel *model = [NSManagedObjectModel MR_defaultManagedObjectModel];
    NSPersistentStoreCoordinator *psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];

    NSURL *storeURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:@"group.yourgroup"];
    storeURL = [storeURL URLByAppendingPathComponent:@"Database.sqlite"];

    [psc MR_addSqliteStoreNamed:storeURL withOptions:nil];
    [NSPersistentStoreCoordinator MR_setDefaultStoreCoordinator:psc];
    [NSManagedObjectContext MR_initializeDefaultContextWithCoordinator:psc];
}

1

同样适用于Swift:

private func setupCoreDataStack() {

    if NSPersistentStoreCoordinator.MR_defaultStoreCoordinator() != nil {
        return
    }

    let managedObjectModel = NSManagedObjectModel.MR_defaultManagedObjectModel()
    let persistentStoreCoordinator = NSPersistentStoreCoordinator(managedObjectModel: managedObjectModel)
    var storePath = NSFileManager.defaultManager().containerURLForSecurityApplicationGroupIdentifier(PBOSharedSuiteGroupName)
    storePath = storePath!.URLByAppendingPathComponent("AppName.sqlite")

    var error: NSError?
    persistentStoreCoordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storePath, options: nil, error: &error)
    NSPersistentStoreCoordinator.MR_setDefaultStoreCoordinator(persistentStoreCoordinator)
    NSManagedObjectContext.MR_initializeDefaultContextWithCoordinator(persistentStoreCoordinator)
}

请记得将此方法附加到AppDelegateToday Extension两个文件中。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接