I'm finding it difficult to decide on the best architectural pattern for a SwiftUI app but, for the moment, I'm sticking with MVVM as I found this a good fit conceptually.

I'm trying to avoid the SwiftUI layer knowing too much about the business logic and navigation structure but am struggling to decide how best to achieve this.

Here is the prototype view model (without implementation for the navigation):

final class PatientListViewModel: ViewModelType {
    struct State {
        var patients: [PatientSummaryViewModel]
    @Published var state: State = State(patients: []) {
        willSet { print("Received: \(newValue)") }
    enum Input {
        case onNewPatient
    private let service: PatientServiceType
    private var cancellables = Set<AnyCancellable>()
    init(service: PatientServiceType) {
        self.service = service
            .receive(on: DispatchQueue.main)
            .map { patients in
                patients.map {
                    PatientSummaryViewModel(patient: $0)
            .sink { [weak self] patientViewModel in
                self?.state.patients = patientViewModel
            .store(in: &cancellables)
    func action(_ input: Input) {
        switch input {
        case .onNewPatient:
            print("Create new view and associated viewModel here but how does SwiftUI access it?")
    subscript<Value>(dynamicMember keyPath: KeyPath<State, Value>) -> Value {
        state[keyPath: keyPath]

Associated SwiftUI view:

struct PatientListView: View {
    @ObservedObject var viewModel: PatientListViewModel
    var body: some View {
        NavigationView {
            List(viewModel.patients) { viewModel in
                        PatientSummaryView(viewModel: viewModel),
                    label: {
                        PatientCell(patient: viewModel.patient)
            .navigationBarItems(leading: EditButton(),
                                trailing: Button(action: {
                                    // call to viewmodel to get new view and correctly constructed viewModel
                                                 label: { Image(systemName: "plus")

At this point in time, there are several solutions that present themselves:

  1. Modify action(_ input:) to create the View in situ, returning it to the View that called it. Not all actions will need navigation to take place and so I'd need to add @discardableresult with many actions having to return EmptyView() and then there's the little dance I'd need to do with type erasing the views to consider.

  2. Modify the view model State internal struct to include a "next view" property that is updated by action(_:) when new views are required. Although the idea of keeping state in one place appeals this feels a little clunky somehow.

  3. Finally I have some vague ideas about a callback/closure that is passed to the view model and used to construct the required view but I'm struggling to visualise this as I still find closure syntax confusing, doubly so when working with Views.

  • \$\begingroup\$ Which solution did you use at the end? \$\endgroup\$ Commented Nov 20, 2022 at 20:25


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.