FBConnect iOS SDK publish_action Issue - objective-c

I must say I feel like an idiot right now. :) I've been up and down Facebook, Google and StackOverflow and still cannot get the answer to what I am doing wrong! :) I've looked at both Facebook examples: Hackbook and WishList. Wishlist is obviously the one that should tell me what to do, but ALL the examples I've seen have the OBJECT part as a URL. I do not necessarily desire this, as I just want the post to say (this user) is playing [MyGame].
Ok here's my goal. I have an iPhone game. I want to do what Spotify does when you listen to a song, which posts to timeline and ticker. I'd also like to use this to post the player's score on the user's timeline and ticker.
I setup Open Graph with an action called Play and an object called Game along with it's aggregator. I think I also will need an action called Score?
Anyways, I can successfully post to a user's wall using feed dialog, but that's not what I want for the Play action.
Here's a concise version of what I got so far, any help is much appreciated:
Couple Notes:
I have a singleton FacebookInfo which takes care of handling Facebook delegates and stuff. I also have a FacebookUser class which hold the current user's info for the current session, populated when calling me. I also have a DLog method which simply does an NSlog only on debug mode.
When a user clicks the Play button in my game, I would like to call my method [[Facebook sharedInfo] publishAction:#"play"] below. I'm passing an NSString as action so later I can call the same method and use an action like Score and just modify the post accordingly.
#interface FacebookInfo : NSObject {
Facebook *_facebook;
FacebookUser *_facebookUser;
}
#property (nonatomic, retain) Facebook *facebook;
#property (nonatomic, retain) FacebookUser *facebookUser;
+(id)sharedInfo;
-(BOOL)isFacebookAuthenticated;
-(void)fbDidLogout;
-(void)getMe;
-(void)publishFeed;
-(void)publishWithAction:(NSString *)action;
#end
static FacebookInfo *facebookInfo = nil;
#implementation FacebookInfo
#synthesize facebook = _facebook;
#synthesize facebookUser = _facebookUser;
#pragma mark - Custom Methods
-(void)getMe {
DLog(#"**********");
/* when forcing FBConnect to show inline dialog instead of using SSO, this works.
apparently this fails when using SSO, error:
Err message: (null)
Err code: 10000
*/
[[self facebook] requestWithGraphPath:#"me" andDelegate:self];
}
-(void)publishWithAction:(NSString *)action {
DLog(#"**********");
if ([action isEqualToString:#"play"]) {
// Build the params list
NSMutableDictionary *params = [[NSMutableDictionary alloc] initWithCapacity:1];
// all sample have this pointing to a URL. Do i really need to do that?
[params setValue:kFBAppNameSpace forKey:#"game"];
// I know I may need more parameters, but which are required?
// Do I need to add the one's that Facebook Javascript examples have,
// like title, description? I think it's here where I'm mostly confused.
// Make the Graph API call to add to the wishlist
[[self facebook] requestWithGraphPath:#"me/[myappnamespace]:play"
andParams:params
andHttpMethod:#"POST"
andDelegate:self];
[params release];
}
}
-(void)publishFeed {
DLog(#"**********");
/*
This works perfectly fine
*/
SBJSON *jsonWriter = [[SBJSON new] autorelease];
NSDictionary *actionLinks = [NSArray arrayWithObjects:
[NSDictionary dictionaryWithObjectsAndKeys:
#"Get The App",#"name",
kFBAppURL,#"link",
nil],
nil];
NSString *actionLinksStr = [jsonWriter stringWithObject:actionLinks];
NSString *app_id = kFBAppID;
NSString *user_message_prompt = [NSString stringWithFormat:#"Post to Your Wall!"];
NSString *name = [NSString stringWithFormat:#"[MyGameName]"];
NSString *caption = [NSString stringWithFormat:#"%# has gotten a score of %#!",[[self facebookUser] firstName],[[[GameInfo sharedGameInfo] scoreTotal] stringValue]];
NSString *description = [NSString stringWithFormat:#"Can you beat this score?!"];
NSString *link = kFBAppURL;
NSString *picture = kFBAppImage;
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithObjectsAndKeys:
app_id, #"app_id",
user_message_prompt, #"user_message_prompt",
name, #"name",
caption, #"caption",
description, #"description",
link, #"link",
picture, #"picture",
actionLinksStr, #"actions",
nil];
[[self facebook] dialog:#"feed"
andParams:params
andDelegate:self];
}
-(BOOL)checkForPreviousAccessToken {
DLog(#"**********");
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([defaults objectForKey:#"FBAccessTokenKey"] && [defaults objectForKey:#"FBExpirationDateKey"]) {
DLog(#"FB: Token Exists!");
[[self facebook] setAccessToken:[defaults objectForKey:#"FBAccessTokenKey"]];
[[self facebook] setExpirationDate:[defaults objectForKey:#"FBExpirationDateKey"]];
}
if (![[self facebook] isSessionValid]) {
DLog(#"FB: Authorizing...");
NSArray *permissions = [[NSArray alloc] initWithObjects:
#"publish_stream",
#"publish_actions",
#"offline_access",
nil];
[[self facebook] authorize:permissions];
[permissions release];
} else {
DLog(#"FB: Authorized!!!");
// show logged in
[self getMe];
}
return [[self facebook] isSessionValid];
}
-(BOOL)isFacebookAuthenticated {
DLog(#"**********");
return [self checkForPreviousAccessToken];
}
-(void)extendAccessTokenIfNeeded {
DLog(#"**********");
[[self facebook] extendAccessTokenIfNeeded];
[[FacebookInfo sharedInfo] getMe];
}
-(void)logout {
DLog(#"**********");
[[self facebook] logout:self];
}
#pragma mark - FBConnect Delegate Methods
-(void)fbDidLogin {
DLog(#"**********");
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:[[self facebook] accessToken] forKey:#"FBAccessTokenKey"];
[defaults setObject:[[self facebook] expirationDate] forKey:#"FBExpirationDateKey"];
[defaults synchronize];
[self getMe];
}
- (void)fbDidNotLogin:(BOOL)cancelled {
DLog(#"**********");
}
- (void)fbDidExtendToken:(NSString*)accessToken expiresAt:(NSDate*)expiresAt {
DLog(#"**********");
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:accessToken forKey:#"FBAccessTokenKey"];
[defaults setObject:expiresAt forKey:#"FBExpirationDateKey"];
[defaults synchronize];
}
-(void)fbDidLogout {
DLog(#"**********");
// Remove saved authorization information if it exists
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
if ([defaults objectForKey:#"FBAccessTokenKey"]) {
[defaults removeObjectForKey:#"FBAccessTokenKey"];
[defaults removeObjectForKey:#"FBExpirationDateKey"];
[defaults synchronize];
}
}
- (void)fbSessionInvalidated {
DLog(#"**********");
}
#pragma mark - FBRequestDelegate Methods
/**
* Called when the Facebook API request has returned a response. This callback
* gives you access to the raw response. It's called before
* (void)request:(FBRequest *)request didLoad:(id)result,
* which is passed the parsed response object.
*/
- (void)request:(FBRequest *)request didReceiveResponse:(NSURLResponse *)response {
DLog(#"**********");
//DLog(#"received response");
}
/**
* Called when a request returns and its response has been parsed into
* an object. The resulting object may be a dictionary, an array, a string,
* or a number, depending on the format of the API response. If you need access
* to the raw response, use:
*
* (void)request:(FBRequest *)request
* didReceiveResponse:(NSURLResponse *)response
*/
- (void)request:(FBRequest *)request didLoad:(id)result {
DLog(#"**********");
//code removed for this example
}
/**
* Called when an error prevents the Facebook API request from completing
* successfully.
*/
- (void)request:(FBRequest *)request didFailWithError:(NSError *)error {
DLog(#"**********");
DLog(#"Err message: %#", [[error userInfo] objectForKey:#"error_msg"]);
DLog(#"Err code: %d", [error code]);
if ([error code] == 190) {
// logout
} else {
DLog(#"There was an error making your request.");
}
}
#pragma mark - Singleton Methods
+ (id)sharedInfo {
//DLog(#"**********");
#synchronized(self) {
if(facebookInfo == nil)
facebookInfo = [[super allocWithZone:NULL] init];
}
return facebookInfo;
}
+ (id)allocWithZone:(NSZone *)zone {
DLog(#"**********");
return [[self sharedInfo] retain];
}
- (id)copyWithZone:(NSZone *)zone {
DLog(#"**********");
return self;
}
- (id)retain {
DLog(#"**********");
return self;
}
- (unsigned)retainCount {
DLog(#"**********");
return UINT_MAX; //denotes an object that cannot be released
}
- (oneway void)release {
DLog(#"**********");
// never release
}
- (id)autorelease {
DLog(#"**********");
return self;
}
- (id)init {
DLog(#"**********");
if ((self = [super init]) != NULL) {
//Init
[self setFacebook:[[[Facebook alloc] initWithAppId:kFBAppID urlSchemeSuffix:kFBUrlSchemeSuffix andDelegate:self] autorelease]];
[self setFacebookUser:[[[FacebookUser alloc] init] autorelease]];
}
return self;
}
- (void)dealloc {
DLog(#"**********");
// Should never be called, but just here for clarity really.
DLog(#"Release FacebookInfo...");
[super dealloc];
}
#end
#implementation AppDelegate_iPhone
// Add for Facebook SSO support (4.2+)
- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
[[[FacebookInfo sharedInfo] facebook] handleOpenURL:url];
}
// Add for Facebook SSO support (pre 4.2)
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
[[[FacebookInfo sharedInfo] facebook] handleOpenURL:url];
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
DLog(#"**********");
[[FacebookInfo sharedInfo] extendAccessTokenIfNeeded];
}
Wow, I know this is a long post, but I hope someone can help point me in the right direction.
UPDATE 1: (2012/18/02)
Ok. So I settled for making a page which contains the og meta-data which I did not want to do and supplied the url for my object. As per the FB Documentation, found here:
Open Graph Mechanics
When users take an action in your app, such as cook the Stuffed Cookie, the app calls a Graph API to create a new cook action that connects the user with the Stuffed Cookie object. This is accomplished by issuing a HTTP POST to the user’s /me/myapp:cook connection with the URL of the recipe object. Facebook will then crawl the object URL, read the metadata, and connect the object to user's Graph via the action.
The diagram below illustrates the process:
User takes an action in the app, such as "cook" a "recipe"
App calls a Graph API /me/action:object=Object_URL
Facebook will crawl the object URL, read its meta tags and connects the object to the user's Graph via the action.
It would be cool if we can define these things in the app itself as params for cases where I do not need a website for.

As of 4/21/12 Facebook requires you to have created a page to get the data desired.

Related

presentRequestsDialogModallyWithSession does not work, but gives good result

When I use the webdialog for a friendrequest, everything is going fine, except no request or anything is made.
The code:
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithObjectsAndKeys:
facebookFriend.id, #"to",
nil];
[FBWebDialogs presentRequestsDialogModallyWithSession:FBSession.activeSession
message:NSLocalizedString(#"FB_FRIEND_INVITE_MESSAGE", #"Facebook friend invite message")
title:NSLocalizedString(#"FB_FRIEND_INVITE_TITLE", #"Facebook friend invite title")
parameters:params
handler:^(FBWebDialogResult result, NSURL *resultURL, NSError *error) {
}];
This is the result I get:
fbconnect://success?request=xxxxxxxxxxxx&to%5B0%5D=xxxxxxxx
How can I debug what is going wrong?
Thanks in advance.
Ruud
For SDK 3.2 or above we have a facility to use FBWebDialogs class that will help us to show a popup along with the friend list and pick one or more from list to send invitation.
Lets do it step by step:
1) Download and setup SDK 3.2 or above.
2) First setup your application on facebook by following this url.
3) Then use the attached code.
Sample Code: (It generates invite friend request)
-(void)inviteFriends
{
if ([[FBSession activeSession] isOpen])
{
NSMutableDictionary* params = [NSMutableDictionary dictionaryWithObjectsAndKeys:nil];
[FBWebDialogs presentRequestsDialogModallyWithSession:nil
message:[self getInviteFriendMessage]
title:nil
parameters:params
handler:^(FBWebDialogResult result, NSURL *resultURL, NSError *error)
{
if (error)
{
[self requestFailedWithError:error];
}
else
{
if (result == FBWebDialogResultDialogNotCompleted)
{
[self requestFailedWithError:nil];
}
else if([[resultURL description] hasPrefix:#"fbconnect://success?request="])
{
// Facebook returns FBWebDialogResultDialogCompleted even user
// presses "Cancel" button, so we differentiate it on the basis of
// url value, since it returns "Request" when we ACTUALLY
// completes Dialog
[self requestSucceeded];
}
else
{
// User Cancelled the dialog
[self requestFailedWithError:nil];
}
}
}
];
}
else
{
/*
* open a new session with publish permission
*/
[FBSession openActiveSessionWithPublishPermissions:[NSArray arrayWithObject:#"publish_stream"]
defaultAudience:FBSessionDefaultAudienceFriends
allowLoginUI:YES
completionHandler:^(FBSession *session, FBSessionState status, NSError *error)
{
if (!error && status == FBSessionStateOpen)
{
NSMutableDictionary* params = [NSMutableDictionary dictionaryWithObjectsAndKeys:nil];
[FBWebDialogs presentRequestsDialogModallyWithSession:nil
message:[self getInviteFriendMessage]
title:nil
parameters:params
handler:^(FBWebDialogResult result, NSURL *resultURL, NSError *error)
{
if (error)
{
[self requestFailedWithError:error];
}
else
{
if (result == FBWebDialogResultDialogNotCompleted)
{
[self requestFailedWithError:nil];
}
else if([[resultURL description] hasPrefix:#"fbconnect://success?request="])
{
// Facebook returns FBWebDialogResultDialogCompleted even user
// presses "Cancel" button, so we differentiate it on the basis of
// url value, since it returns "Request" when we ACTUALLY
// completes Dialog
[self requestSucceeded];
}
else
{
// User Cancelled the dialog
[self requestFailedWithError:nil];
}
}
}];
}
else
{
[self requestFailedWithError:error];
}
}];
}
}
and here are the helper functions that calls delegates function OnFBSuccess and OnFBFailed.
- (void)requestSucceeded
{
NSLog(#"requestSucceeded");
id owner = [fbDelegate class];
SEL selector = NSSelectorFromString(#"OnFBSuccess");
NSMethodSignature *sig = [owner instanceMethodSignatureForSelector:selector];
_callback = [NSInvocation invocationWithMethodSignature:sig];
[_callback setTarget:owner];
[_callback setSelector:selector];
[_callback retain];
[_callback invokeWithTarget:fbDelegate];
}
- (void)requestFailedWithError:(NSError *)error
{
NSLog(#"requestFailed");
id owner = [fbDelegate class];
SEL selector = NSSelectorFromString(#"OnFBFailed:");
NSMethodSignature *sig = [owner instanceMethodSignatureForSelector:selector];
_callback = [NSInvocation invocationWithMethodSignature:sig];
[_callback setTarget:owner];
[_callback setSelector:selector];
[_callback setArgument:&error atIndex:2];
[_callback retain];
[_callback invokeWithTarget:fbDelegate];
}
So the class taht calls method InviteFriend MUST have these functions:
-(void)OnFBSuccess
{
CCLOG(#"successful");
// do stuff here
[login release];
}
-(void)OnFBFailed:(NSError *)error
{
if(error == nil)
CCLOG(#"user cancelled");
else
CCLOG(#"failed");
// do stuff here
[login release];
}
Recommended Reads:
Send Invitation via Facebook
API Permissions
An Example
NOTE:
1) Don't forget to setup Facebook application ID in plist.
2) Don't forget to adjust AppDelegate to handle urls.
Partial snippet taken from above link in point 2:
/*
* If we have a valid session at the time of openURL call, we handle
* Facebook transitions by passing the url argument to handleOpenURL
*/
- (BOOL)application:(UIApplication *)application
openURL:(NSURL *)url
sourceApplication:(NSString *)sourceApplication
annotation:(id)annotation {
// attempt to extract a token from the url
return [FBSession.activeSession handleOpenURL:url];
}
Hope it helps!
EDIT
Here:
declaration for fbDelegate is:
#property (nonatomic, assign) id <FBLoginDelegate> fbDelegate;
#protocol FBLoginDelegate <NSObject>
#required
-(void) OnFBSuccess;
-(void) OnFBFailed : (NSError *)error;
#end
and this is how you can consume this code:
FBLoginHandler *login = [[FBLoginHandler alloc] initWithDelegate:self]; // here 'self' is the fbDelegate you have asked about
[login inviteFriends];
I think your application is not enable for Android and for web . And you are trying to get notification on web or on Android device.
Points : For getting notification on Android or on web you have to enable your app for Android and web too .
To Enable Android and Web on your App : GoTo your App > Setting > Click on + Add platform add enter necessary information and Save .
Lets Enjoy Notification . :-)

UIWebview does not start loading

I'm developing a native application for a website. The application is basically a wrapper around the website, which implements things like push notifications. When a push notification is clicked, the following code is used to go to the appropriate page:
- (NSString *)handlePush:(NSDictionary *)notification
{
if ([[notification objectForKey:#"aps"] objectForKey:#"badge"]) {
int badgeNum = [[[notification objectForKey:#"aps"] objectForKey:#"badge"] integerValue];
NSLog(#"Badge: %i, got from %#", badgeNum, [[notification objectForKey:#"aps"] objectForKey:#"badge"]);
[UIApplication sharedApplication].applicationIconBadgeNumber = badgeNum;
}
if (!active) {
NSLog(#"Got noti while not active, going to that chat!");
NSString *hash;
if ((hash = [notification objectForKey:#"h"])) {
NSLog(#"Hash: %#", [hash stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding]);
return [NSString stringWithFormat:#"#%#", hash];
}
return #"";
}
return nil;
}
Active is changed when the application enters the background and after it resumes, to make sure it does not trigger when a push notification arrives when the user is using the app.
The URL is parsed properly, because if i manually paste the exact same URL in the browser, i do go to the correct page.
I am 100% certain the delegate is set, as the UIWebView: ShouldStartLoadWithRequest method is called:
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *url = [[request URL] absoluteString];
NSLog(#"shouldStartLoadWithRequest called with url %#", url);
if ([url hasPrefix:BASE_URL]) {
NSLog(#"Yea, i'll load this one for ya");
// remove any get-params / hash suffix
NSRange r = [url rangeOfString:#"?"];
if (r.location == NSNotFound)
r = [url rangeOfString:#"#"];
if (r.location != NSNotFound)
url = [url substringToIndex:r.location];
if (![url isEqualToString:[defaults valueForKey:#"baseUrl"]]) {
NSLog(#"setting new baseUrl %#", url);
[defaults setValue:url forKey:#"baseUrl"];
[defaults synchronize];
}
NSLog(#"Should go and load it now...");
return YES;
}
}
There's some logic for caching versions of the webpage in there. I stepped through with breakpoints, and it reaches the return YES part, and the logs in there are called. However, in that same delegate, the didStartLoad and didFailLoadWithError are not called, which contain purely NSLogs.
On the initial application start, this does work, and it has worded one time too where i stepped through for a long time using the breakpoint, so it seems it's some timing issue. Hoping not having to use any hacky solutions like timers, i was hoping that anyone around here has experience with a similar problem, or has any other valuable input.

Message wont connect to method

I'm kind of new to this, so it might be a beginners mistake. The problem is that when i send a message to a method it doesn't connect.
Here's the calling method:
-(BOOL) login:(LoginInfo *) info{
NSString *url = [NSString stringWithFormat:#"%#/%#?name=%#&password=%#", FAMORABLE_API_URL,FAMORABLE_API_ACTION_LOGIN, info.username, info.password];
NSDictionary* json = [self getJson:url];
NSString *token = [json objectForKey:#"Token"];
NSLog(#"results: %#", token);
LoginInfo *loginResult = [LoginInfo alloc];
loginResult.token = token;
//TODO
NSLog(#"test 1 %#", loginResult.token);
[clientService saveLoginInfo:loginResult];
return YES;
}
On the line above the last you can see I'm sending to saveLoginInfo in clientService which is declared in ClientService.h which is imported in this file.
-(void) saveLoginInfo:(LoginInfo *)info{
NSLog(#"test 2 %#", info);
NSLog(#"test 3%#", info.token);
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:info.token forKey:KEY_TOKEN];
NSString *string = [defaults stringForKey:KEY_TOKEN];
NSLog(#"test 4%#", string);
// save it
[defaults synchronize];
if (!(info.token)){
self.currentUser = nil;
self.isLoggedOn=false;
}else{
self.currentUser = info;
self.isLoggedOn=true;
}
}
This is the method being called. I've put out a bunch of logs, mostly as a safe if i did one wrong, but none of them are being executed...
Do you have any ideas of what might be wrong?
Thanks in advance
Tom
Is clientService in the line [clientService saveLoginInfo:loginResult]; an instance variable in your class?
If so, make sure that you have instantiated the object somewhere before you call it. (Either in your class's init method, or possibly viewDidLoad if it's a UIViewController.)
E.g.
- (void)viewDidLoad {
[super viewDidLoad];
clientService = [[MyClientService alloc] init];
}
- (id)init {
self = [super init];
if (self) {
clientService = [[MyClientService alloc] init];
}
return self;
}
(Make sure you also release the instance in the dealloc method.

Any way to have completionHandler style syntax using [facebook authorize] on iOS 5?

The code below is trying to lazy login to Facebook right before posting a photo, but has an asynchronous problem. In the logs, the after isSessionValid block will appear before the fbDidLogin and then a facebookErrDomain error 10000 will happen ("OAuthException", "active access token must be used", etc).
MyAppDelegate *appDelegate = (MyAppDelegate *)[[UIApplication sharedApplication] delegate];
if (![appDelegate.facebook isSessionValid]) {
[appDelegate.facebook authorize:[NSArray arrayWithObjects:#"publish_stream", #"user_photos", nil]];
}
NSLog(#"after isSessionValid block");
NSData *imageData = UIImageJPEGRepresentation(image, 1);
NSMutableDictionary* params = [NSMutableDictionary dictionaryWithObjectsAndKeys:
FACEBOOK_APP_ID, #"app_id",
imageData, #"source",
message, #"message",
nil];
[appDelegate.facebook requestWithGraphPath:#"me/photos" andParams:params andHttpMethod:#"POST" andDelegate:self];
Here is the fbDidLogin in MyAppDelegate
- (void)fbDidLogin {
NSLog(#"fbDidLogin");
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setObject:[facebook accessToken] forKey:#"FBAccessTokenKey"];
[defaults setObject:[facebook expirationDate] forKey:#"FBExpirationDateKey"];
[defaults synchronize];
}
I realize that the facebook requestWithGraphPath is running before the fbDidLogin on the FBSessionDelegate but not sure the best way to take the code below the after isSessionValid block log statement and have it run inside fbDidLogin?
Question
I would love to have a completionHandler style API like below. Is there an easy way do that? Alternatively, is there good way to add a callback or block to MyAppDelegate that would be called once from fbDidLogin and then removed?
[appDelegate.facebook authorize:array completionHandler:^(BOOL success) {
// other setup stuff from first example
[appDelegate.facebook requestWithGraphPath:#"me/photos" andParams:params andHttpMethod:#"POST" andDelegate:self];
}];
Update
An answer to How to react to asynchronous events (login)? might be what I am looking for.
One possibility is to use a GCD semaphore and put requestWithGraph: onto a background queue.
Add a property to MyAppDelegate to hold the semaphore; create it with dispatch_semaphore_create, passing 1 because you're dealing with two threads -- you only want one to be able to work at a time:
#property (assign) dispatch_semaphore_t authSemaphore;
authSemaphore = dispatch_semaphore_create(1);
Then decrement the semaphore right before you authorize:
if (![appDelegate.facebook isSessionValid]) {
dispatch_semaphore_wait([appDelegate authSemaphore], DISPATCH_TIME_FOREVER);
[appDelegate.facebook authorize:[NSArray arrayWithObjects:#"publish_stream", #"user_photos", nil]];
}
And signal it when the authorization succeeds:
- (void)fbDidLogin {
//...
[defaults synchronize];
dispatch_semaphore_signal([self authSemaphore]);
}
Now you can wait on the semaphore right before you try to post the image. If authorization is in progress, the wait call will block, and the following code will not run until the semaphore is signaled. If the semaphore is available, however, the code will proceed normally.
You have to put this onto a background queue to avoid blocking the main thread, but you won't need to create a new one; one of the global concurrent queues should work fine:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
dispatch_semaphore_wait([appDelegate authSemaphore], DISPATCH_TIME_FOREVER);
[appDelegate.facebook requestWithGraphPath:#"me/photos" andParams:params andHttpMethod:#"POST" andDelegate:self];
dispatch_semaphore_signal([appDelegate authSemaphore]);
});
Here is what I put in MyAppDelegate, which saves off a completionHandler, and either calls with YES or NO depending on login, then sets completionHandler to nil. I'm sure I should put a #try/#catch in there somewhere to ensure completionHandler gets set to nil.
#interface MyAppDelegate : UIResponder <UIApplicationDelegate, FBSessionDelegate>
{
void (^_completionHandler)(BOOL success);
}
- (void)facebookAuthorizeWithCompletionHandler:(void (^)(BOOL success))completionHandler {
if (![facebook isSessionValid]) {
_completionHandler = completionHandler;
[facebook authorize:[NSArray arrayWithObjects:#"publish_stream", #"user_photos", nil]];
} else {
completionHandler(YES);
}
}
- (void)fbDidLogin {
NSLog(#"fbDidLogin");
// removed code that saves accessToken/expirationDate to NSUserDefaults
if (_completionHandler) {
_completionHandler(YES);
_completionHandler = nil;
}
}
- (void)fbDidNotLogin:(BOOL)cancelled {
NSLog(#"fbDidNotLogin");
if (_completionHandler) {
_completionHandler(NO);
_completionHandler = nil;
}
}
I called into it using code like this, which seems to work (but I want to reread about Blocks and Variables to make sure I understand the memory management issues).
[appDelegate facebookAuthorizeWithCompletionHandler:^(BOOL success) {
NSLog(#"in completionHandler, success=%d", success);
if (success) {
// other setup stuff from first example
[appDelegate.facebook requestWithGraphPath:#"me/photos" andParams:params andHttpMethod:#"POST" andDelegate:self];
}
}];
You can write an extension to the Facebook object, use a secondary object (instance of another class) that registers itself as the delegate and stores the handler. You can use associative references to save that object into the Facebook instance. So, when the delegate methods get executed, that secondary object could simply execute the handler block.
In this project, there are a lot of examples of this (NSURLConnection-BKAdditions.m for example) Here you have another link that might help you with the associative references.

how to send post to facebook using fbconnect in iphone?

FBSession.m
- (BOOL)resume {
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
FBUID uid = [[defaults objectForKey:#"FBUserId"] longLongValue];
if (uid) {
NSDate* expirationDate = [defaults objectForKey:#"FBSessionExpires"];
if (!expirationDate || [expirationDate timeIntervalSinceNow] > 0) {
_uid = uid;
_sessionKey = [[defaults stringForKey:#"FBSessionKey"] copy];
_sessionSecret = [[defaults stringForKey:#"FBSessionSecret"] copy];
_expirationDate = [expirationDate retain];
for (id<FBSessionDelegate> delegate in _delegates) {
[delegate session:self didLogin:_uid]; // this is the line where i m getting stuck while looping break
}
return YES;
}
}
return NO;
}
#pragma mark Facebook
- (void)session:(FBSession *)session didLogin:(FBUID)uid{
NSLog(#"Facebook Logged in");
FBStreamDialog *dialog = [[[FBStreamDialog alloc] initWithSession:fbsession] autorelease];
dialog.delegate=self;
Note *note;
NSString *str = [note noteTitle];
dialog.attachment = [NSString stringWithFormat: #"{\"name\" : \"Death Diary Bucket List Item\",\"href\" : \"http://ifidieapp.com\",\"caption\" : \"{*actor*} added a Bucket List Item\",\"description\" : \"%# \",\"media\": [{\"type\": \"image\",\"src\": \"http://deathdiaryapp.com/wp-content/uploads/2010/01/facebook-big-icon.jpg\",\"href\": \"http://ifidieapp.com\"}]}",str];
}
dialog.userMessagePrompt = #"Enter anything about the item here:";
[dialog show];
}
What could be wrong?
not sure why you should have a array of delegate...
if stuck, try the old API:
facebook = [[Facebook alloc] initWithAppId:#"YOUR_APP_ID"];
[facebook dialog:#"stream.publish" andParams:fbArguments andDelegate:self];
easier, faster (for prototyping or light needs.
if you want a specific answer to your issue, please provide more details (where do you initialize your fb object, where do you call FBSession from).