如何将iPhone Core Data与Web服务器同步,然后推送到其他设备?
我一直在研究一种方法来同步多个设备(如iPad或Mac)之间存储在iPhone应用程序中的核心数据。 在iOS上使用Core Data的同步框架并不多(如果有的话)。 不过,我一直在想下面这个概念:
- 对本地核心数据存储进行更改,并保存更改。 (a)如果设备处于联机状态,它会尝试将更改集发送到服务器,包括发送更改集的设备的设备标识。 (b)如果变更集没有到达服务器,或者设备不在线,应用程序会将变更集添加到队列中,以便在联机时发送。
- 坐在云中的服务器将接收到的特定变更集合与其主数据库合并。
- 在云服务器上合并更改集(或更改集队列)之后,服务器会使用某种轮询系统将所有这些更改集推送到在服务器上注册的其他设备。 (我以为使用苹果的Push服务,但显然根据评论,这不是一个可行的系统。)
有什么我想要考虑的幻想? 我看过REST框架,如ObjectiveResource , Core Resource和RestfulCoreData 。 当然,这些都是和Ruby on Rails一起工作的,我并没有把它绑在里面,但是这是一个开始的地方。 我对我的解决scheme的主要要求是:
- 任何改变都应该在后台发送,而不要暂停主线程。
- 它应该使用尽可能less的带宽。
我曾考虑过一些挑战:
- 确保服务器上连接了不同设备上不同数据存储的对象ID。 也就是说,我将拥有一个对象ID和设备ID的表格,这些表格通过对存储在数据库中的对象的引用进行绑定。 我将有一个logging(DatabaseId [此表唯一的],ObjectId [唯一的整个数据库中的项],Datafield1,Datafield2),ObjectId字段将引用另一个表,所有对象:(ObjectId,DeviceId,DeviceObjectId)。 然后,当设备推送一个更改集时,它将从本地数据存储中的核心数据对象传递设备Id和objectId。 然后,我的云服务器将检查AllObjects表中的objectId和设备Id,并在初始表中查找要更改的logging。
- 所有更改都应该有时间戳,以便可以合并。
- 该设备将不得不轮询服务器,而不用消耗太多的电池。
- 如果/从服务器接收到更改,本地设备也将需要更新内存中的任何内容。
还有什么我在这里失踪? 我应该考虑什么样的框架来实现这一点?
我build议您仔细阅读并实施Dan Grover在iPhone 2009大会上讨论的同步策略, 这里可以通过pdf文档获得。
这是一个可行的解决scheme,并不难实现(Dan在其几个应用程序中实现了这一点),重叠了Chris所描述的解决scheme。 有关同步的深入的理论讨论,请参阅Russ Cox(MIT)和William Josephson(Princeton)的论文:
与vector时间对的文件同步
这同样适用于核心数据并做了一些明显的修改。 这提供了一个总体上更强大和更可靠的同步策略,但需要更多努力才能正确实施。
编辑:
Grover的pdf文件似乎不再可用(断开的链接,2015年3月)。 更新:链接可通过回机器在这里
由于iCloud最终似乎支持正确的核心数据同步,所以名为ZSync并由Marcus Zarra开发的Objective-C框架已被弃用。
我做了类似于你想做的事情。 让我告诉你我学到了什么,是怎么做到的。
我假设你的核心数据对象和服务器上的模型(或数据库模式)之间有一对一的关系。 您只需保持服务器内容与客户端同步,但客户端也可以修改和添加数据。 如果我说得对,那么继续阅读。
我添加了四个字段来协助同步:
- sync_status – 仅将此字段添加到您的核心数据模型。 它被应用程序用来确定您是否有一个项目的待定更改。 我使用下面的代码:0意味着没有改变,1意味着它排队等待与服务器同步,2意味着它是一个临时对象,可以清除。
- is_deleted – 将其添加到服务器和核心数据模型。 删除事件不应该实际上从数据库或从客户端模型中删除一行,因为它没有任何东西可以同步回去。 有了这个简单的布尔标志,你可以设置is_deleted为1,同步它,每个人都会很高兴。 您还必须修改服务器和客户端上的代码,以“is_deleted = 0”查询未删除的项目。
- last_modified – 将其添加到服务器和核心数据模型。 每当在该logging上发生任何变化时,该字段应该自动地由服务器更新当前的date和时间。 它不应该由客户修改。
- guid – 在服务器和核心数据模型中添加一个全局唯一的ID(请参阅http://en.wikipedia.org/wiki/Globally_unique_identifier )字段。 该字段成为主键,在客户端上创build新logging时变得重要。 通常情况下,您的主键是服务器上递增的整数,但是我们必须记住,可以离线创build内容并稍后进行同步。 GUID允许我们在离线状态下创build密钥。
在客户端上,当模型对象发生更改并需要同步到服务器时,添加代码以将sync_status设置为1。 新模型对象必须生成一个GUID。
同步是一个单一的请求。 该请求包含:
- 模型对象的MAX last_modified时间戳。 这告诉服务器你只需要在这个时间戳之后进行修改。
- 包含sync_status = 1的所有项目的JSON数组。
服务器获取请求并执行此操作:
- 它从JSON数组中获取内容并修改或添加它包含的logging。 last_modified字段会自动更新。
- 服务器返回一个JSON数组,其中包含所有对象的last_modified时间戳大于请求中发送的时间戳。 这将包括它刚刚收到的对象,作为确认logging已成功同步到服务器的确认。
应用程序收到回应,并执行此操作:
- 它从JSON数组中获取内容并修改或添加它包含的logging。 每个logging设置一个sync_status为0。
我希望有帮助。 我可以互换地使用logging和模型这个词,但是我想你会明白的。 祝你好运。
如果您仍然在寻找一条出路,请查看Couchbase移动版。 这基本上是你想要的。 ( http://www.couchbase.com/nosql-databases/couchbase-mobile )
类似@Cris我已经实现了客户端和服务器之间的同步类,并解决了迄今为止所有已知的问题(从/向服务器发送/接收数据,基于时间戳合并冲突,在不可靠的networking条件下删除重复条目,同步嵌套数据和文件等..)
你只要告诉类应该同步哪个实体和哪些列,以及你的服务器在哪里。
M3Synchronization * syncEntity = [[M3Synchronization alloc] initForClass: @"Car" andContext: context andServerUrl: kWebsiteUrl andServerReceiverScriptName: kServerReceiverScript andServerFetcherScriptName: kServerFetcherScript ansSyncedTableFields:@[@"licenceNumber", @"manufacturer", @"model"] andUniqueTableFields:@[@"licenceNumber"]]; syncEntity.delegate = self; // delegate should implement onComplete and onError methods syncEntity.additionalPostParamsDictionary = ... // add some POST params to authenticate current user [syncEntity sync];
您可以在这里find源代码,工作示例和更多说明: github.com/knagode/M3Synchronization 。
注意用户通过推送通知更新数据。 在应用程序中使用后台线程来检查本地数据和云服务器上的数据,而在服务器上发生变化时,请更改本地数据,反之亦然。
所以我觉得最难的部分是估计哪一方无效的数据。
希望这可以帮助你
我刚刚发布了新的核心数据云同步API的第一个版本,即SynCloud。 SynCloud与iCloud有很多不同,因为它允许多用户同步界面。 它与其他同步API也不同,因为它允许多表,关系数据。
请通过http://www.syncloudapi.com了解更多信息;
使用iOS 6 SDK进行构build,截至2012年9月27日为止最新。
我认为一个很好的解决scheme的GUID问题是“分布式ID系统”。 我不确定什么是正确的术语,但我认为这就是MS SQL服务器文档用来调用它(SQL使用/使用此方法分布式/同步数据库)。 这很简单:
服务器分配所有ID。 每次同步完成后,首先检查的是“我在此客户端上剩余了多less个ID?” 如果客户端运行不正常,则向服务器请求一个新的ID块。 客户端然后使用该范围中的ID作为新logging。 这对于大多数需求来说是非常有效的,如果你可以分配一个足够大的块,在下次同步之前“永远”不会耗尽,但不会太长,以至于服务器会随着时间的推移而耗尽。 如果客户端耗尽,处理可能非常简单,只是告诉用户“抱歉,您不能添加更多的项目,直到你同步”…如果他们添加很多项目,不应该同步,以避免陈旧的数据问题呢?
我认为这比使用随机的GUID更好,因为随机的GUID不是100%安全的,通常需要比标准ID(128位和32位)长得多。 您通常通过身份证索引,并经常保持在内存中的身份证号码,所以重要的是要保持他们的小。
真的不想作为回答发布,但我不知道有谁会看到作为评论,我认为这个主题是重要的,不包括在其他答案。
首先,您应该重新考虑您将拥有多less数据,表格和关系。 在我的解决scheme中,我通过Dropbox文件实现了同步。 我观察主MOC中的变化并将这些数据保存到文件(每行保存为gzip json)。 如果有一个互联网连接工作,我检查是否有任何Dropbox的变化(Dropbox给我增量变化),下载并合并(最新的胜利),最后把更改的文件。 在同步之前,我把锁文件放在Dropbox上,以防止其他客户端同步不完整的数据。 当下载更改时,只有部分数据被下载(例如,互联网连接丢失)是安全的。 当下载完成(完全或部分),它开始加载文件到核心数据。 当没有解决的关系(并非所有文件都下载)时,它将停止加载文件,并尝试稍后完成下载。 关系仅作为GUID存储,所以我可以轻松地检查要加载的文件是否具有完整的数据完整性。 在对核心数据进行更改之后,正在开始同步。 如果没有更改,则每隔几分钟在应用程序启动时检查Dropbox上的更改。 另外,当更改发送到服务器时,我会向其他设备发送广播以通知其更改,以便它们可以更快地同步。 每个已同步的实体都具有GUID属性(guid也用作交换文件的文件名)。 我也有同步数据库,我存储每个文件的Dropbox修订版(我可以比较Dropbox德尔塔重置它的状态)。 文件还包含实体名称,状态(已删除/未删除),guid(与文件名相同),数据库修订(检测数据迁移或避免同步从未应用程序版本),当然还有数据(如果行未被删除)。
这个解决scheme为数千个文件和大约30个实体工作。 现在,在我看来,我的解决scheme比iCloud更加可靠,而且这非常重要,我完全控制它是如何工作的(主要是因为它是我自己的代码)。
另一个解决scheme是将MOC更改保存为事务 – 与服务器交换的文件将less得多,但是按照正确的顺序对空白的核心数据进行初始加载比较困难。 iCloud正在以这种方式工作,而其他同步解决scheme也有类似的方法,例如TICoreDataSync 。
– 更新
过了一段时间,我转移到了Ensembles – 我推荐这个解决scheme重新发明轮子。