Search code examples
xcodeswiftuiios15

Message in console on iOS 15: "Binding<String> action tried to update multiple times per frame."


I have the following code:

struct Credential: Equatable {
    var username = ""
    var password = ""
}

enum Database {
    static var credential = Credential()
}

struct UsernamePasswordInput: View {
    @Binding var credential: Credential

    var body: some View {
        Group {
            TextField("Username", text: self.$credential.username)
            SecureField("password", text: self.$credential.password)
        }
        .textFieldStyle(RoundedBorderTextFieldStyle())
    }
}

struct Login: View {
    private var credential: Binding<Credential>

    var body: some View {
        UsernamePasswordInput(credential: self.credential)
    }

    init() {
        self.credential = Binding<Credential>(
            get: {
                Database.credential
            }, set: { newValue in
                Database.credential = newValue
            }
        )
    }
}

struct ContentView: View {
    var body: some View {
        Login()
    }
}

When you run this code from Xcode 13.0 on an iOS 15 device, the message Binding<String> action tried to update multiple times per frame. appears in the Xcode console. This happens whenever you type a character into either the TextField or the SecureField. The message does not appear on iOS 13 or 14 devices.

I know I can solve it by just using a @StateObject but the above code reflects the structure of a larger project. My question is: is the Xcode message somehow indicative of a problem? Is the code above principally incorrect somehow?


Solution

  • Here is a possible safe workaround - use inline separated binding for each text field:

    struct UsernamePasswordInput: View {
        @Binding var credential: Credential
    
        var body: some View {
            Group {
                TextField("Username", text: Binding(
                    get: { credential.username },
                    set: { credential.username = $0 }
                    ))
                SecureField("password", text: Binding(
                    get: { credential.password },
                    set: { credential.password = $0 }
                    ))
            }
            .textFieldStyle(RoundedBorderTextFieldStyle())
        }
    }
    

    Tested with Xcode 13 / iOS 15