3

I followed the instructions on https://developers.google.com/identity/sign-in/ios/sdk/ to integrate the google login in my swift project. As you might know the login calls the function below when successful. It gets called but i cannot figure out how i get only the first name and the last name of the field user.profile.name . I only get the full name, but for my purposes i need the name separated :-(

func signIn(signIn: GIDSignIn, didSignInForUser user: GIDGoogleUser, withError error: NSError) {
// Perform any operations on signed in user here.
var userId: String = user.userID
// For client-side use only!
var idToken: String = user.authentication.idToken
// Safe to send to the server
var name: String = user.profile.name
var email: String = user.profile.email
}

Can somebody please explain to me how i get the information??

Phil

Philip
  • 171
  • 1
  • 3
  • 11
  • You have a string like "firstName lastName". Read and do this for the variable 'name'. http://stackoverflow.com/questions/25678373/swift-split-a-string-into-an-array – Onur Tuna Jan 14 '16 at 22:05
  • 1
    thanks for the fast answer! I think this solution will make problems if the user has a middle name or multiple last names (e.g. John Michael van Doe). What do you think? I have to set up such an account quickly to test this.... – Philip Jan 14 '16 at 22:10

4 Answers4

10

Ok... so after a few hours of digging around in the Google references i found something interesting for Google APIs and OAuth https://developers.google.com/identity/protocols/OAuth2 We can get additional information by querying https://www.googleapis.com/oauth2/v3/userinfo?access_token= (thanks to this question What are the end points to get the emailId using oauth for the google, yahoo, twitter service providers?) i figured out that i can get a lot of information with the Authentication Token if the user has successfully logged in... i don't think the solutions with google+ above should be used, because they use old SDKs https://developers.google.com/+/mobile/ios/upgrading-sdk (you need these to use e.g. GTLServicePlus ) It would be safer (for future purposes) to use the solution with OAuth and the latest google SDK ->https://developers.google.com/identity/sign-in/ios/start

But thanks for your answers :-)

for anybody with the same problem this code below should work ->

cheers Phil

EDIT: Thanks jcaron! indeed that would be better asynchronous had forgotten to do this -> updated the solution

func signIn(signIn: GIDSignIn!, didSignInForUser user: GIDGoogleUser!, withError error: NSError!) {
    if (error == nil) {
        var firstName = ""
        var lastName = ""

        UIApplication.sharedApplication().networkActivityIndicatorVisible = true
        let url = NSURL(string:  "https://www.googleapis.com/oauth2/v3/userinfo?access_token=\(user.authentication.accessToken)")
        let session = NSURLSession.sharedSession()
        session.dataTaskWithURL(url!) {(data, response, error) -> Void in
            UIApplication.sharedApplication().networkActivityIndicatorVisible = false
            do {
                let userData = try NSJSONSerialization.JSONObjectWithData(data!, options:[]) as? [String:AnyObject]
                /*
                Get the account information you want here from the dictionary
                Possible values are
                "id": "...",
                "email": "...",
                "verified_email": ...,
                "name": "...",
                "given_name": "...",
                "family_name": "...",
                "link": "https://plus.google.com/...",
                "picture": "https://lh5.googleuserco...",
                "gender": "...",
                "locale": "..."

                so in my case:
                */
                firstName = userData!["given_name"] as! String
                lastName = userData!["family_name"] as! String
                print(firstName)
                print(lastName)
            } catch {
                NSLog("Account Information could not be loaded")
            }
        }.resume()
    }
    else {
        //Login Failed
    }
}
Community
  • 1
  • 1
Philip
  • 171
  • 1
  • 3
  • 11
  • 1
    Good find! However, you most certainly don't want to use `NSString(contentsOfURL:useEncoding:)` as this is a sync call. Either offload that to a background thread, or use an `NSURLSession dataTask*` with a completion block. – jcaron Jan 15 '16 at 17:46
1

Here's something similar to what we have in production:

private func finishAuthorizationForUser(signInUser: GIDGoogleUser) {

    let servicePlus = GTLServicePlus()
    servicePlus.authorizer = signInUser.authentication.fetcherAuthorizer()
    servicePlus.fetchObjectWithURL(NSURL(string: "https://www.googleapis.com/plus/v1/people/me")!, completionHandler: { (ticket, object, error) -> Void in

        guard error == nil else {
            self.showUnsuccessfulLoginAlertWithMessage(error.description)
            return
        }

        guard let user = object as? GTLObject else {
            self.showUnsuccessfulLoginAlertWithMessage("Bad user")
            return
        }

        let userJson = NSDictionary(dictionary: user.JSON) as! [String: AnyObject]

        if let names = userJson["name"] as? [String: String] {
            let lastName = names["familyName"]
            let firstName = names["givenName"]
            //...do something with these names
        }
    })

}
noobular
  • 3,257
  • 2
  • 24
  • 19
