Core Data Store Included in App Bundle

Core Data Store included in App Bundle

You can include the store file (sqlite db most of the time) in your app.
Then in your app delegate edit the persistentStoreCoordinator getter merhod :

- (NSPersistentStoreCoordinator *)persistentStoreCoordinator {

if (persistentStoreCoordinator_ != nil) {
return persistentStoreCoordinator_;
}

NSString *storePath = [[self applicationDocumentsDirectory] stringByAppendingPathComponent: @"CoreDataStore.sqlite"];

// Check if the store exists in.
if (![[NSFileManager defaultManager] fileExistsAtPath:storePath]) {
// copy the payload to the store location.
NSString *bundleStore = [[NSBundle mainBundle] pathForResource:@"YourPayload" ofType:@"sqlite"];

NSError *error = nil;
[[NSFileManager defaultManager] copyItemAtPath:bundleStore toPath:storePath error:&error];

if (error){
NSLog(@"Error copying payload: %@", error);
}
}

NSError *error = nil;
NSURL *storeURL = [NSURL fileURLWithPath:storePath];
persistentStoreCoordinator_ = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
if (![persistentStoreCoordinator_ addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:nil error:&error]) {
NSLog(@"Unresolved error %@, %@", error, [error userInfo]);
abort();
}

return persistentStoreCoordinator_;
}

Loading images from AppBundle vs. CoreData

Now in Core Data there is an "allows external storage" option for binary data, which basically means if your file is bigger than 1 MB it will be stored automatically outside of your database, and you have to do nothing differently. In my opinion that's the way to get the best of both worlds, increased performance + automatization + fast queries (although they are slower than usual when you allow external storage, but still faster than doing it yourself)

Can I keep my Core data store on the Bundle if its readonly

Yes, you can put read-only Core Data stores in the bundle and access them just fine. As long as you never have to change the store, you're OK. If your data ever needs to change, you'll have to update the binary.

How can I ship my app with a pre-populated Core Data database?

If you're already loading the pre-load data via a temporary routine for testing in your current code there's no reason you can't use the sqlite file it creates in the simulator's directory (no need to write a separate Mac app).

If you're not already filling that db you can still write an iOS app that does it. Odds are you've already written the methods for adding data to your store so you can use them to import the pre-load data as well.

Either way you'd grab the sqlite file from the simulator's directory and add it to your app's bundle; on first launch you'll copy it into the appropriate place in the app's directory before pointing Core Data to it. If it's really large the downside is that there will be a copy in the bundle and another on disk, but there's not much you can do about that other than grabbing the data over the network.

As others have suggested, if the amount of data is small you can just import it at first launch, using the methods you've already written for adding data as part of the normal app's workflow.

iOS - CoreData completely change model with new data

If you don't to write migration policy, then way for you is, delete the old store create the new one in didFinishLaunchingWithOptions and fire initial sync so that whole data is downloaded again for the user. then there won't be any crash.

But it is always preferred to use core data migration and writing migration policy's for heavy migration.

Core Data: Persistent Store Works in Xcode but not on Device

It's quite easy ...

  • prepare your CoreData database file
  • name it, InitialData.sqlite for example
  • add it to your application bundle

... and add something like to this just before NSPersistentStoreCreation allocation & initialization.

NSString *storePath = ... your store path for persistent store coordinator ...;

NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:storePath]) {
NSString *initialStorePath = [[NSBundle mainBundle] pathForResource:@"InitialData" ofType:@"sqlite"];
if (defaultStorePath) {
[fileManager copyItemAtPath:initialStorePath toPath:storePath error:NULL];
}
}

I assume you want to copy some default data to your application. That's all I can deduce from your question - it's quite unclear what you really want.

Where are my core data entities?

I did some additional Duck Duck Going and managed to find the information I needed in the this answer. The upshot is that, because a test target doesn't use a "main" bundle, I have to instantiate the test bundle. So instead of this line:

    model = [NSManagedObjectModel mergedModelFromBundles:nil];

I now have these three lines:

    NSBundle *bundle = [NSBundle bundleWithIdentifier:@"com.example.LogicTests"];
NSURL *url = [bundle URLForResource:@"MyModels" withExtension:@"momd"];
model = [[NSManagedObjectModel alloc] initWithContentsOfURL:url];

The bundle identifier comes directly from my target build info, while "MyModels" comes from my data model file, which is named "MyModels.xcdatamodeld" and included in the app bundle as "MyModels.momd". And that, of course, contains my models.

How to pre-load Core Data with a SQLite file that have references to images that were saved using external storage?

Step 1: Create "MyAppSeedData" dir and paste MyApp.sqlite, the MyApp_SUPPORT, the MyApp.sqilte-smh, MyApp.sqilte-wal files inside.

Step 2: Drag MyAppSeedData to the bundle under AppDelegate and tick the box add target.

Step 3: These functions must be in AppDelegate file:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool
{
//If first launch condition == true {
seedData()
//}
return true
}


func seedData() {
let fm = FileManager.default

//Destination URL: Application Folder
let libURL = fm.urls(for: .libraryDirectory, in: .userDomainMask).first!
let destFolder = libURL.appendingPathComponent("Application Support").path
//Or
//let l1 = NSSearchPathForDirectoriesInDomains(.applicationSupportDirectory, .userDomainMask, true).last!
//

//Starting URL: MyAppSeedData dir
let folderPath = Bundle.main.resourceURL!.appendingPathComponent("MyAppSeedData").path

let fileManager = FileManager.default
let urls = fileManager.urls(for: .applicationSupportDirectory, in: .userDomainMask)
if let applicationSupportURL = urls.last {
do{
try fileManager.createDirectory(at: applicationSupportURL, withIntermediateDirectories: true, attributes: nil)
}
catch{
print(error)
}
}
copyFiles(pathFromBundle: folderPath, pathDestDocs: destFolder)
}

func copyFiles(pathFromBundle : String, pathDestDocs: String) {
let fm = FileManager.default
do {
let filelist = try fm.contentsOfDirectory(atPath: pathFromBundle)
let fileDestList = try fm.contentsOfDirectory(atPath: pathDestDocs)

for filename in fileDestList {
try FileManager.default.removeItem(atPath: "\(pathDestDocs)/\(filename)")
}

for filename in filelist {
try? fm.copyItem(atPath: "\(pathFromBundle)/\(filename)", toPath: "\(pathDestDocs)/\(filename)")
}
} catch {
print("Error info: \(error)")
}
}

// MARK: - Core Data stack

lazy var persistentContainer: NSPersistentContainer = {
let modelName = "MyApp"

var container: NSPersistentContainer!

container = NSPersistentContainer(name: modelName)

container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()


Related Topics



Leave a reply



Submit