Im looking for a quick fix to my problems with the iPhone Simulator.
The code I use to access my database is:
+ (NSURL*) databaseURL
{
if(!databaseURL){
databaseURL = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
databaseURL = [databaseURL URLByAppendingPathComponent:databaseNameString];
}
return databaseURL;
}
+ (UIManagedDocument*) managedDocument
{
if(!managedDocument){
managedDocument = [[UIManagedDocument alloc] initWithFileURL:[FLOWDatabase databaseURL]];
}
return managedDocument;
}
+ (void) initialiseDatabase
{
if ([[NSFileManager defaultManager] fileExistsAtPath:[[FLOWDatabase databaseURL] path]])
[managedDocument openWithCompletionHandler:^(BOOL success) {
//...
}];
else
[managedDocument saveToURL:[FLOWDatabase databaseURL] forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success){
//...
}];
}
The thing is, it works PERFECTLY on the actual iPhone but not on the simulator.
The lag time (of 40 seconds) is for initial creation as well as editing an existing file.
I have tried 'reset contents and settings', clean on xcode, delete+rebuild app on the simulator - none of those work.
What to do next?
Vb
I tried reinstalling Xcode and it didn't work.
The solution I have found is to manually delete the persistent store file after every use.
That resolves the problem.
Related
I have a chat app with existing sqlite file, now I create a Notification Service Extension and need to share that sqlite file with NSE, so I move existing sqlite to public location with the following code:
NSURL *source = [[Utils applicationDocumentsDirectory] URLByAppendingPathComponent:#"file_name.sqlite"];
NSURL *destination = [[Utils applicationDocumentsDirectoryPublic] URLByAppendingPathComponent:#"file_name.sqlite"];
if ([[NSFileManager defaultManager] isReadableFileAtPath:source.path]) {
BOOL isSuccess = [[NSFileManager defaultManager] moveItemAtURL:source toURL:destination error:nil];
if(isSuccess) {
NSLog(#"movingCoreData success");
}
}
+ (NSURL *)applicationDocumentsDirectory {
NSURL *path = [[[NSFileManager defaultManager] URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
return path;
}
+ (NSURL *)applicationDocumentsDirectoryPublic {
NSURL *path = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:APP_GROUP];
return path;
}
The sqlite file move success, but some data is lost, and it's not happend for all user (I tried with account A and account B, account A is lost half data (almost is group chat) while account B is fine. And when I fetch data in NSE, it's not return data for me, I see a warning:
CoreData: warning: Unable to load class named 'Conversation' for entity 'Conversation'. Class not found, using default NSManagedObject instead.
Can anyone explain this?
This is probably a result of CoreData using SQLite’s WAL mode by default, which means many of the transactions for the database are stored in a separate file (“...sqlite-wal”). Your copy process does not copy this file, so the transactions go missing.
You could just add another NSFileManager operation to move the WAL file, but as set out in this Technical Q&A it is better to use NSPersistentStoreCoordinator’s migratePersistentStore:toURL:options:withType:error: method.
I want to have my app save the documents it creates to iCloud Drive, but I am having a hard time following along with what Apple has written. Here is what I have so far, but I'm not for sure where to go from here.
UPDATE2
I have the following in my code to manually save a document to iCloud Drive:
- (void)initializeiCloudAccessWithCompletion:(void (^)(BOOL available)) completion {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
self.ubiquityURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
if (self.ubiquityURL != nil) {
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"iCloud available at: %#", self.ubiquityURL);
completion(TRUE);
});
}
else {
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"iCloud not available");
completion(FALSE);
});
}
});
}
if (buttonIndex == 4) {
[self initializeiCloudAccessWithCompletion:^(BOOL available) {
_iCloudAvailable = available;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
NSString *pdfPath = [documentsDirectory stringByAppendingPathComponent:selectedCountry];
NSURL* url = [NSURL fileURLWithPath: pdfPath];
[self.manager setUbiquitous:YES itemAtURL:url destinationURL:self.ubiquityURL error:nil];
}];
}
I have the entitlements set up for the App ID and in Xcode itself. I click the button to save to iCloud Drive, and no errors pop up, the app doesn't crash, but nothing shows up on my Mac in iCloud Drive. The app is running on my iPhone 6 Plus via Test Flight while using iOS 8.1.1.
If I run it on Simulator (I know that it won't work due to iCloud Drive not working with simulator), I get the crash error: 'NSInvalidArgumentException', reason: '*** -[__NSPlaceholderDictionary initWithObjects:forKeys:count:]: attempt to insert nil object from objects[3]'
Well, you've got me interested in this matter myself and as a result I've spent way to much time on this question, but now that I've got it working I hope it helps you as well!
To see what actually happens in the background, you can have a look at ~/Library/Mobile Documents/, as this is the folder where the files eventually will show up. Another very cool utility is brctl, to monitor what happens on your mac after storing a file in the iCloud. Run brctl log --wait --shorten from a Terminal window to start the log.
First thing to do, after enabling the iCloud ability (with iCloud documents selected), is provide information for iCloud Drive Support (Enabling iCloud Drive Support). I also had to bump my bundle version before running the app again; took me some time to figure this out. Add the following to your info.plist:
<key>NSUbiquitousContainers</key>
<dict>
<key>iCloud.YOUR_BUNDLE_IDENTIFIER</key>
<dict>
<key>NSUbiquitousContainerIsDocumentScopePublic</key>
<true/>
<key>NSUbiquitousContainerSupportedFolderLevels</key>
<string>Any</string>
<key>NSUbiquitousContainerName</key>
<string>iCloudDriveDemo</string>
</dict>
</dict>
Next up, the code:
- (IBAction)btnStoreTapped:(id)sender {
// Let's get the root directory for storing the file on iCloud Drive
[self rootDirectoryForICloud:^(NSURL *ubiquityURL) {
NSLog(#"1. ubiquityURL = %#", ubiquityURL);
if (ubiquityURL) {
// We also need the 'local' URL to the file we want to store
NSURL *localURL = [self localPathForResource:#"demo" ofType:#"pdf"];
NSLog(#"2. localURL = %#", localURL);
// Now, append the local filename to the ubiquityURL
ubiquityURL = [ubiquityURL URLByAppendingPathComponent:localURL.lastPathComponent];
NSLog(#"3. ubiquityURL = %#", ubiquityURL);
// And finish up the 'store' action
NSError *error;
if (![[NSFileManager defaultManager] setUbiquitous:YES itemAtURL:localURL destinationURL:ubiquityURL error:&error]) {
NSLog(#"Error occurred: %#", error);
}
}
else {
NSLog(#"Could not retrieve a ubiquityURL");
}
}];
}
- (void)rootDirectoryForICloud:(void (^)(NSURL *))completionHandler {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURL *rootDirectory = [[[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]URLByAppendingPathComponent:#"Documents"];
if (rootDirectory) {
if (![[NSFileManager defaultManager] fileExistsAtPath:rootDirectory.path isDirectory:nil]) {
NSLog(#"Create directory");
[[NSFileManager defaultManager] createDirectoryAtURL:rootDirectory withIntermediateDirectories:YES attributes:nil error:nil];
}
}
dispatch_async(dispatch_get_main_queue(), ^{
completionHandler(rootDirectory);
});
});
}
- (NSURL *)localPathForResource:(NSString *)resource ofType:(NSString *)type {
NSString *documentsDirectory = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
NSString *resourcePath = [[documentsDirectory stringByAppendingPathComponent:resource] stringByAppendingPathExtension:type];
return [NSURL fileURLWithPath:resourcePath];
}
I have a file called demo.pdf stored in the Documents folder, which I'll be 'uploading'.
I'll highlight some parts:
URLForUbiquityContainerIdentifier: provides the root directory for storing files, if you want to them to show up in de iCloud Drive on your Mac, then you need to store them in the Documents folder, so here we add that folder to the root:
NSURL *rootDirectory = [[[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil]URLByAppendingPathComponent:#"Documents"];
You also need to add the file name to the URL, here I copy the filename from the localURL (which is demo.pdf):
// Now, append the local filename to the ubiquityURL
ubiquityURL = [ubiquityURL URLByAppendingPathComponent:localURL.lastPathComponent];
And that's basically it...
As a bonus, check out how you can provide an NSError pointer to get potential error information:
// And finish up the 'store' action
NSError *error;
if (![[NSFileManager defaultManager] setUbiquitous:YES itemAtURL:localURL destinationURL:ubiquityURL error:&error]) {
NSLog(#"Error occurred: %#", error);
}
If you are intending to work with UIDocument and iCloud, this guide from Apple is pretty good:
https://developer.apple.com/library/ios/documentation/DataManagement/Conceptual/UsingCoreDataWithiCloudPG/Introduction/Introduction.html
EDITED:
Don't know of any better guide of hand, so this may help:
You will need to fetch the ubiquityURL using the URLForUbuiquityContainerIdentifier function on NSFileManager (which should be done asynchronously).
Once that is done, you can use code like the following to create your document.
NSString* fileName = #"sampledoc";
NSURL* fileURL = [[self.ubiquityURL URLByAppendingPathComponent:#"Documents" isDirectory:YES] URLByAppendingPathComponent:fileName isDirectory:NO];
UIManagedDocument* document = [[UIManagedDocument alloc] initWithFileURL:fileURL];
document.persistentStoreOptions = #{
NSMigratePersistentStoresAutomaticallyOption : #(YES),
NSInferMappingModelAutomaticallyOption: #(YES),
NSPersistentStoreUbiquitousContentNameKey: fileName,
NSPersistentStoreUbiquitousContentURLKey: [self.ubiquityURL URLByAppendingPathComponent:#"TransactionLogs" isDirectory:YES]
};
[document saveToURL:fileURL forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) {
}];
You'll also want to look into using NSMetadataQuery to detect documents uploaded from other devices and potentially queue them for download, and observing the NSPersistentStoreDidImportUbiquitousContentChangesNotification to find about changes made via iCloud, among other things.
** Edit 2 **
Looks like you are trying to save a PDF file, which is not quite what Apple considers a "document" in terms of iCloud syncing. No need to use UIManagedDocument. Remove the last 3 lines of your completion handler and instead just use NSFileManager's
setUbiquitous:itemAtURL:destinationURL:error: function. The first URL should be a local path to the PDF. The second URL should be the path within the ubiquiuty container to save as.
You may also need to look into NSFileCoordinator perhaps.
I think this guide from Apple may be the most relevant:
https://developer.apple.com/library/ios/documentation/FileManagement/Conceptual/FileSystemProgrammingGuide/iCloud/iCloud.html
I am trying to use iCloud Documents to store XML files from my iOS App. Everything seems to be working fine (I can write and read files without errors) except my App's files do not show up in iCloud Documents neither in icloud.com nor in developer.icloud.com nor on my Windows PC in iCloud Drive folder. I was running the app in simulator and tested with TestFlight on a real device. I have the latest version of iCloud for Windows 4.0 installed. The App is created in Xcode 6.
Does anyone know how to make the files appear in iCloud Documents?
The code I am using for the saving the file:
NSLog(#"Syncing with iCloud");
NSURL *ubiq = [filemgr URLForUbiquityContainerIdentifier:nil];
if (ubiq) {
NSURL *ubiquitousPackage = [ubiq URLByAppendingPathComponent:#"Documents" isDirectory:YES];
if ([filemgr fileExistsAtPath:[ubiquitousPackage path]] == NO)
[filemgr createDirectoryAtURL:ubiquitousPackage
withIntermediateDirectories:YES
attributes:nil
error:nil];
ubiquitousPackage = [ubiquitousPackage URLByAppendingPathComponent:#"data.pxa"];
DataFile *file = [[DataFile alloc] initWithFileURL:ubiquitousPackage];
file.xmlContent = doc.XMLString;
[file saveToURL:[file fileURL] forSaveOperation:UIDocumentSaveForCreating | UIDocumentSaveForOverwriting completionHandler:^(BOOL success) {
if (success) {
NSLog(#"Synced with iCloud: %#", [ubiquitousPackage path]);
} else {
NSLog(#"Syncing with iCloud failed");
}
}];
} else {
NSLog(#"iCloud not available");
}
I found out what the problem was:
The key in Info.plist for the iCloud container was a bit different from the format "iCloud.com.example.MyApp".
I follow instructions at this link to see files created by my app in iCloudDrive. I can see file from settings>iCloud>Storage from device. Hope it helps.
I am learning iOS via Standford CS193P, I met a problem in core data part.
I am trying to build an app using core data, I create a category for the AppDelegate(I will create a UIManagedDocument in didFinishLaunchingWithOptions), this category implement one method to create a UIManagedDocument and return it to the AppDelegate, so that I can call self.managedDocument = [self createUIManagedDocument] to get one:
- (UIManagedDocument *)createUIManagedDocument
{
NSFileManager *fileManager = [NSFileManager defaultManager];
NSURL *documentsDirectory = [[fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] firstObject];
NSURL *url = [documentsDirectory URLByAppendingPathComponent:APP_NAME];
UIManagedDocument *managedDocument = [[UIManagedDocument alloc] initWithFileURL:url];
if ([[NSFileManager defaultManager] fileExistsAtPath:[url path]]) {
// If the file exists, open it
NSLog(#"File exists, not opening...");
[managedDocument openWithCompletionHandler:^(BOOL success) {
NSLog(#"File opened.");
}];
} else {
// If the file not exists, create it
NSLog(#"File not exists, now creating...");
[managedDocument saveToURL:url forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) {
// If the file create succesfully, open it
[managedDocument openWithCompletionHandler:^(BOOL success) {
NSLog(#"File opened.");
}];
}];
}
return managedDocument;
}
After the UIManagedDocument creation, I am trying to pass this UIManagedDocument to my view controllers using:
RootViewController *rootViewController = (RootViewController *)self.window.rootViewController;
rootViewController.managedDocument = self.managedDocument;
And the problem occurs, I am unable to open the UIManagedDocument in my view controllers. I searched it for a whole day and got the answer: I was trying to open it twice at the same time while it is asynchronous, it takes time to process the IO request. There are some approaches, and most of them using Singleton.
Here is my question:
Can I do this by using the approach above?
If no, what is the standard(or correct) way to create and pass this UIManagedDocument around my app?
Should I pass NSManagedObjectContext in this UIManagedDocument to my view controllers instead of passing UIManagedDocument?
Thank you for reviewing my question.
In the course, we are told to do this using notifications, he explains this in a demo in lecture 13 at 51:20.
I have a weird problem. My app works fine on my device but the sqlite database does not work on the simulator. The file "database.sqlite" exists in the same folder as my apps, it has the same name and the columns names are also correct.
So I assume there is something wrong with the configuration but I don't know what. Can someone please help me out.
Thanks
Here are some posts that seem to address the problem : http://forums.macrumors.com/showthread.php?t=484899
One reason might be because you should ensure the database is copied from your Supporting Files application directory (read only) to library or documents before you use it. Here's an ensurePrepared function from a sample of mine that uses sqlite that does just that. In this case, it's called contacts.db
- (BOOL)ensureDatabasePrepared: (NSError **)error
{
// already prepared
if ((_dbPath != nil) &&
([[NSFileManager defaultManager] fileExistsAtPath:_dbPath]))
{
return YES;
}
// db in main bundle - cant edit. copy to library if !exist
NSString *dbTemplatePath = [[NSBundle mainBundle] pathForResource:#"contacts" ofType:#"db"];
NSLog(#"%#", dbTemplatePath);
NSString *libraryPath = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) lastObject];
_dbPath = [libraryPath stringByAppendingPathComponent:#"contacts.db"];
NSLog(#"dbPath: %#", _dbPath);
// copy db from template to library
if (![[NSFileManager defaultManager] fileExistsAtPath:_dbPath])
{
NSLog(#"db not exists");
NSError *error = nil;
if (![[NSFileManager defaultManager] copyItemAtPath:dbTemplatePath toPath:_dbPath error:&error])
{
return NO;
}
NSLog(#"copied");
}
return YES;
}