【问题标题】:SwiftUI using Firebase AuthenticationSwiftUI 使用 Firebase 身份验证
【发布时间】:2020-03-16 18:37:07
【问题描述】:

我正在尝试通过电子邮件/密码登录来使用 SwiftUI + Firebase 身份验证。我的问题是,有没有办法在用户创建帐户时将个人资料信息附加到用户的身份验证信息中,还是我必须将 Firebase Auth 与 Firestore 或 Firebase 数据库结合使用?我只是想收集用户的名字和姓氏,可能还有城市/州/国家/地区。

import SwiftUI
import Firebase
import Combine

class SessionStore: ObservableObject {
    var didChange = PassthroughSubject<SessionStore, Never>()
    @Published var session: User? {didSet {self.didChange.send(self) }}
    var handle: AuthStateDidChangeListenerHandle?

    func listen() {
        handle = Auth.auth().addStateDidChangeListener({ (auth, user) in
            if let user = user {
                self.session = User(uid: user.uid, email: user.email, displayName: user.displayName)
            } else {
                self.session = nil
            }
        })
    }

    func signUp(email: String, password: String, handler: @escaping AuthDataResultCallback) {
        Auth.auth().createUser(withEmail: email, password: password, completion: handler)
    }

    func signIn(email: String, password: String, handler: @escaping AuthDataResultCallback) {
        Auth.auth().signIn(withEmail: email, password: password, completion: handler)
    }

    func signOut() {
        do {
            try Auth.auth().signOut()
            self.session = nil
        } catch {
            print("Error Signing Out")
        }
    }

    func unbind() {
        if let handle = handle {
            Auth.auth().removeStateDidChangeListener(handle)
        }
    }

    deinit {
        unbind()
    }
}

struct User {
    var uid: String
    var email: String?
    var displayName: String?

    init(uid: String, email: String?, displayName: String?) {
        self.uid = uid
        self.email = email
        self.displayName = displayName
    }
}

【问题讨论】:

  • 到目前为止你尝试了什么?
  • 我用我当前的代码更新了我的问题。
  • 没有使用 Firestore 或 Firebase 的经验我不得不说您需要先使用 Firebase 创建帐户,然后将其他信息存储在单独的表中。
  • 我认为@Published 不再需要PassthroughSubject。它自己做。

标签: firebase firebase-authentication swiftui


【解决方案1】:

CombineFirebase 库将简化您的问题。 您可以使用您的 userProfilePublisher 进行简单的登录/stateChangePublisher flatMapped

