Ulli H
Ulli H

Reputation: 1878

Swift: cannot preload Coredata

When I include a SQLite file with Objective-C under "Target - Build Phases - Copy Bundle Resources", this file will be completely copied to the target, i.e. device or simulator. On the target, I get the whole file: tables and contents (Records/rows).

Doing the same with Swift, the tables will be copied, but they are empty: no records/rows. :-(

Can I do something additional? Is there any "trick"?

How can I preload my core data with base-records using Swift???

I'm using Xcode v6.1.1, with Beta 6.2 it is the same.

Upvotes: 2

Views: 1281

Answers (3)

marciokoko
marciokoko

Reputation: 4986

I had a related issue related to this. I started working on some tutorials which used this line:

documentsURL.URLByAppendingPathComponent("StoreName")

At some point this stopped working. Data was not being written to the store anymore. To see why data was not being written, I tried opening the sqlite file but could not find it on my mac at the URL being used. The app folder was there, but there was no sqlite file. I looked back at older core data projects and found they used .sqlite extension. So I modified the line to:

documentsURL.URLByAppendingPathComponent("StoreName.sqlite")

This seemed to work so give it a try.

Upvotes: 0

Ulli H
Ulli H

Reputation: 1878

This is my solution (for sqlite-files). I don't know why it is so "extravagant" in Swift. Perhaps there's an easier way? Many THX to pbasdf!!!

Important: beneath the *.sqlite-file you must add the *.sqlite-shm and the *.sqlite-wal to your project This files will automatically be added to "Target - Build Phases - Copy Bundle Resources"

This is based on the Standard-Template for the "AppDelegate.swift"

lazy var persistentStoreCoordinator: NSPersistentStoreCoordinator? = {
    // The persistent store coordinator for the application. This implementation creates and return a coordinator, having added the store for the application to it. This property is optional since there are legitimate error conditions that could cause the creation of the store to fail.
    // Create the coordinator and store
    var coordinator: NSPersistentStoreCoordinator? = NSPersistentStoreCoordinator(managedObjectModel: self.managedObjectModel)

    let dataName = „MyData.sqlite" // must be replaced by the name of your file!
    let modelName = „MyData“ // must be replaced by the name of your model!

    let url = self.applicationDocumentsDirectory.URLByAppendingPathComponent(dataName)

    let fileManager = NSFileManager.defaultManager()
    let bundle = NSBundle.mainBundle()
    let fromURL = bundle.URLForResource(modelName, withExtension: "sqlite")

    // check sqlite-file: must it be installed?
    if !fileManager.fileExistsAtPath(url.path!) {
        self.copySqlliteFiles(modelName, databaseName: dataName)
    }

    var error: NSError? = nil
    var failureReason = "There was an error creating or loading the application's saved data."

    if coordinator!.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: url, options:  nil, error: &error) == nil {

...}

   return coordinator
}()

// MARK: - copy sqllite-files (c) by Ray Wenderlich & Team in „“Core Data by Tutorials“

// check sqlite-files: they must be installed...
func copySqlliteFiles(modelName: String, databaseName: String)
{
    let bundle = NSBundle.mainBundle()

    let baseDatabaseURL = bundle.URLForResource(modelName, withExtension: "sqlite")
    let documentsURL = self.applicationDocumentsDirectory
    let storeURL = documentsURL.URLByAppendingPathComponent(databaseName)
    NSFileManager.defaultManager().copyItemAtURL(baseDatabaseURL!, toURL: storeURL,error: nil)

    let baseSHMURL = bundle.URLForResource(modelName,withExtension: "sqlite-shm")
    let shmURL = self.applicationDocumentsDirectory.URLByAppendingPathComponent(databaseName + "-shm")
    NSFileManager.defaultManager().copyItemAtURL(baseSHMURL!, toURL: shmURL, error: nil)

    let walURL = self.applicationDocumentsDirectory.URLByAppendingPathComponent(databaseName + "-wal")
    let baseWALURL = bundle.URLForResource(modelName, withExtension: "sqlite-wal")
    NSFileManager.defaultManager().copyItemAtURL(baseWALURL!, toURL: walURL, error: nil)
}

Upvotes: 4

David Berry
David Berry

Reputation: 41226

I think this probably has to do with how files are laid out on iOS. Specifically:

  1. when you copy the source file (by Xcode or at install time) it gets placed into the application bundle/container directory (which is read-only)
  2. The standard CoreData initialization code looks for the data store in the Documents directory.
  3. Not finding your source data (because it's in the wrong place), CoreData creates a new empty store.

To fix this, you need to copy the store from the Application directory to the Documents directory if it doesn't already exist.

This link gives an explanation and example code to do that. But in Objective-C it looks like:

NSString *storePath = [[self applicationDocumentsDirectory] 
    stringByAppendingPathComponent: @"MyDB.sqlite"];
NSURL *storeUrl = [NSURL fileURLWithPath:storePath];

// Copy the default db if it doesn't already exist
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:storePath]) {
    NSString *defaultStorePath = [[NSBundle mainBundle] 
        pathForResource:@"MyDB" ofType:@"sqlite"];
    if (defaultStorePath) {
        [fileManager copyItemAtPath:defaultStorePath 
toPath:storePath error:NULL];
    }
}

Upvotes: 0

Related Questions