[英]How to pass user id to view with firebase
我正在开发社交媒体应用程序,但无法显示特定用户的个人资料。 我可以用 currentUser?.uid 做到这一点,但我不知道如何将不同的 ID/用户传递给配置文件视图。 抱歉,如果这个解释令人困惑,我也很难用语言表达。
这是我的视图,它获取当前登录的用户并显示他们的用户名:
import SwiftUI
class TestProfileViewModel: ObservableObject {
@Published var qUser: User?
init() {
fetchCurrentUser()
}
func fetchCurrentUser() {
guard let uid = FirebaseManager.shared.auth.currentUser?.uid else { return }
FirebaseManager.shared.firestore.collection("users").document(uid).getDocument { snapshot, err in
if let err = err {
print("\(err)")
return
}
guard let data = snapshot?.data() else { return }
print(data)
self.qUser = .init(data: data)
}
}
}
struct TestProfileView: View {
@ObservedObject var vm = TestProfileViewModel()
var body: some View {
Text(vm.qUser?.username ?? "No User")
}
}
struct TestProfileView_Previews: PreviewProvider {
static var previews: some View {
TestProfileView()
}
}
这是视图,我想在其中使用 ID 从我的数据库中获取用户并将其用于配置文件视图,就像我对 currentUser 的处理方式一样:
import SwiftUI
struct TestSongVIew: View {
let testUsername = "John"
let testUserID = "123123"
@State var showingUserProfile = false
var body: some View {
VStack {
Button("Open \(testUsername)'s Profile") {
}
}
.fullScreenCover(isPresented: $showingUserProfile, onDismiss: nil) {
TestProfileView()
}
}
}
struct TestSongVIew_Previews: PreviewProvider {
static var previews: some View {
TestSongVIew()
}
}
这是我的 FirebaseManager 代码
import Foundation
import Firebase
import FirebaseFirestore
class FirebaseManager: NSObject {
let auth: Auth
let storage: Storage
let firestore: Firestore
static let shared = FirebaseManager()
override init() {
FirebaseApp.configure()
self.auth = Auth.auth()
self.storage = Storage.storage()
self.firestore = Firestore.firestore()
super .init()
}
}
有几种方法可以实现这一点,它们都取决于您如何为您的应用程序设置导航。
我目前正在撰写一篇博客文章/视频,以演示如何在 SwiftUI 应用程序中监控身份验证 state。 为了演示如何实现您的用例,我添加了一个配置文件屏幕,您可以通过两种方式使用它:
List
视图导航到配置文件屏幕,该视图显示 Firestore 中用户配置文件集合中的所有用户配置文件。 如果您想要实现一个高分屏幕,允许用户导航到游戏中排名前 10 的每个玩家的个人资料屏幕,这可能很有用。好的,这里是:
import Foundation
import FirebaseFirestore
import FirebaseFirestoreSwift
struct Profile: Identifiable, Codable {
@DocumentID var id: String? = ""
var nickname: String
}
extension Profile {
static let empty = Profile(nickname: "")
}
import Foundation
import Combine
import FirebaseFirestore
import FirebaseFirestoreSwift
class ProfileViewModel: ObservableObject {
// MARK: - Output
@Published var profile: Profile
init(profile: Profile) {
self.profile = profile
}
init(uid: String) {
self.profile = Profile.empty
fetchProfile(uid)
}
// MARK: - Private attributes
private var db = Firestore.firestore()
func fetchProfile(_ uid: String) {
db.collection("profiles")
.whereField("uid", isEqualTo: uid)
.getDocuments { querySnapshot, error in
if let error = error {
print("Error getting documents: \(error)")
}
else {
if let querySnapshot = querySnapshot {
if let document = querySnapshot.documents.first {
do {
self.profile = try document.data(as: Profile.self)
}
catch {
}
}
}
}
}
}
}
struct ProfileView: View {
@ObservedObject var viewModel: ProfileViewModel
init(profile: Profile) {
self.viewModel = ProfileViewModel(profile: profile)
}
init(uid: String) {
self.viewModel = ProfileViewModel(uid: uid)
}
var body: some View {
Form {
Text(viewModel.profile.nickname)
}
.navigationTitle("Details")
}
}
struct ProfileView_Previews: PreviewProvider {
static var previews: some View {
ProfileView(profile: Profile(nickname: "freter"))
}
}
import Foundation
import Combine
import FirebaseAuth
class SettingsViewModel: ObservableObject {
// MARK: - Output
@Published var email: String = ""
@Published var idToken: String = ""
@Published var user: User?
@Published var authenticationState: AuthenticationState = .unauthenticated
// MARK: - Dependencies
private var authenticationService: AuthenticationService?
func connect(authenticationService: AuthenticationService) {
if self.authenticationService == nil {
self.authenticationService = authenticationService
self.authenticationService?
.$authenticationState
.assign(to: &$authenticationState)
self.authenticationService?
.$user
.assign(to: &$user)
$user
.map { $0?.email }
.replaceNil(with: "(no email address)")
.assign(to: &$email)
}
}
@MainActor
func refreshIDToken() {
Task {
do {
idToken = try await user?.idTokenForcingRefresh(true) ?? ""
}
catch {
idToken = error.localizedDescription
print(error)
}
}
}
}
import SwiftUI
struct SettingsView: View {
@StateObject var viewModel = SettingsViewModel()
@Environment(\.dismiss) var dismiss
@EnvironmentObject var authenticationService: AuthenticationService
@State private var presentingLoginScreen = false
var loginButton: some View {
Button(authenticationService.authenticationState == .unauthenticated ? "Login" : "Logout") {
if authenticationService.authenticationState == .unauthenticated {
presentingLoginScreen.toggle()
}
else {
authenticationService.signOut()
}
}
.frame(maxWidth: .infinity)
}
var body: some View {
Form {
Section {
Label("Help & Feedback", systemImage: "questionmark.circle")
Label("About", systemImage: "info.circle")
}
Section {
Label(viewModel.email, systemImage: "at")
Label(viewModel.idToken, systemImage: "person")
Button(action: viewModel.refreshIDToken) {
Text("Refresh ID token")
}
NavigationLink(destination: ProfileView(uid: viewModel.user?.uid ?? "unknown")) {
Label("Show user profile", systemImage: "person")
}
} header: {
Text("User Details")
}
Section {
loginButton
}
}
.sheet(isPresented: $presentingLoginScreen) {
LoginView()
}
.navigationTitle("Settings")
.navigationBarTitleDisplayMode(.inline)
.toolbar {
ToolbarItem(placement: .confirmationAction) {
Button("Done") {
dismiss()
}
}
}
.onAppear {
viewModel.connect(authenticationService: authenticationService)
}
}
}
struct SettingsView_Previews: PreviewProvider {
static var previews: some View {
NavigationView {
SettingsView()
.environmentObject(AuthenticationService())
}
}
}
import Foundation
import FirebaseAuth
enum AuthenticationState {
case unauthenticated
case authenticating
case authenticated
}
class AuthenticationService: ObservableObject {
// MARK: - Output
@Published var authenticationState: AuthenticationState = .unauthenticated
@Published var errorMessage: String = ""
@Published var user: User?
init() {
registerAuthStateListener()
}
@MainActor
func signIn(withEmail email: String, password: String) async -> Bool {
authenticationState = .authenticating
do {
try await Auth.auth().signIn(withEmail: email, password: password)
return true
}
catch {
await MainActor.run {
errorMessage = error.localizedDescription
authenticationState = .unauthenticated
}
print(error)
return false
}
}
func signOut() {
do {
try Auth.auth().signOut()
}
catch {
print(error)
}
}
private var handle: AuthStateDidChangeListenerHandle?
private func registerAuthStateListener() {
if handle == nil {
handle = Auth.auth().addStateDidChangeListener { auth, user in
Task {
await MainActor.run {
self.user = user
if let user = user {
self.authenticationState = .authenticated
print("User \(user.uid) signed in. Email: \(user.email ?? "(no email address set)"), anonymous: \(user.isAnonymous)")
}
else {
self.authenticationState = .unauthenticated
print("User signed out.")
}
}
}
}
}
}
}
import Foundation
import Combine
import FirebaseAuth
class LoginViewModel: ObservableObject {
// MARK: - Input
@Published var email: String = ""
@Published var password: String = ""
// MARK: - Output
@Published var isValid: Bool = false
@Published var authenticationState: AuthenticationState = .unauthenticated
@Published var errorMessage: String = ""
@Published var user: User?
// MARK: - Dependencies
private var authenticationService: AuthenticationService?
func connect(authenticationService: AuthenticationService) {
if self.authenticationService == nil {
self.authenticationService = authenticationService
self.authenticationService?
.$authenticationState
.assign(to: &$authenticationState)
self.authenticationService?
.$errorMessage
.assign(to: &$errorMessage)
self.authenticationService?
.$user
.assign(to: &$user)
Publishers.CombineLatest($email, $password)
.map { !($0.isEmpty && $1.isEmpty) }
.print()
.assign(to: &$isValid)
}
}
func signInWithEmailPassword() async -> Bool {
if let authenticationService = authenticationService {
return await authenticationService.signIn(withEmail: email, password: password)
}
else {
return false
}
}
}
import SwiftUI
enum FocusableField: Hashable {
case email
case password
}
struct LoginView: View {
@StateObject var viewModel = LoginViewModel()
@EnvironmentObject var authenticationService: AuthenticationService
@Environment(\.dismiss) var dismiss
@FocusState private var focus: FocusableField?
private func signInWithEmailPassword() {
Task {
if await viewModel.signInWithEmailPassword() == true {
dismiss()
}
}
}
var body: some View {
VStack {
Image("Login")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(minHeight: 0)
Text("Login")
.font(.largeTitle)
.fontWeight(.bold)
.frame(maxWidth: .infinity, alignment: .leading)
HStack {
Image(systemName: "at")
TextField("Email", text: $viewModel.email)
.textInputAutocapitalization(.never)
.disableAutocorrection(true)
.focused($focus, equals: .email)
.submitLabel(.next)
.onSubmit {
self.focus = .password
}
}
.padding(.vertical, 6)
.background(Divider(), alignment: .bottom)
.padding(.bottom, 4)
HStack {
Image(systemName: "lock")
SecureField("Password", text: $viewModel.password)
.focused($focus, equals: .password)
.submitLabel(.go)
.onSubmit {
signInWithEmailPassword()
}
}
.padding(.vertical, 6)
.background(Divider(), alignment: .bottom)
.padding(.bottom, 8)
if !viewModel.errorMessage.isEmpty {
VStack {
Text(viewModel.errorMessage)
.foregroundColor(Color(UIColor.systemRed))
}
}
Button(action: signInWithEmailPassword) {
if viewModel.authenticationState != .authenticating {
Text("Login")
.frame(maxWidth: .infinity)
}
else {
ProgressView()
.progressViewStyle(CircularProgressViewStyle(tint: .white))
.frame(maxWidth: .infinity)
}
}
.disabled(!viewModel.isValid)
.frame(maxWidth: .infinity)
.buttonStyle(.borderedProminent)
.controlSize(.large)
HStack {
VStack { Divider() }
Text("or")
VStack { Divider() }
}
Button(action: { }) {
Image(systemName: "applelogo")
.frame(maxWidth: .infinity)
}
.foregroundColor(.black)
.buttonStyle(.bordered)
.controlSize(.large)
HStack {
Text("Don't have an account yet?")
Button(action: {}) {
Text("Sign up")
.fontWeight(.semibold)
.foregroundColor(.blue)
}
}
.padding([.top, .bottom], 50)
}
.onAppear {
viewModel.connect(authenticationService: authenticationService)
}
.listStyle(.plain)
.padding()
}
}
struct LoginView_Previews: PreviewProvider {
static var previews: some View {
Group {
LoginView()
.environmentObject(AuthenticationService())
LoginView()
.preferredColorScheme(.dark)
.environmentObject(AuthenticationService())
}
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.