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?
Related
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.
I read below github page and used the code for sharing coredata between my ios Keyboard Extension and the app.
https://github.com/maximbilan/iOS-Shared-CoreData-Storage-for-App-Groups
The code is working right on the simulator but it is not working but addPersistentStore not working on real device:
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 directory = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier:"group.testKeyboard.asanpardakht.ir")!
let url = directory.appendingPathComponent("AsanPardakhtSharedData.sqlite")
do {
try coordinator!.addPersistentStore(ofType: NSSQLiteStoreType, configurationName: nil, at: url, options: nil)
} catch var error as NSError {
coordinator = nil
NSLog("Unresolved error \(error), \(error.userInfo)")
abort()
} catch {
fatalError()
}
print("\(String(describing: coordinator?.persistentStores))")
return coordinator
}()
I get below errors:
NSError domain: "NSCocoaErrorDomain" - code: 512
"reason" : "Failed to create file; code = 1"
I tried setting RequestOpenAccess to YES in my info.plist file but it is not still working. Does someone has any idead about the problem?
I figured out that below post works right for keyboard extension too:
https://github.com/maximbilan/iOS-Shared-CoreData-Storage-for-App-Groups
The only problem is that for keyboard extension we should also set RequestOpenAccess at info.plist to YES.
When set it to YES in the setting of the keyboard an option appears for the Full Access we should turn it On and then the code also works on real device.
Developers should make sure that before calling the method in the quest ask user to turn the option to On.
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.
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?
Is it possible to work with my CoreData model in the today extension in swift like in the original app? If yes, how can I create the NSManagedObjectContext?
I really have no clue, beside the group-identifier, but unfortunatly I don't know how to get the context..
In the past I created apps with the check at the beginning that I want to use CoreData and then I got the managedObjectContext via my AppDelegate.. But how can I do somethink like that in an extension? Apple doesn't offer information about that..
I edited this line in AppDelegate:
NSURL *storeURL = [[self applicationDocumentsDirectory] URLByAppendingPathComponent:#"HTWcampus.sqlite"];
to this (after including the group to both targets):
NSURL *storeURL = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:#"group.BenchR.TodayExtensionSharingDefaults"];
storeURL = [storeURL URLByAppendingPathComponent:#"HTWcampus.sqlite"];
NSLog(#"StoreURL2: %#", storeURL);
With that the existing database in my app was gone (what is great, because I think it worked to put the database in the shared segment).
But how can I create an instance of my context in the extension? And how can I access my NSManagedObject-subclasses?
In the extension I have this code so far:
var context: NSManagedObjectContext!
override func viewDidLoad() {
super.viewDidLoad()
var storeURL = NSFileManager.defaultManager().containerURLForSecurityApplicationGroupIdentifier("group.BenchR.TodayExtensionSharingDefaults")
storeURL = storeURL?.URLByAppendingPathComponent("HTWcampus.sqlite")
let modelURL = NSBundle.mainBundle().URLForResource("HTWcampus", withExtension: "momd")
let model = NSManagedObjectModel(contentsOfURL: modelURL)
let coordinator = NSPersistentStoreCoordinator(managedObjectModel: model)
coordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration: nil, URL: storeURL, options: nil, error: nil)
context = NSManagedObjectContext()
context.persistentStoreCoordinator = coordinator
}
Is this right? And if yes, how can I get my NSManagedObject-Subclasses in there? And do I have to add the momd-file to the extensions target? If yes, how can I do that?
What you really want is to access your persistent store (most likely a SQLite database).
In order to achieve that, you need to configure App Groups and make sure that your host app configures the Core Data stack using your shared container (so your store is accessible in extension as well).
Something like:
NSString *containerPath = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:YOUR_SECURITY_APP_GROUP].path;
NSString *sqlitePath = [NSString stringWithFormat:#"%#/%#", containerPath, #"database.sqlite"];
Then in your extension just create persistent store coordinator with managed object contexts using database in shared container.
You can share your model (.momd) and managed object subclasses with extension just by making sure they are included in extension target as well.
Edit:
To add your model and managed object subclasses:
Make sure you have your app and extension targets
Click on your model file, and select both targets under 'Target Membership' on right-hand panel
Repeat the same with all your managed object subclasses