You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SwiftUI从Firebase加载用户数据至个人资料页问题求助

问题解决:SwiftUI + Firebase 加载用户个人资料失败

核心问题分析

你的代码存在几个关键错误,导致无法正确读取Firestore中的用户数据:

  • 集合名称不统一:保存时用db.collection(name)(用户输入的名字),读取时却用db.collection("name"),两者完全不匹配,根本找不到对应数据。
  • 图片引用存储错误:直接把Storage引用对象转成字符串存入Firestore,这不是有效的图片下载地址,后续无法加载图片。
  • 读取逻辑错误:全量读取集合数据,没有根据当前登录用户的UID筛选,而且强制解包QuerySnapshot容易崩溃。
  • 异步操作顺序问题:保存图片和写入Firestore是并行异步操作,可能Firestore已经写入,但图片还没上传完成,导致引用无效。

修正后的代码实现

1. 保持Model不变(无需修改)

import SwiftUI
import Firebase

// MARK: - User Model
struct FirebaseUserModel: Identifiable {
    var id: String
    var coverImage: String
    var profileImage: String
    var username: String
    var name: String
    var age: String
    var description: String
    var gender: String
}

2. 修正FirebaseViewModel

import Firebase
import SwiftUI

class FirebaseViewModel: ObservableObject {
    
    @Published var currentUser: FirebaseUserModel?
    @Published var isLoading = false
    
    // MARK: - 保存用户信息(含图片)
    func saveInfoUser(coverImage: Data, profileImage: Data, username: String, name: String, age: String, description: String, gender: String, completion: @escaping (_ success: Bool) -> Void) {
        guard let currentUserUID = Auth.auth().currentUser?.uid else {
            completion(false)
            return
        }
        
        isLoading = true
        let storage = Storage.storage().reference()
        
        // 生成图片存储路径,用用户UID作为目录,避免重名
        let profileImageRef = storage.child("ProfileImages/\(currentUserUID)/profile.png")
        let coverImageRef = storage.child("CoverImages/\(currentUserUID)/cover.png")
        
        let metadata = StorageMetadata()
        metadata.contentType = "image/png"
        
        // 按顺序上传头像、封面,最后写入Firestore
        profileImageRef.putData(profileImage, metadata: metadata) { [weak self] _, profileError in
            guard let self = self else { return }
            
            if let profileError = profileError {
                print("头像上传失败: \(profileError.localizedDescription)")
                self.isLoading = false
                completion(false)
                return
            }
            
            // 获取头像下载URL
            profileImageRef.downloadURL { profileURL, urlError in
                guard let profileURL = profileURL?.absoluteString else {
                    print("无法获取头像URL")
                    self.isLoading = false
                    completion(false)
                    return
                }
                
                // 上传封面图片
                coverImageRef.putData(coverImage, metadata: metadata) { _, coverError in
                    if let coverError = coverError {
                        print("封面上传失败: \(coverError.localizedDescription)")
                        self.isLoading = false
                        completion(false)
                        return
                    }
                    
                    // 获取封面下载URL
                    coverImageRef.downloadURL { coverURL, coverURLError in
                        guard let coverURL = coverURL?.absoluteString else {
                            print("无法获取封面URL")
                            self.isLoading = false
                            completion(false)
                            return
                        }
                        
                        // 写入Firestore,用用户UID作为文档ID,方便后续读取
                        let db = Firestore.firestore()
                        let userData: [String: Any] = [
                            "coverImage": coverURL,
                            "profileImage": profileURL,
                            "username": username,
                            "name": name,
                            "age": age,
                            "description": description,
                            "gender": gender,
                            "idUser": currentUserUID,
                            "email": Auth.auth().currentUser?.email ?? ""
                        ]
                        
                        db.collection("Users").document(currentUserUID).setData(userData) { firestoreError in
                            self.isLoading = false
                            if let firestoreError = firestoreError {
                                print("Firestore写入失败: \(firestoreError.localizedDescription)")
                                completion(false)
                            } else {
                                print("数据保存成功")
                                completion(true)
                            }
                        }
                    }
                }
            }
        }
    }
    
