In questa lezione illustreremo i passi per aggiungere il social login di Google tramite il servizio Firebase Authentication a partire da un’applicazione iOS appena creata, come illustrato nella precedente lezione.
Configurazione del PodFile
Per iniziare l’integrazione, il primo passo da compiere è l’aggiunta della SDK di Facebook attraverso la modifica del Podfile inizializzato in precedenza.
Aprire il Podfile con un qualsiasi editor di testo e aggiungere le SDK di Facebook nell’apposita sezione come segue:
target 'SocialLoginHMTLit' do
. . .
pod 'GoogleSignIn’
end
Una volta salvato e chiuso il file, da terminale eseguiamo il comando:
pod install
Esso effettua l’installazione del pod nel progetto e crea il nuovo workspace da usare per lo sviluppo. In caso di corretta installazione, il terminale mostrerà un output simile al seguente.
Si ricorda che nella lezione 19 sono stati installati i pod Core
e Auth
di Firebase, altrettanto necessari per i nostri scopi.
Modifica dell’URL Schemes
Per permettere all’applicazione di utilizzare il meccanismo di autenticazione di Google, devono essere aggiunti due URL Scheme al progetto:
- il reversed client ID, presente nel GoogleService-Info.plist;
- il bundle ID.
Nel tab Info delle configurazioni del progetto, espandiamo la sezione URL Types e clicchiamo sul pulsante + riportando nella textbox dell’URL Schemes il reversed client ID (reperibile nel GoogleService-Info.plist). Ripetiamo la medesima operazione riportando questa volta il bundle ID e otterremo il seguente risultato.
Modifica dell’AppDelegate
Per inizializzare l’SDK di Google all’avvio dell’applicazione, importiamo nell’AppDelegate la classe GoogleSignIn
Swift
import GoogleSignIn
Objective-C
//Nel file .h
@import GoogleSignIn;
La classe GoogleSignIn
offre il single sign-on tramite un’applicazione di Google già abilitata o in assenza attraverso il browser.
Successivamente, va implementata la classe GIDSignInDelegate
che definisce un protocollo utilizzato dal delegato del GIDSignIn
. Al termine della comunicazione verrà restituito all’applicazione un nuovo token o, in caso di fallimento, un errore.
Swift
class AppDelegate: UIResponder, UIApplicationDelegate, GIDSignInDelegate {
…
}
Objective-C
//Aggiungere nel file .h
@interface AppDelegate : UIResponder <UIApplicationDelegate, GIDSignInDelegate>
Inizializziamo il GIDSignIn
impostando il client ID e il delegate nel metodo application:didFinishLaunchingWithOptions
:
Swift
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
. . .
GIDSignIn.sharedInstance().clientID = FirebaseApp.app()?.options.clientID
GIDSignIn.sharedInstance().delegate = self
return true
}
Objective-C
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
. . .
[GIDSignIn sharedInstance].clientID = [FIRApp defaultApp].options.clientID;
[GIDSignIn sharedInstance].delegate = self;
return YES;
}
Implementiamo ora il metodo application:openURL:options
.
Swift
func application(application: UIApplication,
openURL url: NSURL, sourceApplication: String?, annotation: AnyObject?) -> Bool {
var options: [String: AnyObject] = [UIApplicationOpenURLOptionsSourceApplicationKey: sourceApplication,
UIApplicationOpenURLOptionsAnnotationKey: annotation]
return GIDSignIn.sharedInstance().handleURL(url,
sourceApplication: sourceApplication,
annotation: annotation)
}
Objective-C
- (BOOL)application:(UIApplication *)app
openURL:(NSURL *)url
options:(NSDictionary<NSString *, id> *)options {
return [[GIDSignIn sharedInstance] handleURL:url
sourceApplication:options[UIApplicationOpenURLOptionsSourceApplicationKey]
annotation:options[UIApplicationOpenURLOptionsAnnotationKey]];
}
Come è possibile notare, è stato invocato il metodo handleURL
dell’istanza GIDSignIn
per gestire correttamente l’URL inviata all’applicazione al termine del processo di autenticazione.
Infine, non resta che definire una possibile implementazione del metodo di login.
Swift
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!,
withError error: Error!) {
if let error = error {
print("\(error.localizedDescription)")
NotificationCenter.default.post(
name: Notification.Name(rawValue: "ToggleAuthUINotification"), object: nil, userInfo: nil)
}
guard let authentication = user.authentication else { return }
let credential = GoogleAuthProvider.credential(withIDToken: authentication.idToken,
accessToken: authentication.accessToken)
Auth.auth().signIn(with: credential) { (user, error) in
if (error) != nil {
print("Google Authentification Fail")
} else {
print("Google Authentification Success")
if let displayName = user?.displayName{
print(displayName)
NotificationCenter.default.post(
name: Notification.Name(rawValue: "ToggleAuthUINotification"),
object: nil,
userInfo: ["statusText": "Hi \(displayName)!"])
}
}
}
}
Objective-C
- (void)signIn:(GIDSignIn *)signIn
didSignInForUser:(GIDGoogleUser *)user
withError:(NSError *)error {
if (error == nil) {
GIDAuthentication *authentication = user.authentication;
FIRAuthCredential *credential =
[FIRGoogleAuthProvider credentialWithIDToken:authentication.idToken
accessToken:authentication.accessToken];
NSString *fullName = user.profile.name;
NSDictionary *statusText = @{@"statusText":
[NSString stringWithFormat:@"Hi %@", fullName]};
[[NSNotificationCenter defaultCenter] postNotificationName:@"ToggleAuthUINotification" object:nil userInfo:statusText];
}
}
Ricapitolando:
- è stata effettuata l’autenticazione tramite Google;
- sono state create le credenziali di accesso tramite il metodo
credential()
della classeGoogleAuthProvider
; - le credenziali così ottenute sono state utilizzate per autenticare l’utente tramite il metodo
signIn
della classeAuth
(responsabile dell’autenticazione delle app basate su Firebase); - è stato estratto il
displayName
dell’utente corrente per mostrarlo in unaUILabel
tramite ilNotificationCenter
e impostando ilpostNotificationName
aToggleAuthUINotification
e il campouserInfo
astatusText
per la visualizzazione del nome utente.
Aggiunta del pulsante di login
Aggiungiamo, infine, il pulsante di autenticazione nella schermata principale.
Nella Main.storyboard selezioniamo l'oggetto View
dall’Object Library e posizioniamolo al centro della schermata. Impostiamo, poi, la classe della View
a GIDSignInButton
.
Avendo creato il pulsante come un oggetto View, questo non verrà visualizzato all’interno della schermata.
Colleghiamo poi il bottone di login con il codice del ViewController per creare il seguente IBOutlet
:
Swift
@IBOutlet weak var signInButton: GIDSignInButton!
Objective-C
@property(weak, nonatomic) IBOutlet GIDSignInButton *signInButton;
Nel corpo del metodo viewDidLoad
, impostiamo il UiDelegate
e il NotificationCenter
come segue
Swift
override func viewDidLoad() {
. . .
GIDSignIn.sharedInstance().uiDelegate = self
GIDSignIn.sharedInstance().signIn()
NotificationCenter.default.addObserver(self,
selector: #selector(ViewController.receiveToggleAuthUINotification(_:)),
name: NSNotification.Name(rawValue: "ToggleAuthUINotification"),
object: nil)
. . .
}
Objective-C
- (void)viewDidLoad {
. . .
[GIDSignIn sharedInstance].uiDelegate = self;
[[GIDSignIn sharedInstance] signIn];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:@selector(receiveToggleAuthUINotification:)
name:@"ToggleAuthUINotification"
object:nil];
. . .
}
In questo modo, abbiamo aggiunto un Observer
alla schermata corrente in modo da estrarre la userInfo
impostata nell’AppDelegate attraverso il metodo receiveToggleAuthUINotification
impostato come selector e implementato come segue:
Swift
@objc func receiveToggleAuthUINotification(_ notification: NSNotification) {
if notification.name.rawValue == "ToggleAuthUINotification" {
. . .
if notification.userInfo != nil {
guard let userInfo = notification.userInfo as? [String:String] else { return }
self.statusText.text = userInfo["statusText"]!
}
}
}
Objective-C
- (void) receiveToggleAuthUINotification:(NSNotification *) notification {
if ([notification.name isEqualToString:@"ToggleAuthUINotification"]) {
. . .
self.statusText.text = notification.userInfo[@"statusText"];
}
}
Aggiungiamo ora il pulsante di logout.
Nella Main.storyboard, selezioniamo l'oggetto UIButton dall’Object Library e posizioniamolo al centro della schermata. Successivamente, impostiamo il testo del pulsante a Sign Out e lo sfondo a grigio attraverso la proprietà background dell’Attribute Inspector.
Colleghiamo poi il bottone con il codice del ViewController per creare un @IBOutlet
di tipo UIButton
e aggiungiamo un nuovo metodo per il logout da Google e Firebase come segue.
Swift
@IBAction func didTapSignOut(_ sender: AnyObject) {
GIDSignIn.sharedInstance().signOut()
let firebaseAuth = Auth.auth()
do {
try firebaseAuth.signOut()
statusText.text = "Signed out"
toggleAuthUI()
} catch let signOutError as NSError {
print ("Error signing out: %@", signOutError)
}
}
Objective-C
- (IBAction)didTapSignOut:(id)sender {
[[GIDSignIn sharedInstance] signOut];
NSError *signOutError;
BOOL status = [[FIRAuth auth] signOut:&signOutError];
if (!status) {
NSLog(@"Error signing out: %@", signOutError);
return;
}
[self toggleAuthUI];
}
In questo modo, l’utente verrà disconnesso sia da Google che da Firebase e in caso di fallimento verrà mostrato un messaggio di errore.
Colleghiamo infine il bottone di logout al metodo didTapSignOut
.
Inseriamo ora una label di tipo UILabel
per mostrare il nome dell’utente loggato posizionandola sopra il bottone di logout e collegandola al codice, come fatto in precedenza.
Gestiamo infine, la visibilità dei bottoni creati e del testo della label attraverso il seguente metodo.
Swift
func toggleAuthUI() {
if GIDSignIn.sharedInstance().hasAuthInKeychain() {
signInButton.isHidden = true
signOutButton.isHidden = false
} else {
signInButton.isHidden = false
signOutButton.isHidden = true
statusText.text = "Google Sign in"
}
}
Objective-C
- (void)toggleAuthUI {
if ([GIDSignIn sharedInstance].currentUser.authentication != nil) {
self.signInButton.hidden = YES;
self.signOutButton.hidden = NO;
} else {
self.signInButton.hidden = NO;
self.signOutButton.hidden = YES;
self.statusText.text = @"Google Sign in";
}
}
Questa funzione nasconderà il pulsante di login in caso di login, mostrando quello di logout. Contrariamente, in caso di fallimento, verrà visualizzato il pulsante di login e reimpostato il testo della label.
Per completare le modifiche, richiamiamo il metodo toggleAuthUI
nei seguenti metodi:
viewDidLoad
;didTapSignOut
;receiveToggleAuthUINotification
.
Risultato finale
All’avvio dell’applicazione sul dispositivo mobile o sull’emulatore, verrà mostrata la schermata contenente il pulsante di default precedentemente aggiunto all’interfaccia.
Cliccando sul pulsante, verrà richiesto all’utente di autenticarsi con il proprio account Google e di accettare i termini del servizio.
Una volta autenticato, l’utente viene rimandato nuovamente alla scheramta di avvio dove sarà visualizzato il nome dell’utente e il pulsante di logout, come in figura.
Figura 105. Schermata principale dopo l’autenticazione dell’utente tramite Google (click per ingrandire)
Cliccando sul pulsante di sign out, l’utente verrà disconnesso.
Il codice sorgente di questa sezione è disponibile su GitHub.