我必须创建一个具有类似于联系人应用程序的功能的应用程序.您可以在客户端的iPhone上添加联系人,并将其上传到客户端的iPad上.如果客户端更新iPad上的联系人,应该在iPhone上更新.

这大部分是相当简单的.我使用Parse.com作为我的后端,并使用Core Data在本地保存联系人.遇到的唯一问题是在用户离线时管理联系人.

假设我有一个iPhone和一个iPad.他们目前拥有相同版本的在线数据库.我的iPhone现在离线了.上午9点

上午10点,我更新iPad上联系人的电话号码.它可以在本地和在线保存更改.上午11点,我更新iPhone上同一联系人的电子邮件地址,但我仍然处于离线状态.

中午,我的iPhone连接到互联网,检查服务器的变化.它看到它的更改比最新更新更新(检查updatedAt时间戳属性),因此,不是下载联系人的新电话号码(这是“过时的”),它会覆盖电话号码以及电子邮件地址(将新的电话号码更新为旧版本,因为它在上午10点的电话号码更新期间处于离线状态,并且其更改据称更新).

我应该如何管理遇到的在线/离线问题,如上述问题?我可以想到的一个解决方案是在联系人的每个属性上保留更新的时间戳,而不是为整个联系人提供一般的updatedAt属性.何时更名,何时更名,然后手动检查离线设备是否在每个属性上都有更新的更改,而不是覆盖整个对象,但是看起来很乱.

我也在考虑在每个Core Data对象上都有一个updatedLocally和updatedOnline timestamp属性.这样,如果两个不匹配,我可以做差异检查,并使用最近的一个冲突,但这仍然看起来不像最干净的解决方案.有没有人遇到类似的东西?如果是这样,你是如何解决的?

伪代码/总结为什么我想?涵盖每个测试用例,但仍然不是很优雅/完整:

Parse.com上的2个实体:联系人和联系人历史记录

联系人有第一个,最后一个,电话,电子邮件,onlineUpdate

联系人历史记录具有联系人的主键以及具有历史记录的相同属性.例如首先:[{value:“josue”,onlineUpdate:“9AM”},{value:“j”,onlineUpdate:“10AM”},{value:“JOSUEESP”,onlineUpdate:“11AM”}]

1核心数据实体,联系人:

联系人有第一个,最后一个电话,onlineUpdate和offlineUpdate(重要:这只是在核心数据,而不是在解析)

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
        }
}

TL; DR:您应该如何构建一种用于在线/离线更新的版本控制系统,而不会意外覆盖?我想将带宽使用限制在最低限度.

解决方法

我建议使用基于键的更新,而不是基于联系的更新.
您不应该将整个联系人发送到服务器,在大多数情况下,用户只会更改几个属性(诸如“姓氏”通常不会更改).这也减少了带宽使用.
随着您发送的离线联系人的应用更改
本地联系人到服务器的旧版本号/上次更新时间戳.服务器现在可以
确定您的本地数据是否是最新的,只需查看您的旧版本号码即可.如果您的旧版本号与服务器的当前版本号相匹配,则不需要您的客户端更新任何其他信息.如果不是这种情况,服务器应该向您发送新的联系人(应用您所请求的更新后).
您还可以保存这些提交,这将导致联系人历史记录
每次更改密钥时,它不会保存整个联系人,但只有更改本身.
伪代码中的简单实现可能如下所示:
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);
    }
}

为了更好地了解工作流程,我将提供一个例子:

上午8点,客户端和服务器都是最新的,每个设备都在线

每个设备对于具有主键ID的联系人“Foo Bar”都有一个条目(在这种情况下是一行).
每个条目的版本是相同的,因此它们都是最新的.

_        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

(借口这可怕的格式,很遗憾不支持任何一种表…)

上午9点你的iPhone离线您注意到Foo Bar的电子邮件更改为“foo @ 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

10 AM你的iPad是离线的.你注意到“Foo Bar”实际上被写成“Voo Bar”!您可以在iPad上立即应用更改.

UPDATE 42 FROM 1 TO 2 First=Voo

