So I just added Facebook to a previous app that was working. And I basically just included the code for a button press to upload/post the picture selected to Facebook -- nothing happened.
Are there any good resources for learning the syntax for iOS Facebook apps? Because I'm lost. Also, could you tell me why this seemingly simple code doesn't work? Because I pretty much copied it from the demo app example.
- (IBAction) sendPressed:(UIButton *)sender
{
for(UIViewController *controller in self.tabBarController.viewControllers)
{
if([controller isKindOfClass:[FirstViewController class]])
{
FirstViewController *firstViewController = (FirstViewController *)controller;
[firstViewController realLabel];
}
}
Facebook *facebook = [[Facebook alloc] initWithAppId:#"198223696897308"];
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithObjectsAndKeys:
image, #"picture",
nil];
[facebook requestWithGraphPath:#"me/photos"
andParams:params
andHttpMethod:#"POST"
andDelegate:self];
self.tabBarController.selectedIndex = 0;//switch over to the first view to see if it worked
}
The first thing I'd do is verify that the method is actually being called by setting a breakpoint or outputting a log statement.
The second thing I'd do is look at the API documentation to see if any of the methods I'm calling provide errors in any way. They almost certainly do, so check what they say.
The third thing I'd do is set the device to use a proxy (e.g. Charles) to see if the requests are actually being sent out over the wire.
Related
I recently integrated the Google + API in my App, it was a breeze, my only problem with it, is that everything requires you to leave the app and then come back (it uses URL schemes for this). This is not the behavior I would like, is there a way to directly call their services and do whatever I want with the responses just like in LinkedIn API?.
I really want to avoid going back and forth between safari and my app. Any suggestions/documentation is appreciated.
Thank you,
Oscar
UPDATE FROM GOOGLE
today, we released a new Google Sign In iOS SDK with full built-in
support for Sign In via WebView:
developers.google.com/identity/sign-in/ios The SDK supports dispatch
to any of a number of Google apps handling Sign In when present, with
WebView fallback after. In all cases, the Safari switch is avoided,
which we've seen to be the key element in avoiding app rejection.
We're looking forward to getting feedback from people using the new
SDK, and hope its use can replace the (ingenious and diligent)
workarounds people have implemented in the meantime.
THE METHOD BELLOW IS NO LONGER NEEDED
THIS METHOD HANDLES THE LOGIN INTERNAL WITH A CUSTOM UIWebView
THIS WORKS AND WAS APPROVED BY APPLE
My app got kicked from review cause of this
"The app opens a web page in mobile Safari for logging in to Google plus,
then returns the user to the app. The user should be able log in without opening
Safari first."
See this link https://code.google.com/p/google-plus-platform/issues/detail?id=900
I did solved it by following steps
1) create a subclass of UIApplication, which overrides openURL:
.h
#import <UIKit/UIKit.h>
#define ApplicationOpenGoogleAuthNotification #"ApplicationOpenGoogleAuthNotification"
#interface Application : UIApplication
#end
.m
#import "Application.h"
#implementation Application
- (BOOL)openURL:(NSURL*)url {
if ([[url absoluteString] hasPrefix:#"googlechrome-x-callback:"]) {
return NO;
} else if ([[url absoluteString] hasPrefix:#"https://accounts.google.com/o/oauth2/auth"]) {
[[NSNotificationCenter defaultCenter] postNotificationName:ApplicationOpenGoogleAuthNotification object:url];
return NO;
}
return [super openURL:url];
}
#end
this will basically prevent anything to be opened from Chrome on iOS
we catch the auth call and redirect it to our internal UIWebView
2) to info.plist, add the Principal class, and for it Application (or whatever you named the class)
Add plist key "NSPrincipalClass" and as the value the class of your main application (class which extends UIApplication, in this case Application (see code above))
3) catch the notification and open an internal webview
When your custom Application class sends ApplicationOpenGoogleAuthNotification, listen for it somewhere (in the AppDelegate maybe) and when you catch this notification, open a UIWebView (use the URL passed by the notification as the url for the webview) (in my case the LoginViewController listens for this notification and when received, it opens a view controller containing only a webview hooked up to delegate)
4) inside the webview
- (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {
if ([[[request URL] absoluteString] hasPrefix:#"com.XXX.XXX:/oauth2callback"]) {
[GPPURLHandler handleURL:url sourceApplication:#"com.google.chrome.ios"n annotation:nil];
// Looks like we did log in (onhand of the url), we are logged in, the Google APi handles the rest
[self.navigationController popViewControllerAnimated:YES];
return NO;
}
return YES;
}
or simmilar code, that handles the response
com.XXX.XXX:/oauth2callback from code above, replace with your company and app identifier, like "com.company.appname:/oauth2callback"
you might want to use #"com.apple.mobilesafari" as sourceApplication parameter
So, it depends what you want to do.
Sign-In: this will always call out to another application. If the Google+ application is installed it will call out to that, else it will fall back to Chrome and Safari.
Sharing/Interactive Posts: right now this always uses Chrome or Mobile Safari.
Retrieving friends, writing app activities, retrieving profile information: All this is done with the access token retrieved after sign in, so does not require leaving the application.
It is possible, though rather unsupported, to skip the SDK and pop up a UIWebView, construct the OAuth link dynamically and send the user to that (take a look at GTMOAuth2ViewControllerTouch in the open source libraries that ship with the SDK). Below is the a very rough example of the kind of thing you could do to plumb it back into the GPPSignIn instance.
However, you would be guaranteeing that the user has to enter their username and password (and maybe 2nd factor). With the Google+ app you're pretty much guaranteed to be already signed in, and with the Chrome/Safari route, there is a chance the user is already signed in (particularly if they're using other apps with Google+ Sign-In).
This also doesn't address sharing, so I would strongly recommend using the existing SDK as far as possible. Filing a feature request for the way you would prefer it to work would be a good thing to do as well: https://code.google.com/p/google-plus-platform/issues/list
#interface ViewController() {
GTMOAuth2ViewControllerTouch *controller;
}
#end;
#implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
GPPSignIn *signIn = [GPPSignIn sharedInstance];
signIn.clientID = #""; // YOUR CLIENT ID HERE.
signIn.delegate = self;
}
- (IBAction)didTapSignIn:(id)sender {
void (^handler)(id, id, id) =
^(GTMOAuth2ViewControllerTouch *viewController,
GTMOAuth2Authentication *auth,
NSError *error) {
[self dismissViewControllerAnimated:YES completion:^{
[controller release];
}];
if (error) {
NSLog(#"%#", error);
return;
} else {
BOOL signedIn = [[GPPSignIn sharedInstance] trySilentAuthentication];
if(!signedIn) {
NSLog(#"Sign In failed");
}
}
};
controller = [[GTMOAuth2ViewControllerTouch
controllerWithScope:kGTLAuthScopePlusLogin
clientID:[GPPSignIn sharedInstance].clientID
clientSecret:nil
keychainItemName:[GPPSignIn sharedInstance].keychainName
completionHandler:handler] retain];
[self presentViewController:controller animated:YES completion:nil];
}
- (void)finishedWithAuth:(GTMOAuth2Authentication *)auth
error:(NSError *)error {
if (!error) {
UIAlertView * al = [[UIAlertView alloc] initWithTitle:#"Authorised"
message:#"Authorised!"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[al show];
[al release];
}
}
The only real trick to this code is that it uses the [GPPSignIn sharedInstance].keychainName - this means that the auth tokens get stored in the same keychain entry as the GPPSignIn button would, which in turn means we can use [[GPPSignIn sharedInstance] trySilentAuthentication] once it has been populated, and keep the same callback based flow as the main library.
#PeterLapisu approach works good if the Google Plus App is not installed.
Then outgoing url prefixes from app are as follows:
#"googlechrome-x-callback:"
#"https://accounts.google.com/o/oauth2/auth"
However if the Google App is installed there is one more outgoing url and the prefix list looks as follows:
#"com.google.gppconsent.2.4.1:"
#"googlechrome-x-callback:"
#"https://accounts.google.com/o/oauth2/auth"
So, if the Google App is installed, it will be launched simultaneously with our app UIViewController that contains webview.Then if user sucessfully logs in with Google App he will be directed back to our app and the ViewController will be visible.
To prevent this Google App should be allowed to login user and direct him back to our app. According to this discussion: https://code.google.com/p/google-plus-platform/issues/detail?id=900 it is allowed by Apple.
So in my implementation firstly I am checking if the Google App is installed:
- (BOOL)openURL:(NSURL*)url {
NSURL *googlePlusURL = [[NSURL alloc] initWithString:#"gplus://plus.google.com/"];
BOOL hasGPPlusAppInstalled = [[UIApplication sharedApplication] canOpenURL:googlePlusURL];
if(!hasGPPlusAppInstalled)
{
if ([[url absoluteString] hasPrefix:#"googlechrome-x-callback:"]) {
return NO;
} else if ([[url absoluteString] hasPrefix:#"https://accounts.google.com/o/oauth2/auth"]) {
[[NSNotificationCenter defaultCenter] postNotificationName:ApplicationOpenGoogleAuthNotification object:url];
return NO;
}
}
return [super openURL:url];
}
EDIT:
Now I can confirm that my app was finally approved with this solution.
I hope it will help somebody.
It merges Google+ and Gmail samples and completely avoids using Google SignIn Button, i.e you do not leave the app.
Add both Google+ and Gmail API to you Google project, in your app login to google as you would to gmail using GTMOAuth2ViewControllerTouch.xib from OAuth2 and set scope to Google+:
-(IBAction)dologin{
NSString *scope = kGTLAuthScopePlusLogin;//Google+ scope
GTMOAuth2Authentication * auth = [GTMOAuth2ViewControllerTouch
authForGoogleFromKeychainForName:kKeychainItemName
clientID:kClientID
clientSecret:kClientSecret];
if ([auth refreshToken] == nil) {
GTMOAuth2ViewControllerTouch *authController;
authController = [[GTMOAuth2ViewControllerTouch alloc]
initWithScope:scope
clientID:kClientID
clientSecret:kClientSecret
keychainItemName:kKeychainItemName
delegate:self
finishedSelector:#selector(viewController:finishedWithAuth:error:)];
[[self navigationController] pushViewController:authController animated:YES];
}else{
[auth beginTokenFetchWithDelegate:self didFinishSelector:#selector(auth:finishedRefreshWithFetcher:error:)];
}
}
and RETAIN the authentication object if signed in successfully, then use that auth object when using google plus services:
GTLServicePlus* plusService = [[[GTLServicePlus alloc] init] autorelease];
[plusService setAuthorizer:self.auth];//!!!here use our authentication object!!!
No need for GPPSignIn.
Full write up is here: Here is Another Solution
Use the (new) Google Sign In iOS SDK.
The SDK natively supports Sign In through WebView when no Google app is present to complete the Sign In process. It also supports potential dispatch to several Google apps for this purpose.
I'm trying to use UIActivityViewController to share an image from my app. This is the source code:
UIImage *sourceImage = [[UIImage alloc] initWithContentsOfFile:[[self currentMedia] path]];
NSArray *activityItems = #[sourceImage];
UIActivityViewController *avc = [[UIActivityViewController alloc] initWithActivityItems:activityItems applicationActivities:nil];
avc.excludedActivityTypes = [NSArray arrayWithObjects:UIActivityTypePrint, UIActivityTypeCopyToPasteboard, UIActivityTypeAssignToContact, UIActivityTypeSaveToCameraRoll, nil];
[self presentViewController:avc animated:YES completion:nil];
[avc release];
[sourceImage release];
Everything works fine except for one issue: I don't see service names (Email, Twitter, Facebook etc) in the service picker. Look at the screenshot below:
Am I doing something wrong?
I am using ARC and don't have such problem. As far as UIActivityViewController is hardly rely on multithreading and blocks it is possible to affect some objects lifetime. So I would try to switch to ARC as recommends Apple or at least to make retained class property for UIActivityViewController.
If you pass nil for the activity types and you specify excluded types, nothing will show up.
However if you just pass nil for the first parameter, all the available built-in service types will show up.
I'm still having a bit of trouble getting this API to behave as I want it to, but that's one thing I did find.
You can use UIActivity class for this purpose. Activity objects are used in conjunction with a UIActivityViewController object, which is responsible for presenting services to the user.
look into activityTitle method of UIActivity class it returns string that describes the service.
Here is Example project.
Hope it helps you.
I am attempting to add facebook integration to my ios game built using Cocos2D. I initially just made the CCLayer object (subclass of NSObject) a FBRequestDelegate, FBDialogDelegate, and FBSessionDelegate. Then I created a facebook object with
fb_permissions = [[NSArray arrayWithObjects:
#"read_stream", #"publish_stream", #"offline_access",nil] retain];
facebook = [[Facebook alloc] initWithAppId:kAppId
andDelegate:self];
next I call
[facebook authorize:fb_permissions];
when the user pushes a button. It all works fine, goes to the facebook login page, correctly authorizes my application etc. Once it returns execution to my game, I expected the
- (void)fbDidLogin
method to be called, but it doesn't seem to be. I'm a little confused and just wondering if I've gone about this wrong? Should I implement my facebook sdk stuff in my root viewcontroller? ie. make my viewcontroller the FB delegate?
Is it that I'm missing a call to handleOpenURL? Which appears to be depracated? I'm having trouble locating decent documentation on this particular issue...
thanks!!
I think you will need to implement handleOpenURL in your App Delegate:
- (BOOL)application:(UIApplication *)application handleOpenURL:(NSURL *)url {
return [facebook handleOpenURL:url];
}
Edit: I see that execution returns to your game, so you may have already completed the following:
You will also need to edit your .plist file to handle the return from the authorization page. You will add an entry to MyApp-info.plist under
Information Property List->URL Types->Item 0->URL Schemes->Item 0 = "fbYOUR_APP_ID"
Follow the instructions at the end of Step 6 here: http://developers.facebook.com/docs/guides/mobile/#ios
I know that it was possible before the Graph API.
I work on an iPhone app that may not be in the background (one of the requirements).
In addition there is a login screen on the app launching.
Therefore it is not suitable to go to background in order to authenticate to Facebook and then return to the app and login once again each time the user wants to share something.
So, my question is if there is a way to authenticate with Facebook without leaving the app.
BTW, I have tried to use the old API.
It worked in the beginning but yesterday it stopped working.
I just see a blank screen inside the old Facebook login web view.
I have also checked one of my old apps that use that old Facebook Connect API and I get the same blank login screen in that app too.
Any idea will be appreciated.
Thank you in advance.
--
Michael.
In Facebook.m
- (void)authorizeWithFBAppAuth:(BOOL)tryFBAppAuth
safariAuth:(BOOL)trySafariAuth {
NSMutableDictionary* params = [NSMutableDictionary dictionaryWithObjectsAndKeys:
_appId, #"client_id",
#"user_agent", #"type",
kRedirectURL, #"redirect_uri",
#"touch", #"display",
kSDKVersion, #"sdk",
nil];
method comment out this
UIDevice *device = [UIDevice currentDevice];
if ([device respondsToSelector:#selector(isMultitaskingSupported)] && [device isMultitaskingSupported]) {
if (tryFBAppAuth) {
NSString *fbAppUrl = [FBRequest serializeURL:kFBAppAuthURL params:params];
didOpenOtherApp = [[UIApplication sharedApplication] openURL:[NSURL URLWithString:fbAppUrl]];
}
if (trySafariAuth && !didOpenOtherApp) {
NSString *nextUrl = [NSString stringWithFormat:#"fb%#://authorize", _appId];
[params setValue:nextUrl forKey:#"redirect_uri"];
NSString *fbAppUrl = [FBRequest serializeURL:loginDialogURL params:params];
didOpenOtherApp = [[UIApplication sharedApplication] openURL:[NSURL URLWithString:fbAppUrl]];
}
}
This will prevent the app from going to background and show you the standard fb dialog.
Here you have an alternative solution.
If you don't like to change the facebook sdk code and want a solution that allows you to choose between SSO or the old-fashioned mechanism, you can Implement an extension like this:
//Facebook_SSOExtension.h
--------------------------------------------------------
#interface Facebook(SSOExtension)
-(void) authorize:(NSArray*)permissions useSSO:(BOOL) useSSO;
#end
//Facebook_SSOExtension.m
--------------------------------------------------------
//So warnings do not appear
#interface Facebook(PrivateSSOExtension)
- (void)authorizeWithFBAppAuth:(BOOL)tryFBAppAuth
safariAuth:(BOOL)trySafariAuth;
-(void) setPermissions:(NSArray*) permissions;
#end
#implementation Facebook(SSOExtension)
-(void) authorize:(NSArray*)permissions useSSO:(BOOL) useSSO
{
[self setPermissions: permissions];
[self authorizeWithFBAppAuth:useSSO safariAuth:useSSO];
}
#end
Even though this requires more work than commenting-out the sso code, you will be able to update the facebook-sdk without major problems (if they rename authorizeWithFBAppAuth:safariAuth: your extension will not work, use asserts to detect this issue quickly). Also, if you are building a reusable component to interact with facebook without repeating things over and over again, this will save you some work too.
Cheers.
I am currently integrating facebook and twitter for iphone with gigya.
For Twitter sometimes its sharing and sometimes don't. Facebook also happening same as well.
lately facebook not even opening its login in screen. On device unlike simulator delegate methods like LoginDidFail, DidLogin called more than once.not sure why.
I am not storing any object to store provider info when login happens.
Can you please let me know why this inconsis
This seems like a multi part question.I would need more information to get a clearer understanding. Please provide code snippets if possible. Meanwhile, please see see my responses below:
Inconsistent Sharing
This might be something to do with how your userAction is being defined.
(http://wiki.gigya.com/030_API_reference/010_Client_API/010_Objects/UserAction_object)
Login Screen not loading
Typically this is down to social Network Applications not set up correctly.
(http://wiki.gigya.com/035_Socialize_Setup/005_Opening_External_Applications)
Event Delegate methods called repeatedly
This may be down do multiple instances of the GSAPI class.
Hope that helps.
I am using following code snippet
GSAPI *gsAPI // declared this in header file
gsAPI = [[GSAPI alloc] initWithAPIKey:XX viewController:self]; // i kept this in viewDidload
// add this code to have facebook and twitter on provider list. this was put in in one method which will be called when user tries to share
GSDictionary *pParams5 = [[GSDictionary new] autorelease]; [pParams5 putStringValue:#"facebook,twitter" forKey:#"enabledProviders"];
[gsAPI showAddConnectionsUI:pParams5 delegate:self context:nil];
//this method called when login fails -(void)gsLoginUIDidFail:(int)errorCode errorMessage:(NSString*)errorMessage context:(id)context {
}
// this method called on successful login
- (void) gsLoginUIDidLogin:(NSString*)provider user:(GSDictionary*)user context:(id)context {
GSDictionary *userAction = [[GSDictionary new] autorelease];
[userAction putStringValue:#"title" forKey:#"title"];
[userAction putStringValue:#"userMessage" forKey:#"userMessage"];
[userAction putStringValue:#"desc" forKey:#"description"];
[userAction putStringValue:#"imageurl" forKey:#"linkBack"];
GSDictionary *pParams5 = [[GSDictionary new] autorelease];
[pParams5 putGSDictionaryValue:userAction forKey:#"userAction"];
[gsAPI sendRequest:#"socialize.publishUserAction" params:pParams5 delegate:self context:nil];
}
-(void) gsDidReceiveResponse:(NSString*)method response:(GSResponse*)response context:(id)context {
//showing alert messages on successful sharing
//this method getting called more than twice on device
}