Tuesday, March 15, 2016

Google Sign-In for iOS - Create a GIDSignInButton programmatically in Swift

Update: 

May 27, 2016 - Update Step 4 for CocoaPods 1.0.

This tutorial explains how to create a Google Sign-In button without storyboard using Google Sign-In for iOS. The code below was developed in Swift 2 with Xcode 7.2.1.

More information: Google Sign-In iOS SDK Release Notes

Procedure

1. Install CocoaPods. (More information about CocoaPods is written at the bottom of this post.)

Type this command in the terminal:

sudo gem install cocoapods

2. Create an Xcode project and close it.

3. Go to the project directory in the terminal and type:

pod init

4. In the Podfile just created, edit it as below:

pod 'Google/SignIn'

(You may open the Podfile with this command: open -a Xcode Podfile)

Update May 27, 2016:

The above code may not work with the new CocoaPods 1.0. If you see error like this:


The dependency `GoogleMaps` is not used in any concrete target.

Try modify your Podfile as below:

target "Project_name" do
    pod 'Google/SignIn'


end

5. Run the terminal and type the command below in the project directory:

pod install

You may see something like this in the terminal:



5. Open the projectName.xcworkspace file just automatically created. (Don't open the original .xcodeproj file)




You may see the project navigator like this with a new Pods folder:



6. We are going to download a configuration file. Just follow the instructions in the Google Sign-In for iOS page.

Click the GET A CONFIGURATION FILE Button.



Enter the App name and iOS Bundle ID. Click CONTINUE TO Choose and configure services.



Click ENABLE GOOGLE SIGN-IN.




Click CONTINUE TO Generate configuration files.




Download the GoogleService-Info.plist file and drag it to the Xcode project.




Click Continue Adding Sign-In. The browser will be redirected to Integrating Google Sign-In into your iOS app.



Select Swift.
Don't select Download the dependencies and configure your Xcode project.




7. Open the GoogleService-Info.plist  file in the Xcode project. Copy the value of REVERSED_CLIENT_ID.


Select the projectName as Targets. Select Info -> URL Types -> + sign.




Paste the value you just copied into URL Schemes. Give the Identifier any name you like, such as REVERSED_CLIENT_ID.

Select the + sign again and paste the bundle identifier in URL Schemes.




8. Add a temporary Objective-C file to your project. You may give it any name you like.



Select Create Bridging Header to configure an Objective-C bridging header.


Delete the temporary Objective-C file you just created.

8. In the projectName-Bridging-Header.h file just created, add this line:


#import <Google/SignIn.h>


9. Replace the AppDelegate.swift file with this file from GitHub. Put comment characters with these lines to remove warnings: 


//let userId = user.userID                  // For client-side use only!
//let idToken = user.authentication.idToken // Safe to send to the server
let name = user.profile.name

//let email = user.profile.email

10. Edit ViewController.swift as below:

import UIKit

class ViewController: UIViewController, GIDSignInUIDelegate {
    
    var btnSignIn : GIDSignInButton!
    var btnSignOut : UIButton!
    var btnDisconnect : UIButton!
    var label : UILabel!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        GIDSignIn.sharedInstance().uiDelegate = self
        
        btnSignIn = GIDSignInButton(frame: CGRectMake(0,0,230,48))
        btnSignIn.center = view.center
        btnSignIn.style = GIDSignInButtonStyle.Standard
        view.addSubview(btnSignIn)
        
        btnSignOut = UIButton(frame: CGRectMake(0,0,100,30))
        btnSignOut.center = CGPointMake(view.center.x, 100)
        btnSignOut.setTitle("Sign Out", forState: UIControlState.Normal)
        btnSignOut.setTitleColor(UIColor.blueColor(), forState: UIControlState.Normal)
        btnSignOut.setTitleColor(UIColor.cyanColor(), forState: UIControlState.Highlighted)
        btnSignOut.addTarget(self, action: "btnSignOutPressed:", forControlEvents: UIControlEvents.TouchUpInside)
        view.addSubview(btnSignOut)
        
        btnDisconnect = UIButton(frame: CGRectMake(0,0,100,30))
        btnDisconnect.center = CGPointMake(view.center.x, 200)
        btnDisconnect.setTitle("Disconnect", forState: UIControlState.Normal)
        btnDisconnect.setTitleColor(UIColor.blueColor(), forState: UIControlState.Normal)
        btnDisconnect.setTitleColor(UIColor.cyanColor(), forState: UIControlState.Highlighted)
        btnDisconnect.addTarget(self, action: "btnDisconnectPressed:", forControlEvents: UIControlEvents.TouchUpInside)
        view.addSubview(btnDisconnect)
        
        
        label = UILabel(frame: CGRectMake(0,0,200,100))
        label.center = CGPointMake(view.center.x, 400)
        label.numberOfLines = 0 //Multi-lines
        label.text = "Please Sign in."
        label.textAlignment = NSTextAlignment.Center
        view.addSubview(label)
        
        NSNotificationCenter.defaultCenter().addObserver(self,
            selector: "receiveToggleAuthUINotification:",
            name: "ToggleAuthUINotification",
            object: nil)
        
        toggleAuthUI()
    }
    
    func btnSignOutPressed(sender: UIButton) {
        GIDSignIn.sharedInstance().disconnect()
        label.text = "Disconnecting."
    }
    
    func btnDisconnectPressed(sender: UIButton) {
        label.text = "Signed out."
        toggleAuthUI()
    }
    
    func toggleAuthUI() {
        if (GIDSignIn.sharedInstance().hasAuthInKeychain()){
            // Signed in
            btnSignIn.hidden = true
            btnSignOut.hidden = false
            btnDisconnect.hidden = false
        } else {
            btnSignIn.hidden = false
            btnSignOut.hidden = true
            btnDisconnect.hidden = true
        }
    }
    
    deinit {
        NSNotificationCenter.defaultCenter().removeObserver(self,
            name: "ToggleAuthUINotification",
            object: nil)
    }
    
    @objc func receiveToggleAuthUINotification(notification: NSNotification) {
        if (notification.name == "ToggleAuthUINotification") {
            self.toggleAuthUI()
            if notification.userInfo != nil {
                let userInfo:Dictionary<String,String!> =
                notification.userInfo as! Dictionary<String,String!>
                self.label.text = userInfo["statusText"]
            }
        }
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

}

The above code is derived from Google's ViewController.swift example.

Various Sign-In Button Sizes (See GIDSignInButton Class):

GIDSignInButtonStyle.Standard => 230x48
GIDSignInButtonStyle.Wide     => 312x48
GIDSignInButtonStyle.IconOnly =>  48x48

11. If you build the code now, you'll see this error:

clang: error: linker command failed with exit code 1 (use -v to see invocation)



Try to disable the bitcode:

Targets -> Build Settings -> Build Options -> Enable Bitcode (Search: bitcode) -> No


12. Build and run the code. It should work.

Result


Press the Sign in button.

Enter the ID (Gmail address) and password.



Press Allow.

The username is displayed.



=============

Additional Information

Some CocoaPods commands for reference:

1. Check the CocoaPods version installed:
pod --version

2. Check if CocoaPods framework of a project is outdated:

Go to the directory including the Podfile. Type:

pod outdated



The pod outdated command also checks if new CocoaPods version is available.

3. Update CocoaPods using the install command:

sudo gem install cocoapods

4. Update CocoaPods to a pre-release version:

sudo gem install cocoapods --pre

5. Update local repositories:

pod update


2 comments: