How to use a populated SQLite database with SharkORM - ios

I am an Android developer who start learning iOS development. I am still learning so please expect some basic questions here.
Here is what I try to achieve: I have an existing SQLite database (kanadb.db) and I would like to use it in my iOS application. I would like to use an ORM to work with this database (the access will be read only) so I dropped the .db file in the project folder and did this in AppDelegate.swift:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
SharkORM.setDelegate(self)
SharkORM.openDatabaseNamed("kanadb")
// -- Test --
// Try to load some data from the database
let results: SRKResultSet = Info.query().fetch()
print(results)
// ----------
return true
}
But it is not working. I noticed that it creates a new kanadb.db in a folder somewhere in the file system. Something similar is done in Android, at startup we need to see if the DB already exist in the app folder and if not, copy the database from the bundle to the app folder. It looks like I have to do something similar here but I don't know how, as I am still new to iOS.
Can anyone give me some hints / code snippet to would point me to the correct direction?
Thank you!

I just found the solution, so I will post it here, it can be useful for someone.
So, as I expected, we have to copy the bundled database to the document folder of the application. However, the database file must be available in the target, so we have to select it in the files list (in XCode) and check Target Membership.
Then, copy it to the Document folder before using it:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
let DB_NAME = "kanadb"
let DB_EXTENSION = "db"
do{
let databasePath = Bundle.main.url(forResource: DB_NAME, withExtension: DB_EXTENSION)
let documentsDirectory = try FileManager.default.url(for: FileManager.SearchPathDirectory.documentDirectory, in:FileManager.SearchPathDomainMask.userDomainMask, appropriateFor:nil, create:false)
let destination = documentsDirectory.appendingPathComponent(DB_NAME + "." + DB_EXTENSION)
_ = try FileManager.default.copyItem(at: databasePath!, to:destination)
} catch {
// TODO: Catch the errors more gracefuly
print("Couldn't copy the database to the document folder")
}
// Override point for customization after application launch.
SharkORM.setDelegate(self)
SharkORM.openDatabaseNamed(DB_NAME)
// -- Test --
// Try to load some data from the database
let results: SRKResultSet = Info.query().fetch()
print(results)
// ----------
return true
}
And that's it! Now it may be a good idea to check if the database already exist in the Document folder of the app to avoid doing that everytime we launch the app.

Related

"Phantom classes/Models" still appearing in Realm

I'm going mental trying to work this out. I had 2 old classes which are no longer used in my code.
I'm trying to get rid of them in my Realm. I have tried a migration and a delete and restart and they just keep coming back (they don't have any objects in but it's untidy and annoying!)
I've tried this :
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let config = Realm.Configuration(
schemaVersion: 2,
migrationBlock: { migration, oldSchemaVersion in
if (oldSchemaVersion < 1) {
migration.deleteData(forType: "Exercise")
migration.deleteData(forType: "Workout")
}
})
Realm.Configuration.defaultConfiguration = config
let realm = try! Realm()
and I've also tried this :
let realm = try! Realm()
try! realm.write {
realm.deleteAll()
}
One of the classes is called "Workout" and I've got a Struct called "Workout" elsewhere in code but it hasn't even got the same fields etc.
Research I'm sure I've tried what others have in Delete a Realm model with Swift , https://www.realm.io/docs/objc/latest/#migrations and here How to delete a class from realm file
Any ideas? I've done a CMD+SHIFT+F and the old classes definitely aren't in my code so I'm guessing I'm not doing migration correctly?
Update for those having same issue
I deleted the file completely using the details here How to completely remove Realm database from iOS?
I'm not sure what I did wrong though!

Thumbnail Metadata Disappeared When Importing File with Copy to App

