Google + iPhone API sign in and share without leav

2019-01-07 06:10发布

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

5条回答
我命由我不由天
2楼-- · 2019-01-07 06:25

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.

查看更多
等我变得足够好
3楼-- · 2019-01-07 06:26

@PeterLapisu approach works good if the Google Plus App is not installed. Then outgoing url prefixes from app are as follows:

However if the Google App is installed there is one more outgoing url and the prefix list looks as follows:

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.

查看更多
Anthone
4楼-- · 2019-01-07 06:33

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

查看更多
Root(大扎)
5楼-- · 2019-01-07 06:34

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
查看更多
\"骚年 ilove
6楼-- · 2019-01-07 06:37

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.

查看更多
登录 后发表回答