SwiftUI 開發之旅:Face ID 的使用技巧

語言: CN / TW / HK

theme: smartblue

開啟掘金成長之旅!這是我參與「掘金日新計劃 · 12 月更文挑戰」的第2天,點選檢視活動詳情

好久不見,我是 new_cheng。

蘋果在 iPhone X 上釋出 Face ID 後,這一功能基本已經成為 iPhone 系列的標配了;在 IOS 開發中也會經常用到 Face ID。

以下簡稱 面容 ID。

在使用面容 ID 之前,我們先來看看,它的常用場景:

  • APP 解鎖
  • 支付

這裡我們以解鎖的場景為例,看看如何使用面容 ID:

從 App 解鎖的使用角度來看,使用者會有以下相關操作: 1. 首先使用者要去 app 的設定頁面開啟 faceid 解鎖選項。 2. 開啟面容 ID 解鎖時需要檢查 app 是否擁有面容id的授權。 3. 沒有面容id的授權時,彈窗提醒,使用者點選確認後需要跳轉去系統設定頁面給app開啟面容id授權。 4. 授權完成後,使用者可以開啟 faceid 解鎖選項。 5. 重新進入app的時候,顯示一個解鎖頁面,呼叫面容 ID解鎖,如果使用者取消了解鎖,那就無法顯示 app 的其他內容

以上是涉及到面容 ID 解鎖的一個基本業務流程,下面我們來完成具體實現。

面容 ID 解鎖的開關設定

我們先要在 app 的設定頁面裡面加入一個 Face ID 解鎖 的開關設定,先來簡單的編寫這個頁面。

image.png

Setting.swift:

```swift struct Settings: View { @EnvironmentObject var appSetting: AppSetting

@State private var isOpenFaceIdLock = false

var body: some View {
    ScrollView {
        VStack(alignment: .leading) {
            // ...
            HStack {
                Toggle(isOn: $isOpenFaceIdLock) {
                    HStack {
                        Text("😃")
                            .font(.system(size: 16))
                        Text("FaceID 解鎖")
                            .font(.body)
                            .fontWeight(.medium)
                            .foregroundColor(.gray)
                        Spacer()
                        Image(systemName: "chevron.right")
                            .foregroundColor(Color.gray)
                            .font(.system(size: 14))
                    }
                }
                .onChange(of: isOpenFaceIdLock) { value in
                    // 將使用者設定儲存起來
                    appSetting.isOpenFaceIdLock = value
                }
            }
        }
    }
     .onAppear {
        // 初始化時,使用 AppSetting 的值

self.isOpenFaceIdLock = UserDefaults.standard.bool(forKey: "isOpenFaceIdLock") } } } ```

一個簡單的設定頁面就完成了,在這裡我們還引入了 AppSetting,用於持久化儲存使用者的設定,上一次我們在 SwiftUI 開發之旅:適配深色模式 中用到了它。現在,我們會繼續在原有的基礎上往 AppSetting 中新增關於面容 ID 的內容。

對 AppSetting 有不瞭解的可以點選適配深色模式的連結前往檢視。

我們要在 AppSetting 中加入一個新的欄位 isOpenFaceIdLock,用於儲存 面容 ID 解鎖的設定:

swift // 是否開啟 FaceId 解鎖 @Published var isOpenFaceIdLock: Bool = UserDefaults.standard.bool(forKey: "isOpenFaceIdLock") { didSet { // 監聽資料變化,持久化資料 UserDefaults.standard.set(self.isOpenFaceIdLock, forKey: "isOpenFaceIdLock")     }  }

檢查是否授權使用面容 ID

面容 ID 解鎖預設是不開啟的,當用戶開啟該設定的時候,我們要先檢查我們的 App 是否被系統授權使用 面容 ID 解鎖。

在 Setting.swift 中新增 authenticate 函式和用於提示使用者需要開啟授權的控制變數 isGoOpenAuth

  1. 先引入 LocalAuthentication 依賴:

swift import LocalAuthentication

  1. 新增函式和變數:

```swift // 是否需要前往設定頁面開啟許可權 @State private var isGoOpenAuth: Bool = false

// 先檢測是否開啟面容id授權 func authenticate() { let context = LAContext() var error: NSError? // 檢查是否可以進行生物特徵識別 if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) { self.isGoOpenAuth = false } else { // 沒有生物指紋識別功能 if (error?.code == -6) { self.isGoOpenAuth = true print("沒有生物指紋識別功能") } } } ```

  1. 當系統沒有授權使用面容 ID 時,提醒使用者前往開啟授權