I have been using the URL Resources to embed the thumbnail metadata into my custom document-based file. When I export my custom document file, the thumbnail shows up nicely when glanced in iOS Files app.
override func fileAttributesToWrite(to url: URL, for saveOperation: UIDocumentSaveOperation) throws -> [AnyHashable : Any] {
var attr: [AnyHashable : Any] = [URLResourceKey.hasHiddenExtensionKey: true]
// Ignore the proper resizing for now.
...
attr[URLResourceKey.thumbnailDictionaryKey] = [
URLThumbnailDictionaryItem.NSThumbnail1024x1024SizeKey: #imageLiteral(resourceName: "Star")
]
return attr
}
(Icon Credit: Use Your Loaf)
However, when I import the file back into my app with Share action, Copy to <MyApp>, all the metadata seems to survive except the thumbnail.
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
// Other non-image metadata survives, like creationDate.
if let creationDateOpt = try? url.resourceValues(forKeys: [.creationDateKey]).creationDate,
let creationDate = creationDateOpt {
print("creationDate: \(creationDate)")
}
if let thumbnailDictOpt = try? url.resourceValues(forKeys: [.thumbnailDictionaryKey]).thumbnailDictionary,
let thumbnailDict = thumbnailDictOpt,
let thumbnail = thumbnailDict[URLThumbnailDictionaryItem.NSThumbnail1024x1024SizeKey] {
print ("Thumbnail survived. Size: \(thumbnail.size)")
return true
}
print ("Thumbnail disappeard!")
return false
}
Considering that the thumbnail persists when I manual copy the file in Files app, the lost must happen during the system copying my file from Files to my app's container. Why does this happen? I can manually embed thumbnail as part of my file's data too, but I feel there should be a way to solve this, since other text-based metadata persists.
I tried using promisedResourceValue method instead, suspecting that the file might not be fully copied when it's opened, but the result is the same.
I have my full project available here on GitHub.

Location of Sqlite file location of CoreData iOS App

I am developing an iOS application with Objective-C and CoreData. Where can I locate the sqlite file generated on the iOS device?
IF the store uses a persisted file, you can even get it in code
//get you context, the coordinator, attached stores
NSManagedObjectContext *moc = [MyDataStore sharedDataStore].mainManagedObjectContext; //however you get the MOC
NSPersistentStoreCoordinator *coordinator = moc.persistentStoreCoordinator;
NSArray<NSPersistentStore*> *stores = coordinator.persistentStores;
//log it
for (NSPersistentStore *store in stores) {
NSLog(#"%#", store.URL);
}
For XCode 8.0 (iOS 10)
Path to the persistent store created by NSPersistentContainer can be seen by adding following code in existing (didFinishLaunchingWithOptions) method in your AppDelegate:-
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let urls = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
print(urls[urls.count-1] as URL) // look for doc. directory
return true
}
Go to the path in the print log then go to the /Library/Application Support folder. Here is the sample path of .sqlite(persistent store) file:
/Users/Ashish/Library/Developer/CoreSimulator/Devices/F04D26BC-0AA9-4E25-87CC-E635BE86293A/data/Containers/Data/Application/69873117-6AB0-4491-B3BA-302FE63050FB/Library/Application Support
It's wherever you decided to put it. You don't even need to look it up, because you already have that information.
When you call addPersistentStoreWithType:configuration:URL:options:error:, the URL argument that you pass in is the location of the persistent store. You could look it up by the NSManagedObjectContext and the NSPersistentStoreCoordinator, but why do that when the answer is already there in your app?

Error with Parse integration into Swift app

I am trying to integrate Parse into a Swift app. I downloaded the SDK, set the app id and added the dependencies, but when I try to import Parse, it says 'No such module - Parse'.
Check that the Parse framework has been copied to your project folder to wherever you keep your 3rd party dependencies (e.g. Vendor).
Then, add the path to the Parse framework to the Framework Search Paths (FRAMEWORK_SEARCH_PATHS) for your build target.
It should look something like this:
$(inherited)
$(PROJECT_DIR)/Vendor/Parse
I'd clean up the DerivedData folder and rebuild.
I think this link should be solve your Problem:
Set up new Parseproject
or here ist explained it for a existing project please check this
Edit after i saw the Code. At first please do not post api keys here this are your private api keys.
second i think the code should look like:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
Parse.enableLocalDatastore()
// Initialize Parse.
Parse.setApplicationId("appID",
clientKey: "Key")
// [Optional] Track statistics around application opens.
PFAnalytics.trackAppOpenedWithLaunchOptions(launchOptions)
//end parse
// Override point for customization after application launch.
let splitViewController = self.window!.rootViewController as! UISplitViewController
let navigationController = splitViewController.viewControllers[splitViewController.viewControllers.count-1] as! UINavigationController
navigationController.topViewController.navigationItem.leftBarButtonItem = splitViewController.displayModeButtonItem()
splitViewController.delegate = self
let masterNavigationController = splitViewController.viewControllers[0] as! UINavigationController
let controller = masterNavigationController.topViewController as! MasterViewController
controller.managedObjectContext = self.managedObjectContext
return true
}
you have to put in your Key i corrected the method for you
My problem appears to have been in the naming of the application: I included a number.
Once I corrected this, the error disappeared. Perhaps the name prevents the import of certain frameworks.

