简体   繁体   English

如何保证主机应用程序和扩展程序使用的共享应用程序容器中的Core Data存储中的唯一条目?

[英]How can I guarantee unique entries in a Core Data store in a shared app container used by both the host app and an extension?

To ask my question effectively, let's first consider the exact scenario I'm facing: 为了有效地提出我的问题,我们首先考虑一下我面临的确切情况:

General Setup 常规设置

  • A host iOS 8 app. 主机iOS 8应用程序。
  • One or more iOS 8 extensions (WatchKit, Share, etc.) bundled with the host app. 与主机应用程序捆绑在一起的一个或多个iOS 8扩展(WatchKit,Share等)。
  • The host app and all extensions share the same Core Data SQLite store in the shared app group container. 主机应用程序和所有扩展程序在共享应用程序组容器中共享相同的Core Data SQLite存储。
  • Each app/extension has its own NSPersistentStoreCoordinator and NSManagedObjectContext. 每个app / extension都有自己的NSPersistentStoreCoordinator和NSManagedObjectContext。
  • Each persistent store coordinator uses a persistent store that shares the same SQLite resources in the group container as all the other persistent stores. 每个持久性存储协调器使用持久性存储,该存储在组容器中与所有其他持久性存储共享相同的SQLite资源。
  • The app and all extensions use a common codebase for syncing content from a remote API resource on the Internet. 应用程序和所有扩展使用公共代码库来同步Internet上远程API资源的内容。

Sequence of Events Leading to the Problem 导致问题的事件顺序

  1. The user launches the host app. 用户启动主机应用程序。 It begins fetching data from the remote API resource. 它开始从远程API资源获取数据。 Core data model objects are created based on the API response and "upserted" into the host app's managed object context. 核心数据模型对象基于API响应创建,并“upserted”到主机应用程序的托管对象上下文中。 Each API entity has a uniqueID that identifies it in the remote API backend. 每个API实体都有一个uniqueID,用于在远程API后端中标识它。 By "upsert," I mean that for each API entity, the host app only creates a new entry in Core Data if an existing entry for a given uniqueID cannot be found. 通过“upsert”,我的意思是,对于每个API实体,主机应用程序仅在无法找到给定唯一ID的现有条目时在Core Data中创建新条目。

  2. Meanwhile, the user also launches one of the host app's extensions. 同时,用户还启动了一个主机应用程序的扩展。 It, too, performs some kind of fetch from the same remote API. 它也可以从同一个远程API执行某种提取。 It also attempts to perform an "upsert" when parsing the API responses. 它还尝试在解析API响应时执行“upsert”。

  3. The Problem: What happens if both the host app and an extension try to upsert a Core Data entry for the same API entity at the same time? 问题:如果主机应用程序和扩展程序同时尝试为同一API实体插入Core Data条目,会发生什么? To see how this could come about, let's look at the sequence of events for an upsert: 要了解这是如何产生的,让我们看看upsert的事件序列:

Core Data Upsert Sequence: 核心数据Upsert序列:

  1. The API parsing code parses the uniqueID for a given API entity. API解析代码解析给定API实体的uniqueID。
  2. The parser performs a Core Data fetch for any entry that matches a predicate where uniqueID is equal to the parsed uniqueID. 解析器对与谓词匹配的任何条目执行核心数据提取,其中uniqueID等于解析的唯一ID。
  3. If an existing entry is not found, the parser inserts a new Core Data entry for this API entity, set's its uniqueID attribute to the parsed uniqueID. 如果未找到现有条目,则解析器会为此API实体插入新的Core Data条目,并将其uniqueID属性设置为已解析的uniqueID。
  4. The parser saves the managed object context, which pushes the new entry data down to the SQLite backing store. 解析器保存托管对象上下文,该上下文将新条目数据下推到SQLite后备存储。

Problem in Detail 问题详情

Let's assume the host app and the extension are independently parsing an API response for the same API entity at the same time. 假设主机应用程序和扩展程序同时独立地解析同一API实体的API响应。 If both the host app and an extension reach Step 3 before either of them has finished Step 4, then they will both be trying to insert a new Core Data entry for the same uniqueID. 如果主机应用程序和扩展程序在它们中的任何一个完成步骤4之前到达步骤3,则它们都将尝试为相同的唯一ID插入新的核心数据条目。 When they reach Step 4 and call save: on their respective managed object contexts, Core Data will happily create duplicate entries. 当他们到达步骤4并在各自的托管对象上下文中调用save:时,Core Data将很乐意创建重复的条目。

As far as I'm aware, Core Data doesn't have any way to mark an attribute as unique. 据我所知,Core Data没有任何方法可以将属性标记为唯一。 I need a Core Data equivalent to a SQLite INSERT OR IGNORE + UPDATE combo. 我需要一个等同于SQLite INSERT OR IGNORE + UPDATE组合的核心数据 . Or else I need a way to "lock" the persistent store's SQLite backing store, which sounds like a recipe for trouble. 或者我需要一种方法来“锁定”持久存储的SQLite后备存储,这听起来像是一个麻烦的方法。

