I'm using the Parse SDK to implement a Facebook apprequests dialog in my app. For all intents and purposes it just wraps the Facebook SDK, prefixing Facebook methods with PF_.
I use this code to prepare and raise the dialog:
NSMutableDictionary* params = [NSMutableDictionary dictionaryWithObjectsAndKeys: [NSString stringWithFormat:#"I just challenged you to a game!"], #"message",
[friendIds componentsJoinedByString:#"," ], #"to",
nil];
PF_FBSession *session = [PFFacebookUtils session];
PF_Facebook *facebook = [[PF_Facebook alloc] initWithAppId:session.appID andDelegate:nil];
facebook.accessToken = session.accessToken;
facebook.expirationDate = session.expirationDate;
[facebook dialog:#"apprequests" andParams:params andDelegate:self];
This works well, I'm getting the dialog, I'm able to invite friends to play with the app.
The problem is that the delegate methods are not being called, even though I've set the view controller as a PF_FBDialogDelegate:
#interface ChallengeFriendsViewController : UIViewController <UITableViewDelegate, PF_FBDialogDelegate> {
NSArray *facebookFriends;
NSMutableArray *selectedFriends;
}
These are some of the delegate methods I'm talking about:
- (void)dialog:(PF_FBDialog *)dialog didFailWithError:(NSError *)error {
NSLog(#"Error in Dialog: %#", error);
}
- (void)dialogDidNotCompleteWithUrl:(NSURL *)url {
NSLog(#"Failure on Facebook server side");
}
- (void)dialogCompleteWithUrl:(NSURL *)url {
NSLog(#"Did complete with URL");
[self.navigationController popViewControllerAnimated:YES];
[MBProgressHUD hideAllHUDsForView:self.view animated:YES];
}
- (void)dialogDidNotComplete:(PF_FBDialog *)dialog {
NSLog(#"Cancelled");
[MBProgressHUD hideAllHUDsForView:self.view animated:YES];
}
Without these methods being called I'm really not able to handle the sharing in an intuitive way. I'm stumped as to why they wouldn't be called and feel I've tried everything. Any pointers to where I'm going wrong?
In your code you have
PF_Facebook *facebook = [[PF_Facebook alloc] initWithAppId:session.appID andDelegate:nil];
Since you set a nil delegate, I would expect the delegate methods are never called, as you describe.
Could you change this to
PF_Facebook *facebook = [[PF_Facebook alloc] initWithAppId:session.appID andDelegate:self];
That is assuming that you put the delegate methods in the same class.
I finally got around the problem by using the facebook instance provided by PFFacebookUtils. This is deprecated but appears to be the only way to make it call the correct delegate methods at the moment.
Replaced:
PF_Facebook *facebook = [[PF_Facebook alloc] initWithAppId:session.appID andDelegate:nil];
With:
PF_Facebook *facebook = [PFFacebookUtils facebook];
Thanks to JP and Aromal for your input.
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 attempting to LIKE a business Facebook Like page (facebook.com/[LikePage]) from my native iOS application. I have used FB iOS SDK for login/logout purpose.
I have implemented the LIKE button similar to http://angelolloqui.com/blog/10-Facebook-Like-Button-on-iOS, which is an implementation of the social plugin on a webview. I did so, because of my understanding that to implement a custom LIKE button, I need to use the Built-in Like provided by FB which in turn would require that my actions be approved by FB.
However, it was brought to my notice that the social plugin implementation cannot be used in an iOS native application and can be used only in a mobile web app.
So, here are my questions :-
Is it true that native iOS applications cannot use the social plugin provided by facebook to like a Facebook Page ?
Is it true that in order to build a custom Like button and not the plugin, I need to use the Built-in Likes provided by Facebook, which in turn would require approval of my action types ? (In my case, LIKE a Page)
All I need is some concrete documentation which clearly lets me know which is the best way forward.
FYI, the business page URL's that need to be LIKEd, come dynamically from server.
Thanks in Advance.
There's no API or automatic method to like facebook pages. The built-in open graph action, once approved, allows you to like other URLs which have Open Graph meta tags on them but not Facebook Pages.
The Like button plugin should work in a webview as far as I know.
You can just use a regular NSURLRequest to like the page, or use another library to make a post call to: https://graph.facebook.com/{PAGE_OR_OBJECT_ID}/likes. Make sure you add the acces_token as a parameter.
I use AFNetworking to post requests:
NSURL *baseURL = [NSURL URLWithString:#"https://graph.facebook.com/"];
AFHTTPClient *httpClient = [[AFHTTPClient alloc] initWithBaseURL:baseURL];
NSString *link = [NSString stringWithFormat:#"/%#/likes", myObjectID];
NSDictionary *params = #{#"access_token" : FBSession.activeSession.accessToken};
[httpClient postPath:link parameters:params success:^(AFHTTPRequestOperation *op, id result) {
NSLog(#"result %#", result);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"error %#", error);
}];
An update to this post:-
"With the release of the Facebook SDK version 4.28.0, the Like Button for iOS is deprecated. It will be supported until February 5, 2018."
https://developers.facebook.com/docs/sharing/ios/like-button
try this Code:
I think this will surely help you:
Fb like Widget can be embedded in our application. You just have to add a webView and get the Fb Like Widget html code/URL here.
in ViewController.h where you want to add fb like button:
#import <UIKit/UIKit.h>
#interface TestViewController : UIViewController <UIWebViewDelegate>
#property (strong, nonatomic) UIWebView * fbLikeWebView;
-(void)embedFBLikeButton;
#end
in TestViewController.m
#import "AboutUsViewController.h"
#implementation AboutUsViewController
#synthesize fbLikeWebView = _fbLikeWebView;
- (void)viewDidLoad
{
[super viewDidLoad];
//Add this code for FbLike Webview
self.fbLikeWebView = [[UIWebView alloc] initWithFrame: CGRectMake(100.0, 50.0, 55.0, 70.0)];
_fbLikeWebView.opaque = NO;
_fbLikeWebView.backgroundColor = [UIColor clearColor];
_fbLikeWebView.delegate = self;
[self.view addSubview:_fbLikeWebView];
for (UIScrollView *subview in _fbLikeWebView.subviews)
{
if ([subview isKindOfClass:[UIScrollView class]]) {
subview.scrollEnabled = NO;
subview.bounces = NO;
}
}
}
then in ViewWillAppear method call the enbeddFBLikeButton Method to add the fbLike button wigdet on web view:
-(void)viewWillAppear:(BOOL)animated
{
[self embedFBLikeButton];
[_fbLikeWebView reload];
}
-(void)embedFBLikeButton
{
NSString *facebookUrl = //here paste the url you get from fb developer link above;
[self.fbLikeWebView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:facebookUrl]]];
}
You conform to UIWebViewDelegate now its turn to defining th edelegate method here:
#pragma mark - WebView Delgate Methods
- (BOOL)webView:(UIWebView *)webview shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType
{
if ([request.URL.lastPathComponent isEqualToString:#"login.php"])
{
[self login];
return NO;
}
return YES;
}
-(void)webView:(UIWebView *)webView didFailLoadWithError:(NSError *)error
{
[_fbLikeWebView stopLoading];
}
This method for login the user to facebook Account:
- (void)login
{
[FBSession setActiveSession: [[FBSession alloc] initWithPermissions:#[#"publish_actions", #"publish_stream", #"user_photos"]]];
[[FBSession activeSession] openWithBehavior: FBSessionLoginBehaviorForcingWebView completionHandler:^(FBSession *session, FBSessionState status, NSError *error) {
switch (status) {
case FBSessionStateOpen:
// call the legacy session delegate
//Now the session is open do corresponding UI changes
if (session.isOpen) {
FBRequest *me = [FBRequest requestForMe];
[me startWithCompletionHandler: ^(FBRequestConnection *connection,
NSDictionary<FBGraphUser> *my,
NSError *error) {
if (!my) {
NSLog(#"Facebook error:\n%#", error.description);
[[[UIAlertView alloc] initWithTitle: #"Error"
message: #"Facebook Login error."
delegate: self
cancelButtonTitle: #"Ok"
otherButtonTitles: nil, nil] show];
return;
}
}];
[_fbLikeWebView reload];
[[[UIAlertView alloc] initWithTitle: #""
message: #"Successfully Login. Please click on like button"
delegate: self
cancelButtonTitle: #"Ok"
otherButtonTitles: nil, nil] show];
}
break;
case FBSessionStateClosedLoginFailed:
{
[_fbLikeWebView reload];
}
break;
default:
break; // so we do nothing in response to those state transitions
}
}];
}
Enjoy Coding!!!
I'm working with the engine for the first time and am fairly new to iOS development. I' m curious though if there's a way to call a function after I log in successfully? I can't seem to get it to work along the lines that FBConnect called for fbDidLogin after your initial login function. I have MGTwitterEngineDelegate declared and I tried to use the following code after login:
//TWITTER LOGIN FUNCTION/////////////////////
-(void)twitLogin{
NSLog(#"Inside of twitLogin...");
if(![_engine isAuthorized]){
UIViewController *controller = [SA_OAuthTwitterController controllerToEnterCredentialsWithTwitterEngine:_engine delegate:self];
if (controller){
[self presentModalViewController: controller animated: YES];
NSLog(#"Inside of Twitter login UIWebView.");
} else {
NSLog(#"ONCE WE'RE LOGGED IN, LETS SEND THE TWEET!");
NSString *tweet = [[NSString alloc] initWithFormat:#"My Tweet!"];
[_engine sendUpdate:tweet];
NSLog(#"You just tweeted: %#",tweet);
}
} else {
NSString *twitterToken=[[NSUserDefaults standardUserDefaults] objectForKey:#"authData"];
NSLog(#"Twitter is authorized?: %#",twitterToken);
//Twitter Integration Code Goes Here
NSString *tweet = [[NSString alloc] initWithFormat:#"My Tweet!"];
[_engine sendUpdate:tweet];
NSLog(#"You just tweeted: %#",tweet);
}
}
Any help is appreciated. Thanks so much!
The solution I posted previously didn't work %100, turned out I didn't have the right delegate set for login success in my controller:
#pragma mark SA_OAuthTwitterController Delegate
- (void) OAuthTwitterController: (SA_OAuthTwitterController *) controller authenticatedWithUsername: (NSString *) username {
NSLog(#"Authenticated with user %#", username);
tweets = [[NSMutableArray alloc] init];
[self updateStream:nil];
}
Hope that helps anyone!
I am trying to extract user's basic information through FBConnect latest SDK. My code is simple:
- (void)viewDidLoad {
[super viewDidLoad];
facebook = [[Facebook alloc] initWithAppId:fbAppId];
NSArray* permissions = [[NSArray arrayWithObjects:#"user_about_me",#"read_stream",nil]retain];
[facebook authorize:permissions delegate:self];
[facebook requestWithGraphPath:#"me" andDelegate:self];
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithObjectsAndKeys:
fbAppId, #"app_id",
[NSString stringWithFormat: #"Some Text"], #"description",
#"My Test App", #"name",
#"Test Facebook Graph API",#"message",
nil];
[facebook dialog:#"feed" andParams:params andDelegate:self];
}
- (BOOL) application: (UIApplication*) application handleOpenURL:(NSURL *)url {
return [facebook handleOpenURL:url];
}
- (void) fbDidLogin {
NSLog(#"FB didLogin");
}
- (void) fbDidNotLogin:(BOOL)cancelled {
NSLog(#"FB didNotLogin");
}
- (void) request:(FBRequest *)request didLoad:(id)result {
NSLog(#"request-didLoad-result");
}
- (void)request:(FBRequest *)request didReceiveResponse:(NSURLResponse *)response {
NSLog(#"received response");
}
Up till this point everything goes well apparently. Publish a feed through dialog on user's wall works fine. The problem occurs when I try to get user's information like name with:
[facebook requestWithGraphPath:#"me" andDelegate:self];
But neither fbDidLogin nor requestDidLoad is called. For requestDidLoad, I checked didLoadRawResponse as it is called before request didLoad:
- (void)request:(FBRequest *)request didLoadRawResponse:(NSData*)data {
NSString *response = [[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"Response is = %#",response);
[response release];
}
What I get in response is the following authentication error:
{"error":{"type":"OAuthException","message":"An active access token must be used to query information about the current user."}}
What is the reason and the solution?
I have added my whole code in the following wiki
How to retrieve Facebook response using Facebook iOS SDK
Hope this will help you, this is a working code if you have any question please let me know.
I occasionally need to cancel a FaceBook graph request, but there seems to be no cancel or similar method in their API to do so. At the moment, crashes sometimes occur as the delegate I assigned to the request has been deallocated. Is there any way to cancel a graph request once submitted please?
I'm assuming you're talking about the facebook-ios-sdk project, and the lack of a cancel method in Facebook.h. I noticed this as well, and eventually decided to add my own cancel method. Just to note, the delegate you assign to the request shouldn't ever be dealloc'd and then referenced, because the request retains the delegate. See this similar question. Now, if you find yourself really needing a cancel method for some other reason...
Adding a cancel method:
Facebook requests are made in an opaque manner. You never see them, and only hear about results via the Facebook class. Under the hood, the Facebook class makes Graph API requests with the (not for public use) FBRequest class. This class is is basically a fancy NSURLConnection delegate. So to cancel the request, the member NSURLConnection just has to be told to cancel. Adding this method to FBRequest:
// Add to FBRequest.h
- (void)cancel;
And...
// Add to FBRequest.m
- (void)cancel {
[_connection cancel];
[_connection release], _connection = nil;
}
Now, to expose an interface in the Facebook class to make use of the new method...
// Add to Facebook.h
- (void)cancelPendingRequest;
And...
// Add to Facebook.m
- (void)cancelPendingRequest {
[_request cancel];
[_request release], _request = nil;
}
That's all there is to it. The method above will cancel the most recent request, and you'll never hear from it again.
I've followed Matt Wilding's approach listed here, which was very useful, thanks Matt. Unfortunately it didnt quite work for me, so I made some tweaks and now it works... also this revised approach keeps out of the core facebook classes...
//in .h define an FBRequest property
#property (nonatomic, retain) FBRequest * pendingFBRequest;
//in .m when making your request, store it in your FBRequest property
pendingFBRequest = [facebook requestWithGraphPath:#"me/feed"
andParams:params
andHttpMethod:#"POST"
andDelegate:self];
//create a timer for your timeout
pendingFacebookActionTimer = [NSTimer scheduledTimerWithTimeInterval:15.0 target:self selector:#selector(onPendingFacebookActionTimeout) userInfo:nil repeats:NO];
//cancel the action on the timeout's selector method
-(void)onPendingFacebookActionTimeout {
[pendingFBRequest.connection cancel];
}
Updated on 22/April/2012
I update Matt's version with the most up-to-date Facebook iOS SDK. My Project is using ARC, but I include the non-ARC Facebook sources so that I can modify the codes. (Of Course, we need to set the "-fno-objc-arc" flag for Facebook source files). The tricky part is to prevent the memory leak, and I think I am doing it correctly. But When I test it in the instrument, I still see very small amount of memory leak. Fortunately, the details show that they are not related to these codes, so I just assume they are related to the app resource handling.
Here is the code I implemented:
// Add to Facebook.h
- (void)cancelPendingRequest:(FBRequest *)releasingRequest;
And...
// Add to Facebook.m
- (void)cancelPendingRequest:(FBRequest *) releasingRequest{
[releasingRequest.connection cancel];
[releasingRequest removeObserver:self forKeyPath:requestFinishedKeyPath];
[_requests removeObject:releasingRequest];
}
And in your project which uses FBRequestDelegate
// Declare this member or property to the .h file
FBRequest * currentFbRequest;
// Declare this method
-(void)cancelFBRequest;
And ...
// In .m file
AppDelegate * appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
// prepare your necessary request data and parameter ...
currentFbRequest = [appDelegate.facebook requestWithGraphPath:#"/me/photos"
andParams:params
andHttpMethod:#"POST"
andDelegate:self];
// Then in the method where you want to cancel
AppDelegate * appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
[appDelegate.facebook cancelPendingRequest:currentFbRequest];
currentFbRequest=nil;
For those of us who build the static library and are unable to access the implementation files, a category would be the best way to go.
For those of us who did not build the static library, using a category would be optimal as well because you don't need to modify the existing files.
Here is said category.
// Facebook+Cancel.h
#import "Facebook.h"
#interface Facebook (Facebook_cancel)
- (void)cancelPendingRequest:(FBRequest *)releasingRequest;
- (void)cancelAllRequests;
#end
And then the .m file
// Facebook+Cancel.m
#import "Facebook+Facebook_cancel.h"
#implementation Facebook (Facebook_cancel)
- (void)cancelPendingRequest:(FBRequest *)releasingRequest{
[releasingRequest.connection cancel];
if ([_requests containsObject:releasingRequest]) {
[_requests removeObject:releasingRequest];
[releasingRequest removeObserver:self forKeyPath:#"state"];
}
}
- (void)cancelAllRequests {
for (FBRequest *req in [_requests mutableCopy]) {
[_requests removeObject:req];
[req.connection cancel];
[req removeObserver:self forKeyPath:#"state"];
}
}
#end
For those using any other answer, you are causing a memory leak. The Facebook SDK will warn you through NSLog that you have not removed an observer. The fourth line in the cancelAllRequests method fixes this problem.
Try this instead of using NSTimer:
FBRequest *fbRequest = [facebook requestWithGraphPath:#"me" andDelegate:self];
[self performSelector:#selector(fbRequestTimeout:) withObject:fbRequest afterDelay:30];
- (void)fbRequestTimeout:(FBRequest *)fbRequest
{
[fbRequest.connection cancel];
[fbRequest setDelegate:nil];
}
Since SDK 3.1, it's very easy, as startWithCompletionHandler: returns a FBRequestConnection object, which has a -(void)cancel; method.
For example:
// In interface or .h definitions:
#property (strong, nonatomic) FBRequest *fBRequest;
#property (strong, nonatomic) FBRequestConnection *fbConnection;
// when needed in class (params should be set elsewhere, this is just an example):
self.fBRequest = [[FBRequest alloc] initWithSession:[FBSession activeSession] graphPath:#"me/photos" parameters:params HTTPMethod:#"POST"];
self.fbConnection = [self.fBRequest startWithCompletionHandler:^(FBRequestConnection *connection, id result, NSError *error){
NSLog(#"Publish complete, error: %d", error.code);
}];
// now, to cancel anywhere in the class, just call:
[self.fbConnection cancel];
In FBRequest.h, I've had to add _delegate = nil; because in my case, the request delegate no longer existed (it was dismissed) which caused a crash.
I was having a crash with the previous iOS Facebook SDK which was valid in August 2012 whenever I navigated to another view. My solution is based on #staticfiction response:
Added BOOL viewWillDisappear flag in .h. In -(void) viewWillDisappear: set the flag to YES. Reset flag to NO in -(void) viewDidAppear:
//in .h define an FBRequest property
#property (nonatomic, retain) FBRequest * pendingFBRequest;
/*
* Graph API: Search query to get nearby location.
*/
- (void)apiGraphSearchPlace:(CLLocation *)location {
currentAPICall = kAPIGraphSearchPlace;
NSString *centerLocation = [[NSString alloc] initWithFormat:#"%f,%f",
location.coordinate.latitude,
location.coordinate.longitude];
JMYAppDelegate *delegate = (JMYAppDelegate *)[[UIApplication sharedApplication] delegate];
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithObjectsAndKeys:
#"place", #"type",
centerLocation, #"center",
#"1000", #"distance",
nil];
[centerLocation release];
pendingFBRequest = [[delegate facebook] requestWithGraphPath:#"search" andParams:params andDelegate:self];
if (viewWillDisappear) {
[pendingFBRequest.connection cancel];
[pendingFBRequest setDelegate:nil];
[self hideActivityIndicator];
}
}
Make a CURL call to this URL
https://graph.facebook.com/REQUEST_ID?method=delete