Firebase数据上传时最后字符丢失问题的解决请求
字符串末尾字符上传Firebase时频繁被截断的解决方案
问题现象
向Firebase上传数据时,超半数概率出现最后一个字符被截断:
- 创建用户账号时,用户名丢失最后一个字母
- 内置论坛发帖时,标题或正文最后一个字符被移除
- 无论字符串长度为8位还是30位,均会出现该问题
相关代码
向Firebase存储数据的新用户注册函数
static func signupUser (username: String, email: String, password: String, imageData: Data, onSuccess: @escaping(_ user: User) -> Void, onError: @escaping(_ errorMessage: String) -> Void) { //Firebase.createAccount(username: username, email: email, password: password) Auth.auth().createUser(withEmail: email, password: password) { (authData, error) in if error != nil { print(error!.localizedDescription) onError(error!.localizedDescription) return } guard let userId = authData?.user.uid else { return } let storageAvatarUserId = Ref.STORAGE_AVATAR_USERID(userId: userId) let metadata = StorageMetadata() metadata.contentType = "image/jpg" StorageService.saveAvatar(userId: userId, username: username, email: email, imageData: imageData, metadata: metadata, storageAvatarRef: storageAvatarUserId, onSuccess: onSuccess, onError: onError) } } }
注册视图模型
class SignupViewModel: ObservableObject { var username: String = "" var email: String = "" var password: String = "" var image: Image = Image(IMAGE_USER_PLACEHOLDER) var imageData: Data = Data() var errorString = "" @Published var showImagePicker: Bool = false @Published var showAlert: Bool = false func signUp(username: String, email: String, password: String, imageData: Data, completed: @escaping(_ user: User) -> Void, onError: @escaping(_ errorMesssage: String) -> Void) { if !username.isEmpty && !email.isEmpty && !password.isEmpty && !imageData.isEmpty { AuthService.signupUser(username: username, email: email, password: password, imageData: imageData, onSuccess: completed, onError: onError) } else { showAlert = true errorString = "Please fill in all fields" } } }
注册视图
@ObservedObject var signupViewModel = SignupViewModel() func signUp() { signupViewModel.signUp(username: signupViewModel.username, email: signupViewModel.email, password: signupViewModel.password, imageData: signupViewModel.imageData, completed: { (user) in print(user.email) self.clean() //Switch to main app }) { (errorMessage) in print("Error: \(errorMessage)") self.signupViewModel.showAlert = true self.signupViewModel.errorString = errorMessage self.clean() } } func clean() { self.signupViewModel.username = "" self.signupViewModel.email = "" self.signupViewModel.password = "" } var body: some View { VStack { VStack{ signupViewModel.image .resizable() .aspectRatio(contentMode: .fill) .frame(width: 80, height: 80) .clipShape(Circle()) .onTapGesture { self.signupViewModel.showImagePicker = true } Text("Tap user icon to select a profile picture.") .font(.footnote) .foregroundColor(.gray) .padding(.bottom, 15) TextField("Username", text: $signupViewModel.username) .padding() .background(LinearGradient(colors: [.white.opacity(0.70), .white.opacity(0.70)], startPoint: .topLeading, endPoint: .bottomTrailing)) .cornerRadius(8) // shadow effect... .shadow(color: Color.black.opacity(0.1), radius: 5, x: 0, y: 5) .shadow(color: Color.black.opacity(0.08), radius: 5, x: 0, y: -5) } .padding(.horizontal, 25) .padding(.top, 15) VStack(alignment: .leading, spacing: 12) { TextField("Email", text: $signupViewModel.email) .padding() .background(LinearGradient(colors: [.white.opacity(0.70), .white.opacity(0.70)], startPoint: .topLeading, endPoint: .bottomTrailing)) .cornerRadius(8) // shadow effect... .shadow(color: Color.black.opacity(0.1), radius: 5, x: 0, y: 5) .shadow(color: Color.black.opacity(0.08), radius: 5, x: 0, y: -5) } .padding(.horizontal, 25) .padding(.top, 10) VStack(alignment: .leading, spacing: 15) { SecureField("Password", text: $signupViewModel.password) .padding() .background(LinearGradient(colors: [.white.opacity(0.70), .white.opacity(0.70)], startPoint: .topLeading, endPoint: .bottomTrailing)) .cornerRadius(8) // shadow effect... .shadow(color: Color.black.opacity(0.1), radius: 5, x: 0, y: 5) .shadow(color: Color.black.opacity(0.08), radius: 5, x: 0, y: -5) } .padding(.horizontal, 25) .padding(.top, 10) Button(action: signUp) { Text("Sign Up").foregroundColor(.black) .font(.system(size: 20)) .fontWeight(.bold) .padding(.vertical) .frame(width: UIScreen.main.bounds.width - 50) .background(LinearGradient(colors: [Color("1"), Color("2")], startPoint: .topLeading, endPoint: .bottomTrailing)) .cornerRadius(8) }.alert(isPresented: $signupViewModel.showAlert) { Alert(title: Text("Error"), message: Text(self.signupViewModel.errorString), dismissButton: .default(Text("OK"))) } .padding(.horizontal, 25) .padding(.top, 20) }
可能的原因及解决方案
1. SwiftUI TextField实时更新与clean()方法冲突
用户刚输入最后一个字符就点击按钮时,SwiftUI的@Published属性更新是异步的,此时clean()立即清空字段,可能导致上传的是未完成更新的字符串(缺失最后一个字符)。
解决方法:
先保存输入内容的快照,避免clean()干扰,仅在操作成功后清空字段:
func signUp() { // 保存当前输入的快照 let currentUsername = signupViewModel.username let currentEmail = signupViewModel.email let currentPassword = signupViewModel.password let currentImageData = signupViewModel.imageData signupViewModel.signUp(username: currentUsername, email: currentEmail, password: currentPassword, imageData: currentImageData, completed: { (user) in print(user.email) self.clean() // 仅成功后清空 //Switch to main app }) { (errorMessage) in print("Error: \(errorMessage)") self.signupViewModel.showAlert = true self.signupViewModel.errorString = errorMessage // 失败时保留输入,方便用户修改 } }
2. StorageService.saveAvatar的字符串处理问题
检查saveAvatar方法是否对username进行了不必要的截断(比如误写prefix(upTo: username.count - 1)),或Firestore字段设置了错误的长度限制。
解决方法:
- 查看
saveAvatar实现,确保无截取操作 - 确认Firestore对应字段无不合理的长度限制
3. 键盘收起时的文本同步延迟
点击按钮时键盘未完全收起,TextField内容可能未同步到绑定属性。
解决方法:
强制收起键盘并延迟获取输入内容,确保同步完成:
func signUp() { // 收起键盘 UIApplication.shared.sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil) // 延迟0.1秒确保文本同步 DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { let currentUsername = self.signupViewModel.username let currentEmail = self.signupViewModel.email let currentPassword = self.signupViewModel.password let currentImageData = self.signupViewModel.imageData self.signupViewModel.signUp(username: currentUsername, email: currentEmail, password: currentPassword, imageData: currentImageData, completed: { (user) in print(user.email) self.clean() //Switch to main app }) { (errorMessage) in print("Error: \(errorMessage)") self.signupViewModel.showAlert = true self.signupViewModel.errorString = errorMessage } } }
4. 字符串编码/日志排查
在saveAvatar开头添加日志,确认上传前字符串是否完整:
// 在StorageService.saveAvatar中添加 print("Uploading username: \(username), length: \(username.count)")
如果日志显示字符串完整,问题出在Firebase存储端;如果已缺失,排查视图或ViewModel层的字符串处理逻辑。
内容的提问来源于stack exchange,提问作者LBIrigoyen
相关产品推荐
相关产品推荐

