Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Access to Dropbox Datastore from iOS8 widgets

We use Dropbox Datastore API in our application and it works correctly. We've decided to add a iOS8 widget to our app. But we can't access to app datastore from it. We followed Datastore API install guide, except that you can't add URL Schema to a widget. What is problem?

UPDATE 1

When the below code (in the widget) runs, it returns nil:

DBAccount *account = [[DBAccountManager sharedManager] linkedAccount];

So I think Dropbox SDK can't retrieval authentication data, which it has saved when authentication is done at host app. Where does dropbox save these information? In keychain? Can I get access token from the host app and use it directly in widget? Because widgets can show a UIViewController for doing authentication.

UPDATE 2

I read Dropbox Core API source code. It seems dropbox saves authentication information in keychain. So I set a keychain group for host app and widget. I tested and both of them can read and write on same keychain. But still [[DBAccountManager sharedManager] linkedAccount] on the widget returns null and on the host app return linked account!

like image 272
CoyBit Avatar asked Oct 20 '22 01:10

CoyBit


1 Answers

setting a keychain group was the first step to be able to use Dropbox account from your extension, but you also have to make a modification in DBKeychain-iOS.m !

By default it's setting kSecAttrService to something build with application's bundle identifier !

In your main app it will be "com.coybit.myapp" but in you extension it will be "com.coybit.myapp.extensionName" !

You can hardcode the kSecAttrService value to com.coybit.myapp.dropbox.auth or use a method that will only keep the first 3 elements of the bundle identifier to build kSecAttrService :

+ (NSString *)mainBundleName
{
    // Always return main application bundle name (for app extensions, remove last component)
    NSMutableArray *components = [NSMutableArray arrayWithArray:[[[NSBundle mainBundle] bundleIdentifier] componentsSeparatedByString:@"."]];
    while ([components count] > 3) {
        [components removeLastObject];
    }
    return [components componentsJoinedByString:@"."];
}

The initialize function will then looks like :

+ (void)initialize {
    if ([self class] != [DBKeychain class]) return;
    NSString *keychainId = [NSString stringWithFormat:@"%@.dropbox.auth", [self mainBundleName]];
    kDBKeychainDict = [[NSDictionary alloc] initWithObjectsAndKeys:
                       (id)kSecClassGenericPassword, (id)kSecClass,
                       keychainId, (id)kSecAttrService,
#if TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR
                       @"keychain_access_group_name",(id)kSecAttrAccessGroup,
#endif
                       nil];
}
like image 163
Sylverb Avatar answered Oct 22 '22 15:10

Sylverb