CoyBit
CoyBit

Reputation: 1622

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!

Upvotes: 5

Views: 457

Answers (1)

Sylverb
Sylverb

Reputation: 939

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];
}

Upvotes: 3

Related Questions