A few blog entries that have been getting some attention were posted this week by Brent Simmons, Manton Reece, and Mike Zornek about why they are switching away from Core Data in favor of using SQLite directly. It also turns out that Aperture 3 away from Core Data, which I’m pretty sure means that are currently no major Apple products which use the framework. I wouldn’t claim to be in the same league as those other developers, but I still think a good case can be made for the positive aspects of Core Data and I’m going to try to do that now.
A little background: I’ve been developing Cocoa apps full time for two years and a year before that non-professionally. So that means 10.4 era on the Mac and since the iPhone SDK first became available. Every project I did for the iPhone previous to 3.0 used straight SQLite, either through the C API or FMDB. Since 3.0 every new project that I’ve started has used Core Data with great success and very few gotchas. I’m definitely a big believer in using the right tool for the job, and would also would agree with Brent that 95% of the time Core Data is the right choice. I think he made a pretty excellent case for when direct SQLite is going to make more sense (the example of needing to update lots of rows at once is pretty hard to argue with). So with that being said, I’d like to look at what makes it the right choice for that other 95%.
Disclaimer: This is going to be slanted towards iPhone.
Displaying Lists of Data and Fetch Requests
If large parts of your app essentially consist of displaying lists of data in table views and you don’t need to update lots of objects all at once (1000+) in the database (note: this is a lot of apps) using the NSFetchedResultsController has made it pretty much brainless with Core Data. Set up an NSFetchRequest with the type of entity you want to fetch, optionally define a predicate (‘parent = %@’, for example), fill in a couple of table view delegate/datasource methods and you have a way to display a list that can be added and removed from, complete with animations and automatic UI updating if you implement a few delegate methods. This is completely analogous to doing a query against your SQLite database, except that everything comes out as an object that be used immediately. This saves a ton of code that is essentially total busy work converting back and forth between C types and objects and less code equals less bugs.
Of course that’s a bit mitigated when using something like FMDB, because a string will come back as a string, but you still have to then take each thing allocate the model object you need and set each property. This hits on one of the greatest parts about something like Core Data in that you never have to stop thinking of your objects as objects. They’re objects in the database, they’re objects in memory. It’s all basically made irrelevant to you. You ask for them when you need them and thats it. I don’t completely buy the argument of wanting to feel more in control, because I’ve always been told part of being a Cocoa developer is learning to lean on and trust the frameworks. And I don’t really feel as though Core Data is “black magic” because I can think about it and feel as though I understand what’s probably going on behind the scenes (which admittedly I’m sure is a lot).
Multiple Managed Object Contexts
In apps that I’ve written, I tend to follow a pattern that Apple showed in one of their examples where when you are displaying a view which is essentially a “new object screen,” you can create a separate managed object context for this task and create any new entities that you need within that context. When the user hits cancel, throw the whole context away and it’s gone forever. Otherwise when they hit save you can merge the two MOC’s together which will force an update of your UI when using an NSFetchedResultsController. Easy, customizable, and does exactly what you want. You can do the same thing pretty easily for an edit screen as well.
Multiple Persistent Stores
This maybe doesn’t come up in a lot of apps, but I’ve written at least one where being able to manage an in memory and a SQLite store concurrently and make fetch requests against both was a huge benefit.
Performance and Memory Usage
This is one of the places where for specific use cases using direct SQLite might make a lot of sense. Like Brent said, if you need to update a lot of rows all at once Core Data is going to be slower for that. I also agree that on a Mac this probably wouldn’t have caused unacceptable performance due to having more horsepower. I think that the example of an RSS reader is one where this really stands out as making sense. Its sort of hard to think of to many other places where you might end up in the situation of needing to update thousands of rows in the database all at once and if you do then more power to you.
The fact is that in most cases for the kinds of things most developers are needing to do I think they’re going to see the same or better performance out of Core Data with a lot less work. Core Data’s batching mechanism for example allows you to keep from fully loading objects into memory for as long as possible. In one app I worked on where we needed to make thousands of records available at launch, we were able to lower the load time before the UI came up from several seconds to under a second just by implementing batching and a fetch limit. While the case of changing a large number of objects can be slow, reading can be very fast without very much work. I think more often than not you’re reading large amounts of data than writing it.
Conclusion
I could go on about Core Data for a while. The point of this post isn’t to point out where I think anyone else is wrong or why you shouldn’t use SQLite directly, but more to shed some light on what some of the reasons Core Data could be so good for that other 95%. I’m going to stick with saying that you should always use the right tool for the right job (that seems obvious), but in my experience Core Data is the right tool an awful lot of the time.