I'd be grateful for any suggestions as to how to deal with a problem I'm having posting an image to Facebook.
The following code works just as I would expect:
NSMutableDictionary* params = [NSMutableDictionary dictionaryWithObjectsAndKeys:
#"http://myserver/img.png", #"picture",
#"My Testing", #"name",
#"Enter some text...", #"message",
nil];
[appDelegate.facebook dialog:#"feed" andParams:params andDelegate:self];
However, I really want to use an image that I capture from my camera. I've set up the code to do that earlier in my application and I've put the image on the screen. So, I tried to do this:
CGRect contextRect = CGRectMake(0, 0, 320, 436);
UIGraphicsBeginImageContextWithOptions(contextRect.size, YES, 1);
[self.view.layer renderInContext:UIGraphicsGetCurrentContext()];
UIImage *newImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
I've tested this code and newImage is a valid UIImage that I can place and manipulate just as I'd expect. However, when I try to integrate it into the Facebook dialog as follows:
NSMutableDictionary* params = [NSMutableDictionary dictionaryWithObjectsAndKeys:
newImage, #"picture",
#"My Testing", #"name",
#"Enter some text...", #"message",
nil];
[appDelegate.facebook dialog:#"feed" andParams:params andDelegate:self];
I get these two ridiculous messages in the console:
-[UIImage length]: unrecognized selector sent to instance 0x1c9420
CoreAnimation: ignoring exception: -[UIImage length]: unrecognized selector sent to instance 0x1c9420
So, as a test, I thought I'd try importing an image into my project and accessing it directly. This is the exact image as I referenced online in the first example, so I know it should work. But even when I try this:
UIImage *newImage = [UIImage imageNamed:#"BaB.png"];
I get the same error messages as above.
I see in the Facebook documentation that the "picture" key is supposed to use the URL to an image. But I've seen some examples online that indicate that folks are using an UIImage instead of just an URL.
Clearly, I'm doing something wrong, but I have no idea what it might be.
I'd be very grateful for any pointers as to how to proceed.
A summarized answer that outlines the process...key point is to include the access_token in the graph calls since the facebook-ios-sdk doesn't do this automatically:
1)Update to the latest facebook-ios-sdk.
2)Instantiate with app id.
self.facebook = [[Facebook alloc] initWithAppId:kApplicationFBID];
3) Authorize the app including publish_stream
NSArray * neededPermissions = [[[NSArray alloc] initWithObjects:#"user_about_me", #"publish_stream", #"user_photos", nil] autorelease];
[facebook authorize:neededPermissions delegate:appDelegate];
4) Ensure app delegate fBDidLogin captures and stores the access token & expiration in user defaults (for later optimization of login process).
-(void)fbDidLogin {
DebugLog(#"New Access Token: %#", [facebook accessToken] );
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:[facebook accessToken] forKey:#"FBAccessTokenKey"];
[defaults setObject:[facebook expirationDate] forKey:#"FBExpirationDateKey"];
[defaults synchronize];
}
5) Confirm valid access token (non nil and not expired)...otherwise re authorize app per above.
6) Capture image to UIImage & call this noting the critical addition of the access_token. This will auto create an album for your app and put the image there and post it on the wall per my testing.
-(void)postImageToAlbum:(UIImage *)image {
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithObjectsAndKeys:
image, #"source",
#"caption desc", #"message",
nil];
[facebook requestWithGraphPath:[NSString stringWithFormat:#"/me/photos?access_token=%#", self.facebook.accessToken]
andParams:params andHttpMethod:#"POST" andDelegate:self];
}
7) Check for id result in FBRequestDelegate method
-(void)request:(FBRequest *)request didLoad:(id)result {
NSLog(#"Request didLoad: %# ", [request url ]);
if ([result isKindOfClass:[NSArray class]]) {
result = [result objectAtIndex:0];
}
if ([result isKindOfClass:[NSDictionary class]]){
}
if ([result isKindOfClass:[NSData class]]) {
}
NSLog(#"request returns %#",result);
}
8) DO NOT USE a test user account for testing...currently it will auth the test account and provide an access token but when you try to use graph api it will return unexpected errors. You may be alright if you are using web version vs native app setting in dev.facebook.com since supposedly you can create test users and associate them with the app but I haven't tried that.
Good Luck!
Related
I'm having a problem while trying to post a picture on a FB Page I'm the admin. The picture is uploaded on my own account instead of my page.
Here's the code i'm using to do this:
- (void)apiGraphUserPhotosPost:(UIImage*)img withMessage:(NSString*)message {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSUInteger c;
for (c = 0; c < [gFBPageArray count]; c++)
{
if ([[[gFBPageArray objectAtIndex:c] objectForKey:#"name"] isEqualToString:[defaults objectForKey:#"FBPostAs"]])
break;
}
params = [NSMutableDictionary dictionaryWithObjectsAndKeys:
img, #"picture", message, #"message", [[gFBPageArray objectAtIndex:c] objectForKey:#"access_token"], #"access_token",nil];
[[self facebook] requestWithGraphPath:[NSString stringWithFormat:#"/%#/photos", [[gFBPageArray objectAtIndex:c] objectForKey:#"id"]]
andParams:params
andHttpMethod:#"POST"
andDelegate:self];
}
[defaults objectForKey:#"FBPostAs"] contains the name of the FB page where i want to upload the picture.
gFBPageArray contains the list of FB page where i have admin rights.
Am I doing something wrong?
I've done something similar with the /PageID/feed and /PageID/videos and it works well...
You can find your answer for this problem here: picture-post-to-facebook-ends-up-in-the-wrong-place
If you want I can put more details here (copy/past) the code.
When authenticating with following authorization method i'm getting com.facebook.sdk error 5 with startWithGraphPath and startForMeWithCompletionHandler but not with requestWithGraphPath. I'm succesfully getting token (printing in didLogin) and getting anything which i want with requestwithGraphPath but i'm unable to get work with other methods. If anybody encountered with same issue or something similar or has any idea, i'd be happy if you share it.
Thanks
Method:
NSArray *permissions = [[NSArray alloc] initWithObjects: #"user_likes",#"offline_access",#"read_stream",#"publish_stream",nil];
[_facebook authorize:permissions];
The startWithGraphPath and other start* methods are likely not picking up an active session. Those methods are relying on an active session being set. See:
https://developers.facebook.com/docs/reference/ios/3.1/class/FBRequestConnection#startWithGraphPath%3AcompletionHandler%3A
"The request uses the active session represented by [FBSession activeSession]."
So you'll have to do something like this:
[FBSession setActiveSession:session];
Where session is an FBSession that you had previously set up.
Following solution worked for me. But if you're storing a permanent access token, you need to be sure user not removed application permissions otherwise it will give an error. And you can check that with requestWithGraphPath -> "me/permissions".
Application Init Function (e.g.:didFinishLaunchingWithOptions/or where you init your Facebook object which needs to be fbsessiondelegate in the mean time)
...
NSArray* permissions = [[NSArray alloc] initWithObjects:#"user_likes",#"offline_access", nil];
FBSession*oursession = [[FBSession alloc] initWithPermissions:permissions];
...
FBDidLogin function:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:[facebook accessToken] forKey:#"FBAccessTokenKey"];
[defaults setObject:[facebook expirationDate] forKey:#"FBExpirationDateKey"];
[defaults synchronize];
Example graph api request function:
NSUserDefaults *userDefaults =[NSUserDefaults standardUserDefaults];
NSString *key = [userDefaults stringForKey:#"FBAccessTokenKey"];
FBRequest* ourcon = [[FBRequest alloc] initWithSession:oursession graphPath:#"me/likes" parameters:params HTTPMethod:#"GET"];
[ourcon startWithCompletionHandler: ^(FBRequestConnection *connection, id<FBGraphUser> result, NSError *error){
if(error)
{
//NSLog(error.code);
return;
}
NSArray* collection = (NSArray*)[result data];
NSLog(#"You have %d like", [collection count]);
NSDictionary* name = [collection objectAtIndex:14];
NSLog(#"Like Name: %#", [name objectForKey:#"name"]);
}];
I'm new to iOS development, I have successfully integrated facebook login and such... however my problem is with the Score api. i can read the score but i cant seem to post it, i have the publish_actions permission and also am getting back a valid access_token.Not sure what the problem it, here is my code -
NSString *accessTokenToUse = [NSString stringWithFormat:#"%#",[self.facebook accessToken] ];
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithObjectsAndKeys:
//#"233", #"score",
accessTokenToUse, #"access_token",
nil];
NSMutableDictionary *paramsB = [NSMutableDictionary dictionaryWithObjectsAndKeys:
#"233", #"score",
accessTokenToUse, #"access_token",
nil];
NSLog(#"APP ACCESS TOKEN: %#",accessTokenToUse);
[self.facebook requestWithGraphPath:#"me/scores" andParams:paramsB andHttpMethod:#"POST" andDelegate:self];
[self.facebook requestWithGraphPath:#"APP_ID/scores" andParams:params andHttpMethod:#"GET" andDelegate:self];
***EDIT - Still doesn't work
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithObjectsAndKeys:
#"233", #"score",
#"[APP_ACCESS_TOKEN (omitted for stackoverflow)]", #"access_token", nil];
[self.facebook requestWithGraphPath:#"/[userID]/scores" andParams:params andHttpMethod:#"POST" andDelegate:self];
EDIT - SOLVED
So bascially i WAS passing in the correct token, however the facebook sdk was overwriting it in Facebook.m to the user's access token. So the fix was simple - whenever i need to pass in the App's token i just add an other parameter < key=#"useAppToken" Value=#"yes" > and in Facebook.m just add an if statement within isSessionValid -
- (FBRequest*)openUrl:(NSString *)url
params:(NSMutableDictionary *)params
httpMethod:(NSString *)httpMethod
delegate:(id<FBRequestDelegate>)delegate {
NSLog(#"PARAMS BFORE: %# ", params);
[params setValue:#"json" forKey:#"format"];
[params setValue:kSDK forKey:#"sdk"];
[params setValue:kSDKVersion forKey:#"sdk_version"];
if ([self isSessionValid]) {
if ([params objectForKey:#"useAppToken"] == nil || [params objectForKey:#"useAppToken"] == #"no") {
[params setValue:self.accessToken forKey:#"access_token"];
}
}
NSLog(#"PARAMS AFTER: %# ", params);
[self extendAccessTokenIfNeeded];
FBRequest* _request = [FBRequest getRequestWithParams:params
httpMethod:httpMethod
delegate:delegate
requestURL:url];
[_requests addObject:_request];
[_request addObserver:self forKeyPath:requestFinishedKeyPath options:0 context:finishedContext];
[_request connect];
return _request;
}
Be sure to comment out the NSLogs in this... :)
Scores can only be published using application access_token and not one for user (which you probably using, it's ok for reading the scores but don't allow you to publish 'em).
Citing the Scores documentaion:
Create or update a score for a user
You can post a score or a user by issuing an HTTP POST request to /USER_ID/scores with the app access_token as long as you have the publish_actions permission.
Looks like it's a bug:
http://developers.facebook.com/bugs/461900043821056
But if you change your app settings to web app instead of native it would work.
I'm trying to post a message to the users wall in out iOS app. The first time the user attempts to do this, the app switches to the Facebook app so they can sign in and accept our app. It then automatically goes back to our app as excepted. It then opens a UIWebView dialog (so the user can post their message to their wall), however an error message is shown in the dialog instead, saying "An error has occurred. Please try again later." Subsequent attempts to post the message work as expected, the issue only seems to occur immediately after signing in or accepting our app on Facebook.
Furthermore, when the error is shown, and the user clicks okay to dismiss the dialog, it then loads the user's wall showing all their friends posts, rather than closing the dialog.
Anybody know any solutions to this issue? I am working off the latest SDK.
Here is the code I use to login and display the dialog.
Authenciating session:
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([defaults objectForKey:#"FBAccessTokenKey"]
&& [defaults objectForKey:#"FBExpirationDateKey"]) {
facebook.accessToken = [defaults objectForKey:#"FBAccessTokenKey"];
facebook.expirationDate = [defaults objectForKey:#"FBExpirationDateKey"];
}
if (![facebook isSessionValid]) {
NSArray *permissions = [[NSArray alloc] initWithObjects:
nil];
[facebook authorize:permissions];
[permissions release];
return NO;
}
return YES;
And the code to show the dialog:
NSMutableDictionary* params = [NSMutableDictionary dictionaryWithObjectsAndKeys:
kAppId, #"app_id",
link, #"link",
imageURL, #"picture",
linkName, #"name",
caption, #"caption",
description, #"description",
nil];
[facebook dialog:#"feed" andParams:params andDelegate:self];
Solved the issue by just making a separate call after it returns back to our app.
Thanks for reading.
I went through Facebook iOS SDK Feed Dialog issue after authentication but in vain.
What works:
I created a sample "Hello, World" app using the Facebook iOS Tutorial with the Feed Dialog. This tutorial works fine and I am able to see the post in my feed.
Problem:
When I integrated this code in my app, I don't get to see the Feed Dialog after the authentication when the control reaches back to my app.
Flow of control:
I have a UIImagePickerController showing a camera that takes a picture, then shows UIAlertView to indicate that the image is being uploaded, shows a UIAlertView to display the result returned from server, and then finally shows a UIActionSheet to display the different sharing options (share to Facebook, Twitter, etc.).
When the user hits "Share to Facebook", the following selector gets invoked:
- (void) initFacebook
{
//Init Facebook
facebook = [[Facebook alloc] initWithAppId:#"308136969223987" andDelegate:self];
//Check for access_token
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if([defaults objectForKey:#"FBAccessTokenKey"]
&& [defaults objectForKey:#"FBExpirationDateKey"]) {
facebook.accessToken = [defaults objectForKey:#"FBAccessTokenKey"];
facebook.expirationDate = [defaults objectForKey:#"FBExpirationDateKey"];
}
if(![facebook isSessionValid]) {
[facebook authorize:nil];
}
NSMutableDictionary* params = [NSMutableDictionary dictionaryWithObjectsAndKeys:
kAppId, #"app_id",
#"https://developers.facebook.com/docs/reference/dialogs/", #"link",
#"http://fbrell.com/f8.jpg", #"picture",
#"Facebook Dialogs", #"name",
#"Reference Documentation", #"caption",
#"Using Dialogs to interact with users.", #"description",
#"Facebook Dialogs are so easy!", #"message",
nil];
[_facebook dialog:#"feed" andParams:params andDelegate:self];
}
This launches the Facebook app to authenticate, and then opens my app again with the UIImagePickerController but doesn't show the Feed Dialog.
Can someone please help me out?
You have to manually call the method you want again, in your case -(void)initFacebook, in the -(void)fbDidLogin of the FBSessionDelegate.
In addition, to make this more generic, a little trick I did after [facebook authorize:nil]; I set a pending target and selector to store the pending command that needs to be executed after login. Something like this...
id fbPendingTarget;
SEL fbPendingAction;
if (![delegate.facebook isSessionValid]) {
[delegate.facebook authorize:nil]; // Facebook app or Safari login, mustitask
// Save pending actions
delegate.fbPendingTarget = self;
delegate.fbPendingAction = #selector(facebookButtonPressed);
[self retain]; // *** hold on to self if you will release self and is using fbPendingTarget as self
}
and when the delegate fires, it runs your pending action ...
- (void)fbDidLogin {
[self storeAuthData:[facebook accessToken] expiresAt:[facebook expirationDate]];
[self apiFQLIMe]; // request user info
// perform any pending actions after login
if ([fbPendingTarget respondsToSelector:fbPendingAction]) {
[fbPendingTarget performSelector:fbPendingAction];
}
self.fbPendingTarget = nil;
self.fbPendingAction = nil;
[self release]; // *** let go of self after we finished everything
}
This help executing any pending actions you need after login. And don't forget to clear them after executing or login failed.
-(void)fbDidNotLogin:(BOOL)cancelled {
UIAlertView *alertView = [[[UIAlertView alloc] initWithTitle:#"Cannot Connect"
message:#"There was an error while connecting to Facebook. Please try Again."
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil] autorelease];
[alertView show];
self.fbPendingTarget = nil;
self.fbPendingAction = nil;
[self release]; // *** let go of self after we finished everything
}
Hope this helps :)
Edit: I find it better to set fbPendingTarget to some object that exists throughout the app lifetime (like appDelegate) rather than setting to self. This is because in many cases your self is a view that will be released right after the user sends a post to Facebook. Sending a message to a released object will crash your app.
A dirty way to get around this is to explicitly use [self retain] before sending and then [self release] in your fbPendingAction. But that would retain everything else that should be released in self by that time. I just find it easier to set to appDelegate since most action you will do is just displaying an alert anyway. I'll edit the code just for safety sake.