Core Data with pre-filled .sqlite (Swift3)
This is how I do it:
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "app_name")
let seededData: String = "app_name"
var persistentStoreDescriptions: NSPersistentStoreDescription
let storeUrl = self.applicationDocumentsDirectory.appendingPathComponent("app_name.sqlite")
if !FileManager.default.fileExists(atPath: (storeUrl.path)) {
let seededDataUrl = Bundle.main.url(forResource: seededData, withExtension: "sqlite")
try! FileManager.default.copyItem(at: seededDataUrl!, to: storeUrl)
}
print(storeUrl)
container.persistentStoreDescriptions = [NSPersistentStoreDescription(url: storeUrl)]
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error {
fatalError("Unresolved error \(error),")
}
})
return container
}()
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
}()
SQLite to Core Data Migration
I think @SaintThread hit the main issue here. The reason for it is that Core Data is not a wrapper around SQLite-- it's a different API with different assumptions that just happens to use SQLite internally. Core Data doesn't attempt to be compatible with how you'd use SQLite if you were using SQLite on its own.
That said, if you still want to migrate, you'll have to design a Core Data model and then write fully custom code to migrate from your existing SQLite file to Core Data. Your code would need to read everything from SQLite, convert it to the new Core Data representation, save the changes, and then remove the existing SQLite files.
When removing the existing SQLite file, make sure to also remove the SQLite journal files (if any).
Pre-fill SQLite database with Core Data using Django
You might want to have a look at the Active Record port for cocoa/cocoa touch. I've done an app like this before, and what we (the client and i), choose to do was to import the data from an XML file on the first app launch. The idea being that as the parser was built into the app, we could do OVA updates if we choose to at a later date. The data did have fairly complex relationships, but i decided Core Data was still the way forward.
I've only used raw SQLite once, and that was before we had CoreData on the iPhone. Also you should consider what your solution is for doing schema migrations which are handled by CoreData.
A Hybrid solution is to load the data into Core Data in the simulator, and then ship that SQLite database with the app, and copy it into the app's document directory, on the initial load. Best of both worlds.
Good luck
swift3 CoreData fetch returns nil
NSPersistentContainer
uses the Application Support directory by default, not the documents directory.
If you print out the value of storeURL in your loadPersistentStores completion block, you'll see that it is pointing to Application support. What's happening is that it is creating a blank database based on your model, ignoring the copy you've made.
Either copy into application support instead, or pass in an NSPersistentStoreDescription
object with a URL pointing to the documents directory if you want to control where NSPersistentContainer
does its thing.
It's worth noting that these lines:
let directoryUrls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
let applicationDocumentDirectory = directoryUrls[0]
let storeUrl = applicationDocumentDirectory.appendingPathComponent("GuessGreek.sqlite")
Are doing absolutely nothing in your current code.
Also worthy of note - your SQLiteManager screenshot suggests the file is called GuessGreekDatabase.sqlite
, yet your NSPersistentContainer will default to a store name of GuessGreek.sqlite
, so may also need fixing unless you're doing that during the copy.
Swift Core Data is saving to /dev/null so it is in memory only
When you instantiate your NSPersistentStoreDescription you have the option of passing in a URL. Here's a link to the documentation, and here's a well explained post on the topic
let container = NSPersistentContainer(name: "NameOfDataModel")
let storeURL = try! FileManager
.default
.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
.appendingPathComponent("NameOfDataModel.sqlite")
let storeDescription = NSPersistentStoreDescription(url: storeURL)
container.persistentStoreDescriptions = [storeDescription]
Related Topics
How to Open Your App's Settings (Inside the Settings App) with Swift (iOS 11)
Why Are Properties of an Immutable Object Mutable in Swift
How to Detect Text View Begin Editing and End Editing in Swift 3
Behaviour of Protocols with Self
Swift Compiler Error Int Is Not Convertible to Cgfloat
Create a Navigationlink Without Back Button Swiftui
How to Get Mouse Location with Swiftui
How to Define Static Constant in a Generic Class in Swift
How to Unwrap Arbitrarily Deeply Nested Optionals in Swift
Toolbar Is Deleting My Back Button in the Navigationview
Ambigious Reference to Member Request() Issues with Alamofire After Migration to Swift 3
Xcodebuild -Exportarchive Wont Allow Me to Specify Filename
Running Swift Build in Terminal Leading to "Platform Path" Errors
Swift, Error Exc_Breakpoint (Code=1, Subcode=0X100695474)
Swift Enumerate Functions. How Does It Work Behind