Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Access Facebook Accounts on OS X Mountain Lion

As Mountain Lion now officialy has Facebook integrated I wanted to play a little bit with Accounts.framework and Social.framework of OS X.

I set up an App in the Facebook Developer App and used the following code to request access to the Facebook Accounts of the logged in user:

ACAccountStore *acStore = [[ACAccountStore alloc] init];
ACAccountType *accType = [acStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];

NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:FB_APP_ID, ACFacebookAppIdKey, @"read_stream, publish_stream", ACFacebookPermissionsKey, ACFacebookAudienceFriends, ACFacebookAudienceKey, nil];


[acStore requestAccessToAccountsWithType:accType options:options completion:^(BOOL granted, NSError *error) {
    if (granted) {
        NSLog(@"Accounts: %@", acStore.accounts);
        NSLog(@"Access granted");
    } else {
        NSLog(@"Access denied");
    }
}];

Now I always get the error Error Domain=XPCObjectsErrorDomain Code=2 "The operation couldn’t be completed. (XPCObjectsErrorDomain error 2.)"

When I launched the App from Xcode the first 2-3 times, OS X displayed the confirmation dialog "XYZ wants to access your Facebook Accounts", I clicked ok and I got an error saying something like "The Application is not yet installed". This error now "disappeared" and I'm getting the error noted above.

My question is: Do I have to configure something special in my Xcode Project (Info.plist) or in the Facebook Developer App to make it work with OS X? Or am I missing something in the code? Samples and Tutorials regarding this seem to be very rare :-/

I already watched the WWDC 2012 Video about the Twitter/Facebook Integration but it uses almost exactly the code I used.

Any help is appreciated :-)

Cheers, Björn

Update
I solved the issue, I just did not read the docs carefully enough ;-) I have to pass the permissions as an NSArray and not as a string. I've modified the options dictionary to look like this:

NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: FB_APP_ID, ACFacebookAppIdKey, [NSArray arrayWithObjects:@"read_stream", @"publish_stream", @"email", @"user_likes", nil], ACFacebookPermissionsKey, ACFacebookAudienceFriends, ACFacebookAudienceKey, nil];

Now OS X shows an alert that asks me if I want to allow the App to post to Facebook on my behalf, I click OK and a new error appears resulting in no access to the Facebook accounts:

Error Domain=com.apple.accounts Code=7 "The Facebook server could not fulfill this access request: The app must ask for a basic read permission at install time."

I already modified the permissions to be just email or just user_likes but it all resulted in the same error. If I only request access to read_stream the access gets granted but results in the OAuth error 190 with the message Error validating access token: User XXXXXX has not authorized application XXXXXX.

Update 2:
I now solved all my problems, see my answer below.

like image 683
Björn Kaiser Avatar asked Sep 21 '12 07:09

Björn Kaiser


2 Answers

Alright, I finally solved it :-)

When you first request access to a users Facebook accounts, you may only request permissions from the user listed under "User and Friend permissions" in Facebook's Permission Reference.

Once the user granted access and with that also installed the application on Facebook, you may request any additional permissions required by your App. It's important that you may not ask for read and write permissions at the same time, one after the other! So requesting the permission read_stream and publish_stream in the same request will not work.

This is the basically the code I used:

self.store = [[ACAccountStore alloc] init];

ACAccountType *accType = [self.store accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];

NSMutableDictionary *options = [NSMutableDictionary dictionaryWithObjectsAndKeys:
                                    FB_APP_ID, ACFacebookAppIdKey,
                                    [NSArray arrayWithObjects:@"email", @"user_about_me", @"user_likes", nil], ACFacebookPermissionsKey, ACFacebookAudienceFriends, ACFacebookAudienceKey, nil];    

[self.store requestAccessToAccountsWithType:accType options:options completion:^(BOOL granted, NSError *error) {
    if (granted && error == nil) {
        self.accounts = self.store.accounts;    
        [options setObject:[NSArray arrayWithObjects:@"read_stream, read_mailbox, read_requests", nil] forKey:ACFacebookPermissionsKey];
        [self.store requestAccessToAccountsWithType:accType options:options completion:^(BOOL granted, NSError *error) {
            if (granted && error == nil) {
                completionHandler(granted, error);
            } else {
                NSLog(@"Access denied 2");
                NSLog(@"%@", [error description]);
            }
        }];
    } else {
        NSLog(@"Error: %@", [error description]);
        NSLog(@"Access denied");
    }
}];

Hope this will help somebody :-)

like image 192
Björn Kaiser Avatar answered Nov 18 '22 13:11

Björn Kaiser


I would like to add that there is an additional caveat in the Facebook docs:

// if a user has *never* logged into your app, you MUST include one of
// "email", "user_location", or "user_birthday".  Other read 
// permissions can also be included here.

Failure to do this leads to the same error.

like image 42
shawkinaw Avatar answered Nov 18 '22 15:11

shawkinaw