Perplexing quandary: When I get my friends list with one URL I get the list back fine, using the same URL but with a query string (?fields=id,name,picture), and the exact same permissions, I get an error indicating "An active access token must be used to query information about the current user." What gives?
The permissions currently in effect are publish_stream, email, and read_stream. Why would adding that query string mess it up? The only thing I can think of is that the access key I have is not what I think. Is there a way to pull the actual access key out, expose it in an NSLog, and then test it on the graph explorer?
The URL that works is:
https://graph.facebook.com/me/friends
The URL that doesn't is:
https://graph.facebook.com/me/friends?fields=id,name,picture
This is the code that actually get the permissions. In fact this is the same code that Stuart Breckenridge offered up freely on GitHub (thanks dude!) It seems to work fine as long as I am not appending '?fields=name,id,picture' to the end of the api call:
-(void)requestPermissions
{
if (debugF) NSLog(#"FAM: requestPermissions");
// Specify the Facebook App ID.
_facebookAppID = #"123456789123456"; // You Must Specify Your App ID Here.
// Submit the first "read" request.
// Note the format of the facebookOptions dictionary. You are required to pass these three keys: ACFacebookAppIdKey, ACFacebookAudienceKey, and ACFacebookPermissionsKey
// Specify the read permission
_facebookPermissions = #[#"email"];
// Create & populate the dictionary the dictionary
_facebookOptions = #{ ACFacebookAppIdKey : _facebookAppID,
ACFacebookAudienceKey : ACFacebookAudienceFriends,
ACFacebookPermissionsKey : _facebookPermissions};
_facebookAccountType = [_facebookAccountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];
[_facebookAccountStore requestAccessToAccountsWithType:_facebookAccountType options:_facebookOptions completion:^(BOOL granted, NSError *error)
{
// If read permission are granted, we then ask for write permissions
if (granted) {
_readPermissionsGranted = YES;
// We change the _facebookOptions dictionary to have a publish permission request
_facebookPermissions = #[#"publish_stream", #"read_stream", #"friends_photos"];
_facebookOptions = #{ ACFacebookAppIdKey : _facebookAppID,
ACFacebookAudienceKey : ACFacebookAudienceFriends,
ACFacebookPermissionsKey : _facebookPermissions};
[_facebookAccountStore requestAccessToAccountsWithType:_facebookAccountType options:_facebookOptions completion:^(BOOL granted2, NSError *error)
{
if (granted2)
{
_publishPermissionsGranted = YES;
// Create the facebook account
_facebookAccount = [[ACAccount alloc] initWithAccountType:_facebookAccountType];
_arrayOfAccounts = [_facebookAccountStore accountsWithAccountType:_facebookAccountType];
_facebookAccount = [_arrayOfAccounts lastObject];
}
// If permissions are not granted to publish.
if (!granted2)
{
if (debugF) NSLog(#"Publish permission error: %#", [error localizedDescription]);
_publishPermissionsGranted = NO;
}
}];
}
// If permission are not granted to read.
if (!granted)
{
if (debugF) NSLog(#"Read permission error: %#", [error localizedDescription]);
_readPermissionsGranted = NO;
if ([[error localizedDescription] isEqualToString:#"The operation couldn’t be completed. (com.apple.accounts error 6.)"])
{
[self performSelectorOnMainThread:#selector(showError) withObject:error waitUntilDone:NO];
}
}
}];
}
As it turns out, the answer to my original question was simpler than I thought:
NSString *acessToken = [NSString stringWithFormat:#"%#", self.facebookAccount.credential.oauthToken];
Kudos to all who contributed to the conversation, however.
Related
I have had facebook sharing working fine in my ios app for a year and have upgraded (aka totally rewritten) to use the latest api (4.7.x) and now sharing doesnt work at all. I check that I have publish_actions permission (which I do prior to this method being called, I have 'expicitly shared' checked in open graph settings, action types, capabilities. I am validating the content (I dont get an error) and have a delegate, none of its methods get called.
-(void)shareWithFacebook:(NSString *)message
{
if ([[FBSDKAccessToken currentAccessToken] hasGranted:#"publish_actions"])
{
NIDINFO(#"Facebook sharing has publish_actions permission");
}
else
{
FBSDKLoginManager *loginManager = [[FBSDKLoginManager alloc] init];
[loginManager logInWithPublishPermissions:#[#"publish_actions"]
handler:^(FBSDKLoginManagerLoginResult *result, NSError *error)
{
NIDERROR(#"Facebook sharing getting publish_actions permission failed: %#", error);
}
];
}
NSMutableDictionary *properties = [NSMutableDictionary dictionaryWithDictionary: #{
#"og:type": #"article",
#"og:title": #"Bloc",
#"og:description": message,
#"og:url": #"http://getonbloc.com/download"
}];
FBSDKShareOpenGraphObject *object = [FBSDKShareOpenGraphObject objectWithProperties:properties];
// Create the action
FBSDKShareOpenGraphAction *action = [FBSDKShareOpenGraphAction actionWithType:#"mynamespace:Share" object:object key:#"article"];
[action setString:#"true" forKey:#"fb:explicitly_shared"];
// Create the content
FBSDKShareOpenGraphContent *content = [[FBSDKShareOpenGraphContent alloc] init];
content.action = action;
content.previewPropertyName = #"article";
// Share the content
FBSDKShareAPI *shareAPI = [[FBSDKShareAPI alloc] init];
shareAPI.shareContent = content;
shareAPI.delegate = self;
NSError *error;
if([shareAPI validateWithError:&error] == NO)
{
NIDERROR(#"Facebook sharing content failed: %#", error);
}
[shareAPI share];
}
#pragma mark - FBSDKSharingDelegate
- (void) sharer:(id<FBSDKSharing>)sharer didCompleteWithResults:(NSDictionary *)results
{
NIDINFO(#"Facebook sharing completed: %#", results);
}
- (void) sharer:(id<FBSDKSharing>)sharer didFailWithError:(NSError *)error
{
NIDERROR(#"Facebook sharing failed: %#", error);
}
- (void) sharerDidCancel:(id<FBSDKSharing>)sharer
{
NIDINFO(#"Facebook sharing cancelled.");
}
I have login working and can get photos fine. I don't get any feedback at all from the facebook api, nothing gets posted. Am I doing something particularly stupid here?
Just a possibility, but I find that Facebook integration has become inconvenient because I find that every time I check the current token for granted permission through hasGranted:, it almost always fail even though I gained permission a few minutes ago, or from a previous app launch.
It seems that in your code, if no permission is granted, you try to login and get the permission again. But when that block returns, regardless whether the actual permission is granted or not, you throw an error. Instead, you should continue with sharing if it is successful.
I am trying (failing alot!) to use the Facebook iOS sdk. I want to publish a story about the user running without leaving the app. I am trying to use the Facebook built in object "course" and the built in action for a run.
I find the documentation very confusing, my code has become very tangled and I'm sure its the worst way possible of trying to implement this solution.
The error I'm getting with the following code is:
2014-04-01 23:10:13.238 Fitness_App[2313:60b] Encountered an error posting to Open Graph: Error Domain=com.facebook.sdk Code=5 "The operation couldn’t be completed. (com.facebook.sdk error 5.)" UserInfo=0x16ba5190 {com.facebook.sdk:HTTPStatusCode=500, com.facebook.sdk:ParsedJSONResponseKey={
body = {
error = {
code = 1611072;
message = "The action you're trying to publish is invalid because it does not specify any reference objects. At least one of the following properties must be specified: course.";
type = Exception;
};
};
code = 500;
}, com.facebook.sdk:ErrorSessionKey=}
I have been struggling with this and could not get a solution!
-(void) publishStory
{
// instantiate a Facebook Open Graph object
NSMutableDictionary<FBOpenGraphObject> *object = [FBGraphObject openGraphObjectForPost];
// specify that this Open Graph object will be posted to Facebook
object.provisionedForPost = YES;
// for og:title
object[#"title"] = #"running title";
// for og:type, this corresponds to the Namespace you've set for your app and the object type name
object[#"type"] = #"fitness.course";
// for og:description
object[#"description"] = #"running description";
// Post custom object
[FBRequestConnection startForPostOpenGraphObject:object completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
if(!error) {
// get the object ID for the Open Graph object that is now stored in the Object API
NSString *objectId = [result objectForKey:#"id"];
NSLog([NSString stringWithFormat:#"object id: %#", objectId]);
// Further code to post the OG story goes here
// create an Open Graph action
id<FBOpenGraphAction> action = (id<FBOpenGraphAction>)[FBGraphObject graphObject];
[action setObject:objectId forKey:#"fitness.course"];
[FBRequestConnection startForPostWithGraphPath:#"/me/fitness.runs" graphObject:action completionHandler:^(FBRequestConnection *connection, id result, NSError *error) {
if(!error) {
NSLog([NSString stringWithFormat:#"OG story posted, story id: %#", [result objectForKey:#"id"]]);
} else {
// An error occurred, we need to handle the error
NSLog(#"Encountered an error posting to Open Graph: %#", error.description);
}
}];
} else {
// An error occurred
NSLog(#"Error posting the Open Graph object to the Object API: %#", error);
}
}];
}
Two places where things went wrong.
object[#"type"] = #"fitness.course";
The type should equal #"namespace:object".
[action setObject:objectId forKey:#"fitness.course"];
The key is your object name.
Check your code again and have fun ^-^
Try replacing your this sentence
"[action setObject:objectId forKey:#"fitness.course"];"
with this one
"[action setObject:objectId forKey:#"course"];"
Please see the 29th December update notes at the bottom of the page.
Hi I'm doing maintanance work on someone else's iOS project at work (which is kind of soul destroying because they haven't documented their code).
The problem is that after the user logs in, attempting to share a post to the wall always results in
error 100: "The post's links must direct to the application's connect or canvas URL".
I've searched for the past 2 hours and haven't found any results specifically for iOS (but plenty for wordpress, which didn't help)
Any ideas what might be causing this.
Here's the overseas developer code for posting to the wall:
-(void)uploadPropertyDetailsOnFacebookWall
{
[FBSettings setLoggingBehavior:[NSSet setWithObjects:FBLoggingBehaviorFBRequests, FBLoggingBehaviorFBURLConnections, nil]];
NSString* photoURL = #"";
NSString *strFullPropertyDetailLink=#"";
if (!kIsListOnce) {
photoURL = [currentItem objectForKey:#"Photo1FeaturedURL"];
strFullPropertyDetailLink=[currentItem objectForKey:#"FullDisplayLink"];
}
else {
strFullPropertyDetailLink=[currentItem objectForKey:#"FullDisplayLink"];
NSArray* list = [[currentItem objectForKey:#"objects"] objectForKey:#"img_small"];
;
if ([list count] > 0) {
photoURL = [list objectAtIndex:0];
}
}
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSString *strLinkOfApp=(NSString *)[Utils config:KiTunesstoreAppLink]; //strFullPropertyDetailLink,
NSDictionary *postParams =
[[NSMutableDictionary alloc] initWithObjectsAndKeys:
strFullPropertyDetailLink, #"link",
photoURL, #"picture",
[Utils config:kTextAgentName], #"name",
strAddress, #"caption",
[currentItem objectForKey:#"Description"], #"description",
nil];
[FBRequestConnection startWithGraphPath:#"me/feed"
parameters:postParams
HTTPMethod:#"POST"
completionHandler:^(FBRequestConnection *connection,
id result,
NSError *error)
{
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
NSString *alertText;
NSLog(#"%#",error);
if (error) {
NSDictionary *dict=[error userInfo];
NSLog(#"%#",dict);
NSDictionary *dictJSON=[dict objectForKey:#"com.facebook.sdk:ParsedJSONResponseKey"];
NSDictionary *dictBody=[dictJSON objectForKey:#"body"];
NSDictionary *dictError=[dictBody objectForKey:#"error"];
NSString *strCode=[[dictError objectForKey:#"code"] description];
if([strCode isEqualToString:#"200"])
{
alertText = #"You have not authorized the application to perform this publish action";
}else{
alertText = [#"An error ocurred: " stringByAppendingString:error.description];
alertText=[alertText stringByAppendingString: strFullPropertyDetailLink];
}
} else {
alertText = [NSString stringWithFormat:
#"Property details has been successfully shared on your Facebook Wall"];
}
[[[UIAlertView alloc] initWithTitle:#"Result"
message:alertText
delegate:nil
cancelButtonTitle:#"OK!"
otherButtonTitles:nil] show];
// Show the result in an alert
}];
}
Here's the error I keep getting:
Error Domain=com.facebook.sdk Code=5 "The operation couldn’t be completed(com.facebook.sdk error 5.)" UserInfo=0x1d548710 {com.facebook.sdk:ParsedJSONResponseKey={
body = {
error = {
code = 100;
message = "(#100) The post's links must direct to the application's connect or canvas URL.";
type = OAuthException;
};
};
code = 400;
}, com.facebook.sdk:HTTPStatusCode=400}
2013-08-02 12:06:12.806 RealEstate[385:907] {
"com.facebook.sdk:HTTPStatusCode" = 400;
"com.facebook.sdk:ParsedJSONResponseKey" = {
body = {
error = {
code = 100;
message = "(#100) The post's links must direct to the application's connect or canvas URL.";
type = OAuthException;
};
};
code = 400;
};
}
Please help, I have done more research online since my initial posting, and still can't find the answer.
Update Dec 17th:
I am using SDK 3.1.1. I'd like to avoid having to update, as I'm maintaining someone else's code.
Using me/feed, in a fbrequestconnection, any additional paramater aside from "message", crashes the app.
I've also tried linking the app to a test account with settings suggested by other stack overflow users viewable here
I've also disabled post streaming security
Other Questions
Am I missing something in linking up the app to Facebook?
Why won’t it detect that the “link” parameter is the same as the canvas url?
I've been struggling with posting to facebook wall too.
Why don't you use the facebook SDK instead of the API?
There are two ways to post to facebook wall using the SDK
Via Feed Dialog
or
Via Share Dialog
The Feed Dialog is very easy to implement and you can control what you want to post through the parameters you send, the only bad thing is that the parameters are limited.
The Share Dialog uses OpenGraph and requires the user to have the facebook APP installed, you also have to create an action in app developer page in facebook so your app knows what to do with that action.
The good part is that you can share almost all that you want.
I suggest you check the Feed Dialog if you want a simple facebook share, it's the easiest way.
Edit
NSString *message = [NSString stringWithFormat:#"%#%#/%#",domain,TypeName,[object alias]];
// Put together the dialog parameters
NSMutableDictionary *params =
[NSMutableDictionary dictionaryWithObjectsAndKeys:
#"TITLE", #"description",
message, #"link",
[object image],#"picture",
nil];
// Invoke the dialog
[FBWebDialogs presentFeedDialogModallyWithSession:nil
parameters:params
handler:
^(FBWebDialogResult result, NSURL *resultURL, NSError *error) {
if (error) {
// Error launching the dialog or publishing a story.
//NSLog(#"Error publishing story.");
} else {
if (result == FBWebDialogResultDialogNotCompleted) {
// User clicked the "x" icon
//NSLog(#"User canceled story publishing.");
} else {
// Handle the publish feed callback
NSDictionary *urlParams = [self parseURLParams:[resultURL query]];
if (![urlParams valueForKey:#"post_id"]) {
// User clicked the Cancel button
//NSLog(#"User canceled story publishing.");
} else {
// User clicked the Share button
NSString *msg = #"Partilhado com sucesso";
//NSLog(#"%#", msg);
// Show the result in an alert
[[[UIAlertView alloc] initWithTitle:#"Aviso"
message:msg
delegate:nil
cancelButtonTitle:#"OK!"
otherButtonTitles:nil]
show];
}
}
}
}];
After logging into Twitter, I am able to print out some useful data such as the username and user ID. However, the OAuth token is always null. How do I get it? I need to send OAuth token to my server so it can verify that the user is indeed who he says he is.
ACAccountStore* accountStore = [[ACAccountStore alloc] init];
ACAccountType* twitterType = [self.context.accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
[accountStore
requestAccessToAccountsWithType:twitterType
withCompletionHandler:^(BOOL isAllowed, NSError* error) {
dispatch_sync(dispatch_get_main_queue(), ^(void) {
if (isAllowed) {
ACAccount* account = [[self.context.accountStore accountsWithAccountType:[self.context.accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter]] lastObject];
NSLog(#"username = %#", account.username);
NSLog(#"user_id = %#", [[account valueForKey:#"properties"] valueForKey:#"user_id"]);
// ouath token is always null
NSLog(#"oauth token = %#", account.credential.oauthToken);
}
});
}
];
I "think" I need Reverse Auth, but that tutorial mysteriously left out the code for "step 1".
You will indeed need to use Reverse Auth.
I recently used Sean Cook's TWReverseAuth and it worked very well. Just be careful to turn off ARC for the individual files in the Vendor directory.
I'm using the 3.1 Facebook SDK with iOS 6 Facebook set up in Settings and my app authorized.
This executes flawlessly:
[FBSession openActiveSessionWithReadPermissions:nil allowLoginUI:YES completionHandler:^(FBSession *fbSession, FBSessionState fbState, NSError *error) { ... }
However now when I try to get 'me' information I'm getting an error:
com.facebook.sdk:ParsedJSONResponseKey = {
body = {
error = {
code = 190;
"error_subcode" = 463;
message = "Error validating access token: Session has expired at unix time 1348704000. The current unix time is 1348706984.";
type = OAuthException;
};
};
code = 400;
}
If I look at [error code] it's equal to 5. Shouldn't I have a valid access token after just logging in? Do I need to call reauthorize?
UPDATE: Reauthorizing doesn't help. Oddly the accessToken for my activeSession is always coming back the same. This despite calling closeAndClearToken.
UPDATE:
This issue has been addressed in Facebook iOS SDK 3.1.1.
I synched the code off of github and found that they weren't calling accountStore renewCredentialsForAccount:completion: anywhere. I changed the following code in authorizeUsingSystemAccountStore and it seems to have resolved the issue.
// we will attempt an iOS integrated facebook login
[accountStore requestAccessToAccountsWithType:accountType
options:options
completion:^(BOOL granted, NSError *error) {
// this means the user has not signed-on to Facebook via the OS
BOOL isUntosedDevice = (!granted && error.code == ACErrorAccountNotFound);
dispatch_block_t postReauthorizeBlock = ^{
NSString *oauthToken = nil;
if (granted) {
NSArray *fbAccounts = [accountStore accountsWithAccountType:accountType];
id account = [fbAccounts objectAtIndex:0];
id credential = [account credential];
oauthToken = [credential oauthToken];
}
// initial auth case
if (!isReauthorize) {
if (oauthToken) {
_isFacebookLoginToken = YES;
_isOSIntegratedFacebookLoginToken = YES;
// we received a token just now
self.refreshDate = [NSDate date];
// set token and date, state transition, and call the handler if there is one
[self transitionAndCallHandlerWithState:FBSessionStateOpen
error:nil
token:oauthToken
// BUG: we need a means for fetching the expiration date of the token
expirationDate:[NSDate distantFuture]
shouldCache:YES
loginType:FBSessionLoginTypeSystemAccount];
} else if (isUntosedDevice) {
// even when OS integrated auth is possible we use native-app/safari
// login if the user has not signed on to Facebook via the OS
[self authorizeWithPermissions:permissions
defaultAudience:defaultAudience
integratedAuth:NO
FBAppAuth:YES
safariAuth:YES
fallback:YES
isReauthorize:NO];
} else {
// create an error object with additional info regarding failed login
NSError *err = [FBSession errorLoginFailedWithReason:nil
errorCode:nil
innerError:error];
// state transition, and call the handler if there is one
[self transitionAndCallHandlerWithState:FBSessionStateClosedLoginFailed
error:err
token:nil
expirationDate:nil
shouldCache:NO
loginType:FBSessionLoginTypeNone];
}
} else { // reauth case
if (oauthToken) {
// union the requested permissions with the already granted permissions
NSMutableSet *set = [NSMutableSet setWithArray:self.permissions];
[set addObjectsFromArray:permissions];
// complete the operation: success
[self completeReauthorizeWithAccessToken:oauthToken
expirationDate:[NSDate distantFuture]
permissions:[set allObjects]];
} else {
// no token in this case implies that the user cancelled the permissions upgrade
NSError *error = [FBSession errorLoginFailedWithReason:FBErrorReauthorizeFailedReasonUserCancelled
errorCode:nil
innerError:nil];
// complete the operation: failed
[self callReauthorizeHandlerAndClearState:error];
// if we made it this far into the reauth case with an untosed device, then
// it is time to invalidate the session
if (isUntosedDevice) {
[self closeAndClearTokenInformation];
}
}
}
};
if (granted) {
[accountStore renewCredentialsForAccount:[[accountStore accountsWithAccountType:accountType] lastObject] completion:^(ACAccountCredentialRenewResult renewResult, NSError *error) {
dispatch_async(dispatch_get_main_queue(), postReauthorizeBlock);
}];
} else {
// requestAccessToAccountsWithType:options:completion: completes on an
// arbitrary thread; let's process this back on our main thread
dispatch_async(dispatch_get_main_queue(), postReauthorizeBlock);
}
}];
}
So it's addressed, but I've been calling /me from our backend to verify since you can't trust the device.
So I make a call to FBSession's + (void)renewSystemAuthorization when the backend comes back with an authorization error.