    // MARK: - 加载当前登录用户的信息
    func loadCurrentUser() {
        guard let currentUserUID = Auth.auth().currentUser?.uid else { return }
        
        isLoading = true
        let db = Firestore.firestore()
        
        // 根据用户UID精准获取文档,添加监听实现实时更新
        db.collection("Users").document(currentUserUID).addSnapshotListener { [weak self] documentSnapshot, error in
            guard let self = self else { return }
            self.isLoading = false
            
            if let error = error {
                print("读取用户数据失败: \(error.localizedDescription)")
                return
            }
            
            guard let document = documentSnapshot, document.exists, let data = document.data() else {
                print("用户文档不存在")
                self.currentUser = nil
                return
            }
            
            // 解析数据到模型,确保在主线程更新UI
            DispatchQueue.main.async {
                self.currentUser = FirebaseUserModel(
                    id: document.documentID,
                    coverImage: data["coverImage"] as? String ?? "",
                    profileImage: data["profileImage"] as? String ?? "",
                    username: data["username"] as? String ?? "",
                    name: data["name"] as? String ?? "",
                    age: data["age"] as? String ?? "",
                    description: data["description"] as? String ?? "",
                    gender: data["gender"] as? String ?? ""
                )
            }
        }
    }
}

3. SwiftUI 个人资料展示视图示例

import SwiftUI

struct ProfileView: View {
    @StateObject private var viewModel = FirebaseViewModel()
    
    var body: some View {
        NavigationStack {
            if viewModel.isLoading {
                ProgressView("加载中...")
                    .navigationTitle("个人资料")
            } else if let user = viewModel.currentUser {
                ScrollView {
                    // 封面图
                    AsyncImage(url: URL(string: user.coverImage)) { image in
                        image
                            .resizable()
                            .scaledToFill()
                            .frame(height: 200)
                            .clipped()
                    } placeholder: {
                        Color.gray.opacity(0.3)
                            .frame(height: 200)
                    }
                    
                    // 头像
                    AsyncImage(url: URL(string: user.profileImage)) { image in
                        image
                            .resizable()
                            .scaledToFill()
                            .frame(width: 120, height: 120)
                            .clipShape(Circle())
                            .overlay(Circle().stroke(Color.white, lineWidth: 4))
                            .offset(y: -60)
                            .padding(.bottom, -60)
                    } placeholder: {
                        Circle()
                            .fill(Color.gray.opacity(0.3))
                            .frame(width: 120, height: 120)
                            .offset(y: -60)
                            .padding(.bottom, -60)
                    }
                    
                    // 用户信息
                    VStack(spacing: 8) {
                        Text(user.name)
                            .font(.title)
                            .fontWeight(.bold)
                        Text("@\(user.username)")
                            .font(.subheadline)
                            .foregroundColor(.secondary)
                        Text("年龄: \(user.age) | 性别: \(user.gender)")
                            .font(.caption)
                            .foregroundColor(.secondary)
                        
                        Divider()
                        
                        Text(user.description)
                            .font(.body)
                            .multilineTextAlignment(.center)
                            .padding(.horizontal)
                    }
                    .padding()
                }
                .navigationTitle("个人资料")
            } else {
                Text("未找到用户信息")
                    .font(.headline)
                    .navigationTitle("个人资料")
            }
        }
        .onAppear {
            viewModel.loadCurrentUser()
        }
    }
}

struct ProfileView_Previews: PreviewProvider {
    static var previews: some View {
        ProfileView()
    }
}

关键改进点说明

  • 统一集合名称:用固定的"Users"集合存储所有用户数据,避免混乱。
  • 用户UID作为文档ID:方便精准定位当前用户的文档,无需遍历整个集合。
  • 正确获取图片下载URL:上传图片后获取downloadURL,存入Firestore的是可直接访问的URL,而非引用对象的字符串。
  • 异步操作顺序优化:按顺序上传头像、封面,再写入Firestore,确保所有数据准备完毕后再持久化。
  • 实时监听文档变化:使用addSnapshotListener实现用户资料的实时更新。
  • 避免强制解包:用可选绑定处理可能的空值,提升代码稳定性。
  • 加载状态管理:通过isLoading属性控制加载指示器的显示,优化用户体验。

内容的提问来源于stack exchange,提问作者Luciano Sanchez Balbuena

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 12:59:14