C72. 練習Firebase Authentication 和 Cloud Firestore打造註冊登入功能
Published in
19 min readDec 2, 2023
- 主要是因為當初在透過FavQs的註冊、登入API時,就一直想要來練習Firebase的註冊登入功能,因爲在
個人資訊
以及大頭照圖片
的使用配置上更有彈性!!!
· 參考
· 1.學習目標:Firebase Authentication 和 Cloud Firestore搭配使用
· 2.版面配置
· 3.FirebaseControlle
· 4.Firebase Authentication_電子郵件/密碼
· 5.註冊流程觀察(creatUser)
∘ 5–1.程式碼部分_註冊用戶
∘ 5–2.註冊介面的互動使用
· 6.登入流程觀察
∘ 6–1.程式碼部分_登入 (signIn)
∘ 6–2.登入介面的互動使用
· 7.重設密碼 (sendPasswordReset)
∘ 7–1.程式碼部分(sendPasswordReset)
∘ 7–2.介面互動的處理
· 8.登出功能
· 9.Cloud Firestore
∘ 9–1.將「個人資料」儲存到 Firestore
∘ 9–2.補充:setData、addDocument
∘ 9–3.定義 Firestore 資料對應的自訂型別 (FirebaseFirestoreSwift)
∘ 9–4.獲取用戶資料
∘ 9–5.位於HomeViewController的實際運用
· 10.大頭照圖檔的存取運用
∘ 10–1.處理圖片的上傳和用戶資料的更新
∘ 10–2.實際運用
· 11.維持登入狀態
· GitHub
1. 學習目標: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 來完成
註冊/登入
的功能,這邊先至其控制台啟用權限。
這邊先使用「電子郵件/密碼」,並且新增一個測試用的帳號密碼。
- 在
Users
頁面可以先新增要測試的「信箱、密碼
」。 - 但我這邊會透過
程式碼
來建立帳號,因為屆時會搭配Cloud Firestore
來建立存取額外的資訊(姓名、信箱、uid、圖片url
)。
5.註冊流程觀察(creatUser)
- 首先在註冊頁面輸入姓名、信箱、密碼來完成註冊。
- 可以到
Firebase Authentication
觀察到註冊的資料已經存取成功。
5–1.程式碼部分_註冊用戶
- 使用
FirebaseAuth
進行用戶註冊:
這部分是用戶註冊的核心。
1. 透過 Auth.auth().createUser(withEmail:password:) 方法來創建一個新用戶。
2. 如果這個方法呼叫成功,它會返回一個 result 包含用戶資訊。
或者在有錯誤的情況下返回一個 error。
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.登出功能
- 點擊登出按鈕後,會顯示一個登出訊息提示,接著跳轉至起始頁面。
1. 嘗試登出:
- 使用 Auth.auth().signOut() 嘗試從 Firebase Authentication 服務中登出用戶。
這會終止用戶的登入狀態。
2. 處理登出結果:
- 如果登出成功,則顯示一個提示框通知用戶已成功登出。
- 隨後,在用戶確認提示框後,跳轉畫面至開頭頁面。
9.Cloud Firestore
- 為了製作登入成功後
HomeViewController
的個人資訊頁面,因此搭配使用Cloud Firestore
。
9–1.將「個人資料」儲存到 Firestore
- 基本上可以直接從
Firebase
的Cloud 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
結構體。
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.大頭照圖檔的存取運用
- 先設置
ImagePickerManager
進行圖片的選取。(PHPickerConfiguration)
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)