I'm going to be using a SQLite3 DB (after being told about them earlier today) for saving player progress through various worlds and levels in my game.
I was wondering how I would go about updating the database in a update to the game. So, I release V1.0 which has 10 levels and 1 world, if I then wish to update the DB to have 20 levels and two worlds in a v2.0 release in iTunes six months later, whilst preserving the data in the DB such as the player's score on each level to date, how would I go about that?
My understanding was that the DB is deployed with the app, so part of my question is, what happens when there is already a DB present? Also, how can I avoid overwriting the DB on the device and perform a smooth update procedure for the user?
For reference, I'm using this wrapper.
You can update it without removing the old database. If you database is in main bundle it will be removed but if it is in document directory it will not be remove while updating the application.
Assuming you db is in document directory, You can update you database without any problem, it fully depends on your implementation. You can easily insert your new levels & new world, just be careful about you implementation, do not replace or drop any previous table.
I had asked a similar question before see this. Hope this helps.. :)
Related
I am attempting to complete my first iOS app and could use some help in efficiently merging two persistent stores. By "efficiently", I mean dealing with adds and changes without having to manually do a record-by-record analysis to end up with a single store with no duplicates. I foresee continual updates to the database we supply the user and will need to update their database without destroying any new records they may have added to database we originally supplied them.
Currently, at start-up the app checks for a persistent store in the user's Documents directory - call it "Main.sqlite". If it doesn't find the file, then it copies SeedData.sqlite from the app bundle to Main.sqlite in the user's Documents directory using NSFileManager method, -copyItemAtPath:toPath:error:.
Next, if it DOES find Main.sqlite in the Documents directory, then I would like to merge SeedData.sqlite into Main.sqlite. Both stores have exactly the same structures. The merge criteria would be:
SeedData record MATCHES Main record - take no action.
SeedData record NOT FOUND in Main - add SeedData record to Main.
The number of records in each store would be on the order of a few thousand.
I know Sunny dealt with a more complicated scenario two years ago.
What is an efficient way to Merge two iOS Core Data Persistent Stores?
I'm wondering if anything has changed in the last 2 years that would make the solution even easier and what, if any, impact the change in iOS 7 to the default journal mode from rollback journaling to Write Ahead Logging has on the solution.
I've read Marcus Zarra's blog and Ray Wenderlich's guys say that migration is for moving a database to a new structure and not for moving new data between two identical structures - although it seems as though Sunny disagrees.
What's the best approach?
I have an app that reads wind readings at sites around the world. I decided to use iCloud and Core Data using a shoe-box style app.
The wind readings update hourly, after a few weeks of using the app I realised this was a bad idea as iCloud/Core Data just fills up with megabytes of transactions and restoring a device takes 10 minutes to download the store to a fresh device.
My solution to this was to use Core Data configurations so that the "sites" were stored in the iCloud store but the hourly changing "wind readings" which get deleted after 12 hours were stored in a local store. If it makes it easier to imagine, it works similar to RSS "sites" and "entries" which change hourly.
This all works great but I can't work out how to write migration code for the 2.0 version of my app. After reading how configurations work I had to remove the parent/child relationship between sites and wind readings and use fetch requests to link them up using a common siteIdentifier UUID.
Doing it this way I assume I cannot use light-weight migrations? Also loading up the versioned .momd model file just gives me the latest model so how do I get hold of the original model file to load up the store and do everything manually.
On the other hand, is this just too complicated and I would be better removing iCloud support or there is another way you'd recommend?
You should be able to use a lightweight migration in this situation.
The reason is, as far as your 'iCloud' configuration is concerned you are just deleting an entity and dropping a properties (i.e. dropping a table and a column). Automatic migration can handle that just fine.
However...
There is a catch. It won't copy the data you have to the 'local' configuration first. Therefore you will need to do that manually before the migration. Here are the basic steps:
Determine if this migration needs to occur.
Copy the sqlite file to "local.sqlite".
Stand up the iCloud configuration, this will delete the readings.
Stand up the local configuration, this will delete the sites.
Test, test, test again, and keep testing.
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.
I need to define a large amount of data to be stored within an app and used as a lookup table. For instance, I have an array of manufacturer names, each with a mfg code. Each manufacturer can make different products, each with their own code as well.
A,7 could be deciphered to mean
Manufacturer: Apple(A)
Product: MacMini(7)
I see several ways of defining this, but I'm not sure which would be best.
Option 1) #define these constants in a separate header file such as:
#define MFG_APPLE #"A"
#define MFG_DELL #"B"
#define PRODUCT_MAC_MINI 7
#define PRODUCT_INSPIRON 2
Option 2) create a dictionary object filled with dictionary objects to allow me to index through them easier.
Option 3) use core data to create a database of these mfgs and products and relationships.
If option 2 or 3 is suggested, are there easy ways to pre-populate these data structures instead of hard-coding them to populate during program startup?
Option 4) Create a web service to tie this back to a server, where the data can be updated more often. A JSON query will send the mfg and product codes to the server, where it can respond with the mfg and product names.
You should consider the following: If the database is shipped with the app, you will have to release an update for the app each time the database must be updated. So the question is, how frequently will you have to update the data? If it's fine to update the database once every couple of months or maybe just once a year, shipping the database with your app might be an option, if you need to update it every month or even weekly, you should definitely host the database somewhere on the web; releasing an update in such short intervals is not a feasible option.
Another thing you should consider: If the database exists solely as a web service and each look up requires a JSON call to the server, it won't be possible to perform a lookup if the user is offline (currently has no network access for whatever reason). Also each lookup costs the user traffic, so if the user has a monthly limit, yet needs to perform plenty of lookups a day, using your app may quickly cause him to exceed that monthly limit, leaving him without any Internet service (or a very throttled one) in the end.
From my experience, it is best to host such a database online, yet cache it for offline access if possible. The app itself ships with a database copy, that was up-to-date the day you built the app for distribution. Each time the app is started, and maybe once a day in case the app is never quit, it will query a web server for the current "version" of the database. If this version is newer than the one shipped with the app, it tries to downloads a copy of this database to its local cache and then switches to the cached copy for future lookups. If the cached copy gets lost (caches may be flushed by the system at any time), it will have to re-download it. In the meantime, it can use the shipped database, which is outdated, yet better than nothing. If download is not possible (e.g. not enough free space is available on the device), the app may want to make online queries directly if the user currently is online, fall back to the out-dated shipped database if he is offline, and retry to download a cache copy at some later time (maybe the device will have more free space available at that time).
So basically your app will have a work flow as follows:
START
A locally cached copy exists? If NO Goto 6.
The locally cached copy is up-to-date? If NO Goto 5.
Perform the lookup using the local cached copy. Goto 12.
Delete the outdated cached copy. Goto 1.
The shipped database is up-to-date? If NO Goto 8.
Perform the lookup using the shipped database. Goto 12.
Download the updated database.
Download succeeded? If YES Goto 4.
The user is currently online? If NO Goto 7.
Perform the lookup using a JSON webservice. Goto 12.
END
If you only add more entries to the database in the future, yet existing entries will never change, there is also another, even much better option: You have simply two databases. One that ships with the app and one that only contains the updates (new entries added) after the last app release. This shrinks the amount of data that needs to be downloaded and cached dramatically. In that case your app must always perform two lookups, one in the shipped database (which is always performed first), and if nothing is found there, in the downloaded cached copy, which does not contain the entries already found in the shipped database (or directly online, if no cached copy is available, yet the user currently has Internet access). Each time you release a new update of the app, it will get a new full copy of the database, hence you can reset the update database back to zero entries and only keep adding new entries there (or you can keep different update databases lying around on the server for different app versions that had different databases shipped with them, if you don't think that is too much hassle to manage).
The update database for download may even be created dynamically by the server, that would of course be the best option. E.g. after shipping the app, you add 3 vendors and 30 products to the database, and every vendor and product has a unique ID (that is strictly increasing with each new entry added), then the app can tell the server that the highest vendor it knows has ID X and the highest product has ID Y, in which case the server sends out an update database with all vendors and products whose IDs are higher than X and Y.
All these decisions influence on the database format to use. Generally it sounds a lot like a job for CoreData, yet if you want dynamic update databases, the updates should be delivered in a different format (JSON, XML, CVS, or something else a server can easily generate) and be converted to CoreData by the app after the download is completed, since dynamically generating CoreData databases on a server is rather hard and definitely not recommend.
We're performing our first iOS app update, and also our first Core Data migration.
It seems more complicated than the examples of the Standard and Lightweight Core Data migrations i've seen online, but perhaps i'm missing something.
Our scenario is that we've updated the .xcdatamodel (simply added a new field), and also a lot of the reference data used in our app (stored in our Core Data database), but we need to retain some user data (stored in the same Core Data database).
I've added multiple versions of the model definition into our .xcdatamodelld file, and have played around with a Lightweight Core Data migration process (using a Mapping Model (an .xcmappingmodel file)), which successfully updates the model, but I can't see any obvious way in which it would allow us to import selected data (the user's data) from a previous version of the database into a new one bundled with the next version of the app (containing our updated reference data).
Any advice on how to approach this scenario would be very much appreciated.
Thanks in advance, Ted
Your users' database will be upgraded "in place". There won't be any migration or importing/exporting necessary. When the user runs the new version of your app, the existing database will be upgraded with the new fields. I'm not sure if this answers your question, but there won't be any "importing" going on.
In the end we've worked around this situation by putting the user's data into a plist file (there's a fairly limited amount of this), and retaining the Core Data database to use solely for reference data in the system, so it can be overwritten in future without worry.
A lightweight migration updates the data model on first run, and then a one off migration call creates and populates the user data plist file, renames the v1 core data persistent store *_migrated.sqlite, copies the v2 sqlite database from the bundle into the documents dir, then resets the MOM, and sets the MOM, MOC and Persistent Store to nil, so that the next time Core Data starts it uses the v2 sqlite database as its Persistent Store.
Phew. I hope this makes some sense to anyone reading it, feel free to ask for any other details, but it was honestly a lot simpler than it all sounds!