I am added dropbox support to my app, everything works perfect, the authentication, the login/out, but I can't upload files. I am using
NSArray *p = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* docDir = [p objectAtIndex:0];
NSString *localPath = docDir;
NSString *filename = #"Documents";
NSString *destDir = #"/";
[[self restClient] uploadFile:filename toPath:destDir
withParentRev:nil fromPath:localPath];
to upload my applications /Document directory to Dropbox, but nothing is uploaded. I don't get any error message. The directory Apps/My-Applications-Name is created, but with no content inside.
Anyone know why?
I also have this two delegate methods, but I still don't get a log. I also have <DBRestClientDelegate> behind the #interface … ():
- (void)restClient:(DBRestClient*)client uploadedFile:(NSString*)destPath
from:(NSString*)srcPath metadata:(DBMetadata*)metadata {
NSLog(#"File uploaded successfully to path: %#", metadata.path);
}
- (void)restClient:(DBRestClient*)client uploadFileFailedWithError:(NSError*)error {
NSLog(#"File upload failed with error - %#", error);
}
It sounds like Documents is a directory, but uploadFile is used to upload a file. I don't think there's a method in the Core API to upload the contents of a folder, so you'll have to walk through the folder and upload each file individually.
Did you implement the Dropbox delegate methods? Uploadcompleted and uploadfailedwitherror? You also need to specify in your app delegate that you are the Dropbox delegate in order to implement those methods. Make sure your local path is correct, your upload path is.
Add DBRestClientDelegate to your app delegate or whatever class uploads
NSArray *p = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString* documentDirectory = [p objectAtIndex:0];
NSString *filename = #"png.png";
NSString *destDir = #"/";
[[self restClient] uploadFile:filename
toPath:destDir
withParentRev:nil
fromPath:[documentDirectory stringByAppendingPathComponent:filename] ];
the "fromPath" must be the FULL PATH of the source file. The API Document is WRONG.
Did you check that you're making setting the delegate from the main thread? Otherwise, your thread likely doesn't have a runloop and the delegate callbacks will never be called.
UPDATE my problem was ARC destroying the DBRestClient object before the upload was complete.
Related
I plan to develop a Point of Sales system, and intend to use sqlite3 as the database. After few days research, I can't find any tutorial or example on backup or restore sqlite3 database. The solution I can accept is either upload to cloud or dropbox.
Try this answer.
Creating Backup & Restoring file using Dropbox
Follow the steps that appears in Dropbox integration for your iOS App.
First, Get your local db path as given below:
NSArray *docsDirectory = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *docPath = [docsDirectory objectAtIndex:0];
databasePath = [docPath stringByAppendingPathComponent:#"filename.sqlite"];
NSLog(#"db path -> %#",databasePath);
Then Upload that file to dropbox as following:
NSString *fileName = #"filename.sqlite";
NSString *destDir = #"/";
if (appDelegateObj.parentRevId == nil)
{
//Uploads a fresh file.
[appDelegateObj.restClientObj uploadFile:fileName toPath:destDir withParentRev:nil fromPath:databasePath];
}
else
{
// Uploads a file with already existing file
[appDelegateObj.restClientObj uploadFile:fileName toPath:destDir withParentRev:appDelegateObj.parentRevId fromPath:databasePath];
}
Here, parentRevId is for identifier of existing file.
You can get the parentRevId from Dropbox delegate method.
And then Restore the dropbox file to your local db path as following:
if (appDelegateObj.parentRevId == NULL) // No such File found
{
[SVProgressHUD showErrorWithStatus:#"No Data in Dropbox" maskType:SVProgressHUDMaskTypeBlack]; // Activity Indicator
}
else
{
NSString *destDir = #"/filename.sqlite";
[appDelegateObj.restClientObj loadFile:destDir intoPath:databasePath];
}
restClientObj is the object of DBRestClient (Dropbox)
I have a file located in my app's Documents folder. When the app is terminated I save the file's URL in the AppDelegate's applicationWillTerminate method:
// archiver init code
[archiver encodeObject:file.URL forKey:kFileURL];
// finish encoding and write data to file system
But when trying to restore the file on the next app launch the file manager cannot locate the file: After calling
NSURL *fileURL = [unarchiver decodeObjectForKey:kFileURL];
NSString *filePath = fileURL.path;
the method
[[NSFileManager defaultManager] fileExists:filePath];
returns NO.
I tried to find the reason for this and I discovered that the path to the Documents folder changes with every app launch. The part that changes is the hexadecimal folder in the middle. Here are two examples:
/private/var/mobile/Applications/04083A4A-87AC-4E3C-8BA1-F002B97AE304/Documents/...
/private/var/mobile/Applications/65D136BA-42C3-887A-B947-7FE396978153/Documents/...
I always thought that the hexadecimal part is some sort of ID unique to every app. But as it changes: What exactly is that number?
And how can I relocate my file then after terminating and relaunching my app?
You should just get the directory for the document folder and then load your file.
+ (NSString *)documentDataPath
{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
if ([paths count] == 0) {
return nil;
}
NSString *directory = [paths objectAtIndex:0];
if (directory == nil) {
NSLog(#"NSDocumentDirectory not found!");
}
return directory;
}
I use this code to save some PDF data to a file, send it to another app using the "Open In" menu, then delete the file when that's done:
- (void)openIn:(NSData *)fileData {
// save the PDF data to a temporary file
NSString *fileName = [NSString stringWithFormat:#"%#.pdf", self.name];
NSString *filePath = [NSString stringWithFormat:#"%#/Documents/%#", NSHomeDirectory(), fileName];
BOOL result = [fileData writeToFile:filePath atomically:TRUE];
if (result) {
NSURL *URL = [NSURL fileURLWithPath:filePath];
UIDocumentInteractionController *controller = [[UIDocumentInteractionController interactionControllerWithURL:URL] retain];
controller.delegate = self;
[controller presentOpenInMenuFromBarButtonItem:self.openInButton animated:TRUE];
}
}
- (void)documentInteractionControllerDidDismissOpenInMenu:(UIDocumentInteractionController *)controller {
// when the document interaction controller finishes, delete the temporary file
NSString *fileName = [NSString stringWithFormat:#"%#.pdf", self.name];
NSString *filePath = [NSString stringWithFormat:#"%#/Documents/%#", NSHomeDirectory(), fileName];
[[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
}
This has worked fine until iOS 8. Now, the file is created and I can verify that it contains the correct content, the Open In menu appears, I can select an app, and the delegate method runs and cleans up the file. But instead of iOS switching to the selected app and copying the file into it as it did before, the Open In menu simply closes when I select an app, and the file is not copied.
This works if I give the UIDocumentInteractionController an existing file. It also works if I use the provided fileData but change the destination filename to the filename of an existing file. This suggests a permissions problem -- as if new files are created in iOS 8 with default permissions that UIDocumentInteractionController can't read.
Does anyone know what's happening and how I can work around it?
It looks like the order of operations has changed slightly in iOS 8. DidDismissOpenInMenu used to run after the file was finished sending, but now it runs after the file begins sending. This means my cleanup code was sometimes running before the file was finished sending, leaving no file to send. I figured this out after noticing that smaller files were being sent okay; apparently the processing for smaller files was finishing before my cleanup code got them, but the processing for larger files was not.
To ensure the correct timing, but also clean up files that are created when the user opens the DocumentInteractionController and then dismisses the controller without doing anything, I changed my methods like this:
- (void)openIn:(NSData *)fileData {
// save the PDF data to a temporary file
NSString *fileName = [NSString stringWithFormat:#"%#.pdf", self.name];
NSString *filePath = [NSString stringWithFormat:#"%#/Documents/%#", NSHomeDirectory(), fileName];
BOOL result = [fileData writeToFile:filePath atomically:TRUE];
if (result) {
self.sendingFile = FALSE;
NSURL *URL = [NSURL fileURLWithPath:filePath];
UIDocumentInteractionController *controller = [[UIDocumentInteractionController interactionControllerWithURL:URL] retain];
controller.delegate = self;
[controller presentOpenInMenuFromBarButtonItem:self.openInButton animated:TRUE];
}
}
- (void)documentInteractionController:(UIDocumentInteractionController *)controller willBeginSendingToApplication:(NSString *)application {
// the user chose to send the file, so we shouldn't clean it up until that's done
self.sendingFile = TRUE;
}
- (void)documentInteractionControllerDidDismissOpenInMenu:(UIDocumentInteractionController *)controller {
if (!self.sendingFile) {
// the user didn't choose to send the file, so we can clean it up now
[self openInCleanup];
}
}
- (void)documentInteractionController:(UIDocumentInteractionController *)controller didEndSendingToApplication:(NSString *)application {
// the user chose to send the file, and the sending is finished, so we can clean it up now
[self openInCleanup];
self.sendingFile = FALSE;
}
- (void)openInCleanup {
// delete the temporary file
NSString *fileName = [NSString stringWithFormat:#"%#.pdf", self.name];
NSString *filePath = [NSString stringWithFormat:#"%#/Documents/%#", NSHomeDirectory(), fileName];
[[NSFileManager defaultManager] removeItemAtPath:filePath error:nil];
}
Update for iOS 11
Before iOS 11, it seems that the operating system kept a copy of the file available until the receiving app was finished reading it, even though my cleanup function ran as soon as the file was sent out from my app. In iOS 11, this changed and the receiving app fails to read the file because my app deletes it before that's done. So now instead of saving the temporary file to Documents and using the openInCleanup method to delete it immediately, I'm saving the temporary file to tmp and emptying the tmp folder next time the app launches. This approach should also work with older iOS versions. Just remove openInCleanup, change Documents to tmp in the paths, and add this to applicationDidFinishLaunching:
// clear the tmp directory, which will contain any files saved for Open In
NSString *tmpDirectory = [NSString stringWithFormat:#"%#/tmp", NSHomeDirectory()];
NSArray *tmpFiles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:tmpDirectory error:NULL];
for (NSString *tmpFile in tmpFiles) {
[[NSFileManager defaultManager] removeItemAtPath:[NSString stringWithFormat:#"%#/%#", tmpDirectory, tmpFile] error:NULL];
}
After reading this post, I already hoped to have found the solution to a similar problem:
For me, as of iOS 8, sharing was only working with Mail.app. It was failing for Dropbox, etc.
Turns out it was something else:
On my interactionController I was setting an annotation like this:
interactionController.annotation = #"Some text"
For unknown reasons, this prevented Dropbox to open at all. There were no error messages or anything. Removing this line solved the issue.
All the documents from dropbox and other tutorials are talking about create the new files and sync with dropbox using sync api.
I have some files my app document directory and I'd like to sync them with dropbox server.
Here is what I did.
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *path = [[paths objectAtIndex:0] stringByAppendingPathComponent:#"emmy.txt"];
DBPath *newPath = [[DBPath root] childPath:path];
DBFile *file = [[DBFilesystem sharedFilesystem] createFile:newPath error:nil];
//[file writeString:str error:nil];
DBFileStatus *status = file.status;
if (!status.cached) {
[file addObserver:self block:^() {
}];
}
The file created on dropbox but it is empty. The file in my app has content.
How can I upload the existing files to dropbox in my app using dropbox sync api?
I think you want writeContentsOfFile.
In my game, I'm saving stats of the player in a plist that I store in the Documents directory. I have an empty dictionary of each stats that should be saved named "Default_Stats.plist" so that if it's the first time the app is loaded, it will copy it in the appropriate directory so it could be loaded and overwritten at will. The problem is, every time my app is loaded, it doesn't recognize the "Stats.plist" and overwrite it with the Default Stats, resetting every stats the player have made... And weird enough, it was perfectly working on the simulator, but not on the device. Here's my code :
In this method I read the stats :
- (void) readStatsFromFile{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *statsPath = [[paths objectAtIndex:0] stringByAppendingPathComponent:#"Stats.plist"];
//Check if the file has already been created
if (![[NSFileManager defaultManager] fileExistsAtPath:statsPath]){
[self createStatsList];
}else{
stats = [[NSMutableDictionary dictionaryWithContentsOfFile:statsPath]retain];
}
}
Here's my creating method :
- (void) createStatsList{
NSString *statsPath = [[NSBundle mainBundle] bundlePath];
statsPath = [statsPath stringByAppendingPathComponent:#"Default_Stats.plist"];
stats = [[NSMutableDictionary dictionaryWithContentsOfFile:statsPath] retain];
[self writeStatsToFile];
}
And my writing method :
- (void) writeStatsToFile{
BOOL ok;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *statsPath = [[paths objectAtIndex:0] stringByAppendingPathComponent:#"Stats.plist"];
ok = [stats writeToFile:statsPath atomically:YES];
if (!ok) {
NSLog(#"Couldn't write to file");
}else
NSLog(#"Stats written succesfully!");
}
Please help, I really don't understand what's wrong! I hope I've made myself clear enough!
Use filepath instead of absolute path.
Maybe duplicates exist in your mac, which makes exists=true on simulator, but not on device.
The easiest way to check would be to NSLog the paths encountered. Refer to these tools - they allow console logs to be captured for release builds running on your device.
Most likely that your documents directory just doesn't exist - on the simulator you share a documents directory with everyone on the Mac; on the device everyone has his own directory. Use the file manager method
createDirectoryAtURL:url withIntermediateDirectories:YES
to make sure that the directory is there before you try writing there. (I tend to use the URL methods instead of the file path methods).
PS. I'd recommend having one method that returns the path or url that you want. It's a good habit not to duplicate your code again and again.
I would do pretty much that, like everything in one session:
gets the URL for the file in the Document folder;
if the file is not there yet, copies the file from bundle to the Documents folder;
that should be the method for that, I have defined some macros for avoiding mistyping the file's name in the code:
- (NSURL *)statsFileURL {
#define NSStringFromFileNameWithExtension(filename, extension) [(filename) stringByAppendingPathExtension:(extension)]
#define kExtension #"plist"
#define kDefaultStatsFileName #"Default_Stats"
#define kCustomStatsFileName #"Stats"
NSURL *_returnURL = nil;
NSFileManager *_fileManager = [NSFileManager defaultManager];
NSURL *_documentDirectory = [[_fileManager URLsForDirectory:NSDocumentDirectory inDomains:NSUserDomainMask] lastObject];
NSURL *_myFileURLInDocumentFolder = [_documentDirectory URLByAppendingPathComponent:NSStringFromFileNameWithExtension(kDefaultStatsFileName, kExtension)];
if ([_fileManager fileExistsAtPath:[_myFileURLInDocumentFolder path]]) {
_returnURL = _myFileURLInDocumentFolder;
} else {
NSURL *_myFileURLInBundle = [[NSBundle mainBundle] URLForResource:kDefaultStatsFileName withExtension:kExtension];
if ([_fileManager fileExistsAtPath:[_myFileURLInBundle path]]) {
NSError *_error = nil;
if ([_fileManager copyItemAtURL:_myFileURLInBundle toURL:_myFileURLInDocumentFolder error:&_error]) {
if (_error == nil) {
_returnURL = _myFileURLInDocumentFolder;
} else {
// some error during copying
}
} else {
// some error during copying
}
} else {
// the file does not esixts at all, not even in the bundle
}
}
return _returnURL;
}
the URL always points inside the Documents folder, so you will have read/write access to the file – or will be nil if some error happens.
after you have the URL, you can restore back to file without any issue, and at some other point in runtime you can override the file for your convenience anytime.
NOTE: you may need to extend this code for a more detailed error handling, I put the comment only the places when you need to worry about potential errors.