嘿,我正在为我们的应用程序模型层工作.
一些要求是这样的:
它应该适用于iPhone OS 3.0+.
我们的数据源是RESTful Rails应用程序.
我们应该使用Core Data在本地缓存数据.
客户端代码(我们的UI控制器)应该尽可能少地了解任何网络内容,并且应该使用Core Data API查询/更新模型.
我查看了关于构建服务器驱动的用户体验的WWDC10 Session 117,花了一些时间查看Objective Resource,Core Resource和RestfulCoreData框架.
Objective Resource框架本身不与Core Data对话,仅仅是一个REST客户端实现.Core Resource和RestfulCoreData都假设您在代码中与Core Data交谈,并且它们解决了模型层背景中的所有细节.
到目前为止所有看起来都没问题,最初我虽然核心资源或RestfulCoreData将涵盖所有上述要求,但是...有一些事情似乎没有一个正好解决:
保存服务器的本地更新时,不应阻止主线程.
如果保存操作失败,则应将错误传播到UI,并且不应将更改保存到本地Core Data存储.
当您调用- (BOOL)save:(NSError **)error
托管对象上下文时,核心资源恰好向服务器发出所有请求,因此能够以某种方式向服务器提供正确的基础请求的NSError实例.但它会阻止调用线程,直到保存操作完成.失败.
RestfulCoreData保持您的-save:
调用不变,并且不会为客户端线程引入任何额外的等待时间.它只是注意NSManagedObjectContextDidSaveNotification
,然后在通知处理程序中向服务器发出相应的请求.但这种方式的-save:
呼叫总是成功完成(当然,考虑到核心数据是好的,与保存的更改),并且实际调用它没有办法知道节省,可能也没有传播到因为一些服务器的客户端代码404
或421
或任何发生服务器端错误.甚至更多,本地存储变得更新数据,但服务器永远不知道更改.失败.
所以,我正在寻找可能的解决方案/常见做法来处理所有这些问题:
我不希望调用线程-save:
在网络请求发生时阻塞每次调用.
我想以某种方式在UI中收到某些同步操作出错的通知.
如果服务器请求失败,我希望实际的Core Data保存失败.
有任何想法吗?
你应该真的看看RestKit(http://restkit.org)这个用例.它旨在解决建模和将远程JSON资源同步到本地Core Data支持的缓存的问题.它支持脱机模式,以便在没有可用网络时完全从缓存中工作.所有同步都发生在后台线程(网络访问,有效负载解析和托管对象上下文合并)上,并且有一组丰富的委托方法,因此您可以告诉发生了什么.
有三个基本组成部分:
UI Action并将更改持久保存到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循环,为了保持代码清洁,调用save会很好:并且让更改自动推送到服务器.您可以使用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并在后台线程上创建操作.
如果您的REST API支持批处理,您甚至可以立即发送整个阵列,然后通知您UI已同步多个实体.
我预见到的唯一问题是,没有"真正的"解决方案是用户不希望等待将其更改推送到服务器以允许进行更多更改.我遇到的唯一好的范例是,您允许用户继续编辑对象,并在适当时将其编辑批处理,即您不会推送每个保存通知.