Using an existing SQLite database in MagicalRecord (swift)

I want my iOS swift app to preload existing sqlite file in MagicalRecord.
With reference to this, and others,
add sqlite, sqlite-shm, sqlite-wal file to my project, wrote some code in didFinishLaunchingWithOption like this.
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
// copy initial sqlite file to application directory
let preloadSQLiteURL:NSURL = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("myData", ofType: "sqlite")!)
let preloadShmURL:NSURL = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("myData", ofType: "sqlite-shm")!)
let preloadWalURL:NSURL = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("myData", ofType: "sqlite-wal")!)
let storeSQLiteURL:NSURL = NSPersistentStore.MR_urlForStoreName("myData.sqlite")
let storeShmURL:NSURL = NSPersistentStore.MR_urlForStoreName("myData.sqlite-shm")
let storeWalURL:NSURL = NSPersistentStore.MR_urlForStoreName("myData.sqlite-wal")
if !NSFileManager.defaultManager().copyItemAtURL(preloadSQLiteURL, toURL: storeSQLiteURL, error:&err) {
println("failed to copy sqlite file.")
}
if !NSFileManager.defaultManager().copyItemAtURL(preloadShmURL, toURL: storeShmURL, error:&err) {
println("failed to copy shm file.")
}
if !NSFileManager.defaultManager().copyItemAtURL(preloadWalURL, toURL: storeWalURL, error:&err) {
println("failed to copy wal file.")
}
MagicalRecord.setupCoreDataStackWithAutoMigratingSqliteStoreNamed("myData.sqlite")
return true
}
but copyItemAtURL all fails.
value of paths are these.
preloadSQLiteURL = file:///var/mobile/Applications/XXX/myApp.app/myData.sqlite
preloadShmURL = file:///var/mobile/Applications/XXX/myApp.app/myData.sqlite-shm
preloadWalURL = file:///var/mobile/Applications/XXX/myApp.app/myData.sqlite-wal
storeSQLiteURL = file:///var/mobile/Applications/XXX/Documents/myData.sqlite
storeShmURL = file:///var/mobile/Applications/XXX/Documents/myData.sqlite-shm
storeWalURL = file:///var/mobile/Applications/XXX/Documents/myData.sqlite-wal
my environment is Xcode6 beta7, iOS7.1.3.
Does anyone please help me? What should I do?
Any advice appreciated. Thanks in advance.
As a result, self-resolved.
NSError shows "The Operation couldn't be completed. (Cocoa error 516)"
Cocoa error 516 is "NSFileWriteFileExistsError" means "You can’t move a file to a place where a file already exists."
so, I corrected my code in this way.
let fileManager = NSFileManager.defaulManager()
let preloadSQLiteURL:NSURL = NSURL(fileURLWithPath: NSBundle.mainBundle().pathForResource("myData", ofType: "sqlite")!)
let storeSQLiteURL:NSURL = NSPersistentStore.MR_urlForStoreName("myData.sqlite")
if fileManager.fileExistsAtPath(storeSQLiteURL.path!) {
// file already exists, delete it and try again.
fileManager.removeItemAtURL(storeSQLiteURL, error:&err)
}
fileManager.copyItemAtURL(preloadSQLiteURL, toURL:storeSQLiteURL, error:nil)
and then, copyItemAtURL successfully completed and sqlite file takes effect.
While this is a poorly worded question, I'm assuming you want to load a SQLite database file from the app bundle into the user's documents directory so you can start with that data and then update it from there.
You're on the correct path, however, rather than copying all 3 files, when you create the default data store file, set your journal_mode to "DELETE" using the NSSQLitePragmasOption:
NSDictionary *options = #{NSSQLitePragmasOption: #{#"journal_mode": #"DELETE"}}
using this set of options when you add a persistent store to a coordinator when you're creating your default data set will only create a single SQLite file. From there, it'll be easier to copy only a single file over.
I also noticed in your code that you're copying myData.sqlite, but you're trying to load "acData.sqlite". Are you trying to load a file that isn't there?

Resources