注册时无法上传头像至Firebase Storage与Firestore的问题求助
问题描述
Auth功能正常,可创建包含全名、uid及密码的用户,但头像URL未存入Firestore数据库,实际头像也未上传至Firebase Storage。期望用户点击“Sign Up”时,完成账号创建的同时完成头像存储。
Firebase Storage规则
rules_version = '2'; service firebase.storage { match /b/{bucket}/o { match /{allPaths=**} { allow read, write: if request.auth != null; } } }
CreateAccountAuth 视图
import SwiftUI struct CreateAccountAuth: View { @State private var email = "" @State private var password = "" @State private var fullname = "" @Environment(\.presentationMode) var presentationMode @EnvironmentObject var viewModel: AuthViewModel @State private var showImagePicker = false @State private var selectedImage: UIImage? @State private var profileImage: Image? var body: some View { VStack(alignment: .leading) { Text("Let's get started") .font(.title) .fontWeight(.semibold) Button { showImagePicker.toggle() } label: { if let profileImage = profileImage { profileImage .resizable() .scaledToFill() .frame(width: 100, height: 100) .cornerRadius(10) } else { Image("EmptyProfile") .resizable() .scaledToFill() .frame(width: 100, height: 100) } } .padding(.bottom, 24) .fullScreenCover( isPresented: $showImagePicker, onDismiss: loadImage) { ImagePicker(image: $selectedImage) } VStack (alignment:.leading) { CustomInputField(imageName: "person", placeholderText: "Name", text: $fullname) .padding(.bottom, 24) CustomInputField(imageName: "mail", placeholderText: "Email", text: $email) .padding(.bottom, 24) CustomInputField( imageName: "lock", placeholderText: "Password", isSecureField: true, text: $password) }.padding(.bottom, 16) VStack { Button { viewModel.register( withEmail: email, password: password, fullname: fullname) } label: { Text("Sign Up") .font(.headline) .foregroundColor(.white) .frame(height: 50) .frame(maxWidth: .infinity) .background(Color.accentColor) .clipShape(Capsule()) .padding(.vertical) } Text("or") Button { print("Continue with phone..") } label: { Text("Sign up with phone") .font(.headline) .fontWeight(.regular) .foregroundColor(.black) .frame(height: 50) .frame(maxWidth: .infinity) .background(Color(.systemGray5)) .clipShape(Capsule()) .padding(.vertical) } Spacer() Button { presentationMode.wrappedValue.dismiss() } label: { HStack { Text("Already have an account?") .font(.footnote) Text("Sign in") .font(.footnote) .fontWeight(.semibold) } } .padding(.bottom, 40) } .padding(.top, 56) } .padding(.top, 40) .navigationBarHidden(true) .padding(.leading) .padding(.trailing) } func loadImage() { guard let selectedImage = selectedImage else { return } profileImage = Image(uiImage: selectedImage) } } struct CreateAccountAuth_Previews: PreviewProvider { static var previews: some View { CreateAccountAuth() } }
AuthViewModel
import SwiftUI import FirebaseAuth import FirebaseCore import FirebaseStorage import FirebaseFirestore import FirebaseFirestoreSwift class AuthViewModel: ObservableObject { @Published var userSession: User? @Published var didAuthenticateUser = false private var tempUserSession: User? init() { self.userSession = Auth.auth().currentUser print("DEBUG: User session is \(self.userSession?.uid)") } func login(withEmail email: String, password: String) { Auth.auth().signIn(withEmail: email, password: password) { result, error in if let error = error { print("DEBUG: Failed to register with error \(error.localizedDescription)") return } guard let user = result?.user else { return } self.userSession = user print("DEBUG: Did log user in...") } } func register(withEmail email: String, password: String, fullname: String) { Auth.auth().createUser(withEmail: email, password: password) { result, error in if let error = error { print("DEBUG: Failed to register with error \(error.localizedDescription)") return } guard let user = result?.user else { return } self.tempUserSession = user print("DEBUG: Registered user successfully") print("DEBUG: User is \(self.userSession)") let data = ["email": email, "fullname": fullname, "uid": user.uid] Firestore.firestore().collection("users") .document(user.uid) .setData(data) { _ in self.didAuthenticateUser = true } } } func signOut() { //sets user session to nil so we show login view userSession = nil tempUserSession = nil didAuthenticateUser = false //signs user out on server(backend) try? Auth.auth().signOut() } func uploadProfileImage(_ image: UIImage) { guard let uid = tempUserSession?.uid else { return } ImageUploader.uploadImage(image: image) { profileImageUrl in Firestore.firestore().collection("users") .document(uid) .updateData(["profileImageUrl": profileImageUrl]) { _ in self.userSession = self.tempUserSession } } } }
ImageUploader
import Firebase import UIKit import FirebaseStorage struct ImageUploader { static func uploadImage(image: UIImage, completion: @escaping(String) -> Void) { guard let imageData = image.jpegData(compressionQuality: 0.5) else { return } let filename = NSUUID().uuidString let ref = Storage.storage().reference(withPath: "/profile_image/\(filename)") ref.putData(imageData, metadata: nil) { _, error in if let error = error { print("DEBUG: Failed to upload image with error: \(error.localizedDescription)") return } ref.downloadURL { imageUrl, _ in guard let imageUrl = imageUrl?.absoluteString else { return } completion(imageUrl) } } } }
解决方案
问题核心是点击Sign Up时未触发头像上传逻辑,同时需确保上传时机在用户创建成功后。
1. 修改CreateAccountAuth的Sign Up按钮逻辑
在点击Sign Up时,先完成用户注册,再调用头像上传方法:
Button { viewModel.register(withEmail: email, password: password, fullname: fullname) // 若用户选择了头像,执行上传 if let selectedImage = selectedImage { viewModel.uploadProfileImage(selectedImage) } } label: { Text("Sign Up") .font(.headline) .foregroundColor(.white) .frame(height: 50) .frame(maxWidth: .infinity) .background(Color.accentColor) .clipShape(Capsule()) .padding(.vertical) }
2. 优化Firestore用户数据(可选)
若用户未选择头像,可在注册时写入默认头像URL,保证字段完整性:
// 在AuthViewModel的register方法中修改data字典 let data = [ "email": email, "fullname": fullname, "uid": user.uid, "profileImageUrl": "你的默认头像URL" ]
3. 验证Storage规则
当前规则request.auth != null已满足刚注册用户的auth状态,无需修改,上传不会被拦截。
内容的提问来源于stack exchange,提问作者amelia
相关产品推荐
相关产品推荐

