Tuesday, March 29, 2016

Array of arrays - Multidimensional array

The following code has been tested with Xcode 7.3 playground:

let array : [[Int]] = [[1, 2, 3],[4, 5, 6]]

let element = array[0]

let digit1 = element[0]

let digit2 = array[0][1]

Result:


Monday, March 28, 2016

UIButton - Selector Warning (Update with Swift 3.1)

Update:
May 1, 2017:
Swift 3.1 with Xcode 8.3.2.

May 29, 2016:
The class name in the selector may be removed. Use #selector(buttonPressed) instead of #selector(ViewController.buttonPressed) for code simplicity.

On March 21, 2016, Apple released Xcode 7.3 with Swift 2.2. This new version of Swift is the first official release after the programming language became open source on December 3, 2015. The official Swift.org blog says that 212 non-Apple programmers have contributed to this release. Below is the first issue I face with Swift 2.2.

After I updated Xcode to Version 7.3, a warning appears with the UIButton code I normally use:

let button = UIButton()
.
.
button.addTarget(self, action: "buttonPressed", forControlEvents: UIControlEvents.TouchUpInside)

A warning is shown in front of the line number:


If I ignore the warning and continue to build the code, the UIButton still works perfectly.

By clicking the warning triangle, the warning description says that Use of string literal for Objective-C selectors is deprecated; use '#selector' instead.


Select the Fix-it Replace option.



Then the warning is disappeared as below:

button.addTarget(self, action: #selector(ViewController.buttonPressed), forControlEvents: UIControlEvents.TouchUpInside)


Update May 1, 2017 (Swift 3.1):

import UIKit

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let button = UIButton(frame: CGRect(x: 0, y: 0, width: 200, height: 40))
        button.center = view.center
        button.setTitle("Press", for: UIControlState.normal)
        button.setTitleColor(UIColor.blue, for: UIControlState.normal)
        button.setTitleColor(UIColor.cyan, for: UIControlState.highlighted)
        button.addTarget(self, action: #selector(buttonPressed), for: UIControlEvents.touchUpInside)
        view.addSubview(button)
    }
    
    func buttonPressed() {
        print("button pressed!!")
    }

}

Update May 29, 2016 (Swift 2.2):

Remove the class name in the selector: #selector(ViewController.buttonPressed)and the complete UIButton code now becomes:


override func viewDidLoad() {
    super.viewDidLoad()
        
    let button = UIButton(frame: CGRectMake(0, 0, 200, 200))
    button.center = view.center
    button.setTitle("Press", forState: UIControlState.Normal)
    button.setTitleColor(UIColor.blueColor(), forState: UIControlState.Normal)
    button.setTitleColor(UIColor.cyanColor(), forState: UIControlState.Highlighted)
    button.addTarget(self, action: #selector(buttonPressed), forControlEvents: UIControlEvents.TouchUpInside)
    view.addSubview(button)
}
    
func buttonPressed() {
    print("button pressed!!")
}

Here is more explanation about the modification of Objective-C selectors in Swift 2.2:

Referencing the Objective-C selector of a method

More Information about modifications in Swift 2.2:

Swift 2.2 Released!(Official Swift.org blog)

Reference:

UIButton - Update button label when pressed

Wednesday, March 16, 2016

Google Sign-In for iOS - Display User Email and Profile Picture

Update April 27, 2016:
Step 3 - ViewController.swift is modified with Xcode 7.3 (Swift 2.2).

This post shows how to display the user email and profile picture with Google Sign-In for iOS.

Procedure

1. Create a basic sign-in button with instructions in this tutorial:

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

2. Modify AppDelegate.swift:

//Modify signIn function with didSignInForUser:

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

                let name = user.profile.name
                let email = user.profile.email
                var imageURL = ""
                if user.profile.hasImage {
                    imageURL = user.profile.imageURLWithDimension(100).absoluteString
                }
                NSNotificationCenter.defaultCenter().postNotificationName(
                    "ToggleAuthUINotification",
                    object: nil,
                    userInfo: ["statusText": "Signed in user:\n\(name)""email" : email, "imageURL" : imageURL])

            }
