Need to see if your shared folder is taking up space on your dropbox 👨💻? Find out how to check here.
Forum Discussion
Bob S.15
9 years agoCollaborator | Level 9
migrating from API 1 to 2 - how do I know if I have OAuth 1 tokens?
I am finally getting around to updating my OLD apps from the original Dropbox IOS API to the new one. I'm looking at the migration guide, and the first thing it says is to check my OAuth tokens to se...
Bob S.15
9 years agoCollaborator | Level 9
Hey Greg,
I don't really see anything in this Getting Started about tokens or extracting them? https://github.com/dropbox/dropbox-sdk-obj-c#get-started
I don't understand where these tokens come into it. It seems like if I just follow the authorization flow as described in "Getting Started", it would authorize the app?
EDIT: If you saw that earlier post about the 'image not found' error, I fixed that. I didn't have the framework as an embedded binary...
Thanks!
Bob
Greg-DB
Dropbox Community Moderator
9 years agoHi Bob, yes, if you just follow the getting started guide you linked to, that will have you implement the app authorization flow, allowing users to link to and use your app. That will handle getting new tokens for you and storing them.
It's only if you were using an old SDK and want to automatically migrate the previously stored access tokens from that in your app would you need to put in the extra work to do so, e.g., using the technique from this old blog post.
- Bob S.159 years agoCollaborator | Level 9
Hey Greg,
I have a couple of questions about the regular authorization flow. If I don't have pre-existing tokens, I'm using the web form login to authorize the user. It put up a screen that says "<AppName> would like access to its own folder, Apps->AppName, inside your Dropbox, Learn More." and you can click Cancel or Allow.
If I click Allow, instead of returning to the app immediately, it shows a pop-up dialog that says "Open in "AppName"?" and you again have to press Cancel or Open. That seems wrong. Is it really required to do that second dialog or am I doing something wrong?
Also, once I have done this, how to I get it to remember that it is logged into the app? Currently I have to go through this authorization every time I run the app.
Thanks,
Bob
- Greg-DB9 years ago
Dropbox Community Moderator
That "Open in" confirmation is part of iOS itself and was introduced by Apple somewhat recently (either iOS 9 or iOS 10 if I recall correctly) in order to let the user prevent unwanted redirects between apps. Consequently, that is expected, and we don't have control over it.
Also, the SDK should automatically store the authorization for you. Did you finish setting up the authorization flow as documented in the guide? Are you getting any errors? - Bob S.159 years agoCollaborator | Level 9
Hey Greg,
I have the authorization flow set up -- but the Getting Started guide only describes how to authorize the user, it doesn't say anything about handling when the authorization has already happened. By looking at the DBRoulette example, it looks like I should check [DropboxClientsManager authorizedClient], is that right? If that is not nil then the user is already authorized?
My old app used to authorize and then get the user's account info. How would I do that with the new SDK? I see this Account structure here:
https://dropbox.github.io/dropbox-sdk-obj-c/api-docs/latest/Classes/DBUSERSAccount.html
But it isn't clear how to use the SDK to obtain the information? Is there an example somewhere of how to obtain the account info?
Thanks,
Bob
- Greg-DB9 years ago
Dropbox Community Moderator
Yes, that's right, you should use authorizedClient like that.
You can use the DBUSERSRoutes.getCurrentAccount method to get the account information.
That would look like this:
[[client.usersRoutes getCurrentAccount] response:^(DBUSERSFullAccount *account, DBNilObject *_, DBRequestError *error) { if (account) { NSLog(@"%@", account); } else if (error) { NSLog(@"%@", error); } }];(Note that DBError was renamed to DBRequestError in 2.0.6, so make sure you use the right one, depending on what version you're using.)
- Bob S.159 years agoCollaborator | Level 9
Okay, thanks.
Also I wanted to ask you about retrieving old tokens. Now that I know the user can just re-authorize, it's not that big a deal, but I was trying to get it to work anyway.
My old phone is logged into Dropbox using the old version of the app. Then I run the new version to see if I can convert the old tokens. The new version does not recognize that the user is logged in already, which is expected. Then I call this:
NSArray *credentials = [bigApp getTokensFromSyncAPI];
But [credentials count] always returns 0. Shouldn't it return the old tokens?
Thanks
Bob
- Greg-DB9 years ago
Dropbox Community Moderator
That code is written for getting tokens from the Sync SDK, but you're using the Core SDK, so the values would be a little different.
I put this together for you as a basic concept of how this would work for going from the Core SDK to the API v2 Objective-C SDK:
- (NSArray * )getTokensFromCoreSDK { NSString *keychainPrefix = [[NSBundle mainBundle] bundleIdentifier]; NSString *keychainId = [NSString stringWithFormat:@"%@.dropbox.auth", keychainPrefix]; NSDictionary *keychainDict = @{(__bridge id)kSecClass: (__bridge id)kSecClassGenericPassword, (__bridge id)kSecAttrService: keychainId, (__bridge id)kSecMatchLimit: (__bridge id)kSecMatchLimitOne, (__bridge id)kSecReturnAttributes: (__bridge id)kCFBooleanTrue, (__bridge id)kSecReturnData: (__bridge id)kCFBooleanTrue}; CFDictionaryRef result = NULL; OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)keychainDict, (CFTypeRef * )&result); NSDictionary *attrDict = (__bridge_transfer NSDictionary * )result; NSData *foundValue = [attrDict objectForKey:(__bridge id)kSecValueData]; NSMutableArray *credentials = [[NSMutableArray alloc] init]; if (status == noErr && foundValue) { NSDictionary *savedCreds = [NSKeyedUnarchiver unarchiveObjectWithData:foundValue]; NSArray *credsForApp = [savedCreds objectForKey:@"kDBDropboxUserCredentials"]; for (NSDictionary *credsForUser in credsForApp) { NSDictionary *token = @{ @"userId": [credsForUser objectForKey:@"kDBDropboxUserId"], @"token": [credsForUser objectForKey:@"kMPOAuthCredentialAccessToken"], @"tokenSecret": [credsForUser objectForKey:@"kMPOAuthCredentialAccessTokenSecret"] }; [credentials addObject:token]; } } return credentials; } - (void)getAndSaveOAuth2TokensFromRetrievedOAuth1Tokens:(NSArray * )oauth1Tokens { DBTransportClient *transportClient = [[DBTransportClient alloc] initWithAccessToken:nil selectUser:nil userAgent:nil delegateQueue:nil forceForegroundSession:nil appKey:APP_KEY appSecret:APP_SECRET]; DropboxClient *client = [[DropboxClient alloc] initWithTransportClient:transportClient]; for (NSDictionary *oauth1Token in oauth1Tokens) { [[client.authRoutes tokenFromOauth1:[oauth1Token objectForKey:@"token"] oauth1TokenSecret:[oauth1Token objectForKey:@"tokenSecret"]] response:^(DBAUTHTokenFromOAuth1Result *result, DBAUTHTokenFromOAuth1Error *routeError, DBRequestError *error) { if (result) { NSLog(@"OAuth 2 access token: %@\n", result.oauth2Token); DBAccessToken *accessToken = [[DBAccessToken alloc] initWithAccessToken:result.oauth2Token uid:[oauth1Token objectForKey:@"userId"]]; [[DBOAuthManager sharedOAuthManager] storeAccessToken:accessToken]; } else { NSLog(@"%@\n%@\n", routeError, error); } }]; } }...
NSArray *oauth1Tokens = [self getTokensFromCoreSDK]; [self getAndSaveOAuth2TokensFromRetrievedOAuth1Tokens: oauth1Tokens];
I haven't tested that extensively, so be sure to test it well in your app. I hope it helps though!
- Bob S.159 years agoCollaborator | Level 9
Hey Greg,
That's great, thanks! That code works, but I think I am missing one step. After I go through and store the new tokens, how do I get it to recognize that it is authorized? I am finding that [DropboxClientsManager authorizedClient] is returning nil after storing the tokens. If I quit the app and then run it again, THEN it recognizes the app as being authorized. But how do I get it to know it's authorized without having to restart the app?
Another question if you do not mind. I am making progress with the SDK and have been able to upload files. But I don't understand why some DBFILESRoutes functions list two arguments for the return response, but they seem to require three in practice. For example -createFolder:autorename says it returns a DBFILESFolderMetadata object and a DBFILESCreateFolderError object, but XCode gives me an error unless there is also a third DBRequestError object. Is this a documentation mistake or is the third argument assumed but not shown for some reason?
Thanks!
Bob
- Greg-DB9 years ago
Dropbox Community Moderator
I believe you can get that active within the same session using DropboxClientsManager.reauthorizeClient. Let me know if that doesn't work for you though.
Yes, the third one, a DBRequestError, applies to all calls and covers higher level failures (network issues, invalid access tokens, etc.) so it's not documented on each call individually.
- Bob S.159 years agoCollaborator | Level 9
Hey Greg,
Yes that worked, thanks!
In the process of debugging this I went through all of my various simulators and devices, because I don't know how to remove the OAuth2 tokens once they are on there. I ran the old app to get the old tokens installed, then ran the new one to test the retrieval. Is there any way to remove the new tokens in order to re-test the transfer?
Bob
- Greg-DB9 years ago
Dropbox Community Moderator
Yea, you can use DBOAuthManager.clearStoredAccessToken for specific tokens, or DBOAuthManager.clearStoredAccessTokens for all tokens.
- Bob S.159 years agoCollaborator | Level 9
Ok, thanks Greg.
Also, when I use DBFILESgetMetadata, is there any way to tell if the path is a file or a folder? In the old SDK there was an 'isDirectory' field of the metadata structure, but I don't see one for DBFILESMetadata. ?
Also, if it IS a folder, then how do you access the contents of the folder? With the old SDK I uses a loop like this:
for (DBMetadata *file in metadata.contents)
Thanks
Bob
- Greg-DB9 years ago
Dropbox Community Moderator
You can use isKindOfClass to check the type:
https://github.com/dropbox/dropbox-sdk-obj-c/blob/8d4fcca08a6673af35cf6e116810b13e3e6507e9/README.md#response-handling-edge-cases - Bob S.159 years agoCollaborator | Level 9
OK thanks, and how would you access the contents of the folder, is that included in the metadata or do you have to issue another call to get that? Would I have to use listFolder?
- Greg-DB9 years ago
Dropbox Community Moderator
Yes, you should use listFolder (and listFolderContinue) to get the contents of any given folder. - Bob S.159 years agoCollaborator | Level 9
Hey Greg,
If getMetaData returns an error, how do you retrieve the error information from DBFILESGetMetadataError? It looks like it only contains a path and a tag, and I can't tell what the tag is. The docs say it represents the "union's current tag state", but it doesn't show any values. Is there some other way to get information about why it failed or what the error is ?
Thanks,
Bob
- Greg-DB9 years ago
Dropbox Community Moderator
You can use the "is" methods to check which member of the error union a particular instance of the error is, then you can access the respective property for the specific error value, as shown here:
https://github.com/dropbox/dropbox-sdk-obj-c#route-specific-errors
The documentation details which methods/properties are available for any given type, e.g., for DBFILESGetMetadataError:
https://dropbox.github.io/dropbox-sdk-obj-c/api-docs/latest/Classes/DBFILESGetMetadataError.html - Bob S.159 years agoCollaborator | Level 9
Thanks for the reply. I get that I can use the 'is' commands to check the error, but in the case of the metadata, there is a path and a tag. The path is pretty self evident. But what is the tag? The docs don't seem to provide any details about what that is. I'm just wondering in the case of an error, how I would go about determining the cause or reason. The path I already know. And it doesn't look like the tag tells me anything. Does that make sense?
Thanks!
- Greg-DB9 years ago
Dropbox Community Moderator
The "tag" is the way the API describes which member of the union the object is. The SDK uses it to determine what the "is" methods return. So, you don't need to access tag directly, as long as you use the "is" methods.
To get the actual error information, use the respective field. For example, DBFILESGetMetadataError.path gives you a DBFILESLookupError, which itself has various "is" methods to tell you which error it was (and so on, for any further nested errors).
- Bob S.159 years agoCollaborator | Level 9
Oh, I see, the path contains the error information. I had assumed the path was just the file pathname.
Thanks!
- Bob S.159 years agoCollaborator | Level 9
Hey Greg,
With the old API, when I got a file's metadata, the returned DBMetadata structure included the filename, the path, the revision, the total bytes, and the last modified date. Isn't that stuff commonly considered the metadata? With the new API, getMetadata seems like it only returns the filename and path, is that correct? How do I access that other information?
Thanks
Bob
- Greg-DB9 years ago
Dropbox Community Moderator
This information is still available, but it depends on the object type. The name and path are available on all DBFILESMetadata objects:
https://dropbox.github.io/dropbox-sdk-obj-c/api-docs/latest/Classes/DBFILESMetadata.html
Modified time, size, rev, etc. are available on DBFILESFileMetadata instances:
https://dropbox.github.io/dropbox-sdk-obj-c/api-docs/latest/Classes/DBFILESFileMetadata.html
(DBFILESFileMetadata is a sub-type of DBFILESMetadata.)
You can see an example of how to check and cast these types here:
https://github.com/dropbox/dropbox-sdk-obj-c#response-handling-edge-cases - Bob S.159 years agoCollaborator | Level 9
OK, thanks, I got it now.
Also - is it okay to use the pathDisplay variable as a replacement for the old API's 'path' variable? The docs say pathDisplay is only for display purposes, as if it might be incorrect sometimes. But the other option is 'pathLower' , which is a lower-case version. So what do you use if you just want the exact pathname, like what you had in the old API?
- Greg-DB9 years ago
Dropbox Community Moderator
Yes, v2's pathDisplay is the equivalent of v1's path. You should use that whenever you want to display the correctly cased version of the path. For the API calls themselves though, e.g., if you're taking the path from the metadata to then download the file, you should use pathLower. The Dropbox API is case-insensitive, and pathLower is the canonicalized version of the path. (Most of the time this doesn't matter, but there are a few edge cases that you can avoid by using pathLower when specifying paths in API calls.) - Bob S.159 years agoCollaborator | Level 9
If the API is case insensitive, what happens when you upload a file with capitalization, and then try to download it again later? Wouldn't it lose the capitalization? Like, if I upload from once device and download to another, then if I used pathLower to create the new file, I would lose the original capitalization...
One reason why these docs are so confusing to me, and why I asked that previous question, is that for example, getMetadata says it returns a DBFILESMetadata object. But if you go to the docs for that object, there is no indication that there are subclasses of that object for the various types -- file, folder, etc. How would I know that if you hadn't told me? Is there documentation I'm missing?
So that leads me to another question, sorry! If I use -getMetadata:includeMediaInfo:includeDeleted:includeHasExplicitSharedMembers:
then I assume you can tell whether the file/folder is deleted by the type of DBFILESMetadata object that is returned -- a DBFILESDeletedMetadata object, right? But then how do to tell if it was a file or a folder -- because normally you would check for a
DBFILESFolderMetadata or DBFILESFileMetadata object for that. ??
Finally, the getMetadata docs say this about the 'includeDeleted' argument:
"If true, DeletedMetadata will be returned for deleted file or folder, otherwise notFound in DBFILESLookupError will be returned." What does this mean? Say I have a path to a file that does exist, and I call getMetadata with 'includeDeleted' = true. Will I get both a DBFILESFileMetadata object in the return, as well as a 'notFound' in the DBFilesLookupError? And where would that DBFilesLookupError be? Would it be part of the returned DBFILESGetMetadataError or the DBRequestError?
Thanks
Bob
- Greg-DB9 years ago
Dropbox Community Moderator
The API is case-insensitive, but case-preserving. So when you upload a file with a certain casing, that will be preserved in pathDisplay, though you can reference that file with a lower-cased version of the path.
In the re-creation scenario you describe, you can certainly re-use pathDisplay to create it with the casing you want.
And thanks, that's good feedback regarding the sub-classes in the documentation. I'll send it along to the team.
If you use includeDeleted with getMetadata, the resulting DBFILESMetadata will be one of DBFILESFileMetadata, DBFILESFolderMetadata, DBFILESDeletedMetadata. Deleted entries are neither files nor folders though. They just indicate that there was previously something at that path. (That something could have, at different points in time, been a file and a folder.)
For getMetadata, if you set includeDeleted to true, for a deleted entry, the "result" will be a DBFILESDeletedMetadata. If you set includeDeleted to false, the "route error" will contain a DBFilesLookupError (nested in a DBFILESGetMetadataError.path, to be strictly accurate.) You won't get both though.
You can see an example of the separation of the result, route error, and error for an RPC call like this here:
https://github.com/dropbox/dropbox-sdk-obj-c#rpc-style-request
About Dropbox API Support & Feedback
Find help with the Dropbox API from other developers.
The Dropbox Community team is active from Monday to Friday. We try to respond to you as soon as we can, usually within 2 hours.
If you need more help you can view your support options (expected response time for an email or ticket is 24 hours), or contact us on X, Facebook or Instagram.
For more info on available support options for your Dropbox plan, see this article.
If you found the answer to your question in this Community thread, please 'like' the post to say thanks and to let us know it was useful!