when dealing with CoreData, I've run into a few problems I'm trying to nip in the bud for future proofing the system out of the gate. The simple fact of the matter is that I've never done anything like this before (work with CoreData that is). While I've managed to figure out how to work with it in the app, I need to know a decent practice to signal an app between versions that default data needs to be refresh on first app launch.
So right now, in my AppDelegate, I setup my managed object context, and I perform a fetch request to see if there are any records at all in a particular table/entity. I only want this to happen on first launch so im not constantly rewriting the contents of the DB every app launch. Anyways, so it goes ahead and uses Object Models to handle inserting of data amongst the entities in question (theres a few)
Now, for this version of the app, it's going into the store without an API (thats a far future thing), but between versions released to the app store, we may have to update specific information within the entities (for example: prices), again I only want this refresh to happen on app launch. Also, the schema MIGHT change, Im not sure if or when, but I'd like to make sure this can accomodate that just in case.
I figured, versioning the coredata "Add Model Version" would do the trick, set the new db version as the active version, but when I launch the app in the simulator, nothing happens which tells me that the data inside is being retained.
Any help towards what it is that I should do to accomodate this would be appreciated. Thank you!
You should find the Core Data Model Versioning and Data Migration guide useful:
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/CoreDataVersioning/Articles/Introduction.html
You'll also probably find Method for import initial data with coredata useful.
Related
I am making an app that will store scores (kind of like a quiz app) - I will be playing around with core data and storing values to work out the best way to use core data. I know that if you start storing and fetching from core data and you then add/delete entities without migration, problems occur.
But, as I am only testing, I do not mind deleting the app of my device, and then reinstalling (which will cause me to lose data) - will this help me avoid the need of migrating (because when I download, it is seeing the new core data for the first time)
Thanks!
As you said "I do not mind deleting the app of my device, and then reinstalling (which will cause me to lose data)" so you dont need migration at all. Migration is needed when you have a already published app on appstore and you dont want to create a mess for people who have already installed your app.
I created a framework to share my data objects between the App and the extension. This includes the data model and the sqlite file with my Core Data db.
I am concerned of what might happen if both App and Extension try to access this shared sqlite db.
What could go wrong if the App is making some changes to the db in the background while the extension is using it?
What's the best practice in this case?
As #CL notes, SQLite is fine with this. But you're not using SQLite directly, so you may need to do some Core Data-level work to maintain consistency. With an iOS app and an extension, you have two separate processes that can make changes to the data. Your code needs to account for this.
If your app extension only displays data (for example, a "today" extension that only displays data created in the app), you probably don't need to do anything special. If your app is running in the background and creating new data while the extension is visible, it's possible that the extension's data could be slightly out of date. If that's important, you can refresh it. But today extensions typically aren't visible for very long, so it's probably not worthwhile. In this case I'd use NSReadOnlyPersistentStoreOption when setting up the extension's Core Data stack just to be clear about the intention and prevent inadvertent changes.
If your app extension creates new data or modifies existing data, your app needs to be aware of this and respond appropriately. What changes you would make will depend on how exactly the extension handles the shared data. For modified data, your app probably needs to call refreshObject:mergeChanges: on any in-memory managed objects with NO as the second argument. It also probably needs to redo any fetches where the changes might affect a search predicate. That'll ensure you're getting the most recent updates. For new data you would need to re-fetch any data the app is working with to get new additions/deletions. A good time to check would be when the application comes to the foreground (i.e. when UIApplicationWillEnterForegroundNotification is posted).
I've done a lot of changes to my core data model. In the past we used the simple automatic migration. However this will fail this time. Since I really don't care about the data being migrated I just want to delete the persistent store if auto migration fails and set it up again. Is this a valid way to go? Any thing I have to be careful ? Could this get my app rejected ?
There are some definite problems with doing that, and you need to be careful with it. This answer had some good advice from the NSManagedObjectContext's documentation
A context always has a “parent” persistent store coordinator which
provides the model and dispatches requests to the various persistent
stores containing the data. Without a coordinator, a context is not
fully functional. The context’s coordinator provides the managed
object model and handles persistency. All objects fetched from an
external store are registered in a context together with a global
identifier (an instance of NSManagedObjectID) that’s used to uniquely
identify each object to the external store.
When faced with a similar situation in one of our apps, I opted to make a new persistent store, and deprecate the old one because our old store had been messed up on many of our devices by a previous bad migration. It ended up being a messier transition than I had hoped, but it did work.
The problems with your plan are not insurmountable, I'm just recommending caution. I liked Giao's advice of using NSManagedObjectContext's reset. When deleting and rebuilding, the persistent store coordinator could get confused. I worry because Apple seems to be doing so many things behind the scenes. I also worry because It seems like core data behaves differently on released apps than it does on our debug versions, especially in the upgrade process.
I think you are smart in recognizing that your automigrate is going to have trouble, and that you are looking for another path. In the recent past I've seen a group that really had to scramble for a month to deal with a failed data migration in their app.
I created a new App using UIManagedDocument. While on my devices everything is fine, I got a lot of bad ratings, because there are problems on other devices :(
After a lot of reading and testing, I decided to go back to the traditional Core Data stack.
But what is the best way to do this with an app, that is already in the app store?
How can I build this update? What should I take care of?
Thanks,
Stefan
I think you may be better off to determine your issues with UIManagedDocument and resolve them.
However, if you want to go to plain MOC, you only have a few things to worry about. The biggest is that the UIMD stores things in a file package, and depending on your options you may have to worry about change logs.
In the end, if you want a single sqlite file, and you want to reduce the possibility of confusion, you have a class that simply opens your UIManagedDocument, and fetches each object, then replicates it in the single sqlite file for your new MOC.
Now, you should not need a different object model, so you should not have any migration issues.
Then, just delete the file package that holds the UIManagedDocument, and only use your single file sqlite store.
Basically, on startup, you try to open the UIManagedDocument. If it opens, load every object and copy it into the new database. Then delete it.
From then, you should be good to go.
Note, however, that you may now experience some UI delays because all the database IO is happening on the main UI thread. To work around this, you may need to use a separate MOC, and coordinate changes via the normal COreData notification mechanisms. There are tons of documents, examples, and tutorials on that.
EDIT
Thanks for your answer. My problem with these issues is, that I'm not
able to reproduce them. All my Devices are working fine. But I got a
lot mails, about problems like this: - duplicate entries - no data
after stoping and restarting the app - some say, that the app works
fine for some days and stops working(no new data). These are all
strange things, that don't happen on my devices. So for me the best
way is to go back to plain MOC. My DB doesn't hold many user generated
data, all the data is loaded from a webservice, so it's no problem to
delete the data and start of using a new DB. – Urkman
Duplicate entries. That one sounds like the bug related to temporary/permanent IDs. There are lots of posts about that. Here is one: Core Data could not fullfil fault for object after obtainPermanantIDs
Not saving. Sounds like you are not using the right API for UIManagedDocument. You need to make sure to not save the MOC directly, and either use an undo manager or call updateChangeCount: to notify UIManagedDocument that it has dirty data that you want to be saved. Again, lots of posts about that as well. Search for updateChangeCount.
However, you know your app best, and it may just be better and easier to use plain Core Data.
Remember, if you are doing lots of imports from the web, to use a separate MOC, and have your main MOC watch for DidSave notifications to update itself with the newly imported data.
UIManagedDocument is a special kind of document, an UIDocument subclass, that stores its data using Core Data Framework. So it combines the power of document architecture and core data capabilities.
You can read more about document based architecture from Document Based App Programming Guide for iOS and I recommend WWDC2011 Storing Documents in iCloud using iOS5 session video. I also recommend Stanford CS193P: iPad and iPhone App Development (Fall 2011) Lecture 13.
What is created when you call saveToURL:forSaveOperation:completionHandler: is an implementation detail of UIManagedDocument and UIDocument and you should not really worry or depend on it. However in current implementation a folder containing an sqlite database file is being created.
No. All entities will be contained in a single database file also more generally called: a persistent store. It is possible to use more than one persistent store, but those are more advanced use cases and UIManagedDocument currently uses one.
UIManagedDocument's context refers to a NSManagedObjectContext from underlying Core Data Framework. UIManagedDocument actually operates two of those in parallel to spin off IO operations to a background thread. When it comes to the nature of a context itself here's a quote from Core Data Programming Guide:
You can think of a managed object context as an intelligent scratch pad. When you fetch objects from a persistent store, you bring temporary copies onto the scratch pad where they form an object graph (or a collection of object graphs). You can then modify those objects however you like. Unless you actually save those changes, however, the persistent store remains unaltered.
But it really is a good idea to take a look at the lectures and other material I posted above to get a general picture of the technologies used and their potential value to you as a developer in different situations.
I use CoreData extensively in my app for viewing products. All data syncing is done in the background on a separate NSManagedObjectContexts and when syncing of changes is complete I call mergeChangesFromContextDidSaveNotification
This works perfectly 99% of the time. Here is where it is breaking down:
I view a product in my app.
I decide I want to change a price, so I goto to the web server where I control my products and change my price.
I come back to my app, browse away from that product and my app sync's the changes made on the web server to my app.
I go and view that product again and the price is not changed.
I completely exit the app and come back into it and then I can see the price change.
I believe what is happening is CoreData is serving up a cached version of the product and the fresh one is not served until after the app is restarted of I have viewed a bunch of other items.
How can I clear this cache after my mergeChangesFromContextDidSaveNotification has been called?
This is driving me and my clients crazy - anyone know how I can remedy this situation?
Well, there could actually be a lot of reasons for this. One of the big advantages of Core Data is its flexibility... which is also one of its big drawbacks.
You have a ton of knobs that can be set on your database that relate to caching and fetching. One of the easiest, though, is if the fetch goes to the database or just to the most recent MOC.
Namely, you should look at these:
- (BOOL)includesPendingChanges
- (BOOL)shouldRefreshRefetchedObjects
Also, make sure you are handling the background update and DidSave notification properly, because the MOC should have merged those changes.
Unfortunately, Core Data has some, let's say, hard to discover, interactions when using multiple MOCs. You must be very careful to follow all the rules.