在 Apple 平台上使用電話號碼驗證 Firebase

您可以使用 Firebase Authentication 向使用者傳送簡訊,讓使用者登入。使用者使用簡訊訊息中包含的一次性代碼登入。

在應用程式中加入電話號碼登入功能最簡單的方法,就是使用 FirebaseUI,其中包含置入式登入小工具,可實作電話號碼登入的登入流程,以及密碼和聯合登入。本文說明如何使用 Firebase SDK 導入電話號碼登入流程。

事前準備

  1. 如果您尚未將應用程式連結至 Firebase 專案,請透過 Firebase 主控台連結。
  2. 使用 Swift Package Manager 安裝及管理 Firebase 依附元件。

    1. 在 Xcode 中保持開啟應用程式專案,然後依序點選「File」>「Add Packages」
    2. 系統提示時,請新增 Firebase Apple 平台 SDK 存放區:
    3.   https://github.com/firebase/firebase-ios-sdk.git
    4. 選擇 Firebase Authentication 程式庫。
    5. -ObjC 標記新增至目標的建構設定「Other Linker Flags」部分。
    6. 完成後,Xcode 就會自動開始在背景中解析並下載依附元件。

安全疑慮

雖然只使用電話號碼進行驗證很方便,但比起其他可用方法,安全性較低,因為電話號碼很容易在使用者之間轉移。此外,在有多個使用者設定檔的裝置上,任何可接收簡訊的使用者都可以使用裝置的電話號碼登入帳戶。

如果您在應用程式中使用電話號碼登入功能,應同時提供更安全的登入方式,並告知使用者使用電話號碼登入功能的安全性取捨。

為 Firebase 專案啟用電話號碼登入功能

如要透過簡訊登入使用者,您必須先為 Firebase 專案啟用電話號碼登入方法:

  1. Firebase 主控台中,開啟「驗證」部分。
  2. 在「登入方式」頁面中,啟用「電話號碼」登入方式。

啟用驗證應用程式功能

如要使用電話號碼驗證功能,Firebase 必須能夠驗證電話號碼登入要求是否來自您的應用程式。Firebase Authentication 有兩種方式可達成這項要求:

  • 靜默 APN 通知:當您在裝置上首次使用電話號碼登入使用者時,Firebase Authentication 會透過靜默推播通知將權杖傳送至裝置。如果應用程式成功收到 Firebase 的通知,即可繼續進行電話號碼登入程序。

    針對 iOS 8.0 以上版本,靜默通知不需要明確的使用者同意聲明,因此不會受到使用者拒絕接收應用程式中的 APN 通知影響。因此,在導入 Firebase 電話號碼驗證功能時,應用程式不需要要求使用者授權才能接收推播通知。

  • reCAPTCHA 驗證:如果無法傳送或接收靜默推播通知 (例如使用者已停用應用程式的背景重新整理功能,或是在 iOS 模擬器上測試應用程式時),Firebase Authentication 會使用 reCAPTCHA 驗證來完成手機登入流程。使用者通常不必回答任何問題,就能完成 reCAPTCHA 驗證。

在正確設定靜默推播通知後,只有極少數使用者會遇到 reCAPTCHA 流程。不過,無論是否提供靜默推播通知,您都應確保電話號碼登入功能正常運作。

開始接收靜音通知

如要啟用 APN 通知,以便與 Firebase Authentication 搭配使用,請按照下列步驟操作:

  1. 在 Xcode 中, 為專案啟用推播通知
  2. 將 APNs 驗證金鑰上傳至 Firebase。如果您尚未取得 APN 驗證金鑰,請務必前往 Apple Developer Member Center 建立金鑰。

    1. Firebase 控制台的專案中,依序選取齒輪圖示、「Project Settings」(專案設定),然後選取「Cloud Messaging」分頁。

    2. 在「iOS 應用程式設定」下方的「APNs 驗證金鑰」中,按一下「上傳」按鈕。

    3. 瀏覽至儲存金鑰的位置,選取金鑰,然後按一下「Open」。新增金鑰的金鑰 ID (可在 Apple Developer Member Center 中找到),然後按一下「上傳」

    如果您已擁有 APN 憑證,可以改為上傳憑證。

  3. 在 Xcode 中, 為專案啟用背景模式功能,然後選取「Background fetch」和「Remote notifications」模式的核取方塊。

設定 reCAPTCHA 驗證

如要讓 Firebase SDK 使用 reCAPTCHA 驗證功能,請按照下列步驟操作:

  1. 在 Xcode 專案中新增自訂網址配置:
    1. 開啟專案設定:在左側樹狀檢視畫面中,按兩下專案名稱。從「TARGETS」部分選取應用程式,然後選取「Info」分頁標籤,並展開「URL Types」部分。
    2. 按一下「+」按鈕,然後將已編碼的應用程式 ID 新增為網址配置。您可以在 Firebase 控制台的「General Settings」頁面中,找到 iOS 應用程式的「Encoded App ID」部分。請將其他欄位留空。

      完成後,設定應類似於下列內容 (但會顯示應用程式專屬的值):

      Xcode 自訂網址配置介面的螢幕截圖
  2. 選用:如果您想自訂應用程式向使用者顯示 reCAPTCHA 時呈現 SFSafariViewController 的方式,請建立符合 AuthUIDelegate 通訊協定的自訂類別,並將其傳遞至 verifyPhoneNumber(_:uiDelegate:completion:)

將驗證碼傳送至使用者的手機

如要啟動電話號碼登入功能,請向使用者顯示介面,提示他們提供電話號碼,然後呼叫 verifyPhoneNumber(_:uiDelegate:completion:),要求 Firebase 透過簡訊將驗證碼傳送至使用者的手機:

  1. 取得使用者的電話號碼。

    法律規定各有不同,但為了提供最佳做法並為使用者設定預期,您應告知使用者,如果他們使用電話號碼登入,可能會收到驗證簡訊,並須支付一般簡訊費用。

  2. 呼叫 verifyPhoneNumber(_:uiDelegate:completion:),並將使用者的電話號碼傳遞給 verifyPhoneNumber(_:uiDelegate:completion:)
    SwiftObjective-C
    PhoneAuthProvider.provider()
      .verifyPhoneNumber(phoneNumber, uiDelegate: nil) { verificationID, error in
          if let error = error {
            self.showMessagePrompt(error.localizedDescription)
            return
          }
          // Sign in using the verificationID and the code sent to the user
          // ...
      }
    [[FIRPhoneAuthProvider provider] verifyPhoneNumber:userInput
                                            UIDelegate:nil
                                            completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) {
      if (error) {
        [self showMessagePrompt:error.localizedDescription];
        return;
      }
      // Sign in using the verificationID and the code sent to the user
      // ...
    }];

    verifyPhoneNumber 方法是可重入的:如果您多次呼叫此方法 (例如在檢視畫面的 onAppear 方法中),除非原始要求已逾時,否則 verifyPhoneNumber 方法不會傳送第二次簡訊。

    當您呼叫 verifyPhoneNumber(_:uiDelegate:completion:) 時,Firebase 會向您的應用程式傳送靜默推送通知,或向使用者發出 reCAPTCHA 挑戰。應用程式收到通知或使用者完成 reCAPTCHA 挑戰後,Firebase 會傳送含有驗證碼的簡訊至指定的電話號碼,並將驗證 ID 傳遞至完成函式。您需要驗證碼和驗證 ID 才能讓使用者登入。

    您也可以透過 Auth 例項的 languageCode 屬性,指定 Firebase 傳送的簡訊語言,以便進行本地化。

    SwiftObjective-C
     // Change language code to french.
     Auth.auth().languageCode = "fr";
     // Change language code to french.
     [FIRAuth auth].languageCode = @"fr";
  3. 請儲存驗證 ID,並在應用程式載入時還原。這樣一來,如果應用程式在使用者完成登入流程前終止 (例如切換至 SMS 應用程式),您仍可確保擁有有效的驗證 ID。

    您可以隨意保留驗證 ID。最簡單的方法是使用 NSUserDefaults 物件儲存驗證 ID:

    SwiftObjective-C
    UserDefaults.standard.set(verificationID, forKey: "authVerificationID")
    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
    [defaults setObject:verificationID forKey:@"authVerificationID"];

    接著,您可以還原已儲存的值:

    SwiftObjective-C
    let verificationID = UserDefaults.standard.string(forKey: "authVerificationID")
    NSString *verificationID = [defaults stringForKey:@"authVerificationID"];

如果呼叫 verifyPhoneNumber(_:uiDelegate:completion:) 成功,您可以提示使用者在收到簡訊驗證碼時輸入驗證碼。

使用驗證碼登入使用者

使用者將簡訊中的驗證碼提供給應用程式後,請使用驗證碼和驗證 ID 建立 FIRPhoneAuthCredential 物件,然後將該物件傳遞至 signInWithCredential:completion:,以便讓使用者登入。

  1. 向使用者取得驗證碼。
  2. 使用驗證碼和驗證 ID 建立 FIRPhoneAuthCredential 物件。
    SwiftObjective-C
    let credential = PhoneAuthProvider.provider().credential(
      withVerificationID: verificationID,
      verificationCode: verificationCode
    )
    FIRAuthCredential *credential = [[FIRPhoneAuthProvider provider]
        credentialWithVerificationID:verificationID
                    verificationCode:userInput];
  3. 使用 FIRPhoneAuthCredential 物件登入使用者:
    SwiftObjective-C
    Auth.auth().signIn(with: credential) { authResult, error in
        if let error = error {
          let authError = error as NSError
          if isMFAEnabled, authError.code == AuthErrorCode.secondFactorRequired.rawValue {
            // The user is a multi-factor user. Second factor challenge is required.
            let resolver = authError
              .userInfo[AuthErrorUserInfoMultiFactorResolverKey] as! MultiFactorResolver
            var displayNameString = ""
            for tmpFactorInfo in resolver.hints {
              displayNameString += tmpFactorInfo.displayName ?? ""
              displayNameString += " "
            }
            self.showTextInputPrompt(
              withMessage: "Select factor to sign in\n\(displayNameString)",
              completionBlock: { userPressedOK, displayName in
                var selectedHint: PhoneMultiFactorInfo?
                for tmpFactorInfo in resolver.hints {
                  if displayName == tmpFactorInfo.displayName {
                    selectedHint = tmpFactorInfo as? PhoneMultiFactorInfo
                  }
                }
                PhoneAuthProvider.provider()
                  .verifyPhoneNumber(with: selectedHint!, uiDelegate: nil,
                                     multiFactorSession: resolver
                                       .session) { verificationID, error in
                    if error != nil {
                      print(
                        "Multi factor start sign in failed. Error: \(error.debugDescription)"
                      )
                    } else {
                      self.showTextInputPrompt(
                        withMessage: "Verification code for \(selectedHint?.displayName ?? "")",
                        completionBlock: { userPressedOK, verificationCode in
                          let credential: PhoneAuthCredential? = PhoneAuthProvider.provider()
                            .credential(withVerificationID: verificationID!,
                                        verificationCode: verificationCode!)
                          let assertion: MultiFactorAssertion? = PhoneMultiFactorGenerator
                            .assertion(with: credential!)
                          resolver.resolveSignIn(with: assertion!) { authResult, error in
                            if error != nil {
                              print(
                                "Multi factor finanlize sign in failed. Error: \(error.debugDescription)"
                              )
                            } else {
                              self.navigationController?.popViewController(animated: true)
                            }
                          }
                        }
                      )
                    }
                  }
              }
            )
          } else {
            self.showMessagePrompt(error.localizedDescription)
            return
          }
          // ...
          return
        }
        // User is signed in
        // ...
    }
    [[FIRAuth auth] signInWithCredential:credential
                              completion:^(FIRAuthDataResult * _Nullable authResult,
                                           NSError * _Nullable error) {
        if (isMFAEnabled && error && error.code == FIRAuthErrorCodeSecondFactorRequired) {
          FIRMultiFactorResolver *resolver = error.userInfo[FIRAuthErrorUserInfoMultiFactorResolverKey];
          NSMutableString *displayNameString = [NSMutableString string];
          for (FIRMultiFactorInfo *tmpFactorInfo in resolver.hints) {
            [displayNameString appendString:tmpFactorInfo.displayName];
            [displayNameString appendString:@" "];
          }
          [self showTextInputPromptWithMessage:[NSString stringWithFormat:@"Select factor to sign in\n%@", displayNameString]
                               completionBlock:^(BOOL userPressedOK, NSString *_Nullable displayName) {
           FIRPhoneMultiFactorInfo* selectedHint;
           for (FIRMultiFactorInfo *tmpFactorInfo in resolver.hints) {
             if ([displayName isEqualToString:tmpFactorInfo.displayName]) {
               selectedHint = (FIRPhoneMultiFactorInfo *)tmpFactorInfo;
             }
           }
           [FIRPhoneAuthProvider.provider
            verifyPhoneNumberWithMultiFactorInfo:selectedHint
            UIDelegate:nil
            multiFactorSession:resolver.session
            completion:^(NSString * _Nullable verificationID, NSError * _Nullable error) {
              if (error) {
                [self showMessagePrompt:error.localizedDescription];
              } else {
                [self showTextInputPromptWithMessage:[NSString stringWithFormat:@"Verification code for %@", selectedHint.displayName]
                                     completionBlock:^(BOOL userPressedOK, NSString *_Nullable verificationCode) {
                 FIRPhoneAuthCredential *credential =
                     [[FIRPhoneAuthProvider provider] credentialWithVerificationID:verificationID
                                                                  verificationCode:verificationCode];
                 FIRMultiFactorAssertion *assertion = [FIRPhoneMultiFactorGenerator assertionWithCredential:credential];
                 [resolver resolveSignInWithAssertion:assertion completion:^(FIRAuthDataResult * _Nullable authResult, NSError * _Nullable error) {
                   if (error) {
                     [self showMessagePrompt:error.localizedDescription];
                   } else {
                     NSLog(@"Multi factor finanlize sign in succeeded.");
                   }
                 }];
               }];
              }
            }];
         }];
        }
      else if (error) {
        // ...
        return;
      }
      // User successfully signed in. Get user data from the FIRUser object
      if (authResult == nil) { return; }
      FIRUser *user = authResult.user;
      // ...
    }];

使用虛構的電話號碼進行測試

您可以透過 Firebase 控制台設定虛構的電話號碼,用於開發作業。使用虛構的電話號碼進行測試可帶來以下好處:

  • 在不消耗使用配額的情況下測試電話號碼驗證。
  • 在不傳送實際簡訊的情況下,測試電話號碼驗證機制。
  • 使用相同的電話號碼連續執行測試,且不會受到頻寬限制。這樣一來,如果審查人員剛好使用相同的電話號碼進行測試,就不會在應用程式商店審查程序中遭到拒絕。
  • 無需額外付出任何努力,即可在開發環境中輕鬆測試,例如在 iOS 模擬器或 Android 模擬器中開發,而無需 Google Play 服務。
  • 編寫整合測試時,不會受到實際電話號碼在實際環境中通常會受到的安全檢查阻擋。

虛構的電話號碼必須符合下列規定:

  1. 請務必使用虛構的電話號碼,且該號碼不應已存在。Firebase Authentication 不允許您將真實使用者使用的現有電話號碼設為測試號碼。您可以使用開頭為 555 的號碼做為美國測試電話號碼,例如: +1 650-555-3434
  2. 電話號碼的長度和其他限制條件必須符合正確的格式。這類號碼仍須通過與真實使用者電話號碼相同的驗證程序。
  3. 您最多可以新增 10 個開發用電話號碼。
  4. 請使用不易猜測且經常變更的測試電話號碼/驗證碼。

建立虛構的電話號碼和驗證碼

  1. Firebase 主控台中,開啟「驗證」部分。
  2. 在「登入方式」分頁中,啟用電話服務供應商 (如果尚未啟用的話)。
  3. 開啟「測試用電話號碼」摺疊式選單。
  4. 提供要測試的電話號碼,例如:+1 650-555-3434
  5. 請提供該特定號碼的 6 位數驗證碼,例如:654321
  6. 新增電話號碼。如有需要,您可以將滑鼠游標懸停在對應的資料列上,然後按一下垃圾桶圖示,即可刪除電話號碼和代碼。

手動測試

您可以在應用程式中直接開始使用虛構的電話號碼。這樣一來,您就能在開發階段執行手動測試,而不必擔心會發生配額問題或頻寬限制。您也可以直接透過未安裝 Google Play 服務的 iOS 模擬器或 Android 模擬器進行測試。

當您提供虛構的電話號碼並傳送驗證碼時,系統不會傳送實際的簡訊。您必須提供先前設定的驗證碼,才能完成登入程序。

登入完成後,系統會使用該電話號碼建立 Firebase 使用者。使用者具有與實際電話號碼使用者相同的行為和屬性,且可以相同方式存取 Realtime Database/Cloud Firestore 和其他服務。這個程序中產生的 ID 權杖,其簽章與真實電話號碼使用者相同。

如果您想進一步限制存取權,另一個做法是透過自訂聲明設定測試角色,將這些使用者視為假使用者。

整合測試

除了手動測試之外,Firebase Authentication 也提供 API,協助您為電話驗證測試編寫整合測試。這些 API 會停用網頁版 reCAPTCHA 和 iOS 版靜默推播通知的規定,進而停用應用程式驗證功能。這樣一來,您就能在這些流程中進行自動化測試,並更輕鬆地實作。此外,這些測試還可協助您在 Android 上測試即時驗證流程。

在 iOS 上,必須先將 appVerificationDisabledForTesting 設定設為 TRUE,才能呼叫 verifyPhoneNumber。這項作業不需要任何 APN 權杖,也不需要在背景傳送靜默推播通知,因此更容易在模擬器中進行測試。這麼做也會停用 reCAPTCHA 備用流程。

請注意,如果停用應用程式驗證功能,使用非虛構的電話號碼就無法完成登入程序。這個 API 只能搭配虛構的電話號碼使用。

SwiftObjective-C
let phoneNumber = "+16505554567"

// This test verification code is specified for the given test phone number in the developer console.
let testVerificationCode = "123456"

Auth.auth().settings.isAppVerificationDisabledForTesting = TRUE
PhoneAuthProvider.provider().verifyPhoneNumber(phoneNumber, uiDelegate:nil) {
                                                            verificationID, error in
    if (error) {
      // Handles error
      self.handleError(error)
      return
    }
    let credential = PhoneAuthProvider.provider().credential(withVerificationID: verificationID ?? "",
                                                               verificationCode: testVerificationCode)
    Auth.auth().signInAndRetrieveData(with: credential) { authData, error in
      if (error) {
        // Handles error
        self.handleError(error)
        return
      }
      _user = authData.user
    }];
}];
NSString *phoneNumber = @"+16505554567";

// This test verification code is specified for the given test phone number in the developer console.
NSString *testVerificationCode = @"123456";

[FIRAuth auth].settings.appVerificationDisabledForTesting = YES;
[[FIRPhoneAuthProvider provider] verifyPhoneNumber:phoneNumber
                                        completion:^(NSString *_Nullable verificationID,
                                                     NSError *_Nullable error) {
    if (error) {
      // Handles error
      [self handleError:error];
      return;
    }
    FIRAuthCredential *credential =
        [FIRPhoneAuthProvider credentialWithVerificationID:verificationID
                                          verificationCode:testVerificationCode];
    [FIRAuth auth] signInWithAndRetrieveDataWithCredential:credential
                                                completion:^(FIRUser *_Nullable user,
                                                             NSError *_Nullable error) {
      if (error) {
        // Handles error
        [self handleError:error];
        return;
      }
      _user = user;
    }];
}];

附錄:不使用 swizzling 的手機登入功能

Firebase Authentication 會使用方法混合功能自動取得應用程式的 APN 權杖,以便處理 Firebase 傳送至應用程式的靜默推播通知,並在驗證期間自動攔截來自 reCAPTCHA 驗證頁面的自訂配置轉送。

如果您不想使用 swizzling,請將標記 FirebaseAppDelegateProxyEnabled 新增至應用程式的 Info.plist 檔案,並將其設為 NO,即可停用 swizzling。請注意,將這個標記設為 NO 也會停用其他 Firebase 產品 (包括 Firebase Cloud Messaging) 的 swizzling 功能。

如果停用 swizzling,您必須明確將 APNs 裝置驗證碼、推播通知和自訂配置文件重新導向網址傳遞至 Firebase Authentication

如果您要建構 SwiftUI 應用程式,也應明確將 APNs 裝置 Token、推播通知和自訂配置重新導向網址傳送至 Firebase Authentication

如要取得 APN 裝置符記,請實作 application(_:didRegisterForRemoteNotificationsWithDeviceToken:) 方法,並在其中將裝置符記傳遞至 AuthsetAPNSToken(_:type:) 方法。

