Search code examples
swiftuiappstorage

SwiftUI unable to convert string from Appstorage into Int. Also while updating the second view it is creating a new view after every every keystroke


New to SwiftUI.

I want to change the string calBudget in the UserSettings view and convert it to Int in the ContentView. the first problem is the Integer conversion is not working with my code. The second problem is, every keystroke in the UserSettings view is generating a new UserSettings view creating a bunch of nested views.

struct ContentView: View {

    @AppStorage("calBudget") var calBudget = "1700"
    @AppStorage("calsBudget") var calsBudget = 0

    var body: some View {
        NavigationView {
            Form {
                Text("Budget: \(self.calBudget)")
                Text("to integer \(String(self.calsBudget))")
            }.toolbar {
                ToolbarItem() {
                    NavigationLink( destination: UserSettings(calBudget: $calBudget, calsBudget: $calsBudget)) { Text("settings") }
                }
            }
        }
    }
}

struct UserSettings: View {
    @Binding var calBudget: String
    @Binding var calsBudget: Int
    var body: some View {
        Form {
            HStack {
                TextField("Budget: ", text: self.$calBudget)
                Button(action: {
                    let calsBudget: Int = Int(self.calBudget ) ?? 1000
                }) { Text("make into integer")}
            
            }
        }
    
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
    
        ContentView( )
    }
}

Solution

  • the first problem is the Integer conversion is not working with my code

    This is your code currently:

    let calsBudget: Int = Int(self.calBudget ) ?? 1000
    

    Here, you're creating a new constant, calsBudget. Then you do nothing with it, throwing it away. Instead, you want to modify the existing @Binding var calsBudget: Int, so assign the value.

    calsBudget = Int(self.calBudget ) ?? 1000
    

    The second problem is, every keystroke in the UserSettings view is generating a new UserSettings view creating a bunch of nested views

    This happens because of this code:

    .toolbar {
        ToolbarItem() {
            NavigationLink( destination: UserSettings(calBudget: $calBudget, calsBudget: $calsBudget)) { Text("settings") }
        }
    }
    

    NavigationLink must always be inside a NavigationView. Whenever you put it outside, for example in a toolbar, you'll run into weird issues.

    Here's the fixed code:

    struct ContentView: View {
    
        @AppStorage("calBudget") var calBudget = "1700"
        @AppStorage("calsBudget") var calsBudget = 0
        @State var settingsPresented = false
    
        var body: some View {
            NavigationView {
                
                /// NavigationView must only contain 1 view, so wrap Form and NavigationLink inside VStack
                VStack {
                    Form {
                        Text("Budget: \(self.calBudget)")
                        Text("to integer \(String(self.calsBudget))")
                    }
                    
                    /// NavigationLink must be inside NavigationView
                    NavigationLink(
                        destination: UserSettings(calBudget: $calBudget, calsBudget: $calsBudget),
                        isActive: $settingsPresented) /// activates when `settingsPresented` is true
                        { EmptyView() }
                }
                .toolbar {
                    ToolbarItem() {
                        Button("settings") {
                            settingsPresented = true /// activate the NavigationLink
                        }
                    }
                }
            }
        }
    }
    
    struct UserSettings: View {
        @Binding var calBudget: String
        @Binding var calsBudget: Int
        var body: some View {
            Form {
                HStack {
                    TextField("Budget: ", text: self.$calBudget)
                    
                    Button(action: {
                        calsBudget = Int(self.calBudget ) ?? 1000 /// assign value, not create
                    }) { Text("make into integer")}
                
                }
            }
        }
    }