【在 iOS 开发路上的大小事-Day20】透过 Firebase 来管理使用者 (Sign in with Facebook 篇) Part2

昨天我们已经完成了 Sign in with Facebook 的前置作业了,今天我们要来将功能实作出来

画面设计

我们先在 Storyboard/Xib 上拉出一个 UIButton

程序实作

在要实作 Sign in with Facebook 功能的 Controller 引入下面两个

import FirebaseAuth
import FBSDKLoginKit

元件的 IBOutlet 如下

@IBOutlet weak var signInWithFacebookBtn: UIButton!

宣告一个 isSign 变数,型别为 Bool,用来判断是否已经登入
然後在宣告一个 loginManager 常数,让他等於 LoginManager(),用来进行登入/登出

var isSign: Bool = false // 预设为尚未登入
let loginManager = LoginManager()

接着在登入按钮的 IBAction 加入下面的程序码

@IBAction func signInWithFacebook(sender: UIButton) {
    if (self.isSign) {
        self.facebookAccountSignOut() // 已经登入的话,按下按钮会执行登出功能
    } else {
        self.signInWithFacebook() // 尚未登入的话,按下按钮会执行登入功能
    }
}

按下按钮登入成功後,Facebook 会回传使用者的 accessToken
接着就可以用 FacebookAuthProivder.credential() 这个 method 来产生该使用者的凭证
後面就可以透过这个凭证来与 Firebase 串接在一起了

extension SignInWithFacebookVC {
    // MARK: - Firebase Sign in with Facebook
    // 登入帐号
    func signInWithFacebook() {
        loginManager.logIn(permissions: ["email"], from: self) { loginResult, error in
            guard error == nil else {
                CustomFunc.customAlert(title: "", message: "\(String(describing: error!.localizedDescription))", vc: self, actionHandler: nil)
                return
            }
            guard ((loginResult?.isCancelled) != nil) else {
                CustomFunc.customAlert(title: "Facebook 登入失败!", message: "", vc: self, actionHandler: nil)
                return
            }
            guard let accessToken = AccessToken.current else {
                CustomFunc.customAlert(title: "无法取得 AccessToken!", message: "", vc: self, actionHandler: nil)
                return
            }
            let credential = FacebookAuthProvider.credential(withAccessToken: accessToken.tokenString)
            self.firebaseSignInWithFacebook(credential: credential)
        }
    }
    
    func firebaseSignInWithFacebook(credential: AuthCredential) {
        Auth.auth().signIn(with: credential) { authResult, error in
            guard error == nil else {
                CustomFunc.customAlert(title: "", message: "\(String(describing: error!.localizedDescription))", vc: self, actionHandler: nil)
                return
            }
            CustomFunc.customAlert(title: "登入成功!", message: "", vc: self, actionHandler: self.getFirebaseUserInfo)
            self.signInWithFacebookBtn.setTitle("Facebook Account Sign Out", for: .normal)
            self.isSignIn = true
        }
    }
    
    // 登出帐号
    func facebookAccountSignOut() {
        do {
            try Auth.auth().signOut()
            loginManager.logOut()
            CustomFunc.customAlert(title: "帐号已登出!", message: "", vc: self, actionHandler: nil)
            self.signInWithFacebookBtn.setTitle("Connect with Facebook", for: .normal)
            self.isSignIn = false
        } catch let error as NSError {
            CustomFunc.customAlert(title: "", message: "\(String(describing: error.localizedDescription))", vc: self, actionHandler: nil)
        }
    }
}

如果要判断目前是否已经有使用者登入了,可以在 viewWillAppear 加入判断

// MARK: - 加入 Firebase 帐号状态监听
override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)
    if let token = AccessToken.current, !token.isExpired {
        CustomFunc.customAlert(title: "帐号已登入过!", message: "", vc: self, actionHandler: self.getFirebaseUserInfo)
        self.signInWithFacebookBtn.setTitle("Sign Out", for: .normal)
        self.isSignIn = true
    } else {
        // 目前尚无用户登入
        print("目前尚无用户登入!")
    }
}

要读取目前登入使用者的资料,可以这样来读取

// MARK: - Firebase 取得登入使用者的资讯
func getFirebaseUserInfo() {
    let currentUser = Auth.auth().currentUser
    guard let user = currentUser else {
        CustomFunc.customAlert(title: "使用者资讯", message: "无法取得使用者资料", vc: self, actionHandler: nil)
        return
    }
    let uid = user.uid
    let email = user.email
    let name = user.displayName
    CustomFunc.customAlert(title: "使用者资讯", message: "User Name:\(name!)\nUID:\(uid)\nEmail:\(email!)", vc: self, actionHandler: nil)
}

成果

本篇的范例程序码:Github


<<:  Day18 使用 GCP 免费云端主机测试 Turn server

>>:  Day 18 - [语料库模型] 06-程序码: TF、IDF、TF-IDF

11 - exa - 总览目录的工具

ls 指令会列出目录中的各个档案与目录,供使用者浏览整个目录的结构,是个十分常用的指令。 但它的设计...

Day 24 : 案例分享(7.3) 库存与制造 - 从单纯的制造开始

案例说明及适用场景 自己购买原物料做组装 2~3个商品,透过包装,形成库存 需扣料形成产品转换 一....

第五章

依照之前介绍的内容都是属於在Hostinger提供的功能面,当然还有许多细项的功能或建置时会用到的,...

从 IT 技术面细说 Search Console 的 27 组数字 KPI (3) 点击 (2) 网页搜寻

上一篇提到可以从 Search Console 看到 6 种不同的流量来源,而 SC 提供的概要是用...

学习Ruby、Rails事前准备工作

专有名词 整理了我觉得该先了解的一些专有名词 wiki-物件导向程序设计、菜鸟式回答:是一种将资料,...