SwiftObjective-C
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
  // Pass device token to auth
  Auth.auth().setAPNSToken(deviceToken, type: .unknown)

  // Further handling of the device token if needed by the app
  // ...
}
- (void)application:(UIApplication *)application
    didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
  // Pass device token to auth.
  [[FIRAuth auth] setAPNSToken:deviceToken type:FIRAuthAPNSTokenTypeProd];
  // Further handling of the device token if needed by the app.
}

如要處理推播通知,請在 application(_:didReceiveRemoteNotification:fetchCompletionHandler:): 方法中呼叫 AuthcanHandleNotification(_:) 方法,檢查 Firebase 驗證相關通知。

SwiftObjective-C
func application(_ application: UIApplication,
    didReceiveRemoteNotification notification: [AnyHashable : Any],
    fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
  if Auth.auth().canHandleNotification(notification) {
    completionHandler(.noData)
    return
  }
  // This notification is not auth related; it should be handled separately.
}
- (void)application:(UIApplication *)application
    didReceiveRemoteNotification:(NSDictionary *)notification
          fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
  // Pass notification to auth and check if they can handle it.
  if ([[FIRAuth auth] canHandleNotification:notification]) {
    completionHandler(UIBackgroundFetchResultNoData);
    return;
  }
  // This notification is not auth related; it should be handled separately.
}

如要處理自訂配置重新導向網址,請實作 application(_:open:options:) 方法,並在其中將網址傳遞至 AuthcanHandleURL(_:) 方法。

SwiftObjective-C
func application(_ application: UIApplication, open url: URL,
    options: [UIApplicationOpenURLOptionsKey : Any]) -> Bool {
  if Auth.auth().canHandle(url) {
    return true
  }
  // URL not auth related; it should be handled separately.
}
- (BOOL)application:(UIApplication *)app
            openURL:(NSURL *)url
            options:(NSDictionary<UIApplicationOpenURLOptionsKey, id> *)options {
  if ([[FIRAuth auth] canHandleURL:url]) {
    return YES;
  }
  // URL not auth related; it should be handled separately.
}

如果您使用的是 SwiftUI 或 UISceneDelegate,請實作 scene(_:openURLContexts:) 方法,並在其中將網址傳遞至 AuthcanHandleURL(_:) 方法。

SwiftObjective-C
func scene(_ scene: UIScene, openURLContexts URLContexts: Set&ltUIOpenURLContext&gt) {
  for urlContext in URLContexts {
      let url = urlContext.url
      _ = Auth.auth().canHandle(url)
  }
  // URL not auth related; it should be handled separately.
}
- (void)scene:(UIScene *)scene openURLContexts:(NSSet&ltUIOpenURLContext *&gt *)URLContexts {
  for (UIOpenURLContext *urlContext in URLContexts) {
    [FIRAuth.auth canHandleURL:urlContext.url];
    // URL not auth related; it should be handled separately.
  }
}

後續步驟

使用者首次登入後,系統會建立新使用者帳戶,並連結至使用者登入時所用的憑證 (即使用者名稱和密碼、電話號碼或驗證服務提供者資訊)。這個新帳戶會儲存在 Firebase 專案中,無論使用者如何登入,都可以用於在專案中的每個應用程式中識別使用者。

  • 在應用程式中,您可以從 User 物件取得使用者的個人資料基本資訊。請參閱「管理使用者」。

  • Firebase Realtime DatabaseCloud Storage 安全性規則中,您可以從 auth 變數取得已登入使用者的專屬使用者 ID,並利用該 ID 控管使用者可存取的資料。

您可以將驗證服務供應商憑證連結至現有使用者帳戶,讓使用者使用多個驗證服務供應商登入應用程式。

如要讓使用者登出,請呼叫 signOut:

SwiftObjective-C
let firebaseAuth = Auth.auth()
do {
  try firebaseAuth.signOut()
} catch let signOutError as NSError {
  print("Error signing out: %@", signOutError)
}
NSError *signOutError;
BOOL status = [[FIRAuth auth] signOut:&signOutError];
if (!status) {
  NSLog(@"Error signing out: %@", signOutError);
  return;
}

您可能還想為各種驗證錯誤新增錯誤處理程式碼。請參閱「處理錯誤」。