Access Facebook Accounts on OS X Mountain Lion - objective-c

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.

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 :-)

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.

Related

Not getting the email from twitter using Fabric Twitter SDK

This seems to be age old question with no specific answers or solutions for it. I had my application whitelisted from twitter. Done all the things. Checked the request email from user box and used the following code
[[Twitter sharedInstance] startWithConsumerKey:#"FabricKey" consumerSecret:#"FabricSecretKey"];
if ([[Twitter sharedInstance] session]) {
TWTRShareEmailViewController *shareEmailViewController =
[[TWTRShareEmailViewController alloc]
initWithCompletion:^(NSString *email, NSError *error) {
NSLog(#"Email %# | Error: %#", email, error);
}];
[self presentViewController:shareEmailViewController
animated:YES
completion:nil];
But still it is returning null. Why is this?? I even created the application in twitter developer portal. Please help me. Is twitter trying to be more over secure?? It is just pissing me out......

OSX ACAccountStore requestAccessToAccountsWithType... returning 'granted=No' and error=nil after denying permissions

I am trying to request access to my facebook account. The first time I ran this it went well and I saw the popup requesting permissions. The popup asked me to allow or deny. I chose to deny because I wanted to test that use case. Now I cannot figure out how to get it to allow access again. I have deleted the app from my facebook account and deleted my facebook account from my mac. I've also tried adding the property
#"auth_type": #"rerequest"
to the options dictionary. I get the same result no matter what.
As mentioned in the title, the error is nil and granted is False.
Thanks in advance.
ACAccountType* accountType = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];
NSDictionary* options = #{
ACFacebookAppIdKey: [[NSBundle mainBundle] objectForInfoDictionaryKey:#"FacebookAppID"],
ACFacebookPermissionsKey: #[#"public_profile", #"email", #"user_friends"],
};
[accountStore requestAccessToAccountsWithType:accountType options:options completion:^(BOOL granted, NSError* error)
{
if (granted)
{
ACAccount* account = [[accountStore accountsWithAccountType:accountType] lastObject];
macFacebookAccount = account;
}
else
{
NSLog(#"%#", error);
}
}];
I solved this: Facebook permission settings for an app are located under System Preferences > Security and Privacy. From there just click facebook and check the box next to your app

Stuck Uploading (Google Drive Objective-C API on OS X)

I am trying to upload simple .txt file as a test. It's only 4kb. Here is my code:
-(IBAction)uploadFile:(id)sender{
if (![self.credentials canAuthorize]) {
GTMOAuth2WindowController *windowController;
windowController = [[[GTMOAuth2WindowController alloc] initWithScope:scope clientID:kClientID clientSecret:kClientSecret keychainItemName:kKeychainItemName resourceBundle:nil] autorelease];
[windowController signInSheetModalForWindow:self.window
delegate:self
finishedSelector:#selector(windowController:finishedWithAuth:error:)];
}else{
NSLog(#"Credentials already authorized.");
}
GTLDriveFile *driveFile = [GTLDriveFile object];
driveFile.title = #"myfile";
driveFile.descriptionProperty = #"Uploaded by Google Drive API test.";
driveFile.mimeType = #"text/plain";
NSData *data = [[NSFileManager defaultManager] contentsAtPath:#"/Users/Blake/Downloads/glm/util/autoexp.txt"];
GTLUploadParameters *params = [GTLUploadParameters uploadParametersWithData:data MIMEType:driveFile.mimeType];
GTLQueryDrive *query = [GTLQueryDrive queryForFilesInsertWithObject:driveFile uploadParameters:params];
NSLog(#"Starting Upload to Google Drive");
[self.progressBar setHidden:FALSE];
[self.progressBar startAnimation:sender];
[self.driveService executeQuery:query completionHandler:^(GTLServiceTicket *st, GTLDriveFile *df, NSError *error){
[self.progressBar setHidden:TRUE];
if (error == nil) {
NSLog(#"File upload succeeded");
}else{
NSLog(#"Uh-oh: File upload failed");
}
}];
}
The GTMOAuth2Authentication is authorized and the completion handler block never executes.
I looked at Activity Monitor and it says my application has sent 54 packets in the beginning. So I guess something is being sent.
How can I tell what's going wrong here, if the block never finishes to give me an error?
Could someone please help me understand what I am doing wrong?
I've recently got problem with sticking on GTMOAuth2WindowController. It was unable to present self window. I spent a lot of time in debugger and found that I forget to add WebKit.framework. It was not documented explicitly and there were no any messages in runtime (minus in google dev karma)))). So, maybe your problem also in that? Try to check your frameworks set and compare it with examples shipped with GTL.

Facebook login failing on iOS 6

When I try to login using Facebook iOS SDK I get the error The operation couldn't be completed (com.facebook.sdk error 2).
The state of the session is: FBSessionStateClosedLoginFailed.
THis is my code now:
-(void) callFBService{
NSArray *permissions = [[NSArray alloc] initWithObjects:#"email, publish_stream, user_likes, friends_likes", nil];
[FBSession openActiveSessionWithReadPermissions:permissions allowLoginUI:YES
completionHandler:^(FBSession *fbsession,
FBSessionState status,
NSError *error) {
if(error)
{
NSLog(#"Session error");
[self fbResync];
[NSThread sleepForTimeInterval:0.5]; //half a second
[FBSession openActiveSessionWithReadPermissions:permissions
allowLoginUI:YES
completionHandler:^(FBSession *fbsession, FBSessionState status, NSError *error) {
[self sessionStateChanged:fbsession state:status error:error];
}];
}
else
[self sessionStateChanged:fbsession state:status error:error];
}];
}
I have tried everything in the following posts:
The operation couldn’t be completed. (com.facebook.sdk error 2.) ios6
Facebook Registration : The operation couldn't be completed (com.facebook.sdk error 2)
Facebook SDK 3.1 iOS: Handle login if user remove app from Facebook Settings
Any ideas??? Please!
You're passing publish_stream in with read permissions, but publish_stream is a write permission. It's also deprecated (use publish_actions instead). Try removing that permission. You'll need to ask for that permission separately, after you get your user logged in with read permissions. See the SDK docs: https://developers.facebook.com/docs/technical-guides/iossdk/login/#read
In addition, a few things to check: Make sure your app on Facebook.com is configured correctly, including the bundle ID. Make sure the user you're attempting to log in has rights to the app (if the app is in sandbox mode, make sure the user is added as a tester and has approved this).

Mac OS X Facebook login failed - no stored remote_app_id for app

I am trying to use the new ACAccountStore capabilities on Mac OS X 10.8 to login via Facebook but I get an error:
The Facebook server could not fulfill this access request: no stored remote_app_id for app
When the code executes the requestAccessToAccountsWithType message it does prompt me for access to Facebook (which I allow) and I do have Facebook credentials stored in my Settings. I also have another code path for legacy versions of OS X which logs into Facebook using the WebView control. It does work with the same APP_ID. So I should have the app correctly setup in the Facebook developer settings. In there some other configuration that I'm missing? I search on the Internet for "remote_app_id" and I get the empty set.
ACAccountStore *account = [[ACAccountStore alloc] init];
ACAccountType *accountType = [account accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys: FB_APP_ID, ACFacebookAppIdKey, [NSArray arrayWithObjects:#"email", nil], ACFacebookPermissionsKey, ACFacebookAudienceFriends, ACFacebookAudienceKey, nil];
[account requestAccessToAccountsWithType:accountType options:options completion:^(BOOL granted, NSError *error) {
if (granted) {
NSArray *accountList = [account accountsWithAccountType:accountType];
for (ACAccount *thisAccount in accountList) {
NSLog(#"Found account: %#", [thisAccount accountDescription]);
}
}
else {
NSLog(#"Not granted because: %#", [error localizedDescription]);
}
}];
useful note for iPhone devs: to exhibit this problem 100%:
Regarding the same issue on the iPhone (this page is the main google landing for that): The issue is this: on the iPhone, go to Settings, left menu Facebook, then on the right username/password - login to Facebook. So that's the "Settings Facebook Login". If the iPhone is in fact logged in to FB on the "Settings Facebook Login" then the problem will exhibit. If you explicitly log out on "Settings Facebook Login" (and indeed, perhaps uninstall the FacebookApp), the problem will not exhibit.
Looks like you have to set bundle ID of your iOS/OSX app in Facebook app settings (that fixed thing in my case)
Set the bundle ID on the Facebook web interface, and it will get updated right away.
Its position on the updated developer UI is below: