C72. 練習Firebase Authentication 和 Cloud Firestore打造註冊登入功能

  • 主要是因為當初在透過FavQs的註冊、登入API時,就一直想要來練習Firebase的註冊登入功能,因爲在個人資訊以及大頭照圖片的使用配置上更有彈性!!!

1. 學習目標:Firebase Authentication 和 Cloud Firestore搭配使用:

Firebase Authentication 和 Cloud Firestore 的功能和用途
1. 當用戶通過 Firebase Authentication 註冊或登入後,藉此可以獲得用戶ID(UID)。
這個 UID 可以用來在 Cloud Firestore 中創建和引用與該用戶相關的資料記錄。

2. 如想要儲存用戶的個人資料訊息(如姓名、聯絡方式等)。
- Firebase Authentication 並不儲存這些訊息,但可以使用用戶的 UID 在
Cloud Firestore 中為每個用戶創建一個文件,並在其中儲存這些額外的用戶訊息。

2. 當需要「讀取」或「更新用戶」的個人資料訊息時,你可以查詢 Cloud Firestore 中
與當前登入用戶 UID 匹配的文件。

2. 版面配置

1. FirstViewController:可以選擇要登入、註冊的頁面跳轉。

- LoginViewController:處理登入的行為,或是前進忘記密碼頁面。
- ForgotPasswordViewController:專門處理忘記密碼的部分。

- SignUpViewController:註冊的行為處理,填寫註冊資料。

2. HomeViewController:當登入、註冊成功,就會跳轉到該頁面展示個人資訊,並提供登出功能。

3. FirebaseControlle

  • 此次我將與Firebase功能相關的程式碼都將其整合在此,方便呼叫使用。
1. 檢查電子郵件、密碼的驗證:
- 檢查用戶設定的電子郵件、密碼是否符合有效的要求。

2. 用戶註冊 (creatUser):
- 使用 FirebaseAuth 進行新用戶的註冊,並將用戶資料(姓名、郵件等)儲存到 Firestore。

3. 用戶登入 (signIn):
- 使用 FirebaseAuth 進行用戶的登入操作。

4. 密碼重置 (sendPasswordRest):
- 使用 FirebaseAuth 來發送重置密碼的郵件。

5. 獲取用戶資料 (fetchUserProfile):
- 從 Firestore 獲取指定用戶的個人資料。

6. 上傳大頭照 (uploadProfileImage):
- 上傳用戶大頭照到 Firebase Storage,並獲取存儲後的 URL。

7. 更新大頭照 URL (updateUserProfilePhoto):
- 更新 Firestore 中用戶資料裡的大頭照 URL。

4. Firebase Authentication _ 電子郵件/密碼

  • 為了透過 Firebase 來完成註冊/登入的功能,這邊先至其控制台啟用權限。
這邊先使用「電子郵件/密碼」,並且新增一個測試用的帳號密碼。
這邊先啟用email
  • Users頁面可以先新增要測試的「信箱、密碼」。
  • 但我這邊會透過程式碼來建立帳號,因為屆時會搭配Cloud Firestore 來建立存取額外的資訊(姓名、信箱、uid、圖片url)。

5.註冊流程觀察(creatUser)

  • 首先在註冊頁面輸入姓名、信箱、密碼來完成註冊。
註冊成功!但是姓名、信箱資訊的取用則是透過 Cloud Firestore,後續會說明。
  • 可以到 Firebase Authentication 觀察到註冊的資料已經存取成功。

5–1.程式碼部分_註冊用戶

  • 使用 FirebaseAuth 進行用戶註冊:
這部分是用戶註冊的核心。

1. 透過 Auth.auth().createUser(withEmail:password:) 方法來創建一個新用戶。

2. 如果這個方法呼叫成功,它會返回一個 result 包含用戶資訊。
或者在有錯誤的情況下返回一個 error。
同時處理用戶註冊(通過 FirebaseAuth)以及將額外資料存放到 Firestore。(後續說明)

5–2. 註冊介面的互動使用