0

You know the last string will be the last name every time :) Until the last name in the array consider as name or names. Consider the last string as last name. You are going to have an array of two string at the end.

Onur Tuna
  • 1,030
  • 1
  • 10
  • 28
  • 2
    You can't base detection on the 'space' character. Some first name or last name are composed of several parts. If you select only the last one, you might end up with only a part of the last name, and an incorrect first name. – rockeye Jan 15 '16 at 14:17
  • My solution is costly but works. First one can convert any string to a string array based on white spaces. Then the last element of a string would be the last name. – Onur Tuna Jan 15 '16 at 15:16
  • 1
    Tough luck for Julio José Iglesias de la Cueva. – Terry Torres Apr 26 '21 at 18:36
0

You'll have to make an additional query to Google+ to get the user's details. This requires the GooglePlus and GoogleOpenSource frameworks to be linked in.

Here's the Objective-C code for this, translation to Swift is left as an exercice for the reader:

#import <Google/SignIn.h>
#import <GooglePlus/GooglePlus.h>
#import <GoogleOpenSource/GoogleOpenSource.h>

...

- (void)signIn:(GIDSignIn *)signIn
didSignInForUser:(GIDGoogleUser *)user
     withError:(NSError *)error
{
    if (error)
    {
        NSLog(@"Google sign-in error: %@", error);
        // do any cleanup like re-enabling buttons here
        return;
    }
    if (!user)
    {
        NSLog(@"Google sign-in returned nil user");
        // do any cleanup like re-enabling buttons here
        return;
    }

    NSLog(@"Google sign-in successfull: %@, userId: %@ token: %@ name: %@ email: %@",
          user,
          user.userID,
          user.authentication.idToken,
          user.profile.name,
          user.profile.email);

    NSString *userId = user.userID;               // For client-side use only!
    NSString *idToken = user.authentication.idToken; // Safe to send to the server
    NSString *name = user.profile.name;
    NSString *email = user.profile.email;
    __block NSString *firstName = @"";
    __block NSString *lastName = @"";
    __block NSString *title = @"";

    NSArray *nameComponents = [name componentsSeparatedByString:@" "];
    if (nameComponents.count == 2)
    {
        firstName = nameComponents[0];
        lastName = nameComponents[1];
    }

    GTMOAuth2Authentication *auth = [[GTMOAuth2Authentication alloc] init];
    [auth setClientID:signIn.clientID];
    [auth setUserEmail:user.profile.email];
    [auth setUserID:user.userID];
    [auth setAccessToken:user.authentication.accessToken];
    [auth setRefreshToken:user.authentication.refreshToken];
    [auth setExpirationDate: user.authentication.accessTokenExpirationDate];

    GTLServicePlus* plusService = [[GTLServicePlus alloc] init];
    plusService.retryEnabled = YES;
    plusService.authorizer = auth;
    GTLQueryPlus *query = [GTLQueryPlus queryForPeopleGetWithUserId:@"me"];

    [plusService executeQuery:query
            completionHandler:^(GTLServiceTicket *ticket,
                                GTLPlusPerson *person,
                                NSError *error)
     {
         if (error)
         {
             GTMLoggerError(@"Error: %@", error);
         }
         else
         {
             NSLog(@"me: %@, display name: %@, about me: %@, name: %@ / %@ gender: %@",
                   person, person.displayName, person.aboutMe, person.name.givenName, person.name.familyName, person.gender);
             firstName = person.name.givenName;
             lastName = person.name.familyName;
         }
         // do whatever you need with the data here
     }];
}
jcaron
  • 17,302
  • 6
  • 32
  • 46
  • but to use this solution and the one below i have to use the old frameworks for google+ i can find on https://developers.google.com/+/mobile/ios/upgrading-sdk i cannot use GTMOAuth2Authentication without importing these "deprecated" frameworks -> but with these i could not compile... or do i get something wrong here? – Philip Jan 15 '16 at 15:40
  • What error are you getting when you try to build the project with those frameworks? – jcaron Jan 15 '16 at 15:51
  • 1
    thanks for getting back jcaron! the two deprecated frameworks GooglePlus.framework and GoogleOpenSource.framework wouldn't work in combination with the latest google sdk (2.4.0) that is the problem (a lot of funny errors about ObjC linking etc...) i figured out another solution using OAuth -> check it out at the end of this post cheers phil – Philip Jan 15 '16 at 17:31