【讨论】:

    【解决方案2】:

    Firebase 身份验证管理用户身份验证 - 虽然它确实存储了一些联合身份提供者提供的其他信息(例如个人资料图片 URL),但它并非旨在成为个人资料管理解决方案。如果您想存储有关用户的其他信息,可以使用 Cloud Firestore 或 Firebase 实时数据库来实现。

    请注意,Custom Claims feature 用于管理高级角色管理功能 - documentation actually discourages 开发人员使用此功能来存储其他用户信息,因为自定义声明实际上存储在 ID 令牌中。

    正如@krjw 正确提到的那样,使用@Published 时不需要使用PassthroughObject。

    这是一个示例实现:

    // File: UserProfileRepository.swift
    import Foundation
    import Firebase
    import FirebaseFirestoreSwift
    
    struct UserProfile: Codable {
      var uid: String
      var firstName: String
      var lastName: String
      var city: String
    }
    
    class UserProfileRepository: ObservableObject {
      private var db = Firestore.firestore()
    
      func createProfile(profile: UserProfile, completion: @escaping (_ profile: UserProfile?, _ error: Error?) -> Void) {
        do {
          let _ = try db.collection("profiles").document(profile.uid).setData(from: profile)
          completion(profile, nil)
        }
        catch let error {
          print("Error writing city to Firestore: \(error)")
          completion(nil, error)
        }
      }
    
      func fetchProfile(userId: String, completion: @escaping (_ profile: UserProfile?, _ error: Error?) -> Void) {
        db.collection("profiles").document(userId).getDocument { (snapshot, error) in
          let profile = try? snapshot?.data(as: UserProfile.self)
          completion(profile, error)
        }
      }
    }
    
    // File: SessionStore.swift
    import Foundation
    import Combine
    import Firebase
    
    class SessionStore: ObservableObject {
      @Published var session: User?
      @Published var profile: UserProfile?
    
      private var profileRepository = UserProfileRepository()
    
      func signUp(email: String, password: String, firstName: String, lastName: String, city: String, completion: @escaping (_ profile: UserProfile?, _ error: Error?) -> Void) {
        Auth.auth().createUser(withEmail: email, password: password) { (result, error) in
          if let error = error {
            print("Error signing up: \(error)")
            completion(nil, error)
            return
          }
    
          guard let user = result?.user else { return }
          print("User \(user.uid) signed up.")
    
          let userProfile = UserProfile(uid: user.uid, firstName: firstName, lastName: lastName, city: city)
          self.profileRepository.createProfile(profile: userProfile) { (profile, error) in
            if let error = error {
              print("Error while fetching the user profile: \(error)")
              completion(nil, error)
              return
            }
            self.profile = profile
            completion(profile, nil)
          }
        }
      }
    
      func signIn(email: String, password: String, completion: @escaping (_ profile: UserProfile?, _ error: Error?) -> Void) {
        Auth.auth().signIn(withEmail: email, password: password) { (result, error) in
          if let error = error {
            print("Error signing in: \(error)")
            completion(nil, error)
            return
          }
    
          guard let user = result?.user else { return }
          print("User \(user.uid) signed in.")
    
          self.profileRepository.fetchProfile(userId: user.uid) { (profile, error) in
            if let error = error {
              print("Error while fetching the user profile: \(error)")
              completion(nil, error)
              return
            }
    
            self.profile = profile
            completion(profile, nil)
          }
        }
      }
    
      func signOut() {
        do {
          try Auth.auth().signOut()
          self.session = nil
          self.profile = nil
        }
        catch let signOutError as NSError {
          print("Error signing out: \(signOutError)")
        }
      }
    }
    

    对于用户界面:

    // File: ContentView.swift
    import SwiftUI
    
    struct ContentView: View {
      @State var firstName: String = ""
      @State var lastName: String = ""
      @State var city: String = ""
      @State var email: String = ""
      @State var password: String = ""
      @State var confirmPassword: String = ""
    
      @State var showSignUpForm = true
      @State var showDetails = false
    
      @ObservedObject var sessionStore = SessionStore()
      @State var profile: UserProfile?
    
      var body: some View {
        NavigationView {
          VStack {
            if self.showSignUpForm {
              Form {
                Section {
                  TextField("First name", text: $firstName)
                    .textContentType(.givenName)
                  TextField("Last name", text: $lastName)
                    .textContentType(.familyName)
                  TextField("City", text: $city)
                    .textContentType(.addressCity)
                }
                Section {
                  TextField("Email", text: $email)
                    .textContentType(.emailAddress)
                    .autocapitalization(.none)
                  SecureField("Password", text: $password)
                  SecureField("Confirm password", text: $confirmPassword)
                }
                Button(action: { self.signUp() }) {
                  Text("Sign up")
                }
              }
              .navigationBarTitle("Sign up")
            }
            else {
              Form {
                TextField("Email", text: $email)
                  .textContentType(.emailAddress)
                  .autocapitalization(.none)
                SecureField("Password", text: $password)
                Button(action: { self.signIn() }) {
                  Text("Sign in")
                }
              }
              .navigationBarTitle("Sign in")
            }
            Button(action: { self.showSignUpForm.toggle() }) {
              Text(self.showSignUpForm ? "Have an account? Sign in instead." : "No account yet? Click here to sign up instead.")
            }
          }
          .sheet(isPresented: $showDetails) {
            UserProfileView(userProfile: self.profile ??  UserProfile(uid: "", firstName: "", lastName: "", city: ""))
          }
        }
      }
    
      func signUp() {
        sessionStore.signUp(email: self.email, password: self.password, firstName: self.firstName, lastName: self.lastName, city: self.city) { (profile, error) in
          if let error = error {
            print("Error when signing up: \(error)")
            return
          }
          self.profile = profile
          self.showDetails.toggle()
        }
      }
    
      func signIn() {
        sessionStore.signIn(email: self.email, password: self.password) { (profile, error) in
          if let error = error {
            print("Error when signing up: \(error)")
            return
          }
          self.profile = profile
          self.showDetails.toggle()
        }
      }
    }
    
    struct ContentView_Previews: PreviewProvider {
      static var previews: some View {
        ContentView()
      }
    }
    
    // File: UserProfileView.swift
    import SwiftUI
    
    struct UserProfileView: View {
      var userProfile: UserProfile
      var body: some View {
        NavigationView {
          Form {
            Text(userProfile.uid)
            Text(userProfile.firstName)
            Text(userProfile.lastName)
            Text(userProfile.city)
          }
          .navigationBarTitle("User \(userProfile.uid)")
        }
      }
    }
    
    struct UserProfileView_Previews: PreviewProvider {
      static var previews: some View {
        let userProfile = UserProfile(uid: "TEST1234", firstName: "Peter", lastName: "Friese", city: "Hamburg")
        return UserProfileView(userProfile: userProfile)
      }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-07-03
      • 1970-01-01
      • 2016-09-26
      • 2017-03-11
      • 1970-01-01
      • 2020-04-01
      • 2022-12-10
      相关资源
      最近更新 更多