Synching web services with Core Data
When creating iPhone and iPad apps that communicate with web services (typically some kind of REST API), it is often desirable to cache data wherever you can - your app shouldn't be dependent on a network connection when it doesn't have to be.
To cache or not to cache
Caching is a notoriously difficult issue (or rather, cache expiration is) and there are many ways of going about it. One way of doing it would be to create a mirror of the remote objects in a local Core Data store. This would allow you to take advantage of everything that Core Data has to offer and lets you more easily create apps that work offline, as they have their own object model to work with and are not dependent on the remote API.
The biggest issue with this approach is keeping the local and remote objects in sync with each other. You would need to be able to:
- Populate your Core Data store initially with data from the API
- Keep local objects updated with remote changes
- Keep the server updated with local changes either immediately (if a network connection is available), or in the future (when a network connection becomes available).
The ideal solution would be relatively seamless. Your application would deal with its own object model and the synching would just work.
On possible solution: Core Resource
Whilst there are number of solutions to synchronzing Core Data stores across devices (such as Apple's own Sync Services framework, and Zsync), the only attempt to solve the issue of synching with a web service is Core Resource.
Core Resource seems like a cool project, but based on my initial impressions, it makes some design decisions that I felt were inappropriate:
- Core Resource knows too much about your remote resource. My preferred solution would be completely agnostic to how your remote resource is fetched. My preferred solution doesn't prescribe a particular type of web service (REST, SOAP etc.) nor how that data should be fetched.
- Related to the above, because Core Resource concerns itself with fetching the remote data, it has a dependency on ASIHTTPRequest. ASIHTTPRequest is a pretty cool library, but what if I want to use the awesome HTTPRiot instead?
- Core Resource depends on inheritance. All of your entities need to inherit from
CoreResource, which in turn inherits from
NSManagedObject. My preferred solution makes no assumptions about your class hierarchy and doesn't force unnecessary coupling; I feel that protocols would be the better, more Cocoa-centric approach.
- Core Resource has some cool user-interface related widgets (such as a custom UITableViewController) but I'd prefer if my synching library focussed on just one thing - synching - whilst allowing your app to hook into certain events using notifications and delegation.
Working towards a solution, current progress
Over the past week, I've been working on my own solution to this problem. It's in the very early stages and has changed a lot already. I've made decisions which turned out to naïve or just plain stupid. But that's OK.
So far, I've got basic synching with a single entity in place. Fetching, creating, updating and deleting works as you'd expect. Association support is non-existent. The code is up on GitHub - I encourage you to look not only at the code, but the commit log too, as it is a reasonably good account of the various decisions I've made and then changed my mind about as I've worked on this.
The code is all wrapped up in an example app that uses HTTPRiot to interact with a bundled Rails 3 app which provides a REST interface (using standard Rails scaffolding) to its object model (currently just one entity).
All code that relates to synchronisation is agnostic of how and where the remote data comes from and that should be clear from the design. I've chosen to use a Rails app and HTTPRiot for convenience only. Over time, I will start to separate out the synching components from the main application.
Moving forward, a request for feedback
With the advent of the iPad, I think apps that interact with remote services are going to take an even more prominent role. I have a few ideas of my own that I hope would be able to use the code that I am writing now. I'd love it if others were able to eventually start using my code to write their own Core Data backed, web resource synched apps too.
I'd love to get some community involvement with this project. One of the reason I pushed it to GitHub so soon was to get it out there and make it real. I have so many projects in the dark corners of my file system that never see the light of day and I didn't want this to be one of them.
I wanted the decisions I make in the development of this to be as public as possible. I encourage other developers - especially those with good Core Data experience (which I lack) - to get involved. Use the GitHub Issues page to ask questions and leave feedback. Fork the project and see where you can take it.
Comments on this blog are still disabled, so if you want to make your thoughts known, as well as using the aforementioned GitHub issues page, I encourage you write your own blog post and @message me the link on Twitter and I'll happily update this post with links to interesting feedback.
Now what are you waiting for? Get forking!