swift ScrollView { // ... } .alert("", isPresented: $isGoOpenAuth) { Button(role: .cancel) { self.isGoOpenAuth = false self.isOpenFaceIdLock = false } label: { Text("取消") } Button() { // 前往設定頁面進行授權 guard let url = URL(string: UIApplication.openSettingsURLString) **else** { return } if #available(iOS 10.0, *) { UIApplication.shared.open(url, options: [:], completionHandler: nil) } else { UIApplication.shared.openURL(url) } } label: { Text("去開啟") } } message: { Text("開啟面容 ID 許可權才能夠使用解鎖哦") }

2721669086717_.pic.jpg

請用真機執行。

點選 【去開啟】 後,會直接跳轉到系統設定中對應 App 的設定頁面。

到這裡,一個檢查授權面容 ID 和持久化使用者設定的功能就完成了。

面容 ID 的解鎖頁面

在用面容 ID 解鎖前,為了使用者資訊保安,是不能顯示 App 內的頁面內容的。這時候我們就需要一個專用與解鎖的頁面,當沒有進行解鎖的時候, App 會一直顯示該頁面直到解鎖成功。

FaceIdLock.swift:

```swift import SwiftUI

struct FaceIdLock: View {     @EnvironmentObject var appSetting: AppSetting // 是否需要前往設定頁面開啟許可權     @State private var isGoOpenAuth: Bool = false

// 先檢測是否開啟面容id授權     func authenticateFaceId() {         let context = LAContext()         var error: NSError?         // 檢查是否可以進行生物特徵識別         if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {            self.isGoOpenAuth = false         } else {             // 沒有生物指紋識別功能             if (error?.code == -6) {                 self.isGoOpenAuth = true                 print("沒有生物指紋識別功能")             }         }     }

var body: some View {         ZStack {             Color("mainBg").edgesIgnoringSafeArea(.all)             VStack {                 Button(action: { // 如果使用者不小心取消了解鎖,需要提供一個點選重新解鎖的方式:當用戶點選時,呼叫面容 ID 解鎖                     authenticateFaceId() // 還是先檢查系統是否授權了面容 ID                     if !isGoOpenAuth {                         appSetting.authenticate()                     }                 }, label: {                     VStack {                         Image(systemName: "faceid")                             .foregroundColor(Color.blue)                             .font(.system(size: 54))                             .padding()                         Text("點選進行面容 ID 登入")                             .foregroundColor(Color.textColor)                     }                 })             }         } .alert("", isPresented: $isGoOpenAuth) {             Button(role: .cancel) {                 self.isGoOpenAuth = false             } label: {                 Text("取消")             }             Button() {                // 前往設定頁面進行授權                guard let url = URL(string: UIApplication.openSettingsURLString) else {                    return                }                if #available(iOS 10.0, *) {                    UIApplication.shared.open(url, options: [:], completionHandler: nil)                } else {                    UIApplication.shared.openURL(url)                }             } label: {                 Text("去開啟")             }         } message: {             Text("開啟面容 ID 許可權才能夠使用解鎖哦")         }         .ignoresSafeArea(edges: .top)         .onAppear { // 初始化顯示時,先判斷是否授權了faceid             authenticateFaceId()             if !isGoOpenAuth {                 appSetting.authenticate()             }         }     } }

struct FaceIdLock_Previews: PreviewProvider {     static var previews: some View {         FaceIdLock()             .environmentObject(AppSetting())     } } ```

image.png

在 FaceIdLock 頁面,我們同樣需要檢測是否授權了面容 ID 許可權,不然,App 會一直停留在該頁面,且無法調起面容 ID 進行解鎖。

接著在入口頁面 ContentView.swift 中新增一個條件判斷:

```swift struct ContentView: View { @EnvironmentObject var appSetting: AppSetting

var body: some View {
    VStack {
        if appSetting.isOpenFaceIdLock && !appSetting.isUnlocked {

FaceIdLock()                     .frame(maxHeight: .infinity)             } else { // ... } } } } ```

呼叫面容 ID API

接下來我們需要完成在 FaceIdLock.swift 中解鎖時呼叫的 AppSetting 的 authenticate 函式。

AppSetting.swift:

```swift import LocalAuthentication

class AppSetting: ObservableObject { // 是否已解鎖,只有在使用 faceid 的前提下才能使用該變數,用於判斷後面的操作是否能進行     @Published var isUnlocked: Bool = false

func authenticate() {
    let context = LAContext()
    var error: NSError?
    // 檢查是否可以進行生物特徵識別
    if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error) {
        // 如果可以,執行識別
        let reason = "開啟面容 ID 許可權才能夠使用解鎖哦"
        context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { success, authenticationError **in**
            // 鑑權完成
            DispatchQueue.main.async {
                if success {
                    // 鑑權成功
                    self.isUnlocked = true
                } else {
                    // 鑑權失敗
                    self.isUnlocked = false
                }
            }
        }
    } else {
        // 沒有生物指紋識別功能
        if (error?.code == -6) {
            print("沒有生物指紋識別功能")
        }
    }
}

} ```

到這裡,我們已經完成了一個比較完整的使用面容 ID 解鎖的功能了🎉

總結

我們通過實操,完成了一個面容 ID 的使用功能,完整還原了使用面容 ID 的的業務流程。除了自己編寫程式碼來使用面容 ID,你還可以通過諸如 BiometricAuthentication 的第三方庫來完成面容 ID 或者指紋識別的使用。合理的運用蘋果提供的功能,來提升你應用的使用者體驗吧。

這是 SwiftUI 開發之旅專欄的文章,是 swiftui 開發學習的經驗總結及實用技巧分享,歡迎關注該專欄,會堅持輸出。同時歡迎關注我的個人公眾號 @JSHub:提供最新的開發資訊速報,優質的技術乾貨推薦。或是檢視我的個人部落格:Devcursor

👍點贊:如果有收穫和幫助,請點個贊支援一下!

🌟收藏:歡迎收藏文章,隨時檢視!

💬評論:歡迎評論交流學習,共同進步!