请注意,iPad仍然认为当前版本的联系人42是1.由于没有设备连接到网络,服务器和iPad都没有注意到您更改了邮件地址并增加了版本号.这些更改仅在本地存储并在iPad上可见.

上午11点,您将iPad连接到网络. iPad发送最近的更新
到服务器.之前:

_        Server    iPad
 ID       42        42 
 Ver      1         2
 First    Foo       Voo
 Last     Bar       Bar
 Mail     f@b       f@b

iPad – >服务器:

UPDATE 42 FROM 1 TO 2 First=Voo

服务器现在可以看到您正在更新联系人42的版本1.由于版本1是当前版本,您的客户端是最新的(在离线的同时没有更改).
服务器 – > iPad的

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

上午12点,您将iPad从网络中断开并连接您的iPhone.
iPhone尝试提交最近的更改.之前:

_        Server    iPhone
 ID       42        42 
 Ver      2         2
 First    Voo       Voo
 Last     Bar       Bar
 Mail     f@b       foo@b

iPhone – >服务器

UPDATE 42 FROM 1 TO 2 Mail=foo@b

服务器注意到您如何尝试更新同一联系人的旧版本.
他将应用您的更新,因为它比iPad的更新更新
将向您发送新的联系人数据,以确保您获得更新的名字.After:

_        Server    iPhone
 ID       42        42 
 Ver      2         2
 First    Voo       Voo
 Last     Bar       Bar
 Mail     foo@b     foo@b

服务器 – > iPad的

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
*/

iPad的下一次连接到网络并且没有更改提交,它应该只是发送当前版本的联系人,看看它是否仍然是最新的.

现在您已经提交了两个离线更改,而不会相互覆盖.
您可以轻松地扩展此方法,并进行一些优化.例如:

>如果客户端尝试更新旧版本的联系人,请勿将其全部联系人作为答案发送给他们.而是向他们发送他们错过的提交,让他们自己更新他们的联系.如果您存储有关客户端的大量信息,并希望在更新之间进行几个更改,这将非常有用.
>如果客户更新了有关联系人的所有信息,我们可以假定他不需要知道错过的更新,但是我们会让他知道他错过的所有内容(但是这对他应该没有影响)

我希望这有帮助.

ios – 在线/离线数据管理的更多相关文章

  1. HTML5 播放 RTSP 视频的实例代码

    目前大多数网络摄像头都是通过 RTSP 协议传输视频流的,但是 HTML 并不标准支持 RTSP 流。本文重点给大家介绍HTML5 播放 RTSP 视频的实例代码,需要的朋友参考下吧

  2. 利用Node实现HTML5离线存储的方法

    这篇文章主要介绍了利用Node实现HTML5离线存储的方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  3. 详解如何通过H5(浏览器/WebView/其他)唤起本地app

    这篇文章主要介绍了详解如何通过H5(浏览器/WebView/其他)唤起本地app的相关资料,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  4. H5混合开发app如何升级的方法

    本篇文章主要介绍了H5混合开发app如何升级的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  5. AmazeUI 折叠面板的实现代码

    这篇文章主要介绍了AmazeUI 折叠面板的实例代码,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下

  6. html5 canvas合成海报所遇问题及解决方案总结

    这篇文章主要介绍了html5 canvas合成海报所遇问题及解决方案总结,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

  7. Html5移动端适配IphoneX等机型的方法

    这篇文章主要介绍了Html5移动端适配IphoneX等机型的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

  8. HTML5手指下滑弹出负一屏阻止移动端浏览器内置下拉刷新功能的实现代码

    这篇文章主要介绍了HTML5手指下滑弹出负一屏阻止移动端浏览器内置下拉刷新功能的实现代码,代码简单易懂,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧

  9. 有关HTML5页面在iPhoneX适配问题

    这篇文章主要介绍了有关HTML5页面在iPhoneX适配问题,需要的朋友可以参考下

  10. Html5 video标签视频的最佳实践

    这篇文章主要介绍了Html5 video标签视频的最佳实践,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

