Iphone 如何异步同步CoreData和REST web服务,同时正确地将任何REST错误传播到UI中

Iphone 如何异步同步CoreData和REST web服务,同时正确地将任何REST错误传播到UI中,iphone,objective-c,rest,core-data,Iphone,Objective C,Rest,Core Data,嘿,我正在为我们的应用程序制作模型层 一些要求如下: 它应该可以在iPhone OS 3.0+上运行 我们的数据源是一个RESTful Rails应用程序 我们应该使用核心数据在本地缓存数据 客户端代码(我们的UI控制器)应该尽可能少地了解任何网络内容,并且应该使用核心数据API查询/更新模型 我已经查看了WWDC10会话117中关于构建服务器驱动的用户体验的内容,花了一些时间查看了,以及框架 Objective Resource框架本身并不与核心数据通信,只是一个REST客户机实现。核心资源和

嘿,我正在为我们的应用程序制作模型层

一些要求如下:

  • 它应该可以在iPhone OS 3.0+上运行
  • 我们的数据源是一个RESTful Rails应用程序
  • 我们应该使用核心数据在本地缓存数据
  • 客户端代码(我们的UI控制器)应该尽可能少地了解任何网络内容,并且应该使用核心数据API查询/更新模型
  • 我已经查看了WWDC10会话117中关于构建服务器驱动的用户体验的内容,花了一些时间查看了,以及框架

    Objective Resource框架本身并不与核心数据通信,只是一个REST客户机实现。核心资源和RestfulCoreData都假设您在代码中与核心数据对话,它们解决了模型层后台的所有问题

    到目前为止一切看起来都不错,我认为核心资源或RestfulCoreData都可以满足上述所有要求,但是。。。有两件事似乎没有一件能正确解决:

  • 将本地更新保存到服务器时,不应阻止主线程
  • 如果保存操作失败,则应将错误传播到UI,并且不应将任何更改保存到本地核心数据存储
  • 当您在托管对象上下文上调用
    -(BOOL)save:(NSError**)error
    时,核心资源会向服务器发出所有请求,因此能够向服务器提供正确的底层请求NSError实例。但它会阻止调用线程,直到save操作完成。失败

    RestfulCoreData使您的
    -save:
    调用保持不变,并且不会为客户端线程引入任何额外的等待时间。它只关注
    NSManagedObjectContextDidSaveNotification
    ,然后在通知处理程序中向服务器发出相应的请求。但是通过这种方式,
    -save:
    调用始终成功完成(好吧,给定的核心数据与保存的更改是一致的),并且实际调用它的客户端代码无法知道save可能由于一些
    404
    421
    或任何服务器端错误而无法传播到服务器。而且,本地存储变得需要更新数据,但服务器永远不知道这些更改。失败

    因此,我正在寻找解决所有这些问题的可能解决方案/常见做法:

  • 我不希望调用线程在网络请求发生时阻塞每个
    -save:
    调用
  • 我想以某种方式在UI中得到一些同步操作出错的通知
  • 如果服务器请求失败,我希望实际的核心数据保存也会失败

  • 有什么想法吗?

    您需要一个回调函数,该函数将在另一个线程(实际发生服务器交互的线程)上运行,然后将结果代码/错误信息放入半全局数据,UI线程将定期检查该数据。确保用作标志的数字的连接是原子的,否则将出现竞争条件-例如,如果错误响应为32字节,则需要一个int(该int应具有原子访问),然后将该int保持在off/false/notready状态,直到写入较大的数据块,然后才写入“true”可以这么说,扳动开关

    对于客户端上的相关保存,您必须要么保留该数据,要么在服务器确认之前不保存该数据,确保您有一个kinnf of rollback选项,比如说,删除的方法是服务器失败

    请注意,除非您执行完整的两阶段提交过程(客户端保存或删除可能会在服务器发出信号后失败),否则它永远不会是100%安全的,但这至少会花费您两次到服务器的行程(如果您唯一的回滚选项是delete,则可能会花费您4次)


    理想情况下,您可以在一个单独的线程上执行整个阻塞版本的操作,但您需要4.0版本。

    这会成为一个同步问题,不容易解决。我要做的是:在iPhone UI中使用一个上下文,然后使用另一个上下文(和另一个线程)从web服务下载数据。完成后,请执行下面推荐的同步/导入过程,然后在所有内容正确导入后刷新UI。如果在访问网络时出现问题,只需回滚非UI上下文中的更改。这是一系列的工作,但我认为这是最好的方法


    有三个基本组成部分:

  • UI操作并将更改持久化到CoreData
  • 将更改持久化到服务器
  • 使用服务器的响应刷新UI
  • NSOperation+NSOperationQueue将有助于保持网络请求有序。委托协议将帮助UI类了解网络请求的状态,例如:

    @protocol NetworkOperationDelegate
      - (void)operation:(NSOperation *)op willSendRequest:(NSURLRequest *)request forChangedEntityWithId:(NSManagedObjectID *)entity;
      - (void)operation:(NSOperation *)op didSuccessfullySendRequest:(NSURLRequest *)request forChangedEntityWithId:(NSManagedObjectID *)entity;
      - (void)operation:(NSOperation *)op encounteredAnError:(NSError *)error afterSendingRequest:(NSURLRequest *)request forChangedEntityWithId:(NSManagedObjectID *)entity;
    @end
    
    协议格式当然取决于您的特定用例,但本质上您所创建的是一种机制,通过该机制可以将更改“推”到服务器上

    接下来有UI循环来考虑,为了保持代码干净,调用Soice是很好的:并且将这些更改自动地推送到服务器上。您可以为此使用NSManagedObjectContextDidSave通知

    - (void)managedObjectContextDidSave:(NSNotification *)saveNotification {
      NSArray *inserted = [[saveNotification userInfo] valueForKey:NSInsertedObjects];
      for (NSManagedObject *obj in inserted) {
        //create a new NSOperation for this entity which will invoke the appropraite rest api
        //add to operation queue
      }
    
      //do the same thing for deleted and updated objects
    }
    
    插入网络操作的计算开销应该相当低,但是,如果它在UI上造成明显的延迟,您可以简单地从保存通知中获取实体ID,并在后台线程上创建操作

    如果RESTAPI支持批处理,您甚至可以一次发送整个阵列,然后通知UI多个实体已同步<