jjuser19jj
jjuser19jj

Reputation: 1699

SwiftUI Passing variables in view hierarchy

I have a question regarding the architecture of my Swift / SwiftUI app consisting of a ListView, detail views and the detail views hold 2 input views. Specifically I want to know if this is the right way to do it.

The architecture is as follows:

The list view initiates a @StateObjct of the view model controller.

@StateObject var viewModel = ListViewModelController()

View model controller:

class ListViewModelController: ObservableObject {
@Published var model: ListViewModel

init() {
    self.model = ListViewModel()
 }
}

The List view model looks as follows:

struct Exercise: Hashable, Identifiable {
let id =  UUID()
var name: String
var weight: String
var repetitions: String
}

struct ListViewModel {
var exercises = [
    Exercise(name: "crunches", weight: "80 kg", repetitions: "100"),
    Exercise(name: "curls", weight: "10 kg", repetitions: "12"),
    Exercise(name: "dips", weight: "Bodyweight", repetitions: "10")
]
}

I pass the exercise variable to the detail view in the following way:

List {
        ForEach($viewModel.model.exercises, id: \.self) {$exercise in
            ExerciseConfigutationView(exercise: $exercise)
        }
        .onDelete(perform: delete)
        .onMove(perform: move)
    }

The detail view accepts the exercise variable as a binding:

@Binding var exercise: Exercise

The problem is, every time I try to present the ListView, the programm freezes without a warning or error. I assume I have misunderstood something about the binding / property wrappers.

Thanks for your help.

Upvotes: 3

Views: 925

Answers (1)

malhal
malhal

Reputation: 30746

ForEach with id: \.self is the mistake however a more serious problem is that in SwiftUI we do not use view model objects, only model objects. The View data structs are already the view model that SwiftUI uses to create and update actual views like UILabels, etc. on the screen.

Here is your fixed code:

@StateObject var model = ListModel()

//The List model:
class ListModel: ObservableObject {
    @Published var var exercises = [Exercise(name: "crunches", weight: "80 kg", repetitions: "100"),
                                    Exercise(name: "curls", weight: "10 kg", repetitions: "12"),
                                    Exercise(name: "dips", weight: "Bodyweight", repetitions: "10")]
}

struct Exercise: Hashable, Identifiable {
    let id =  UUID()
    var name: String
    var weight: String
    var repetitions: String
}

// Pass the exercise variable to the detail view in the following way:
List {
        ForEach($model.exercises) { $exercise in
            ExerciseConfigurationView(exercise: $exercise)
        }
        .onDelete(perform: delete)
        .onMove(perform: move)
    }


// pass the exercise variable to the detail view in the following way:
@Binding var exercise: Exercise

Upvotes: 2

Related Questions