随机推荐

  1. iOS实现拖拽View跟随手指浮动效果

    这篇文章主要为大家详细介绍了iOS实现拖拽View跟随手指浮动,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  2. iOS – genstrings:无法连接到输出目录en.lproj

    使用我桌面上的项目文件夹,我启动终端输入:cd然后将我的项目文件夹拖到终端,它给了我路径.然后我将这行代码粘贴到终端中找.-name*.m|xargsgenstrings-oen.lproj我在终端中收到此错误消息:genstrings:无法连接到输出目录en.lproj它多次打印这行,然后说我的项目是一个目录的路径?没有.strings文件.对我做错了什么的想法?

  3. iOS 7 UIButtonBarItem图像没有色调

    如何确保按钮图标采用全局色调?解决方法只是想将其转换为根注释,以便为“回答”复选标记提供更好的上下文,并提供更好的格式.我能想出这个!

  4. ios – 在自定义相机层的AVFoundation中自动对焦和自动曝光

    为AVFoundation定制图层相机创建精确的自动对焦和曝光的最佳方法是什么?

  5. ios – Xcode找不到Alamofire,错误:没有这样的模块’Alamofire’

    我正在尝试按照github(https://github.com/Alamofire/Alamofire#cocoapods)指令将Alamofire包含在我的Swift项目中.我创建了一个新项目,导航到项目目录并运行此命令sudogeminstallcocoapods.然后我面临以下错误:搜索后我设法通过运行此命令安装cocoapodssudogeminstall-n/usr/local/bin

  6. ios – 在没有iPhone6s或更新的情况下测试ARKit

    我在决定下载Xcode9之前.我想玩新的框架–ARKit.我知道要用ARKit运行app我需要一个带有A9芯片或更新版本的设备.不幸的是我有一个较旧的.我的问题是已经下载了新Xcode的人.在我的情况下有可能运行ARKit应用程序吗?那个或其他任何模拟器?任何想法或我将不得不购买新设备?解决方法任何iOS11设备都可以使用ARKit,但是具有高质量AR体验的全球跟踪功能需要使用A9或更高版本处理器的设备.使用iOS11测试版更新您的设备是必要的.

  7. 将iOS应用移植到Android

    我们制作了一个具有2000个目标c类的退出大型iOS应用程序.我想知道有一个最佳实践指南将其移植到Android?此外,由于我们的应用程序大量使用UINavigation和UIView控制器,我想知道在Android上有类似的模型和实现.谢谢到目前为止,guenter解决方法老实说,我认为你正在计划的只是制作难以维护的糟糕代码.我意识到这听起来像很多工作,但从长远来看它会更容易,我只是将应用程序的概念“移植”到android并从头开始编写.

  8. ios – 在Swift中覆盖Objective C类方法

    我是Swift的初学者,我正在尝试在Swift项目中使用JSONModel.我想从JSONModel覆盖方法keyMapper,但我没有找到如何覆盖模型类中的Objective-C类方法.该方法的签名是:我怎样才能做到这一点?解决方法您可以像覆盖实例方法一样执行此操作,但使用class关键字除外:

  9. ios – 在WKWebView中获取链接URL

    我想在WKWebView中获取tapped链接的url.链接采用自定义格式,可触发应用中的某些操作.例如HTTP://我的网站/帮助#深层链接对讲.我这样使用KVO:这在第一次点击链接时效果很好.但是,如果我连续两次点击相同的链接,它将不报告链接点击.是否有解决方法来解决这个问题,以便我可以检测每个点击并获取链接?任何关于这个的指针都会很棒!解决方法像这样更改addobserver在observeValue函数中,您可以获得两个值

  10. ios – 在Swift的UIView中找到UILabel

    我正在尝试在我的UIViewControllers的超级视图中找到我的UILabels.这是我的代码:这是在Objective-C中推荐的方式,但是在Swift中我只得到UIViews和CALayer.我肯定在提供给这个方法的视图中有UILabel.我错过了什么?我的UIViewController中的调用:解决方法使用函数式编程概念可以更轻松地实现这一目标.

返回
顶部