The Problem:
I've got some pretty simple code (below) that causes an exception during 'migratePersistentStore' with the error message
Exception:*** -[__NSArrayM replaceObjectAtIndex:withObject:]: object cannot be nil
The Code:
NSPersistentStore * oldStore = [_persistentStoreCoordinator persistentStores][0];
if (oldStore) {
#try {
[_persistentStoreCoordinator migratePersistentStore:oldStore
toURL:[self storeURL]
options: #{ NSPersistentStoreRemoveUbiquitousMetadataOption : #YES }
withType:NSSQLiteStoreType error:&error];
}
#catch(NSException* ex) {
NSLog(#"Exception:%#", ex.description);
}
}
Further Info:
It appears that if no data is present, the exception does not occur. Instead the same function sets an error, with userInfo "Can't add the same store twice".
I'm currently making a small simple project to try and replicate the problem, I'll post a link here once it's done.
Apparently, Core Data doesn't like migration when your iCloud URL and local URL are the same. I thought this shouldn't matter because really, iCloud data is stored in its own directory. BUT, it seems during migration, using the same name causes problems.
Easy fix - just do something like this:
- (NSURL *)storeURL {
NSURL * documentsDirectory = [[NSFileManager defaultManager] URLForDirectory:NSDocumentDirectory inDomain:NSUserDomainMask appropriateForURL:nil create:YES error:NULL];
if (iCloudEnabled]) {
return [documentsDirectory URLByAppendingPathComponent:#"iCloudEventData.sqlite"];
} else {
return [documentsDirectory URLByAppendingPathComponent:#"EventData.sqlite"];
}
}
There really isn't enough information here to tell you the exact cause. As you may have guessed, CoreData is running into a problem where it is replacing an object in an array with a nil object. This can happen if your mapping models do not match correctly.
To troubleshoot your problem, you should do two things:
Remove that try/catch an instead set a symbolic breakpoint for all exceptions. This will cause the debugger to stop when it hits this exception, and you can see the stack trace and everything else that is going on in your application. Note that if CoreData runs into merge conflicts these are handled inside CoreData as exceptions and the debugger will stop on those as well. In that case, just continue until your array exception.
Set your application's launch arguments to turn on migration logging to see how it's getting to this point. In Xcode, edit your application scheme's Run task to pass -com.apple.CoreData.MigrationDebug. Note that the dash preceding the argument is important. CoreData will log what happens during your migration, which should help pinpoint the problem.
A random guess: do you have any NSManagedObject subclasses declared in your model that you've forgotten to include an implementation for in your build? In that case it's imaginable that the migration code might attempt to create an instance of the custom subclass, fail to do so, then fail to check that it succeeded before throwing the result into an array.
Run without a migration and perform something like this to check:
for(NSEntityDescription *entityDescription in model.entities)
{
NSString *className = [entityDescription managedObjectClassName];
Class class = NSClassFromString(className);
if(className && !class)
NSLog(#"An implementation of class %# is missing from the build",
className);
}
Related
I have an app with core data and icloud to allow the user to use the same database on several devices. The sync works most of the time perfect, but sometimes it doesn't, i.e. some transactions are simply skipped.
When I check the console, I get the error message:
__45-[PFUbiquityFilePresenter processPendingURLs]_block_invoke(439): CoreData: Ubiquity: Librarian returned a serious error for starting downloads Error Domain=BRCloudDocsErrorDomain Code=5 "The operation couldn’t be completed. (BRCloudDocsErrorDomain error 5 - No document at URL)"
Funny enough this message appears even when the sync works.
The code fits to Apple latest version of " iCloud Programming Guide for Core Data".
The storeURL is coded as follows:
NSURL *documentsDirectory = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
NSURL *storeURL = [documentsDirectory URLByAppendingPathComponent:#"iCloudSample.sqlite"];
The code for the options:
storeOptions = #{NSPersistentStoreUbiquitousContentNameKey: #"iCloudSampleStore" ,
NSPersistentStoreUbiquitousContentURLKey: #"iCloudSampleURL"};
The code for the store:
NSPersistentStore *store = [_persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil
URL:storeURL
options:storeOptions
error:&error];
The merge policy (MOC is on the main qeue)
_managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSMainQueueConcurrencyType];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
_managedObjectContext.mergePolicy = [[NSMergePolicy alloc]
initWithMergeType:NSMergeByPropertyObjectTrumpMergePolicyType];
As I understand the docs, there is not really much more to do(?).
But I guess I'm missing something, does anyone has an idea what to check, change, try?
Anyone who had the same problem? Any ideas are appreciated!
I have the same problem and solved it by removing the persistentStore and adding it again while the app is running. It is always the same problem when doing several changes in the first device, the second device doesn't get all the changes. I use the following code when applicationDidBecomeActive to remove the persistent store first:
var storeCoordinator:NSPersistentStoreCoordinator = persistentStoreCoordinator!
var store:NSPersistentStore = storeCoordinator.persistentStores[0] as! NSPersistentStore
var storeURL:NSURL = store.URL!
storeCoordinator.removePersistentStore(store, error: nil)
NSFileManager.defaultManager().removeItemAtPath(storeURL.path!, error: nil)
Following this I proceed to add the persistentStore again. Then I received all the notifications as it was the first run after installation but with the full data synched. it always works but I don't know if it is the proper way to handle the problem.
I believe that all the changes made in the first device always reach the second device but for some weird reason they don't merge completely with the local core data. I have made some weird observations about the iCloud-CoreData issue in:
https://stackoverflow.com/questions/32084952/core-data-to-icloud-doesnt-sync-all-changes
I hope it helps.
I found that when my store URL had .s in the path
(e.g. ---/com.companyName.project/---)
let applicationSupportDirectory = applicationSupportDirectoryURL.URLByAppendingPathComponent("com.companyName.project")
let iCloudStoreFileName = "iCloud.sqlite"
lazy var iCloudStoreURL: NSURL = {
return self.applicationSupportDirectory.URLByAppendingPathComponent(self.iCloudStoreFileName)
}()
When I enabled logging, by going to Edit Scheme... in Xcode for my project and adding:
-com.apple.CoreData.SyntaxColoredLogging 1
the log file in the debug window showed the path it was searching was
---/com~companyName~project/---
Core data had changed the .s to ~s. I eliminated the entire com.companyName.project from the URL and synchronizing seems to work better. But with core data and iCloud you never know - there's soooo much magic going on behind the scenes.
I'm trying to create a NSSQLiteStoreType with the readonly option (NSReadOnlyPersistentStoreOption). This fails if the sqlite file doesn't exist (see code below). If it does exist, the store is added without any errors.
The error I get is Cocoa Error 260:
NSFileReadNoSuchFileError = 260, // Read error (no such file)
So it looks like CoreData tries to read a file that doesn't exist, instead of creating a new one...
It seems that when adding NSReadOnlyPersistentStoreOption you can only open a previously existing store, but not create one. This doesn't make sense to me.
Is there any way to create a brand new readonly store in Core Data?
If not, is there some workaround?
// DB URL
NSFileManager *fm = [NSFileManager defaultManager];
NSURL *dbURL = [[fm URLsForDirectory:NSDocumentDirectory
inDomains:NSUserDomainMask] lastObject];
dbURL = [dbURL URLByAppendingPathComponent:#"store.sqlite"];
// Object Model
NSURL *modelURL = [[NSBundle mainBundle] URLForResource:#"Model" withExtension:#"momd"];
NSAssert([fm fileExistsAtPath:[modelURL path]], #"File not found");
NSManagedObjectModel *model = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL];
// Store Coordinator
NSPersistentStoreCoordinator *coord = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:model];
// Add a readonly SQLite store
NSError *err = nil;
NSPersistentStore *store = [coord addPersistentStoreWithType:NSSQLiteStoreType
configuration:nil URL:dbURL
options:#{NSReadOnlyPersistentStoreOption : #YES}
error:&err];
if (store == nil) {
// I get a Cocoa Error 260.
NSLog(#"Error: %#", err);
}
Creating a new, empty, read-only store makes no sense, and the results you're seeing are exactly what would be expected. By specifying read-only you are specifically indicating that no file should be written, so as a result.... no file is written.
It's hard to tell what you're trying to accomplish. If the file were created, you would not be able to use it, since it would contain no data and since the read-only flag would prevent you from adding any data. An empty file would be exactly as useful.
But no, there is no way to tell Core Data to create a new persistent store file but have that file be read only, mainly because such an operation would be nonsensical and useless.
If you have some reason to want a persistent store file which is both empty and unwritable (and if you do, please share), you would need to
Add the persistent store without the read-only flag
Call removePersistentStore:error: to remove that persistent store
Add the persistent store again, with the read-only flag.
You will now have a persistent store which contains no data, and which you are prevented from adding data to.
A simpler alternative that is just as effective is to not create the file in the first place. An empty read-only persistent store serves literally no purpose at all, so the easy approach is to just not bother creating it.
While deleting existing the file with this command:
[[NSFileManager defaultManager] removeItemAtPath:self.sourceFileName error:&error];
I got the following error
Error: ImageIO: CGImageReadCreateDataWithMappedFile 'open' failed '/Users/asdasd/Library/Application Support/iPhone Simulator/7.1/Applications/DD251D7D-F0AF-40E1-A033-F221623D589D/Library/ScanSession/Source/page3.jpeg
error = 2 (No such file or directory)'
This happens while I copied pic from album into app folder. The most interesting thing is that file exists, but not fully copied. Is there a way to check wether file is file operation completed?
check weather your file & Directory available
for (NSString *filename in files) {
NSString *path = [yourPath stringByAppendingPathComponent:yourFileName];
BOOL isDir;
if([[NSFileManager defaultManager] fileExistsAtPath:yourPath isDirectory:&isAvilDir] && isAvilDir){
NSLog(#"%# Check is a directory", your path file);
}
else {
NSLog (#"%# Check is a file",your path file);
}
}
I have a similar problem right now, and it seems as though when you delete a file that certain other methods you may have called immediately prior may actually not have completed yet. I'm considering delaying the actual deletion of files to allow for background processes to complete.
Solved it 2 yars ago. Forgot to post & close the question. It was caused by another thread, where the file was deleted first. I think its one of the standart mutlithreading issues while working with CoreData
Conclusion
Problem closed, I think.
Looks like the problem had nothing to do with the methodology, but that the XCode did not clean the project correctly in between builds.
It looks like after all those tests, the sqlite file that was being used was still the very first one that wasn't indexed......
Beware of XCode 4.3.2, I have nothing but problems with Clean not cleaning, or adding files to project not automatically being added to the bundle resources...
Thanks for the different answers..
Update 3
Since I invite anybody to just try the same steps to see if they get the same results, let me detail what I did:
I start with blank project
I defined a datamodel with one Entity, 3 attributes (2 strings, 1 float)
The first string is indexed
In did finishLaunchingWithOptions, I am calling:
[self performSelectorInBackground:#selector(populateDB) withObject:nil];
The code for populateDb is below:
-(void)populateDB{
NSLog(#"start");
NSPersistentStoreCoordinator *coordinator = [self persistentStoreCoordinator];
NSManagedObjectContext *context;
if (coordinator != nil) {
context = [[NSManagedObjectContext alloc] init];
[context setPersistentStoreCoordinator:coordinator];
}
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"input" ofType:#"txt"];
if (filePath) {
NSString * myText = [[NSString alloc]
initWithContentsOfFile:filePath
encoding:NSUTF8StringEncoding
error:nil];
if (myText) {
__block int count = 0;
[myText enumerateLinesUsingBlock:^(NSString * line, BOOL * stop) {
line=[line stringByReplacingOccurrencesOfString:#"\t" withString:#" "];
NSArray *lineComponents=[line componentsSeparatedByString:#" "];
if(lineComponents){
if([lineComponents count]==3){
float f=[[lineComponents objectAtIndex:0] floatValue];
NSNumber *number=[NSNumber numberWithFloat:f];
NSString *string1=[lineComponents objectAtIndex:1];
NSString *string2=[lineComponents objectAtIndex:2];
NSManagedObject *object=[NSEntityDescription insertNewObjectForEntityForName:#"Bigram" inManagedObjectContext:context];
[object setValue:number forKey:#"number"];
[object setValue:string1 forKey:#"string1"];
[object setValue:string2 forKey:#"string2"];
NSError *error;
count++;
if(count>=1000){
if (![context save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
count=0;
}
}
}
}];
NSLog(#"done importing");
NSError *error;
if (![context save:&error]) {
NSLog(#"Whoops, couldn't save: %#", [error localizedDescription]);
}
}
}
NSLog(#"end");
}
Everything else is default core data code, nothing added.
I run that in the simulator.
I go to ~/Library/Application Support/iPhone Simulator/5.1/Applications//Documents
There is the sqlite file that is generated
I take that and I copy it in my bundle
I comment out the call to populateDb
I edit persistentStoreCoordinator to copy the sqlite file from bundle to documents at first run
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
#synchronized (self)
{
if (__persistentStoreCoordinator != nil)
return __persistentStoreCoordinator;
NSString *defaultStorePath = [[NSBundle mainBundle] pathForResource:#"myProject" ofType:#"sqlite"];
NSString *storePath = [[[self applicationDocumentsDirectory] path] stringByAppendingPathComponent: #"myProject.sqlite"];
NSError *error;
if (![[NSFileManager defaultManager] fileExistsAtPath:storePath])
{
if ([[NSFileManager defaultManager] copyItemAtPath:defaultStorePath toPath:storePath error:&error])
NSLog(#"Copied starting data to %#", storePath);
else
NSLog(#"Error copying default DB to %# (%#)", storePath, error);
}
NSURL *storeURL = [NSURL fileURLWithPath:storePath];
__persistentStoreCoordinator = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:[self managedObjectModel]];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
if (![__persistentStoreCoordinator addPersistentStoreWithType:NSSQLiteStoreType configuration:nil URL:storeURL options:options error:&error])
{
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
return __persistentStoreCoordinator;
}
}
I remove the app from the simulator, I check that ~/Library/Application Support/iPhone Simulator/5.1/Applications/ is now removedI rebuild and launch again
As expected, the sqlite file is copied over to ~/Library/Application Support/iPhone Simulator/5.1/Applications//Documents
However the size of the file is smaller than in the bundle, significantly!
Also, doing a simple query with a predicate like this predicate = [NSPredicate predicateWithFormat:#"string1 == %#", string1]; clearly shows that string1 is not indexed anymore
Following that, I create a new version of the datamodel, with a meaningless update, just to force a lightweight migration
If run on the simulator, the migration takes a few seconds, the database doubles in size and the same query now takes less than a second to return instead of minutes.
This would solve my problem, force a migration, but that same migration takes 3 minutes on the iPad and happens in the foreground.
So hat's where I am at right now, the best solution for me would still be to prevent the indexes to be removed, any other importing solution at launch time just takes too much time.
Let me know if you need more clarifications...
Update 2
So the best result I have had so far is to seed the core data database with the sqlite file produced from a quick tool with similar data model, but without the indexes set when producing the sqlite file. Then, I import this sqlite file in the core data app with the indexes set, and allowing for a lightweight migration. For 2 millions record on the new iPad, this migration stills take 3 minutes. The final app should have 5 times this number of records, so we're still looking at a long long processing time.
If I go that route, the new question would be: can a lightweight migration be performed in the background?
Update
My question is NOT how to create a tool to populate a Core Data database, and then import the sqlite file into my app. I know how to do this, I have done it countless times. But until now, I had not realized that such method could have some side effect: in my case, an indexed attribute in the resulting database clearly got 'unindexed' when importing the sqlite file that way.
If you were able to verify that any indexed data is still indexed after such transfer, I am interested to know how you proceed, or otherwise what would be the best strategy to seed such database efficiently.
Original
I have a large CSV file (millions of lines) with 4 columns, strings and floats.
This is for an iOS app.
I need this to be loaded into core data the first time the app is loaded.
The app is pretty much non functional until the data is available, so loading time matters, as a first time user obviously does not want the app to take 20 minutes to load before being able to run it.
Right now, my current code takes 20 min on the new iPad to process a 2 millions line csv file.
I am using a background context to not lock the UI, and save the context every 1,000 records
The first idea I had was to generate the database on the simulator, then to copy/paste it in the document folder at first launch, as this is the common non official way of seeding a large database. Unfortunately, the indexes don't seem to survive such a transfer, and although the database was available after just a few seconds, performance is terrible because my indexes were lost. I posted a question about the indexes already, but there doesn't seem to be a good answer to that.
So what I am looking for, either:
a way to improve performance on loading millions of records in core data
if the database is pre-loaded and moved at first startup, a way to keep my indexes
best practices for handling this kind of scenario. I don't remember using any app that requires me to wait for x minutes before first use (but maybe The Daily, and that was a terrible experience).
Any creative way to make the user wait without him realizing it: background import while going through tutorial, etc...
Not Using Core Data?
...
Pre-generate your database using an offline application (say, a command-line utility) written in Cocoa, that runs on OS X, and uses the same Core Data framework that iOS uses. You don't need to worry about "indexes surviving" or anything -- the output is a Core Data-generated .sqlite database file, directly and immediately usable by an iOS app.
As long as you can do the DB generation off-line, it's the best solution by far. I have successfully used this technique to pre-generated databases for iOS deployment myself. Check my previous questions/answers for a bit more detail.
I'm just starting out with SQLite and I need to integrate a DB into one of my apps that will have a lot of indexed data in a SQLite database. I was hoping I could do some method where I could bulk insert my information into a SQLite file and add that file to my project. After discovering and reading through your question, the provided answer and the numerous comments, I decided to check out the SQLite source to see if I could make heads or tails of this issue.
My initial thought was that the iOS implementation of SQLite is, in fact, throwing out your indices. The reason is because you initially create your DB index on x86/x64 system. The iOS is an ARM processor, and numbers are handled differently. If you want your indexes to be fast, you should generate them in such a way that they are optimized for the processor in which they will be searched.
Since SQLite is for multiple platforms, it would make since to drop any indices that have been created in another architecture and rebuild them. However, since no one wants to wait for an index to rebuild the first time it is accessed, the SQLite devs most likely decided to just drop the index.
After digging into the SQLite code, I've come to the conclusion that this is most likely happening. If not for the processor architecture reason, I did find code (see analyze.c and other meta-information in sqliteint.h) where indices were being deleted if they were generated under an unexpected context. My hunch is that the context that drives this process is how the underlying b-tree data structure was constructed for the existing key. If the current instance of SQLite can't consume the key, it deletes it.
It is worth mentioning that the iOS Simulator is just that-- a simulator. It is not an emulator of the, hardware. As such, your app is running in a pseudo-iOS device, running on an x86/x64 processor.
When your app and SQLite DB are loaded to your iOS device, an ARM-compiled variant is loaded, which also links to the ARM compiled libraries within iOS. I couldn't find ARM specific code associated with SQLite, so I imagine Apple had to modify it to their suit. The could also be part of the problem. This may not be an issue with the root-SQLite code, it could be an issue with the Apple/ARM compiled variant.
The only reasonable solution that I can come up with is that you can create a generator application that you run on your iOS machine. Run the application, build the keys, and then rip the SQLite file from the device. I'd imagine such a file would work across all devices, since all ARM processors used by iOS are 32-bit.
Again, this answer is a bit of an educated guess. I'm going to re-tag your question as SQLite. Hopefully a guru may find this and be able to weigh in on this issue. I'd really like to know the truth for my own benefit.
I am using a UIManagedDocument in iOS 5.0, running the app on the simulator, using XCode 4.2 under OSX 10.6. The code in question looks as follows:
if (![[NSFileManager defaultManager] fileExistsAtPath:[self.photoDatabase.fileURL path]]) {
// does not exist on disk, so create it
[self.photoDatabase saveToURL:self.photoDatabase.fileURL forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) {
[self setupFetchedResultsController];
[self fetchFlickrDataIntoDocument:self.photoDatabase];
}];
} else if (self.photoDatabase.documentState == UIDocumentStateClosed) {
// exists on disk, but we need to open it
// *** the following line generates the message ***
[self.photoDatabase openWithCompletionHandler:^(BOOL success) {
//[self setupFetchedResultsController];
}];
} else if (self.photoDatabase.documentState == UIDocumentStateNormal) {
// already open and ready to use
[self setupFetchedResultsController];
}
Running the marked line creates the following message on the log:
2012-01-10 22:33:17.109 Photomania[5149:4803] NSFileCoordinator: A surprising server error was signaled. Details: Connection invalid
After the message is sent, the UIManagedDocument may or may not work—I have not found the circumstances that determine this, yet.
I am pretty sure that the code is correct, as it's actually one of the code examples in the CS193p course from Stanford. The whole example can be downloaded at their website under
http://www.stanford.edu/class/cs193p/cgi-bin/drupal/
Direct link to the code:
http://www.stanford.edu/class/cs193p/cgi-bin/drupal/system/files/sample_code/Photomania_0.zip
Additionally, the code runs fine on the device itself, without generating the "surprising" message, and running all the code that comes afterwards just fine.
I have not found anything on Google, neither on the Apple Developer pages. Restarting the simulator, or XCode, or reinstalling both of them does not change the behaviour.
Any ideas?
I can only say that I've had this happen to me several times. For me, I'm lazy after I update my dataModel and so far, each time I've gotten this error it was because I had changed my data model. Usually, all I need to do is delete my app from the simulator and re-run it and it has always turned out fine. Hope this helps someone out there.
I think I have found the answer. It looks like the automatic saving for UIManagedDocument kicks in only after a few seconds on the simulator.
So I minimized the app on the simulator, by pressing the home button, and then clicked on the icon to maximize it again. And then I terminated the app in simulator.
When I re-launched the app, the database was loaded. The error still shows up - it comes because the document is in "closed" state (that's normal - that's why CS193P asked to call openWithCompletionHandler), but my data across launches is preserved. Unfortunately I have to do the minimize/maximize routine before terminating the app, or the changes are discarded at next launch.
Can you verify that this is the behavior you are able to recreate? At least for testing purposes this should be a good enough trick to use.
Try upgrading to the latest iOS 5.1. I don't think UIManagedDocument with iCloud works reliably in 5.0. This has been my experience.
I love the Stanford iTunes class. However, I think the sample code for using UIManagedDocument is wrong. In fact, he notes in the demo that he is only doing it that way because he wants to just fetch the information right then. In the code comments, he says not to use the auto-save features because the data will not be saved if the app quits. however, UIManagedDocument will save anything that's necessary before quitting. It has all pertinent handlers for quitting/multitasking/etc to make sure the data is saved.
So, if you are using that code as your example, here's a version that should work, and does not use saveToURL (I don't have a flickr account, so I didn't actually run it - but this is how the class is designed to work). Please let me know if it does not work.
- (void)fetchFlickrDataIntoDocument:(UIManagedDocument *)document
{
NSManagedObjectContext *ctx = [[NSManagedObjectContext alloc] initWithConcurrencyType: NSPrivateQueueConcurrencyType];
ctx.parentContext = document.managedObjectContext;
[ctx performBlock:^{
NSArray *photos = [FlickrFetcher recentGeoreferencedPhotos];
for (NSDictionary *flickrInfo in photos) {
[Photo photoWithFlickrInfo:flickrInfo inManagedObjectContext:ctx];
// Push changes to document MOC
[ctx save:0]; // propagates changes to parent MOC
// and tell the document it is dirty and needs to be saved
// It will be saved when the document decides its time to save
// but it *will* be saved.
[document updateChangeCount:UIDocumentChangeDone]
}
}];
}
Still had errors when the last path component for document file url was #"Database". Adding an extension #"Database.db" seems to have fixed it, everything running fine now. Have also upgraded to Lion though.
NSURL *url = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
url = [url URLByAppendingPathComponent:#"Database.db"];