0

If I create a user account, or post to a forum I have built in the app, sometimes the username loses the last letter, and sometimes the title or body in the forum have the last letter removed. It doesn't matter how long the string it, could be 8 letters or 30 letters, but more than half the time, the last character is removed. How do I change this?

Edit: signUpView requested, posted view.

Signup new user function to store data to 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)            
        }
    }
}

SignUp View Model

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"
        }
    }
}

signUpView

@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)
  • Could you add a little bit more about your SignUpView code please? Just to see how you are populating the ViewModel properties from the View – Sarquella Aug 25 '22 at 17:44
  • @Sarquella I have added the entire signUpView. Let me know if you want anything else. – LBIrigoyen Aug 25 '22 at 18:21

1 Answers1

0

SwiftUI expects variables bound from an ObservableObject to be decorated with @Published.

If you take a look to Xcode's console you will see the following error every time you type:

Binding<String> action tried to update multiple times per frame.

By adding @Published to those variables you have bound from your ViewModel, the error will disappear and data will not have missing characters.

Refer here and here as they are the exact same case as yours.


Also note that there is no need to have both the variables as ViewModel properties and then pass them to ViewModel's signUp(...) via parameters again.

Either you:

1.- Keep them as @Published properties in the ViewModel and remove them from parameters at signUp(...) (You already have access to those properties from the ViewModel itself).

Or:

2.- Remove them from the ViewModel and move them to the View as @State properties. Then just pass them as parameters when calling signUp(...) as you already do now.

Sarquella
  • 1,129
  • 9
  • 26