...

//Modify signIn function with didDisconnectWithUser:

    func signIn(signIn: GIDSignIn!, didDisconnectWithUser user:GIDGoogleUser!,
        withError error: NSError!) {
            NSNotificationCenter.defaultCenter().postNotificationName(
                "ToggleAuthUINotification",
                object: nil,
                userInfo: ["statusText": "User has disconnected.", "email" : ""])

    }

3. Modify ViewController.swift:


Update April 27, 2016:

import UIKit

class ViewController: UIViewController, GIDSignInUIDelegate {

    var btnSignIn : GIDSignInButton!
    var btnSignOut : UIButton!
    var btnDisconnect : UIButton!
    var label : UILabel!
    
    var imageView : UIImageView!
    
    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: #selector(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: #selector(btnDisconnectPressed), forControlEvents: UIControlEvents.TouchUpInside)
        view.addSubview(btnDisconnect)
        
        label = UILabel(frame: CGRectMake(0,0,300,200))
        label.center = CGPointMake(view.center.x, 430)
        label.numberOfLines = 0
        label.text = "Please Sign in."
        label.textAlignment = NSTextAlignment.Center
        view.addSubview(label)
        
        imageView = UIImageView(frame: CGRectMake(0, 0, 100, 100))
        imageView.center = view.center
        view.addSubview(imageView)
        
        NSNotificationCenter.defaultCenter().addObserver(self,
            selector: #selector(receiveToggleAuthUINotification(_:)),
            name: "ToggleAuthUINotification",
            object: nil)
        
        toggleAuthUI()
    }
    
    func btnSignOutPressed() {
        print(GIDSignIn.sharedInstance().currentUser.profile.email)
        print(GIDSignIn.sharedInstance().currentUser.profile.name)
        
        GIDSignIn.sharedInstance().disconnect()
        label.text = "Disconnecting."
    }
    
    func btnDisconnectPressed() {
        label.text = "Signed out."
        toggleAuthUI()
    }
    
    func toggleAuthUI() {
        print("toggleAuthUI")
        if (GIDSignIn.sharedInstance().hasAuthInKeychain()){

            // Signed in
            btnSignIn.hidden = true
            btnSignOut.hidden = false
            btnDisconnect.hidden = false
            
            //NEW!! The code below is required if the app is restarted and already signed in previously.
            if (GIDSignIn.sharedInstance().currentUser == nil) {
                print("no user info")
                GIDSignIn.sharedInstance().signInSilently()
            }
        } 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"]!+"\n\(userInfo["email"]!)"
                if userInfo["imageURL"] == nil {
                    self.imageView.image = nil
                } else {
                    let url = NSURL(string: userInfo["imageURL"]!)!
                    self.imageView.image = UIImage(data: NSData(contentsOfURL: url)!)
                }
            }
        }
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

}

=====
Original post March 16, 2016:

//Modified blue texts:


    var imageView : UIImageView!
    
    override func viewDidLoad() {

        ...

        label = UILabel(frame: CGRectMake(0,0,300,200))
        label.center = CGPointMake(view.center.x, 430)
        label.numberOfLines = 0
        label.text = "Please Sign in."
        label.textAlignment = NSTextAlignment.Center
        view.addSubview(label)
        
        imageView = UIImageView(frame: CGRectMake(0, 0, 100, 100))
        imageView.center = view.center
        view.addSubview(imageView)

        ...

//And modify this:


    @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"]!+"\n\(userInfo["email"]!)"
                if userInfo["imageURL"] == nil {
                    self.imageView.image = nil
                } else {
                    let url = NSURL(string: userInfo["imageURL"]!)!
                    self.imageView.image = UIImage(data: NSData(contentsOfURL: url)!)
                }
            }
        }

    }
=====

Result

Related Information:

Google Sign-In for iOS - Create a GIDSignInButton programmatically in Swift
Google Sign-In for iOS - Get User Name, Email and Profile Picture without Nofitication
Google Sign-In for iOS - Create a custom sign-in button programmatically
Facebook SDK and Swift - Display User Name and Profile Picture

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