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 am new to location based applications.
My requirement is, device has to show the location based notifications when the user reaches the selected region.
I implemented perfectly. This app is working on background also.
Now my new requirement is, device has to show the location based notifications even after kill the app. [I saw a couple of iPhone apps working with this functionality. The apps are "Reminder" & "Locationizer" ] .
Can you please check my implementations steps as follows.
Launch the application.
Selected the location alert button.
Called the following CLLocationManager API's to monitor my region.
[locationManager startMonitoringForRegion:#"MyRegion" desiredAccuracy:kCLLocationAccuracyBest];
[locationManager startMonitoringSignificantLocationChanges];
Quit the app [Running in background].
I am getting location based alert notifications properly.
Here the new requirement.
Launch the application.
Selected the location alert Button.
Called the following CLLocationManager API's to monitor my region.
[locationManager startMonitoringForRegion:#"MyRegion" desiredAccuracy:kCLLocationAccuracyBest];
[locationManager startMonitoringSignificantLocationChanges];
Kill the app. [now application is not running in background]
App should show the location based notification when user reach the location.
How do I implement this logic?
Hi Friends I found solution for this issue.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
id locationValue = [launchOptions objectForKey:UIApplicationLaunchOptionsLocationKey];
if (locationValue)
{
[self initLocationMonitoring]; // this will invoke CLLocationManager
}
return YES;
}
An app that is quitted cant perform any functioning...so i will suggest to recheck those apps ..they must be functioning in the background to check the location of the device...they won't be able to alert if they are quitted.
I have an iOS application that uses GPS for Location Services. A user would like to use the app on an iPod Touch with an external GPS, but the problem is that the user can't enable Location Services in Settings->General Settings->Location Services for this app.
When the app is run on an iPhone, in Settings->General Settings->Location Services, the app shows up with a toggle switch to enable/disable Location Services for this app. But when the app is run on an iPod Touch, the app doesn't show up in Settings->General Settings->Location Services.
Does anyone know what I need to do programmatically to cause Location Services for this app to show up with a toggle switch in Settings->General Settings->Location Services when run on an iPod Touch?
I think that the ability comes when CLLocationManager is implemented for the first time. I'm not sure if that is what's used with an external GPS receiver but if you're getting the Lat/Long from the GPS, you can send those coordinates to the location manager.
I figured out what the problem was. I had the following code that only allowed Location Services for iPhone and not for iPod. Oops!
if([model hasPrefix:#"iPhone"]){
self.locationManager = [[CLLocationManager alloc] init];
self.locationManager.delegate = self;
self.locationManager.distanceFilter = 10.0f;
self.locationManager.desiredAccuracy = kCLLocationAccuracyBest;
[self.locationManager startUpdatingLocation];
self.locations = [NSMutableArray arrayWithCapacity:32];
}
I have tried the launch the settings of iPhone in my application using the call
CLLocationManager *locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
[locationManager startUpdatingLocation];
[locationManager release];
but I was able to launch only once,now I am not able to launch the settings in my application.
I am using iPhone 3GS with iOS 4.2
Is there any other way to launch settings any no of time if the location services are off.
but I was able to launch only once,now I am not
If you saw an alert kind of msg on your app asking for your permission for using yous gps location. Then this is the default behavior of iPhone OS. if you try to get gps location in any app the OS ask for user's permission. You can not launch this alert again!
but if you wants to change the setting (means wants to enable or disable the location tracking) then you can open the "Settings" application and look for location services setting. You can find your app there and disable the location tracking. But remember you can not open the "Settings" app using your app.
Now this issue is not in iOS5.
If you are calling startUpdatingLoaction method as below code and if location service is off,the system alert will popup and if you tap setting button,it will navigate to phone settings screen. And in phone setting screen you can switch on the location service.
CLLocationManager *locationManager = [[CLLocationManager alloc] init];
locationManager.delegate = self;
[locationManager startUpdatingLocation];
I am using a method to create a location manager if it doesn't exist and start it. It checks if the user has location services disabled and respects that unless a BOOL is given to start the services anyway. The method returns a BOOL to indicate services have been started or not.
This works fine for starting the service and prompting the user the first time. If they tap allow things proceed as normal and if they tap don't allow I present a view to input location manually.
However I would like the user to be able to change their mind about using location services so I have a button to allow them to go back to using GPS. It calls the same function again, but this time ignores the users location services preference. I believe this should prompt the user to allow/disallow again, but I am getting an immediate kCLDenied error code instead.
Based on http://developer.apple.com/iphone/library/documentation/UserExperience/Conceptual/LocationAwarenessPG/CoreLocation/CoreLocation.html#//apple_ref/doc/uid/TP40009497-CH2-SW11 I would expect another user prompt, not an immediate error.
- (BOOL)startLocationServices:(BOOL)ignorePrefs
{
//
// /*UNREVISEDCOMMENTS*/
//
if (locationManager == nil)
{
if ([CLLocationManager locationServicesEnabled] || ignorePrefs)
{
locationManager = [[CLLocationManager alloc] init];
[locationManager setDelegate:self];
[locationManager setDesiredAccuracy:kCLLocationAccuracyNearestTenMeters];
[locationManager setDistanceFilter:10.0];
}
else
{
return NO;
}
}
[locationManager startUpdatingLocation];
return YES;
}
(My location manager delegate releases and sets location manager to nil if a denied error is received.)
The user will not receive another prompt message. It just the way the iPhone handle things.
You also have to differentiate between:
User disable location services from the Settings application (this status is returned by the + (BOOL)locationServicesEnabled
method)
User declined usage of the location tracking for the current app (+ (CLAuthorizationStatus)authorizationStatus method returns the current status)
I would check both methods and in case show an alertview with a message, about deactivated location services.