Ios 在线/离线数据管理
我必须创建一个功能类似于联系人应用程序的应用程序。您可以在客户的iPhone上添加联系人,并将其上传到客户的iPad上。如果客户在iPad上更新了联系人,则应在iPhone上更新 这其中大部分是相当直截了当的。我使用Ios 在线/离线数据管理,ios,database,algorithm,core-data,parse-platform,Ios,Database,Algorithm,Core Data,Parse Platform,我必须创建一个功能类似于联系人应用程序的应用程序。您可以在客户的iPhone上添加联系人,并将其上传到客户的iPad上。如果客户在iPad上更新了联系人,则应在iPhone上更新 这其中大部分是相当直截了当的。我使用Parse.com作为后端,并使用核心数据在本地保存联系人。我遇到的唯一问题是在用户脱机时管理联系人 假设我有一部iPhone和一部iPad。他们目前都有相同版本的在线数据库。我的iPhone现在离线了。现在是上午9点 上午10点,我在iPad上更新了联系人的电话号码。它在本地和在线
Parse.com
作为后端,并使用核心数据在本地保存联系人。我遇到的唯一问题是在用户脱机时管理联系人
假设我有一部iPhone和一部iPad。他们目前都有相同版本的在线数据库。我的iPhone现在离线了。现在是上午9点
上午10点,我在iPad上更新了联系人的电话号码。它在本地和在线保存更改。上午11点,我在iPhone上更新了同一联系人的电子邮件地址,但仍处于脱机状态
中午,我的iPhone连接到互联网并检查服务器是否有变化。它看到它的更改比最新的更新(检查updatedAt
timestamp属性)更新,因此它不会下载联系人的新电话号码(这是“过时的”),而是覆盖电话号码和电子邮件地址(将新电话号码更新为旧版本,因为在上午10点更新电话号码时,新电话号码处于脱机状态,其更改应该是最新的)
我应该如何管理遇到的在线/离线问题,如上述问题?我能想到的一个解决方案是在联系人的每个属性上保留更新的时间戳,而不是整个联系人的常规updatedAt
属性,例如,名字何时更新,姓氏何时更新,然后手动Ly检查脱机设备是否在每个属性上都有较新的更改,而不是覆盖整个对象,但这看起来很草率
我还考虑在每个核心数据
对象上使用updated local
和updated online
timestamp属性。这样,如果这两个属性不匹配,我可以进行差异检查,并使用最新的一个检查冲突,但这似乎仍然不是最干净的解决方案。还有其他人遇到过类似的问题吗ar?如果是,你是如何解决的
伪代码/我想的摘要?涵盖了每个测试用例,但仍然不是很优雅/完整:
Parse.com上的2个实体:联系人和联系人历史记录
联系人有第一个、最后一个、电话、电子邮件、在线更新
联系人历史记录具有要引用的联系人的主键和与历史记录相同的属性。例如,first:[{value:“josue”,onlineUpdate:“9AM”},{value:“j”,onlineUpdate:“10AM”},{value:“JOSUEESP”,onlineUpdate:“11AM”}]
1个核心数据实体,联系方式:
联系人有第一个、最后一个电话、电子邮件、在线更新和离线更新(重要提示:这仅针对核心数据,不针对解析)
TL;DR:您应该如何构造一种在线/离线更新的版本控制系统,而不会意外覆盖?我想将带宽使用限制在最低限度。
- 您可以使用单独的表来存储所有更新联系人的ID(数据库表中存储联系人信息的主键),而不是为每个核心数据对象使用单独的标志
- 稍后,当用户联机时,您只需从实际联系人详细信息表中获取这些联系人,并将其上载到服务器上
我对iOs、core data和parse.com一无所知,因此我只能建议一个通用的算法解决方案。我认为您可以采用与版本控制系统类似的方法
最简单的方法是保留服务器上的所有历史记录:保留联系人列表的所有修订版。现在,在同步过程中,手机会发送其看到的上一个服务器修订版的信息,并且此修订版将是当前手机修订版和当前服务器修订版的“公共父版本”
现在,您可以看到自该修订版以来,服务器和手机上发生了哪些变化,并应用通常的三方比较:如果某个字段仅在服务器上发生了变化,则将新字段发送到手机;如果某个字段仅在手机上发生了变化,则在服务器上也发生了变化,如果某个字段在手机和服务器上都发生了变化,则将其发送到手机es是不同的,那么您就有冲突,必须询问用户
这种方法的一种变体可能是处理更改,而不是修订。服务器和客户端的主要数据将不是联系人列表,而是其更改的历史记录。(如果需要,还可以保留当前联系人列表以及一组“关键帧”;它不会用于冲突解决算法,但可以用于快速显示和使用。)
然后,当用户同步数据时,您只需下载/上载更改。如果有任何冲突更改,您只需询问用户,否则您只需合并它们。如何定义更改以及哪些更改被视为冲突取决于您。简单的方法是将更改定义为一对(字段、新值),如果两个更改具有相同的字段,则两个更改是冲突的。您还可以使用更高级的冲突解决逻辑,例如,如果一个更改仅更改电子邮件的前半部分,而另一个更改仅更改电子邮件的后半部分,则您可以将其合并。正确的方法是保留事务日志。无论何时保存创建的核心数据事务日志中的日志项。下次联机时,可针对服务器播放事务日志
这就是iCloud和其他同步服务的工作原理。我建议使用基于密钥的更新,而不是基于联系人的更新。
您不应该将整个联系人发送到服务器,在大多数情况下,用户只会更改一些属性(例如“姓氏”通常不会经常更改)。这也会降低
for every contact in parse database as onlineContact {
if onlineContact does not exist in core data {
create contact in core data
}
else {
// found matching local object to online object, check for changes
var localContact = core data contact with same UID as onlineContact
if localContact.offlineUpdate more recent than onlineContact.onlineUpdate {
for every attribute in localContact as attribute {
var lastOnlineValueReceived = Parse database Contact History at the time localContact.onlineUpdate for attribute
if lastOnlineValueReceived == localContact.attribute {
// this attribute did not change in the offline update. use latest available online value
localContact.attribute = onlineContact.attribute
}
else{
// this attribute changed during the more recent offline update, update it online
onlineContact.attribute = localContact.attribute
}
}
}
else if onlineContact.onlineUpdate more recent than localContact.offlineUpdate {
// another device updated the contact. use the online contact.
localContact = offlineContact
}
else{
// when a device is connected to the internet, and it saves a contact
// the offline/online update times are the same
// therefore contacts should be equivalent in this else statement
// do nothing
}
}
for( each currentContact in offlineContacts ) do
{
if( localChanges.length > 0){ // updates to be made
commitAllChanges();
answer = getServerAnswer();
if(answer.containsContact() == true){
// server sent us a contact as answer so
// we should overwrite the contact
currentContact = answer.contact;
} else {
// the server does not want us to overwrite the contact, so we are up to date!
}
// ...
}
} // end of iterating over contacts
for (currentContactToUpdate in contactsToUpdate) do
{
sendBackContact = false; // only send back the updated contact if the client missed updates
for( each currentUpdate in incomingUpdates ) do {
oldClientVersion = currentUpdate.oldversion;
oldServerVersion = currentContact.getVersion();
if( oldClientVersion != oldServerVersion ){
sendBackContact = true;
// the client missed some updates from other devices
// because he tries to update an old version
}
currentContactToUpdate.apply(currentUpdate);
}
if(sendBackContact == true){
sendBack(currentUpdate);
}
}
_ Server iPhone iPad
ID 42 42 42
Ver 1 1 1
First Foo Foo Foo
Last Bar Bar Bar
Mail f@b f@b f@b
UPDATE 42 FROM 1 TO 2 Mail=foo@b
// ^ID ^old version ^new version ^changed attribute(s)
_ iPhone
ID 42
Ver 2
First Foo
Last Bar
Mail foo@b
UPDATE 42 FROM 1 TO 2 First=Voo
_ Server iPad
ID 42 42
Ver 1 2
First Foo Voo
Last Bar Bar
Mail f@b f@b
UPDATE 42 FROM 1 TO 2 First=Voo
UPDATED 42 FROM 1 TO 2 - OK
_ Server iPad
ID 42 42
Ver 2 2
First Voo Voo
Last Bar Bar
Mail f@b f@b
_ Server iPhone
ID 42 42
Ver 2 2
First Voo Voo
Last Bar Bar
Mail f@b foo@b
UPDATE 42 FROM 1 TO 2 Mail=foo@b
_ Server iPhone
ID 42 42
Ver 2 2
First Voo Voo
Last Bar Bar
Mail foo@b foo@b
UPDATED 42 FROM 1 TO 3 - Ver=2;First=Voo;.... // send the whole contact
/* Note how the version number was changed to 3, and not to 2, as requested.
* If the new version number was (still) 2 the iPad would miss the update
*/