Knowing when to use and when not to use a tool is as important as knowing how to use a tool. That is why before learning the easy/magic frameworks I try and do things by hand so I at least understand what the tradeoffs are and what that framework is actually providing.
I think a better explanation in this article would be to not use CoreData until you have hit the particular pain point that CoreData solves.
You are almost always better off making a tradeoff that you understand than one that you don't.
If you plan on using Core Data please read objc.io issue #4[1].
The reason I use Core Data is because the alternatives for data persistence are not that great. We basically have NSCoding Protocol[2] and FMDB[3]. I find both cumbersome to use.
Also, not that it matters for this conversation but it is Core Data, not CoreData.
objc.io issue 4 actually demonstrates one of the issues with Core Data: if you check the code, you see that it is not modeling the domain, instead it is encoding interactions with the database. Sigh.
I find it cumbersome because it's as much work as Core Data and seems to have many of the same API choices around threading, but I also have to write SQL. I don't like writing SQL.
Most iOS devs fail to realize that CoreDate was originally created for managing data in desktop apps, but many of the advantages it offers for Mac development don't exist on mobile. For example, iOS has no bindings. On the Mac you can bind your UI elements to data objects backed by Core Data so that as the data changes everything stays in sync. (Yes, like modern JS libraries.) For simple use cases you can do a surprising amount of development with very little code, although for complex use cases it can sometimes get ugly.
Also, I believe an SQL-based approach is more appropriate for the smaller screen sizes with iOS. The master-detail approach on iOS typically works out to showing a master list with a few labels describing each object, then drilling into to the object's details. In many cases all the labels on a master list (plus an associated object id) can fit into a single in-memory array, and an SQLite query works great to pull these labels strings off disk when the view loads. There's little reason to have CoreData manage faulting/loading entire data objects as users scroll up and down the master list just to view the titles. Of course the SQL data access should still be abstracted, but CoreData is overkill in cases like this.
> It's an older article (2012), but recently came to my attention via Drew McCormack (@drewmccormack): "Great post", he wrote, and after reading the article I not just disagreed, but found that Twitter wasn't really adequate for writing up all the things wrong with that article.
This is how I discovered the article and also felt exactly the same way as OP. It was a massive amount of hand-waving, appeal to authority, and supposition.
Core Data is just not a good technology. People on the other side of this debate will say that there's an appropriate time for everything, but nothing makes sense for everything, so you have to choose based on your needs. This is half true. It misses the fact that some things are just plain bad and are never better than the alternatives in any situation, and IMO Core Data is in this category.
There are several fundamental problems with it.
First, the API is awful. If you want decent model objects in memory you either have to do a bunch of manual work, or use a third-party tool like mogenerator. Even then, the result is a massive soup of mutable objects with no intelligence. The structure of the API encourages passing the entire context around everywhere, which basically turns all of these objects into global variables. Look no further than popular Core Data wrapper APIs to see how bad this can get. For example, ObjectiveRecord[1] adds class methods for looking up objects using a predicate. Modularity? Separation of concerns? Perish the thought.
Second, it ties your on-disk representation to your in-memory representation way too strongly, and this makes it more difficult to choose appropriate structures for either one, and more difficult to make changes to either one.
Third, it locks you into the technology something fierce. Core Data is so different from everything else that once you build your model layer on it, you're just about stuck there forever. You can move away, but it's a ton of work that reaches into every corner of your app. Other solutions, like serializing to a property list or SQLite, simply require a translation layer that you can switch out more or less at will.
Fourth, it's unbelievably slow. Literally unbelievable, as in I tell people about it and they don't believe me. It's too slow for a large number of records. It's fast enough for a very small number of records, but at that level it provides no advantage over using property lists and just loading everything into memory. At the large scale, you don't want to load everything into memory so you need a better scheme, but then you need something like SQLite so you can avoid the slowness and gain more control. Core Data is really only workable when your quantity of data is in a happy medium, which exists roughly within the range of 1097 to 1143 records. (Warning: previous numbers made up.) Since you can almost never guarantee that your data will live in that happy medium and not exceed it, Core Data is a bad choice.
I understand why its proponents advocate for it. It's shiny. Apple pushes it hard, and people tend to like what Apple tells them to like. It gives the illusion of making things easy. It does make certain simple things easy. The problem is that making easy things easier is not a virtue. Making difficult things easier is what counts, and Core Data fails at that.
If you're saying that Core Data locks you into the 'relational database' way of modeling data, that's definitely true. But, you're no longer tied to a specific persistence store. NSIncrementalStore lets you use whatever you want for persistence -- web service, JSON file, etc.
For a lot of applications, a relational model will work just fine. And, the optimizations that Apple provides for SQLite are probably going to be better than the average developer would achieve by writing their own SQLite transaction layer. (A little confused here on your point about needing SQLite at scale for performance -- it's the default persistent store on iOS).
I'm not sure I agree that an app needs to be wed to Core Data -- if the transactions with the persistent store are hidden from the model layer, then they should be swappable for something else.
I just started looking into Core Data over the weekend. My use case is that I need to sync a couple of sqlite tables( only 3 tables) in iCloud. Core Data seems to be the best way to do this. Is there a way to manage my own transactions if I do all the SQL myself?
Great points. I agree with some of them, but disagree with others.
> The structure of the API encourages passing the entire context around everywhere, which basically turns all of these objects into global variables.
I don't see it that way. In many cases, you don't need to pass around a context, you can directly pass the objects that you need to work on. And even when you do pass the context, its not as if you go through its -registeredObjects. You would usually get your objects using a fetch request -- how is it different from fetching your objects directly using SELECT over a db connection?
> For example, ObjectiveRecord[1] adds class methods for looking up objects using a predicate. Modularity? Separation of concerns? Perish the thought.
I really don't see how this is bad for modularity or separation of concerns? Can you please elaborate?
> At the large scale, you don't want to load everything into memory so you need a better scheme, but then you need something like SQLite so you can avoid the slowness and gain more control.
But Core Data does support SQLite as a backend, and it allows you to easily load only a subset of your data. Just create a fetch request with the appropriate predicate and you can work with just the objects you need.
One of the things that is definitely slow though is if you want to mass update or delete a significant number of your objects. In that case you would have to load all the objects in memory and update or delete them one by one, which can be a pain if you need to do this a lot (whereas with SQLite a simple UPDATE or DELETE statement would have sufficed).
Another reason for slowness historically has been that if you use two different contexts (say for multi-threading reasons), then in order to transfer updated information from one context to another you had to first save the information to disk, and only then could you fetch the updated info on the other context. The alternative to this is to lock the context and objects explicitly which has its own problems. However, with the introduction of parent contexts and new concurrency types in iOS 5, it is not a problem anymore. If a context has its parent context set, then when it saves its changes, they won't got to disk -- the changes are only registered in the parent context.
Core Data is not perfect at all, but I don't think it is completely unusable either.
One thing I’ve been doing lately to mitigate #2 and #3 is to use the repository pattern. Models become POCOs and the repositories worry about translating to and from SQLite/Core Data/etc. Is it perfect? Nope. But it does provide a lot of flexibility, and I sleep better at night knowing that I can swap out the data store much more easily should I need to.
I do the same thing (in a completely different language, mind you) -- it's quite handy. Especially when I'm working on the original classes and tests without worrying about data storage to begin with: hard-code the stuff that I need right now in the repository layer, and build up the logic. Then, when I'm happy with how that's going, swap over to a proper data-store, and chuck the routing layer in front of it. Bam, and you have a really nice web-app API ready for a front-end.
It's nice working this way, interestingly, sans-frameworks for once. Code is a lot more maintainable too.
Very true article. CoreData has a lot of path and legacy behind and thinking it would solve some problem magically because it is vended as "the high level problem solver" is a mistake. More than once I have seen performance problem appearing 2 weeks before release by (mis)using it.
Ultimately you need some way of archiving/caching data and Core Data is a great way of doing this. I started off with NSArchiver/NSCoding until I found MagicalRecord which made CoreData infinitely easier for me. Add MR to your project and never look back!
I moved from straight CoreData to ObjectiveRecord and haven't looked back. There are so many excellent solutions[0] out there that make CoreData much easier to use that I can't see much reason to using CoreData directly any more.
As with any technology, CoreData works well when the problem domain falls within its sweet spot. When the problem domain is outside that, CoreData can quickly become a challenge to work with.
While this is true, the problem is that the sweet spot for Core Data is quite small. Given the framework description, it’s quite easy to go with Core Data when some of its many drawbacks mean you really shouldn’t.
I think a better explanation in this article would be to not use CoreData until you have hit the particular pain point that CoreData solves.
You are almost always better off making a tradeoff that you understand than one that you don't.