Is there a known approach to this rather novel problem introduced by iOS 8 extensions? 是否有一种已知的方法来解决iOS 8扩展引入的这个相当新颖的问题?

It seems like the simplest approach to this would be to simply avoid the multiple writers in the first place. 看起来最简单的方法就是首先简单地避免使用多个编写器。 Why not just drive your extensions entirely off cached data, and then only update your data store from your primary iOS app? 为什么不完全从缓存数据驱动扩展,然后只从主iOS应用程序更新您的数据存储?

Is there a known approach to this rather novel problem introduced by iOS 8 extensions? 是否有一种已知的方法来解决iOS 8扩展引入的这个相当新颖的问题?

Yes, it's the same approach that applies when using iCloud with Core Data: let the duplicates happen, but then go and clean them up. 是的,这与将iCloud与Core Data一起使用时采用的方法相同:让重复发生,然后再进行清理。 Both situations run the risk of creating duplicate entries, and there's no completely reliable way to prevent them. 这两种情况都存在创建重复条目的风险,并且没有完全可靠的方法来阻止它们。 Since you have a uniqueID key, you're in good shape as far as this is concerned. 由于您拥有uniqueID键,因此就此而言您处于良好状态。

It would be a lot easier, as Dave DeLong notes, to avoid the problem in the first place. 正如Dave DeLong指出的那样,首先要避免这个问题会容易得多。 If that's impossible, you can deal with it, with some extra work. 如果这是不可能的,你可以通过一些额外的工作来处理它。

Finding duplicates would be something like: 找到重复的东西是这样的:

NSError *error = nil;
NSManagedObjectContext *moc = [[NSManagedObjectContext alloc] init];
[moc setPersistentStoreCoordinator:self.persistentStoreCoordinator];

NSFetchRequest *fr = [[NSFetchRequest alloc] initWithEntityName:@"MyEntityName"];
[fr setIncludesPendingChanges:NO];

NSExpression *countExpr = [NSExpression expressionWithFormat:@"count:(uniqueID)"];
NSExpressionDescription *countExprDesc = [[NSExpressionDescription alloc] init];
[countExprDesc setName:@"count"];
[countExprDesc setExpression:countExpr];
[countExprDesc setExpressionResultType:NSInteger64AttributeType];

NSAttributeDescription *uniqueIDAttr = [[[[[_psc managedObjectModel] entitiesByName] objectForKey:@"MyEntityName"] propertiesByName] objectForKey:@"uniqueID"];
[fr setPropertiesToFetch:[NSArray arrayWithObjects:uniqueIDAttr, countExprDesc, nil]];
[fr setPropertiesToGroupBy:[NSArray arrayWithObject:uniqueIDAttr]];

[fr setResultType:NSDictionaryResultType];

NSArray *countDictionaries = [moc executeFetchRequest:fr error:&error];

This is pretty much the Core Data equivalent of something like this in SQL: 这几乎就是SQL中类似这样的核心数据:

SELECT uniqueID, COUNT(uniqueID) FROM MyEntityName GROUP BY uniqueID;

You get an array of dictionaries, each of which contains a uniqueID and a count of the number of times that value is used. 您将获得一个字典数组,每个字典都包含唯一uniqueID和使用该值的次数。 Run through the dictionary and deal with duplicates appropriately. 运行字典并适当处理重复项。

I described this in more detail in a blog post . 我在博文中更详细地描述了这一点。 There's also a sample project from Apple that demonstrates the process, called SharedCoreData, but I believe it's only available as part of the WWDC 2012 sample code bundle . 还有一个来自Apple的示例项目,演示了名为SharedCoreData的过程,但我相信它只作为WWDC 2012示例代码包的一部分提供 It was also described in session 227 at that conference. 该会议的会议227也对此进行了描述。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 从容器应用程序和扩展程序访问核心数据 - Accessing Core Data from both container app and extension iOS 11+ 如何将现有核心数据迁移到共享应用组以用于扩展? - iOS 11+ How to migrate existing Core Data to Shared App Group for use in extension? 调试 iOS 应用时如何查看 App Group Shared Container 目录的内容? - How can I view the contents of the App Group Shared Container directory when debugging an iOS app? 如何通过 App Store 将数据传递到下载的应用程序? - How can I pass data through the App Store to a downloaded app? 如何观察Core Data持久存储在包含应用程序和扩展程序之间的变化 - How to observe Core Data persistent store changing between containing app and extension 如何为iOS应用程序设置源数据来托管数据 - How can I setup a source to host data for an iOS app 如何创建/使用共享应用程序组容器作为包含应用程序及其在ios中的扩展名之间的缓存存储 - How to create/use a shared app group container as cache storage between containing app and it's extension in ios WatchKit扩展:如何从主机iOS应用读取数据? - WatchKit extension: how to read data from host iOS app? 如何在 iOS 中的扩展和主机应用程序之间共享后删除数据 - How to remove data after share between extension and host app in iOS 如何使用已在 App Store 中的应用测试核心数据迁移? - How to Test Core Data Migration With an App Already in the App Store?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM