Core Data - Add Static / Lookup Data - iphone

I am creating an application, and am using Core Data as my backend data source.
I am wondering how I can manually add entries into a Core Data Entity which can be used to hold static data which will never change throughout the applications lifetime.

There are many solutions to this issue. First, for data entry, you can create a trivial desktop application in a couple of minutes that will let you do the data input. Since the underlying sqlite file is the same, you can then copy the file to your project.
As for accessing it, depends on your data structure. If you have entities that are both static and editable (such as a look up list that can be added to), then I suggest carrying the sqlite file in your bundle and on first launch copy it to the documents directory.
If the tables are NEVER writable then you can leave the static data in your bundle and create a second store in the documents directory that is writable. You can then bundle the two together on creation of your Core Data stack and the rest of your application will treat them as if they are a single database/object graph. The only issue with this is that you cannot have relationships across files.

I have never tried this, however I think you can populate core data with an example program which you run in the simulator which populates the database with the data.
Then you can get .sqlite database out of the simulator and into Xcode resource folder.
Every time your app starts, even first run the database will contain the objects you first populated it with in the example program.

Related

Where is my table in the sqlite file referenced in NSPersistentCloudKitContainer in AppDelegate?

I am new to sqlite. I successfully implemented CRUD methods to CloudKit in my app. I am not using Core Data. I read in the Apple developer documentation on Remote Records that:
CloudKit stores your records in iCloud and uses subscriptions to notify your app in real time about record changes. You then use change tokens to handle these changes efficiently. Additionally, you can improve your app’s performance and support offline use by storing records in a local cache.
I am interested in this local cache. After some digging around, it looks like this cache is automatically created and stored in the container referenced in AppDelegate.swift. I put a breakpoint in my code, and in the Debug Console, I typed: po (UIApplication.shared.delegate as! AppDelegate).persistentContainer
to discover where the .sqlite file is located. It shows:
<NSPersistentCloudKitContainer: 0x600002934c40>
CoreData: debug: CoreData+CloudKit: -NSCloudKitMirroringDelegate observeChangesForStore:inPersistentStoreCoordinator:: <NSCloudKitMirroringDelegate: 0x6000007e9d40>: Observing store: <NSSQLCore: 0x7f8276e1cc70> (URL: file:///Users/xxxxxxxx/Library/Developer/CoreSimulator/Devices/30CE52B9-E246-4B40-82F4-13DBE48EB7D6/data/Containers/Data/Application/5E26076E-12F0-4E86-9B1D-BED642C01BB6/Library/Application%20Support/MyAppName.sqlite)
In a Terminal window, I typed:
open file:///Users/xxxxxxxx/Library/Developer/CoreSimulator/Devices/30CE52B9-E246-4B40-82F4-13DBE48EB7D6/data/Containers/Data/Application/5E26076E-12F0-4E86-9B1D-BED642C01BB6/Library/Application%20Support/MyAppName.sqlite
It opened the .sqlite file in Liya. I see a bunch of tables like these:
.
I inspected each table in the list. None of the table names match the name of the Record Type and none of the field names match the names of the Record Fields that I see in the CloudKit Dashboard. So, is this .sqlite file really the cache that CloudKit created locally to "mirror" the database in iCloud? If so, how do I make use of this local cache when I can't even recognize the table name or field names used in my app?
The database you are viewing is, in fact, Core Data. When you use NSPersistentCloudKitContainer with CloudKit, your app will use Core Data as the local cache.
The SQLite database that Core Data uses is not meant to be manipulated directly since the Core Data framework manages how and when data is transacted with SQLite. You should only interface with the Core Data framework in your app and not worry about what is happening under the hood in SQLite.
If you want raw access to SQLite and full control over how your data is named and structured, then you will need to do things manually. You will need to setup and manage your own SQLite transactions and then manually manage how you sync with CloudKit.
Here are a couple good options for SQLite frameworks:
SQLite.swift: https://github.com/stephencelis/SQLite.swift
GRDB: https://github.com/groue/GRDB.swift

Loading a database directly into CoreData

Is it possible to load a database / entity directly into CoreData? The app relies of data, the initial data load being > 100,000 items. On an iPhone 4 it's taking about 15 minutes to complete the insertion, which is an unpleasantly long time.
Is there any way I could:
Load a pre-populated entity into CoreData
Load a pre-populated database (all entities) into CoreData
Something along these lines
to make the insertion of data significantly quicker
Thanks.
Create your database using CoreData and add it to your project bundle, then at first load, copy the database from the bundle to the file system so it will be writable, then open it from there. You may want to mark the database as don't share to iCloud depending on your needs.
Yes it is possible. The 'normal' approach to this is to pre-build the database, include it as a resource, check on startup whether you have a database in the documents folder and, if not, copy it over from the bundle.
I usually run my code to build the database on the simulator, and then go copy it from the sim folder and add to my project resources.
Copying a pre-made Core Data generated sqlite file via the application bundle is a good idea, even though I find it a bit cumbersome. This make most sense if you have a store for static data and one for dynamic data - in most cases it is feasible to copy the static one like this.
But nevertheless, for the record, 15 minutes is ridiculous. I can close to guarantee that you can cut this down to a few seconds.
I have been able to accomplish 500K inserts in less than 30 seconds on a first generation iPad. The most important optimisation techniques are:
disabling the undo manager,
using a background thread with #autoreleasepool,
saving periodically with exactly the right batch size (determine experimentally).

backing up and restoring .sqlite file with Core Data and externally stored images

I couldn't think of a better way to phrase this question, so let me clarify:
I'm storing many things in Core Data, some of which are images stored as binary data. These images have been set to allow for external storage, meaning, from what I can gather, that Core Data takes care of storing these pictures elsewhere on disk rather than inside the database itself if they are too large (> 1MB?). I am also trying to build a simple backup/restore system for this database using File Sharing - clicking "backup" copies my .sqlite file from my Library folder to my Documents folder (under a different name), allowing the user access through iTunes (to store wherever they please), and clicking "restore" looks for a backup file in the Documents folder, deletes the active .sqlite file in the Library folder and replaces it with the backup.
The problem arises when an image is deleted from Core Data, but then I want to restore my database to a time when that image still existed. When I delete that image using Core Data, I presume both the path to the image that is stored in the database and the image itself stored elsewhere are both deleted. But, when I restore my old database by bringing the .sqlite file back, only the path to the image exists - the image itself was stored externally, so it's nowhere to be found.
First of all, is this a reasonable approach to backing up and restoring a database under Core Data, or is there a clear reason that I shouldn't be going about it by copying the underlying .sqlite file back and forth? Second, can anyone provide advice on how I might go about allowing Core Data to take care of external storage while still having a way to back up those images? I realize that I could probably store images manually, thus allowing me to back up an image folder along with my .sqlite file, but I do like the simplicity of having Core Data manage this for me, so I'm just wondering if there is any better way to do this backup/restore thing.

How to use a pre-populated database on a coredata context

this is my first question here.
Well, I am developing an iPhone application that will use coredata, but the theres not going to be an option to insert data into the database of the app.
I want to know how to pre-populate it's database.
I thought of creating a plist with all the info and make a function to put it all into the coredata database if the database is not set yet...
but I want to know if it is the right way to do so.
Thanks all.
The easiest way is to create a desktop application with the same model (the exact same model even, just link it in). Then have Interface Builder create you a default UI for it and enter the data. Far easier than doing it on a device.
Once you have the data then include the SQLite file in your bundle and since your app is read only as you claimed; point the NSPersistentStoreCoordinator at the file in your app bundle and Bob's your uncle.
Creating an app on the desktop takes about 5 minutes and will save you far more time on the data entry side.
You can pre-create the database file (sqlite file) using your device and put that in the bundle when you distribute it. When you run the app for the first time you can move that from your applications bundle you can move it to the documents directory where core data will read it.
You can also have a function that runs at the start of your applications first run where it reads from your plist and creates the appropriate objects.

iPhone: Core Data: Updating a pre-filled database in future app versions

I am creating an app with a database of information that needs to be pre-filled. This data will change in future versions. In the same database, I also need to store user editable information since that user edited data directly relates to the pre-filled data.
My question is, if I'm pre-filling the database by creating a duplicate data model in a second app and copying over the core data file before release, how would I handle updates to that data in future versions of the app without destroying the user's existing data? Do the core data migration methods handle this, or must I write custom methods to programatically handle the merge at first app launch?
Depending on your data model, you can create two separate persistent files, one writable and one read only. You can then add both files to the NSPersistentStoreCoordinator and reach both of them together. You can then replace the read only file on future upgrades.
However this depends on how intertwined the two halves of the data are. If they are too tightly coupled then you will most likely have to do it the hard way; on future upgrades, purge the old data and copy the new objects over one by one.