58

I have written many iOS apps that was communicating with the backend. Almost every time, I used HTTP cache to cache queries and parse the response data (JSON) into objective-C objects. For this new project, I'm wondering if a Core Data approach would make sense.

Here's what I thought:

The iOS client makes request to the server and parse the objects from JSON to CoreData models.

Every time I need a new object, instead of fetching the server directly, I parse CoreData to see if I already made that request. If that object exists and hasn't expired, I use the fetched object.

However, if the object doesn't exist or has expired (Some caching logic would be applied here), I would fetch the object from the server and update CoreData accordingly.

I think having such an architecture could help with the following: 1. Avoid unnecessary queries to the backend 2. Allow a full support for offline browsing (You can still make relational queries with DataCore's RDBMS)

Now here's my question to SO Gods:

  1. I know this kinda requires to code the backend logic a second time (Server + CoreData) but is this overkill?
  2. Any limitation that I have under estimated?
  3. Any other idea?
Pier-Olivier Thibault
  • 3,907
  • 2
  • 33
  • 33

3 Answers3

96

First of all, If you're a registered iOS Dev, you should have access to the WWDC 2010 Sessions. One of those sessions covered a bit of what you're talking about: "Session 117, Building a Server-driven User Experience". You should be able to find it on iTunes.

A smart combination of REST / JSON / Core Data works like a charm and is a huge time-saver if you plan to reuse your code, but will require knowledge about HTTP (and knowledge about Core Data, if you want your apps to perform well and safe).

So the key is to understand REST and Core Data.

  • Understanding REST means Understanding HTTP Methods (GET, POST, PUT, DELETE, ...HEAD ?) and Response-Codes (2xx, 3xx, 4xx, 5xx) and Headers (Last-Modified, If-Modified-Since, Etag, ...)

  • Understanding Core Data means knowing how to design your Model, setting up relations, handling time-consuming operations (deletes, inserts, updates), and how to make things happen in the background so your UI keeps responsive. And of course how to query locally on sqlite (eg. for prefetching id's so you can update objects instead of create new ones once you get their server-side equivalents).

If you plan to implement a reusable API for the tasks you mentioned, you should make sure you understand REST and Core Data, because that's where you will probably do the most coding. (Existing API's - ASIHttpRequest for the network layer (or any other) and any good JSON lib (eg. SBJSON) for parsing will do the job.

The key to make such an API simple is to have your server provide a RESTful Service, and your Entities holding the required attributes (dateCreated, dateLastModified, etc.) so you can create Requests (easily done with ASIHttpRequest, be they GET, PUT, POST, DELETE) and add the appropriate Http-Headers, e.g. for a Conditional GET: If-Modified-Since.

If you already feel comfortable with Core Data and can handle JSON and can easily do HTTP Request and handle Responses (again, ASIHttpRequest helps a lot here, but there are others, or you can stick to the lower-level Apple NS-Classes and do it yourself), then all you need is to set the correct HTTP Headers for your Requests, and handle the Http-Response-Codes appropriately (assuming your Server is REST-ful).

If your primary goal is to avoid to re-update a Core-Data entity from a server-side equivalent, just make sure you have a "last-modified" attribute in your entity, and do a conditional GET to the server (setting the "If-Modified-Since" Http-Header to your entities "last-modified" date. The server will respond with Status-Code 304 (Not-Modified) if that resource didn't change (assuming the server is REST-ful). If it changed, the server will set the "Last-Modified" Http-Header to the date the last change was made, will respond with Status-Code 200 and deliver the resource in the body (eg. in JSON format).

So, as always, the answer is to your question is as always probably 'it depends'. It mostly depends what you'd like to put in your reusable do-it-all core-data/rest layer.

To tell you numbers: It took me 6 months (in my spare time, at a pace of 3-10 hours per week) to have mine where I wanted it to be, and honestly I'm still refactoring, renaming, to let it handle special use-cases (cancellation of requests, roll-backs etc) and provide fine-grained call-backs (reachability, network-layer, serialization, core data saving...), . But it's pretty clean and elaborate and optimized and hopefully fits my employer's general needs (an online market-place for classifieds with multiple iOS apps). That time included doing learning, testing, optimizing, debugging and constantly changing my API (First adding functionality, then improving it, then radically simplifying it, and debugging it again).

If time-to-market is your priority, you're better off with a simple and pragmatic approach: Nevermind reusability, just keep the learnings in mind, and refactor in the next project, reusing and fixing code here and there. In the end, the sum of all experiences might materialize in a clear vision of HOW your API works and WHAT it provides. If you're not there yet, keep your hands of trying to make it part of project budget, and just try to reuse as much of stable 3'rd-Party API's out there.

Sorry for the lenghty response, I felt you were stepping into something like building a generic API or even framework. Those things take time, knowledge, housekeeping and long-term commitment, and most of the time, they are a waste of time, because you never finish them.

If you just want to handle specific caching scenarios to allow offline usage of your app and minimize network traffic, then you can of course just implement those features. Just set if-modified-since headers in your request, inspect last-modified headers or etags, and keep that info persistent in your persistet entities so you can resubmit this info in later requests. Of course I'd also recommend caching (persistently) resources such as images locally, using the same HTTP headers.

If you have the luxury of modifying (in a REST-ful manner) the server-side service, then you're fine, provided you implement it well (from experience, you can save as much as 3/4 of network/parsing code iOS-side if the service behaves well (returns appropriate HTTP status codes, avoids checks for nil, number transformations from strings, dates, provide lookup-id's instead of implicit strings etc...).

If you don't have that luxury, then either that service is at least REST-ful (which helps a lot), or you'll have to fix things client-side (which is a pain, often).

neoneye
  • 50,398
  • 25
  • 166
  • 151
codeclash
  • 2,053
  • 19
  • 17
  • 1
    Thank you for referring to the WWDC session, I'm downloading it right now. I already know how to handle the flow of a Rest-ful application. What I was asking was if it was too overkill to undertake such an endeavour or not. From your answer I can't tell if you advocate for such an architecture or if you endorse it because you have already done an app using this specific architecture. Do you mind clarifying? – Pier-Olivier Thibault Feb 03 '11 at 15:57
  • As always, the answer is probably 'it depends'. – codeclash Feb 05 '11 at 18:21
  • I'll edit my answer, the comment box doesn't allow that many chars :-) – codeclash Feb 05 '11 at 18:31
  • I'll accept your answer as you provided a lot of information and it was indeed helpful. However, I decided to use CoreData and yes, I have been faced with some issues but right now, its looking good. Thank you! – Pier-Olivier Thibault Feb 16 '11 at 20:14
  • Really insightful answer. I watched the video and is now considering switching from my own caching code to using Core Data. In the WWDC video they recommend using binary plists instead of json, because it's significantly faster to deserialize. – neoneye Aug 20 '11 at 08:05
  • 1
    I just searched for "binary plist performance" and learned that JSONkit is faster than binary plists. http://www.cocoanetics.com/2011/03/json-versus-plist-the-ultimate-showdown/ – neoneye Aug 20 '11 at 08:16
5

I think it's a valid approach. I've done this a number of times. The tricky part is when you need to deal with synchronizing: if client and server can both change things at the same time. You almost always need app-specific merging logic for this.

Chris Eidhof
  • 1,514
  • 1
  • 12
  • 15
5

There is a solution out there that I couldn't try because I'm too far in my project to refactor the server caching aspect of my app but it should be useful for people out there that are still looking for an answer:

http://restkit.org/

It does exactly what I did but it's much more abstracted that what I did. Very insightful stuff there. I hope it helps somebody!

Pier-Olivier Thibault
  • 3,907
  • 2
  • 33
  • 33
  • 5
    For the record: there's another new API out on github: https://github.com/gowalla/AFNetworking. It doesn't provide Core Data persistence (which RestKit does), but it's a very nice and thight block-based API for interaction with RESTful services. It has built-in support for JSON-Parsing too (the fast JSONKit for iOS <5 and Apple's own new JSON serializer starting with iOS 5. IF you want easy async networking but still have full control over Core Data persistence this seems like a very clean solution. – codeclash Aug 20 '11 at 23:23