ios 如何将 iPhone Core Data 与 Web 服务器同步,然后推送到其他设备?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5035132/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-30 18:55:07  来源:igfitidea点击:

How to Sync iPhone Core Data with web server, and then push to other devices?

iphoneioscore-datasyncdata-synchronization

提问by Jason

I have been working on a method to sync core data stored in an iPhone application between multiple devices, such as an iPad or a Mac. There are not many (if any at all) sync frameworks for use with Core Data on iOS. However, I have been thinking about the following concept:

我一直在研究一种在多个设备(例如 iPad 或 Mac)之间同步 iPhone 应用程序中存储的核心数据的方法。iOS 上用于 Core Data 的同步框架并不多(如果有的话)。但是,我一直在思考以下概念:

  1. A change is made to the local core data store, and the change is saved. (a) If the device is online, it tries to send the changeset to the server, including the device ID of the device which sent the changeset. (b) If the changeset does not reach the server, or if the device is not online, the app will add the change set to a queue to send when it does come online.
  2. The server, sitting in the cloud, merges the specific change sets it receives with its master database.
  3. After a change set (or a queue of change sets) is merged on the cloud server, the server pushes all of those change sets to the other devices registered with the server using some sort of polling system. (I thought to use Apple's Push services, but apparently according to the comments this is not a workable system.)
  1. 对本地核心数据存储进行更改,并保存更改。(a) 如果设备在线,它会尝试将变更集发送到服务器,包括发送变更集的设备的设备 ID。(b) 如果变更集没有到达服务器,或者如果设备不在线,应用程序会将变更集添加到队列中,以便在它上线时发送。
  2. 位于云中的服务器将其接收到的特定更改集与其主数据库合并。
  3. 在云服务器上合并变更集(或变更集队列)后,服务器使用某种轮询系统将所有这些变更集推送到向服务器注册的其他设备。(我想使用 Apple 的 Push 服务,但显然根据评论这不是一个可行的系统。)

Is there anything fancy that I need to be thinking about? I have looked at REST frameworks such as ObjectiveResource, Core Resource, and RestfulCoreData. Of course, these are all working with Ruby on Rails, which I am not tied to, but it's a place to start. The main requirements I have for my solution are:

有什么花哨的东西我需要考虑吗?我看过 REST 框架,例如ObjectiveResourceCore ResourceRestfulCoreData。当然,这些都与 Ruby on Rails 一起工作,我不依赖于它,但它是一个起点。我对解决方案的主要要求是:

  1. Any changes should be sent in the background without pausing the main thread.
  2. It should use as little bandwidth as possible.
  1. 任何更改都应在后台发送,而不会暂停主线程。
  2. 它应该使用尽可能少的带宽。

I have thought about a number of the challenges:

我考虑了一些挑战:

  1. Making sure that the object IDs for the different data stores on different devices are attached on the server. That is to say, I will have a table of object IDs and device IDs, which are tied via a reference to the object stored in the database. I will have a record (DatabaseId [unique to this table], ObjectId [unique to the item in the whole database], Datafield1, Datafield2), the ObjectId field will reference another table, AllObjects: (ObjectId, DeviceId, DeviceObjectId). Then, when the device pushes up a change set, it will pass along the device Id and the objectId from the core data object in the local data store. Then my cloud server will check against the objectId and device Id in the AllObjects table, and find the record to change in the initial table.
  2. All changes should be timestamped, so that they can be merged.
  3. The device will have to poll the server, without using up too much battery.
  4. The local devices will also need to update anything held in memory if/when changes are received from the server.
  1. 确保不同设备上不同数据存储的对象 ID 附加在服务器上。也就是说,我将有一个对象 ID 和设备 ID 表,它们通过对存储在数据库中的对象的引用进行绑定。我将有一个记录(DatabaseId [此表唯一]、ObjectId [整个数据库中的项目唯一]、Datafield1、Datafield2),ObjectId 字段将引用另一个表 AllObjects: (ObjectId, DeviceId, DeviceObjectId)。然后,当设备上推一个变更集时,它会传递设备 ID 和来自本地数据存储中的核心数据对象的 objectId。然后我的云服务器会对照AllObjects表中的objectId和deviceId,在初始表中找到要改变的记录。
  2. 所有更改都应该加上时间戳,以便它们可以合并。
  3. 设备必须轮询服务器,而不会消耗太多电池。
  4. 如果/当从服务器收到更改时,本地设备还需要更新内存中保存的任何内容。

Is there anything else I am missing here? What kinds of frameworks should I look at to make this possible?

还有什么我在这里想念的吗?我应该看看什么样的框架才能使这成为可能?

采纳答案by Massimo Cafaro

I suggest carefully reading and implementing the sync strategy discussed by Dan Grover at iPhone 2009 conference, available hereas a pdf document.

我建议仔细阅读并实施 Dan Grover 在 iPhone 2009 会议上讨论的同步策略,这里以 pdf 文档的形式提供。

This is a viable solution and is not that difficult to implement (Dan implemented this in several of its applications), overlapping the solution described by Chris. For an in-depth, theoretical discussion of syncing, see the paper from Russ Cox (MIT) and William Josephson (Princeton):

这是一个可行的解决方案,并且实施起来并不困难(Dan 在其几个应用程序中实现了这一点),与 Chris 描述的解决方案重叠。有关同步的深入理论讨论,请参阅 Russ Cox(麻省理工学院)和 William Josephson(普林斯顿大学)的论文:

File Synchronization with Vector Time Pairs

文件同步与向量时间对

which applies equally well to core data with some obvious modifications. This provides an overall much more robust and reliable sync strategy, but requires more effort to be implemented correctly.

这同样适用于经过一些明显修改的核心数据。这提供了一个整体上更加健壮和可靠的同步策略,但需要更多的努力才能正确实施。

EDIT:

编辑:

It seems that the Grover's pdf file is no longer available (broken link, March 2015). UPDATE: the link is available through the Way Back Machine here

Grover 的 pdf 文件似乎不再可用(断开的链接,2015 年 3 月)。UPDATE:该链接可通过返回的途中机在这里

The Objective-C framework called ZSyncand developed by Marcus Zarra has been deprecated, given that iCloud finally seems to support correct core data synchronization.

考虑到 iCloud 似乎终于支持正确的核心数据同步,由 Marcus Zarra 开发的名为ZSync的 Objective-C 框架已被弃用。

回答by chris

I've done something similar to what you're trying to do. Let me tell you what I've learned and how I did it.

我已经做了类似于你想要做的事情。让我告诉你我学到了什么以及我是如何做到的。

I assume you have a one-to-one relationship between your Core Data object and the model (or db schema) on the server. You simply want to keep the server contents in sync with the clients, but clients can also modify and add data. If I got that right, then keep reading.

我假设你有你的核心数据对象和服务器上的模型(或DB模式)之间的一个一对一的关系。您只是想让服务器内容与客户端同步,但客户端也可以修改和添加数据。如果我猜对了,请继续阅读。

I added four fields to assist with synchronization:

我添加了四个字段来帮助同步:

  1. sync_status- Add this field to your core data model only. It's used by the app to determine if you have a pending change on the item. I use the following codes: 0 means no changes, 1 means it's queued to be synchronized to the server, and 2 means it's a temporary object and can be purged.
  2. is_deleted- Add this to the server and core data model. Delete event shouldn't actually delete a row from the database or from your client model because it leaves you with nothing to synchronize back. By having this simple boolean flag, you can set is_deleted to 1, synchronize it, and everyone will be happy. You must also modify the code on the server and client to query non deleted items with "is_deleted=0".
  3. last_modified- Add this to the server and core data model. This field should automatically be updated with the current date and time by the server whenever anything changes on that record. It should never be modified by the client.
  4. guid- Add a globally unique id (see http://en.wikipedia.org/wiki/Globally_unique_identifier) field to the server and core data model. This field becomes the primary key and becomes important when creating new records on the client. Normally your primary key is an incrementing integer on the server, but we have to keep in mind that content could be created offline and synchronized later. The GUID allows us to create a key while being offline.
  1. sync_status- 仅将此字段添加到您的核心数据模型中。应用程序使用它来确定您是否对项目有待处理的更改。我使用以下代码:0 表示没有更改,1 表示它已排队等待同步到服务器,2 表示它是一个临时对象,可以清除。
  2. is_deleted- 将此添加到服务器和核心数据模型。删除事件实际上不应该从数据库或您的客户端模型中删除一行,因为它会让您没有任何东西可以同步回来。通过使用这个简单的布尔标志,您可以将 is_deleted 设置为 1,进行同步,每个人都会很高兴。您还必须修改服务器和客户端上的代码,以使用“is_deleted=0”查询未删除的项目。
  3. last_modified- 将此添加到服务器和核心数据模型。每当该记录发生任何变化时,服务器都应使用当前日期和时间自动更新此字段。它永远不应该被客户端修改。
  4. guid-向服务器和核心数据模型添加全局唯一 id(参见http://en.wikipedia.org/wiki/Globally_unique_identifier)字段。此字段成为主键,并在客户端上创建新记录时变得重要。通常,您的主键是服务器上的递增整数,但我们必须记住,内容可以离线创建并稍后同步。GUID 允许我们在离线时创建密钥。

On the client, add code to set sync_status to 1 on your model object whenever something changes and needs to be synchronized to the server. New model objects must generate a GUID.

在客户端上,每当发生变化并需要同步到服务器时,添加代码以将模型对象上的 sync_status 设置为 1。新模型对象必须生成 GUID。

Synchronization is a single request. The request contains:

同步是单个请求。该请求包含:

  • The MAX last_modified time stamp of your model objects. This tells the server you only want changes after this time stamp.
  • A JSON array containing all items with sync_status=1.
  • 模型对象的 MAX last_modified 时间戳。这告诉服务器您只希望在此时间戳之后进行更改。
  • 一个包含所有 sync_status=1 项目的 JSON 数组。

The server gets the request and does this:

服务器获取请求并执行以下操作:

  • It takes the contents from the JSON array and modifies or adds the records it contains. The last_modified field is automatically updated.
  • The server returns a JSON array containing all objects with a last_modified time stamp greater than the time stamp sent in the request. This will include the objects it just received, which serves as an acknowledgment that the record was successfully synchronized to the server.
  • 它从 JSON 数组中获取内容并修改或添加它包含的记录。last_modified 字段会自动更新。
  • 服务器返回一个 JSON 数组,其中包含 last_modified 时间戳大于请求中发送的时间戳的所有对象。这将包括它刚刚接收到的对象,作为记录已成功同步到服务器的确认。

The app receives the response and does this:

该应用程序接收响应并执行以下操作:

  • It takes the contents from the JSON array and modifies or adds the records it contains. Each record get set a sync_status of 0.
  • 它从 JSON 数组中获取内容并修改或添加它包含的记录。每条记录的 sync_status 都设置为 0。

I hope that helps. I used the word record and model interchangeably, but I think you get the idea. Good luck.

我希望这有帮助。我交替使用了记录和模型这个词,但我想你明白了。祝你好运。

回答by radiospiel

If you are still looking for a way to go, look into the Couchbase mobile. This basically does all you want. (http://www.couchbase.com/nosql-databases/couchbase-mobile)

如果您仍在寻找方法,请查看 Couchbase 移动设备。这基本上可以满足您的所有需求。( http://www.couchbase.com/nosql-databases/couchbase-mobile)

回答by knagode

Similar like @Cris I've implemented class for synchronization between client and server and solved all known problems so far (send/receive data to/from server, merge conflicts based on timestamps, removed duplicate entries in unreliable network conditions, synchronize nested data and files etc .. )

与@Cris 类似,我已经实现了用于客户端和服务器之间同步的类,并解决了迄今为止的所有已知问题(向/从服务器发送/接收数据、基于时间戳的合并冲突、在不可靠的网络条件下删除重复条目、同步嵌套数据和文件等..)

You just tell the class which entity and which columns should it sync and where is your server.

您只需告诉类应该同步哪个实体和哪些列,以及您的服务器在哪里。

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];

You can find source, working example and more instructions here: github.com/knagode/M3Synchronization.

您可以在此处找到源代码、工作示例和更多说明:github.com/knagode/M3Synchronization

回答by Stan

Notice user to update data via push notification. Use a background thread in the app to check the local data and the data on the cloud server,while change happens on server,change the local data,vice versa.

通过推送通知通知用户更新数据。在应用程序中使用后台线程检查本地数据和云服务器上的数据,当服务器发生更改时,更改本地数据,反之亦然。

So I think the most difficult part is to estimate data in which side is invalidate.

所以我认为最难的部分是估计数据在哪一边无效。

Hope this can help u

希望这可以帮助你

回答by logan

I have just posted the first version of my new Core Data Cloud Syncing API, known as SynCloud. SynCloud has a lot of differences with iCloud because it allows for Multi-user sync interface. It is also different from other syncing api's because it allows for multi-table, relational data.

我刚刚发布了我的新 Core Data Cloud Syncing API 的第一个版本,称为 SynCloud。SynCloud 与 iCloud 有很多不同之处,因为它允许多用户同步界面。它也不同于其他同步 api,因为它允许多表、关系数据。

Please find out more at http://www.syncloudapi.com

请访问http://www.syncloudapi.com了解更多信息

Build with iOS 6 SDK, it is very up to date as of 9/27/2012.

使用 iOS 6 SDK 构建,截至 2012 年 9 月 27 日是最新的。

回答by eselk

I think a good solution to the GUID issue is "distributed ID system". I'm not sure what the correct term is, but I think that's what MS SQL server docs used to call it (SQL uses/used this method for distributed/sync'ed databases). It's pretty simple:

我认为 GUID 问题的一个很好的解决方案是“分布式 ID 系统”。我不确定正确的术语是什么,但我认为 MS SQL 服务器文档曾经这样称呼它(SQL 将这种方法用于分布式/同步数据库)。这很简单:

The server assigns all IDs. Each time a sync is done, the first thing that is checked are "How many IDs do I have left on this client?" If the client is running low, it asks the server for a new block of IDs. The client then uses IDs in that range for new records. This works great for most needs, if you can assign a block large enough that it should "never" run out before the next sync, but not so large that the server runs out over time. If the client ever does run out, the handling can be pretty simple, just tell the user "sorry you cannot add more items until you sync"... if they are adding that many items, shouldn't they sync to avoid stale data issues anyway?

服务器分配所有 ID。每次同步完成时,首先检查的是“我在这个客户端上还剩下多少 ID?” 如果客户端运行不足,它会向服务器请求新的 ID 块。然后客户端使用该范围内的 ID 来获取新记录。如果您可以分配一个足够大的块,使其在下一次同步之前“永远”不会用完,但不会大到服务器随着时间的推移用完,这对大多数需求来说都非常有效。如果客户端确实用完了,处理可以非常简单,只需告诉用户“抱歉,在同步之前您无法添加更多项目”......如果他们添加了那么多项目,他们不应该同步以避免陈旧数据问题呢?

I think this is superior to using random GUIDs because random GUIDs are not 100% safe, and usually need to be much longer than a standard ID (128-bits vs 32-bits). You usually have indexes by ID and often keep ID numbers in memory, so it is important to keep them small.

我认为这优于使用随机 GUID,因为随机 GUID 不是 100% 安全的,并且通常需要比标准 ID(128 位与 32 位)长得多。您通常按 ID 建立索引,并且经常将 ID 编号保存在内存中,因此保持它们的小很重要。

Didn't really want to post as answer, but I don't know that anyone would see as a comment, and I think it's important to this topic and not included in other answers.

并不是真的想作为答案发布,但我不知道有人会看到评论,我认为这对这个主题很重要,不包含在其他答案中。

回答by thom_ek

First you should rethink how many data, tables and relations you will have. In my solution I've implemented syncing through Dropbox files. I observe changes in main MOC and save these data to files (each row is saved as gzipped json). If there is an internet connection working, I check if there are any changes on Dropbox (Dropbox gives me delta changes), download them and merge (latest wins), and finally put changed files. Before sync I put lock file on Dropbox to prevent other clients syncing incomplete data. When downloading changes it's safe that only partial data is downloaded (eg lost internet connection). When downloading is finished (fully or partial) it starts to load files into Core Data. When there are unresolved relations (not all files are downloaded) it stops loading files and tries to finish downloading later. Relations are stored only as GUID, so I can easly check which files to load to have full data integrity. Syncing is starting after changes to core data are made. If there are no changes, than it checks for changes on Dropbox every few minutes and on app startup. Additionaly when changes are sent to server I send a broadcast to other devices to inform them about changes, so they can sync faster. Each synced entity has GUID property (guid is used also as a filename for exchange files). I have also Sync database where I store Dropbox revision of each file (I can compare it when Dropbox delta resets it's state). Files also contain entity name, state (deleted/not deleted), guid (same as filename), database revision (to detect data migrations or to avoid syncing with never app versions) and of course the data (if row is not deleted).

首先,您应该重新考虑将拥有多少数据、表格和关系。在我的解决方案中,我通过 Dropbox 文件实现了同步。我观察主 MOC 的变化并将这些数据保存到文件中(每一行都保存为 gzipped json)。如果互联网连接正常,我会检查 Dropbox 上是否有任何更改(Dropbox 给了我增量更改),下载它们并合并(最新获胜),最后放置更改的文件。在同步之前,我将锁定文件放在 Dropbox 上以防止其他客户端同步不完整的数据。下载更改时,只下载部分数据是安全的(例如,互联网连接丢失)。下载完成(全部或部分)后,它开始将文件加载到 Core Data 中。当存在未解决的关系(并非所有文件都已下载)时,它会停止加载文件并尝试稍后完成下载。关系仅作为 GUID 存储,因此我可以轻松检查加载哪些文件以获得完整的数据完整性。对核心数据进行更改后开始同步。如果没有变化,它会每隔几分钟和应用启动时检查 Dropbox 上的变化。另外,当更改发送到服务器时,我会向其他设备发送广播以通知他们有关更改的信息,以便它们可以更快地同步。每个同步实体都有 GUID 属性(guid 也用作交换文件的文件名)。我还有同步数据库,我在其中存储每个文件的 Dropbox 修订版(我可以在 Dropbox delta 重置其状态时进行比较)。文件还包含实体名称、状态(已删除/未删除)、guid(与文件名相同)、数据库修订(用于检测数据迁移或避免与从不同步的应用程序版本),当然还有数据(如果行未删除)。

This solution is working for thousands of files and about 30 entities. Instead of Dropbox I could use key/value store as REST web service which I want to do later, but have no time for this :) For now, in my opinion, my solution is more reliable than iCloud and, which is very important, I have full control on how it's working (mainly because it's my own code).

此解决方案适用于数千个文件和大约 30 个实体。我可以使用键/值存储作为 REST 网络服务而不是 Dropbox,我以后想这样做,但没有时间:) 目前,在我看来,我的解决方案比 iCloud 更可靠,这非常重要,我可以完全控制它的工作方式(主要是因为它是我自己的代码)。

Another solution is to save MOC changes as transactions - there will be much less files exchanged with server, but it's harder to do initial load in proper order into empty core data. iCloud is working this way, and also other syncing solutions have similar approach, eg TICoreDataSync.

另一种解决方案是将 MOC 更改保存为事务 - 与服务器交换的文件将少得多,但很难以正确的顺序将初始加载到空核心数据中。iCloud 就是这样工作的,其他同步解决方案也有类似的方法,例如TICoreDataSync

-- UPDATE

- 更新

After a while, I migrated to Ensembles- I recommend this solution over reinventing the wheel.

一段时间后,我迁移到Ensembles- 我推荐这个解决方案而不是重新发明轮子。