iOS Dropbox sync before app quits - ios

I'm trying to sync files created in my app to Dropbox, however it seems the syncing only happens after the app quits, and not in real time when files are created and moved between locations in different folders in the app or created/deleted. Is there a certain call I have to make for instance? Appreciate your help!
Below is the code I am using for syncing:
-(void)createFilePathinFolder:(NSString *)folderName FileName:(NSString *)fileName {
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *folder = [self localDocumentsRootPath];
if (![folderName isEqualToString:#"root"]) {
folder = [folder stringByAppendingPathComponent:folderName];
}
NSString *file = [folder stringByAppendingPathComponent:fileName];
if (![fileManager fileExistsAtPath:file]) {
[fileManager createFileAtPath:file contents:[#"0" dataUsingEncoding:NSUTF8StringEncoding] attributes:nil];
}
//Insert to FileTable
[[DBHelper shared]insertToFileTableWithFolder:folderName FileName:fileName MetaFileName:nil Tag:nil Title:nil];
if ([NetworkHelper shared].canSyncWithCloud) {
NSString *filePathStr = [folderName stringByAppendingPathComponent:fileName];;
if ([folderName isEqualToString:#"root"]) {
filePathStr = fileName;
}
DBPath *filePath = [[DBPath root] childPath:filePathStr];
DBError *error;
DBFile *destFile =[[DBFilesystem sharedFilesystem] createFile:filePath error:&error];
NSData *fileData = [NSData dataWithContentsOfFile:file];
[destFile writeData:fileData error:&error];
//[destFile writeContentsOfFile:file shouldSteal:NO error:&error];
[destFile close];
if (error) {
NSLog(#"Error when creating file %# in Dropbox, error description:%#", fileName, error.description);
}
}
}

Your error checking is all wrong. Your code should be more like this:
DBPath *filePath = [[DBPath root] childPath:filePathStr];
DBError *error = nil;
DBFile *destFile =[[DBFilesystem sharedFilesystem] createFile:filePath error:&error];
if (destFile) {
NSData *fileData = [NSData dataWithContentsOfFile:file];
if (![destFile writeData:fileData error:&error]) {
NSLog(#"Error when writing file %# in Dropbox, error description: %#", fileName, error);
}
[destFile close];
} else {
NSLog(#"Error when creating file %# in Dropbox, error description: %#", fileName, error);
}
The file should sync right away with the code that you have. This assumes you have properly linked your app to an account and all.
What version of the Dropbox Sync API are you using? 1.0.7 has some potential networking issues. I have a beta of 1.0.8 that seems to solve these issues. You may need to wait until 1.0.8 comes out.
You can verify if Dropbox is hung. While running your app in the debugger, wait a minute after the file has been created. If the file doesn't appear, pause your app in the debugger and look at all of the threads. You should see one or more dropbox related threads. If one looks blocked with a reference to dbx_cfhttp_request then you have hit a bug in the Dropbox framework. Putting your device in Airplane mode for 10-15 seconds then turning Airplane mode off again should kick it back into gear.

Related

can I Save & access Data from apple iCloud just like i Use to have from other private webserver?

Following is general knowledge question for my owns awareness, actually I am new to iOS programming, in my very first app I used to save access a json file and photos which I placed in a free webserver, but I am not sure about how many days I can acquire benefits from that feee web server. hence I was planing to purchase a web-domain & web-space, meanwhile I come to know about apple icloud storage, so the question is that can I save same json file & other image files at icloud and can access in same way as I used to save & access at private web server? and also willing to know, how much data can be transfer (total traffic consumption of data on daily basis and monthly basis)?
1)You can access the icloud file :
NSArray *tempArray=[[iCloud sharedCloud] listCloudFiles];
2)To Download :
NSFileManager *fm = [NSFileManager defaultManager];
NSURL *ubiq = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
if (ubiq == nil) {
return NO;
}
NSError *theError = nil;
[fm setUbiquitous:true itemAtURL:backupUrl destinationURL:[[ubiq URLByAppendingPathComponent:#"Documents" isDirectory:true] URLByAppendingPathComponent:backupName] error:&theError];
NSFileManager *fm = [NSFileManager defaultManager];
NSURL *ubiq = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
if (ubiq == nil) {
return NO;
}
NSError *theError = nil;
bool started = [fm startDownloadingUbiquitousItemAtURL:[[ubiq URLByAppendingPathComponent:#"Documents" isDirectory:true] URLByAppendingPathComponent:backupName] error:&theError];
NSLog(#"started download for %# %d", backupName, started);
if (theError != nil) {
NSLog(#"iCloud error: %#", [theError localizedDescription]);
}

Save iOS 8 Documents to iCloud Drive

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

newsstand memory storage issue, how do i get the app cache directory?

I have a newsstand app which has magazines and uses the newsstand framework. I realized there was something wrong when deleting the magazines and/or when downloading them because when I accessed settings/usage my app keeps growing in memory usage when downloading and deleting the same magazine.
Found the issue... when downloading the issue in the delegate method:
-(void)connectionDidFinishDownloading:(NSURLConnection *)connection destinationURL:(NSURL *)destinationURL
I just needed to add something like this at the end:
NSError *error;
[[NSFileManager defaultManager] removeItemAtPath:[destinationURL path] error:&error];
if (error){
NSLog(#"ERROR:%#", error);
}
Even the directory is called "caches" you need to manually delete. Ok problem solved but what about the customers who already download my app and have tons of MBs dead in the cache directory.
I wanted to know how to get this directory and delete everything on it at launch and only once...
I can do it only once using a NSUserdefault but how do I get this directory and delete any zip files in it... an example of this directory and a file within is:
/private/var/mobile/Applications/1291CC20-C55F-48F6-86B6-B0909F887C58/Library/Caches/bgdl-280-6e4e063c922d1f58.zip
but this path varies with the device. I want to do this at launch so I'm sure there are no downloads in progress but any other solutions are welcome, thanks in advance.
Everything that you need is enumerate all files from Caches directory and remove ones that have zip extension:
- (void)removeZipFilesFromCachesDirectory {
static NSString *const kZIPExtension = #"zip";
NSFileManager *fileManager = [NSFileManager defaultManager];
NSString *cachesDirectoryPath = [NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES) objectAtIndex:0];
NSError *error = nil;
NSArray *fileNames = [fileManager contentsOfDirectoryAtPath:cachesDirectoryPath error:&error];
if (error == nil) {
for (NSString *fileName in fileNames) {
NSString *filePath = [cachesDirectoryPath stringByAppendingPathComponent:fileName];
if ([filePath.pathExtension.lowercaseString isEqualToString:kZIPExtension]) {
NSError *anError = nil;
[fileManager removeItemAtPath:filePath error:&anError];
if (anError != nil) {
NSLog(#"%#", anError);
}
}
}
} else {
NSLog(#"%#", error);
}
}

copy OSX folder to iOS directory

I'm trying to copy a folder and it's contents to a sub-directory in the documentation directory and it's failing with the error:
"The operation couldn’t be completed. No such file or directory"
First I try to create a folder in the documentation directory like this:
NSString *diagramsDirectory = [docDirectory stringByAppendingPathComponent:#"Diagrams"];
if (![fileManager fileExistsAtPath:docDirectory isDirectory:&isDirectory] || !isDirectory)
{
NSError *error = nil;
NSDictionary *attr = [NSDictionary dictionaryWithObject:NSFileProtectionComplete
forKey:NSFileProtectionKey];
[fileManager createDirectoryAtPath:diagramsDirectory
withIntermediateDirectories:NO
attributes:attr
error:&error];
if (error) {
NSLog(#"error creating dir. path: %#", [error localizedDescription]);
}
}
NSLog(#"diagrams directory = %#", diagramsDirectory);
The console log seems to indicate this works:
diagrams directory = /Users/../iPhone Simulator/../Library/Documentation/Diagrams
However, when I then try to copy a folder called "Diagrams" from a directory on the Mac:
NSString *pathToDirectories = #"/User/Desktop/Project Resource Files/Files/";
NSError *error = nil;
NSArray *folders = [fileManager contentsOfDirectoryAtPath:pathToDirectories error:&error];
for (NSString *folder in folders) {
if ([folder isEqualToString:#"Diagrams"]) {
[self copyFolderAtPath:folder toDestinationFolderAtPath:docDirectory];
}
which calls the "copyFolderAtPath" method:
- (BOOL)copyFolderAtPath:(NSString *)sourceFolder toDestinationFolderAtPath:(NSString *)destinationFolder
{
destinationFolder = [destinationFolder stringByAppendingPathComponent:[sourceFolder lastPathComponent]];
NSFileManager *fileManager = [NSFileManager defaultManager];
NSError *error = nil;
// check for destination folder
if ([fileManager fileExistsAtPath:destinationFolder])
{
if (![fileManager removeItemAtPath:destinationFolder error:&error])
{
NSLog(#"Could not remove old files. Error: %#", error);
return NO;
}
}
error = nil;
// copy destination
if (!([fileManager copyItemAtPath:sourceFolder toPath:destinationFolder error:&error])) {
NSLog(#"failed copying file at path %# to path %#. Error %#", sourceFolder, destinationFolder, error);
return NO;
}
return YES;
}
it returns "no" and I get the error.
Anyone got an idea what I'm doing wrong?
The device (and therefore the simulator) is isolated from the operating system so you cannot directly do file system copies. Imagine even if it let you do it from the simulator, how would a disconnected device running your app access the OS filesystem?
You will have to look into other options like having an application on the mac that opens sockets or having an http end point on the mac that the device copies from. Other options include syncing documents via iCloud or another cloud service. You can also transfer files via iTunes. I'm sure there's many other options ... Also checkout this

NSFileManager removeItemAtPath: error: did not actually free disk space

NSFileManager* fileManager = [NSFileManager defaultManager];
NSURL* url = [[fileManager URLsForDirectory:NSCachesDirectory inDomains:NSUserDomainMask] lastObject];
NSString* directory = [url path];
NSString* filePath = [directory stringByAppendingPathComponent:FILE_NAME];
if ([fileManager fileExistsAtPath:filePath])
{
[fileManager removeItemAtPath:filePath error:nil];
}
Here's my code. When it is executed, the file is deleted, but the space remains occupied. Here's the code for storing something into the file.
NSFileManager* fileManager = [NSFileManager defaultManager];
NSURL* url = [[fileManager URLsForDirectory:NSCachesDirectory inDomains:NSUserDomainMask] lastObject];
NSString* directory = [url path];
NSString* filePath = [directory stringByAppendingPathComponent:FILE_NAME];
NSArray* oldArray = nil;
if ([fileManager fileExistsAtPath:filePath])
{
oldArray = [[NSArray alloc] initWithContentsOfFile:filePath];
[fileManager removeItemAtPath:filePath error:nil];
}
NSMutableArray* mergeArray = [[NSMutableArray alloc] initWithArray:arrayOfPersons];
[mergeArray addObjectsFromArray:oldArray];
if ( [mergeArray writeToFile:filePath atomically:YES]) NSLog(#"Written");
By the way, it cost 1 MB to store an array with only 1 object(an NSDictionary with 2 keys). Is there a cheaper way to store it?
You need to be much more careful with your experiments. The unix file system does lots of stuff with files. In fact, when you "delete" a file, all you do is unlink it from the file system. If that file is open with another file descriptor, anywhere in the OS, it will remain open.
Furthermore, there are lots of optimizations to reuse file nodes. Just because you delete a file, does not mean that space goes back automatically. It could be "reserved" in your app for several reasons, for other files to use. No sense giving it back to the file system until the file system needs it.
settings->general->usage is a very rough measurement of file system utilization. A better measurement would be accessing the attributes of the file and file system directly.
Using your code as a base, consider this:
- (NSString*)workingDirectory {
NSFileManager* fileManager = [NSFileManager defaultManager];
NSURL* url = [[fileManager URLsForDirectory:NSCachesDirectory
inDomains:NSUserDomainMask] lastObject];
return [url path];
}
- (NSString*)filePath {
return [[self workingDirectory] stringByAppendingPathComponent:FILE_NAME];
}
Now, you can see all the attributes of the entire file system with this:
NSDictionary *attributes = [[NSFileManager defaultManager]
attributesOfFileSystemForPath:[self workingDirectory] error:0];
NSLog(#"file system attributes: %#", attributes);
and those for the specific file with this:
NSDictionary *attributes = [[NSFileManager defaultManager]
attributesOfItemAtPath:[self filePath] error:0];
NSLog(#"file attributes: %#", attributes);
Pay attention to NSFileSystemFreeSize and NSFileSize.
Run your app, and dump both of these values. Create your file, and dump them again. Delete the file, and dump them again.
After all that, you may actually see the NSFileSystemFreeSize go UP, even after the delete. Remember, the system itself is creating temporary files, and is probably caching those file system nodes for future use.
You can get more consistent results if you quit all other apps. Then, quit yours (double-click power button, X all running apps). Delete the file before doing this.
Now, start your app, without the file existing.
Dump file system data.
Create the file.
Dump file system data.
Dump file data.
You should see the file taking up about 200-250 bytes, and the file system free size should drop 8192.
Delete the file.
Dump file system data. Is probably at least as big as it was before deleting file.
Quit app (not in XCode -- double-click power, X the app).
Run the app.
Dump file system data. You should see the data back to about what it was when you started earlier.
In conclusion, while it may look like the file system has not released the data, it really has, but maybe the tool you are using to query just does not know the details of the file system.
Note, also, that when an app is running, it will use lots of file system resources for stuff that you are not explicitly doing.
I hope that made sense...
Your code to delete the file looks correct, but you are switching between URLs and Paths when you don't need to. You should also be checking for an error when you try to delete the file so that you can see why it doesn't work. Try this:
NSFileManager *fileManager = [NSFileManager defaultManager];
NSArray *directoryURLs = [fileManager URLsForDirectory:NSCachesDirectory
inDomains:NSUserDomainMask];
NSURL *directoryURL = [directoryURLs objectAtIndex:0];
NSURL *fileURL = [directoryURL URLByAppendingPathComponent:FILE_NAME];
if (!fileURL)
{
NSLog(#"Could not create URL for file.");
return;
}
NSError *err = nil;
if (![fileURL checkResourceIsReachableAndReturnError:&err])
{
NSLog(#"File is not reachable.\n"
"Error: %# %d %#", [err domain], [err code], [[err userInfo] description]);
return;
}
err = nil;
[fileManager removeItemAtURL:fileURL error:&err];
if (err)
{
NSLog(#"Unable to delete existing file.\n"
"Error: %# %d %#", [err domain], [err code], [[err userInfo] description]);
return;
}
May it be, that the length of the ˚FILE_NAME` is greater or equal to 300 chars? This brought me to similar issues with NSFileManager some time ago...

Resources