1. 數據驗證:
- 首先檢查所有輸入欄位是否填寫完整。
- 接著驗證輸入的電子郵件和密碼是否符合格式要求。

2. 與 Firebase 互動:
- 若所有輸入合法,接著呼叫 FirebaseController 的 creatUser 來實際創建新用戶。
- creatUser 內部使用 FirebaseAuth 來註冊用戶。

## 並將額外的用戶信息(如名字、Email)存儲到 Firestore。(後續說明)##

3. 處理註冊結果:
- creatUser 執行完畢後,會通過一個閉包返回執行結果。
- 根據註冊操作的成功或失敗,會顯示「相應的提示訊息」或者「將用戶導向到主頁面」。

6.登入流程觀察

  • 分別輸入錯誤的訊息,得到警告。
  • 最後使用正確的信箱、密碼來完成登入。
  • 而關於最後顯示個人資料的頁面則也是透過Cloud Firestore來取得。

6–1.程式碼部分_登入 (signIn):

1. signIn 中使用的 Auth.auth().signIn(withEmail:password:) 方法:
- 僅涉及到用戶的「電子郵件」和「密碼」,並通過 FirebaseAuth 服務來驗證用戶身份。

2. 當用戶成功登入後,會返回一個 User 對象,這個對象包含了用戶的基本身份信息。
- EX: 用戶的唯一識別碼(UID)、電子郵件地址等。

6–2.登入介面的互動使用

1. 輸入驗證:
- 檢查電子郵件和密碼輸入框是否都已填寫。

2. 呼叫 Firebase 進行登入:
- 使用 FirebaseController.shared.signIn 進行登入,傳入用戶的電子郵件和密碼。
- 這是一個異步操作,需要等待 Firebase 返回登入結果。

3. 處理登入結果:
- 登入成功:將視圖切換到首頁(HomeViewController)。
- 登入失敗:則顯示錯誤提示。錯誤訊息會從 FirebaseController 返回的錯誤中獲取。

7.重設密碼 (sendPasswordReset)

  • 點擊忘記密碼按鈕,前往其頁面。
  • 輸入先前註冊過的信箱,藉此取得其信件。(輸入沒註冊過的信箱還是會發信)
  • Firebase 密碼重置功能的行為
1. 即使輸入的電子郵件地址未在 Firebase 註冊,Firebase 的 sendPasswordReset 
仍然會返回成功。

2. 這是一個安全特性,用來防止有意或無意地透露哪些電子郵件地址是已註冊的。

3. 從用戶體驗的角度來看,當用戶嘗試重置一個未註冊的電子郵件地址的密碼時,他們會收到一個
看似正常的「重置成功」消息。
  • 前往信箱收信:
  • 可到Firebase控制台調整發信內容:

7–1.程式碼部分(sendPasswordReset)

  • sendPasswordRest 的目的是發送一封密碼重置郵件給用戶。這是在用戶忘記密碼時常用的功能。
1. 輸入參數:
- 方法接受一個參數 email,這是用戶用於登錄的電子郵件地址。

2. Firebase Auth 調用:
- 使用 Auth.auth().sendPasswordReset(withEmail:) 來發送密碼重置郵件。
- 這是一個異步操作,Firebase 會處理發送郵件的過程。

3. 錯誤處理與結果回調:
- 發送失敗:發送郵件的過程中出現了錯誤,則這個錯誤會被捕獲並通過閉包 completion 返回。
- 如果郵件成功發送,則通過 completion 以 .success 狀態回調。

7–2.介面互動的處理

1. 電子郵件格式驗證:
- 使用 FirebaseController.isEmailvalid 方法檢查輸入的電子郵件是否符合正確格式。
- 如果格式不正確,顯示錯誤訊息並中止後續操作。

2. 發送密碼重置請求:
- 使用 FirebaseController.shared.sendPasswordRest 方法發送密碼重置請求。
- 這是一個異步操作,需要等待 Firebase 返回操作結果。

3. 處理密碼重置請求的結果:
- 如果請求成功發送(.success),顯示通知訊息,並在用戶確認後進行頁面跳轉。
- 如果請求失敗(.failure),顯示錯誤訊息。

8.登出功能

  • 點擊登出按鈕後,會顯示一個登出訊息提示,接著跳轉至起始頁面。
在HomeViewController直接處理登出
1. 嘗試登出:
- 使用 Auth.auth().signOut() 嘗試從 Firebase Authentication 服務中登出用戶。
這會終止用戶的登入狀態。

2. 處理登出結果:
- 如果登出成功,則顯示一個提示框通知用戶已成功登出。
- 隨後,在用戶確認提示框後,跳轉畫面至開頭頁面。

9–1.將「個人資料」儲存到 Firestore

  • 基本上可以直接從FirebaseCloud Firestore來新增資訊。
  • 但我這部分主要是處理註冊成功時的個人資訊,並將其展示。
  • 使用 setData,將用戶的 UID 作為文件 ID。確保每個用戶的資料都存儲在以他們的 UID 命名的唯一文件中。
1. 獲取用戶資訊:
- 一旦用戶註冊成功,從 Firebase 獲得一個包含用戶基本資訊(如 UID)的 User 對象。

2. 儲存額外用戶資訊至 Firestore:
- 使用 Firestore 的 setData ,將用戶的名字、姓氏、電郵地址和 UID 儲存到 Firestore
資料庫中。
- 這裡用用戶的 UID 作為 Firestore 中文檔的唯一識別碼。

3. 處理 Firestore 存儲結果:
- 如果用戶資訊成功儲存到 Firestore,則透過回傳成功訊息;如果儲存失敗,則回傳錯誤訊息。
  • 這樣就產生相對應的資訊在 Cloud Firestore

9–2.補充:setData、addDocument

  • setData: 用於當你已經有一個特定的文檔ID,想要更新該文檔或在該ID下創建一個新文檔。如果該ID的文檔已存在,setData 會覆蓋原有的數據。
  • addDocument: 用於當你不需要指定文檔ID,只想在某個集合下新增一個文檔。Firestore 會自動為這個新文檔生成一個唯一的ID。
在選擇使用哪種方法時,主要考慮是是否需要控制文檔的ID。

1. 如果需要指定或更新特定ID的文檔,使用 setData。

2. 如果只是想要簡單地添加一個新的文檔並讓 Firestore 自動生成ID,則使用 addDocument。

9–3.定義 Firestore 資料對應的自訂型別 (FirebaseFirestoreSwift)

  • Codable 協議使得 UserProfile 可以方便地從 Firestore 文檔轉換成 Swift 對象,或者反過來。
  • FirebaseFirestoreSwift 提供了將 Firestore 文檔轉換成 Swift 對象的功能。當從 Firestore 讀取用戶資料時,可以直接將其映射到 UserProfile 結構體。
註冊完成後才能夠設置大頭照,因此設為optional

9–4.獲取用戶資料

  • fetchUserProfile 在 FirebaseController 中的主要功能是從 Firestore 數據庫中獲取特定用戶的資料。
  • 透過用戶 id 讀取某個 document。
1. 參數 - 用戶 ID (uid):
- 用戶的唯一識別碼。用於在 Firestore 中定位特定的用戶文檔。

2. 獲取 Firestore 實例:
- 函數首先創建 Firestore 的實例,用於與 Firestore 數據庫進行互動。

3. 訪問 Firestore 文檔:
- 使用 db.collection("users").document(uid) 定位到特定用戶的文檔。
- 這裡的 "users" 是 Firestore 中存儲用戶資料的集合名稱。

4. 異步讀取文檔:
- getDocument 以異步方式讀取用戶文檔。當文檔讀取完成或發生錯誤時,回調函數會被觸發。

5. 處理文檔數據:
- 如果文檔存在且成功讀取,使用 document.data(as: UserProfile.self) 將文檔數據
映射到 UserProfile 結構體。

6. 錯誤處理:
- 如果文檔不存在、讀取失敗,或數據映射出現問題,函數會通過 completion 閉包回調相應的錯誤信息。

9–5.位於HomeViewController的實際運用

1. 確認用戶登入:
- 使用 guard let uid = Auth.auth().currentUser?.uid else { return }
確認當前有用戶已經登入,並獲取他們的唯一識別碼(UID)。

2. 從 Firestore 獲取用戶資料:
- 調用 FirebaseController.shared.fetchUserProfile(uid: uid)
從 Firestore 中獲取與 UID 相對應的用戶資料。這是一個異步操作。

3. 成功時更新 UI:
- 獲取用戶資料(.success(let userProfile)),則更新 UI 顯示用戶的名稱和電子郵件地址。
- 如果用戶資料中包含大頭照的 URL,則使用 Kingfisher 來非同步加載並顯示該圖片。

4. 失敗時處理錯誤:
- 如果獲取用戶資料失敗(.failure(let error)),則print錯誤訊息並顯示錯誤提示給用戶。
關於大頭照的處理後續會再說明。

10–1.處理圖片的上傳和用戶資料的更新

(原先將圖片上傳及用戶資料的更新分開來處理變得有點繁瑣XD)

  • uploadAndUpdateProfileImage 主要負責兩個工作:
  • 將用戶選擇的大頭照上傳至 Firebase Storage,並更新 Firebase Firestore Database中的用戶資料。
  • 因此,當用戶登出後再次登入時,App 可以從 Firestore 獲取用戶資料,包括大頭照的 URL,並使用這個 URL 來顯示用戶的大頭照。
  • 這樣確保用戶的個人資料(包括大頭照)即使在登出後再登入也能保持最新。
1. 上傳圖片至 Firebase Storage:
- 將用戶選擇的圖片上傳至 Firebase Storage。
- 這裡建立了一個指向特定用戶 ID 下的「profile_images」資料夾的參考。

2. 獲取圖片 URL:
- 上傳完成後,會從 Firebase Storage 獲取上傳圖片的 URL。

3. 更新 Firestore 中的用戶資料:
- 獲得的圖片 URL 後,將其作為「photoURL」會被存儲到用戶的 Firestore 文檔中。
- 這樣,用戶的 Firestore 資料就包含了最新的大頭照 URL。
  • 當我設置好金城武的大頭照時也會將其存放至Storage
  • 以及更新用戶資料「photoURL

10–2.實際運用

  • 當用戶選擇大頭照後,App 會執行uploadAndUpdateProfileImage,上傳新照片到 Firebase Storage,並更新 Firestore 中的用戶資料,以便該照片在下次登入時能正確顯示。
1 檢查用戶ID:
- 首先確保當前有用戶登入並獲取其唯一識別碼(UID)。

2. 調用 uploadAndUpdateProfileImage 方法:
- 將選擇的圖片及用戶ID傳入函數中。

3. 執行圖片上傳和資料更新:
3-1.圖片轉換:
- 將選擇的 UIImage 轉換成 JPEG 格式。
3-2.上傳圖片:
- 將轉換後的圖片數據上傳至 Firebase Storage,指定的位置是 "profile_images/(userID).jpg"。
3-3.獲取圖片URL:
- 上傳成功後,從 Storage 獲取上傳圖片的 URL。
3-4.更新 Firestore:
- 使用獲得的圖片 URL,更新 Firestore 中的用戶資料,具體是更新 photoURL 欄位。


4. 處理上傳結果:
4-1.圖片成功上傳並且用戶資料也成功更新:
- 則在界面上更新 userPhotoImageView 為選擇的圖片。
4-2.過程中出現錯誤:
- 則打印錯誤訊息並通過 AlertService 顯示錯誤訊息。

11.維持登入狀態

  • 檢查當前是否有用戶通過 Firebase Auth 登入。
  • 根據登入狀態,設置根視圖控制器:
若用戶已登入,則顯示主頁面(HomeViewController)
若未登入,則顯示登入或註冊介面(FirstViewController)

--

--

wei Tsao 學習紀錄
彼得潘的 Swift iOS / Flutter App 開發教室

Hi ! 我是wei , 先前未接觸過程式開發設計,想藉此來記錄自己的學習歷程,以利培養自己的程式邏輯 :)