Skip to content

Instantly share code, notes, and snippets.

@popaaaandrei
Last active March 1, 2025 03:03
Show Gist options
  • Save popaaaandrei/7e2e6323d9b6d739f8ea58c394bbbfa6 to your computer and use it in GitHub Desktop.
Save popaaaandrei/7e2e6323d9b6d739f8ea58c394bbbfa6 to your computer and use it in GitHub Desktop.
Google SignIn proxy class that exposes Observables
//
// Google.swift
//
//
// Created by Andrei on 24/05/16.
// Copyright © 2016 Andrei Popa. All rights reserved.
//
import UIKit
import RxSwift
import Firebase
// add the following to bridging header
// #import <GoogleSignIn/GoogleSignIn.h>
// ============================================================================
// MARK: GoogleError
// ============================================================================
public enum GoogleError: ErrorType, CustomStringConvertible {
case NotAuthenticated
case AuthenticationError(message: String)
public var description: String {
switch self {
case NotAuthenticated:
return "Not authenticated"
case AuthenticationError(let message):
return "Authentication error: \(message)"
}
}
}
// ============================================================================
// MARK: Firebase auth with Google
// ============================================================================
extension Firebase {
func setupGoogleAuth() {
// pass firebase clientID to Google
if let clientID = clientID {
Google.instance.clientID = clientID
}
// new user chain
Google.instance
.rx_firebaseCredential
.flatMapLatest { credential in
Firebase.instance.rx_signInWithCredential(credential)
}
.subscribeNext({ user in
print("--------------- user logged in: \(user.displayName), \(user.uid)")
if let appDelegate = UIApplication.sharedApplication().delegate as? AppDelegate {
appDelegate.loggedIn.onNext()
}
})
.addDisposableTo(Google.instance.disposeBag)
// error chain
Google.instance
.rx_error
.doOnNext({ _ in print("rx_error doOnNext") })
.filter({ $0 is GoogleError })
.subscribeNext { error in
print("--------------- error: \(error)")
Firebase.instance.signOut()
}
.addDisposableTo(Google.instance.disposeBag)
}
}
// ============================================================================
// MARK: Google
// ============================================================================
class Google : NSObject, GIDSignInDelegate, GIDSignInUIDelegate {
static let instance = Google()
internal let disposeBag = DisposeBag()
let rx_error = ReplaySubject<ErrorType>.create(bufferSize: 1)
var rx_firebaseCredential : Observable<FIRAuthCredential> {
return rx_user
.doOnNext({ _ in print("--------------- rx_firebaseCredential received") })
.map({ $0.authentication })
.map({ authentication in
FIRGoogleAuthProvider.credentialWithIDToken(authentication.idToken,
accessToken: authentication.accessToken)
})
}
private let rx_user = ReplaySubject<GIDGoogleUser>.create(bufferSize: 1)
// view controller that presents the auth UIViewController
var presentingViewController : UIViewController?
// this prevents others from using the
// default '()' initializer for this class.
private override init() {
super.init()
GIDSignIn.sharedInstance().delegate = self
GIDSignIn.sharedInstance().uiDelegate = self
}
func signOut() {
// let hasAuthInKeychain = GIDSignIn.sharedInstance().hasAuthInKeychain()
// print("current user: \(GIDSignIn.sharedInstance().currentUser)")
// print("current user hasAuthInKeychain: \(hasAuthInKeychain)")
GIDSignIn.sharedInstance().signOut()
}
var clientID : String {
get {
return GIDSignIn.sharedInstance().clientID
}
set {
GIDSignIn.sharedInstance().clientID = newValue
}
}
// MARK: GIDSignInDelegate
// ============================================================================
func signIn(signIn: GIDSignIn!, didSignInForUser user: GIDGoogleUser!, withError error: NSError!) {
// print("didSignInForUser: \(user)")
if let error = error {
rx_error.onNext(GoogleError.AuthenticationError(message: error.localizedDescription))
}
if let user = user {
rx_user.onNext(user)
} else {
rx_error.onNext(GoogleError.AuthenticationError(message: "empty user"))
}
}
func signIn(signIn: GIDSignIn!, didDisconnectWithUser user: GIDGoogleUser!, withError error: NSError!) {
// print("didDisconnectWithUser: \(user)")
rx_error.onNext(GoogleError.NotAuthenticated)
}
// MARK: GIDSignInUIDelegate
// ============================================================================
func signIn(signIn: GIDSignIn!, presentViewController viewController: UIViewController!) {
if let presentingViewController = presentingViewController {
presentingViewController.presentViewController(viewController, animated: true, completion: nil)
}
}
func signIn(signIn: GIDSignIn!, dismissViewController viewController: UIViewController!) {
viewController.dismissViewControllerAnimated(true, completion: nil)
}
}
@popaaaandrei
Copy link
Author

in some LoginViewController you can just add a GIDSignInButton & the following:
Google.instance.presentingViewController = self

The Google class will catch all the delegates and forward relevant information on the Observables:

  • rx_user
  • rx_error
  • rx_firebaseCredential

Have fun :]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment