My app shows a list of IDs. When you tap on the ID, a details view is opened, and the ID is used to retrieve details from the network. I'm currently doing this with an onAppear modifier, but I'm not happy with that. It's not really clean, and causes all sorts of other issues. I'd like to specifically trigger a function in the viewModel when the user navigates.
The following code can be pasted into a new SwiftUI project:
import SwiftUI let fleet = Fleet(registries: ["NCC-1031"]) struct Fleet { let registries: [String] } struct Starship { let registry: String let name: String } class StarshipViewModel: ObservableObject { enum Mode { case idle case loading case success(Starship) } @Published var mode: Mode = .idle let registry: String private var timer: Timer? func fetchFromNetwork() { self.mode = .loading self.timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true, block: { _ in let starship = Starship( registry: "NCC-1031", name: "Discovery" ) self.mode = .success(starship) }) } init(registry: String) { self.registry = registry } } struct StarshipDetails: View { @ObservedObject var viewModel: StarshipViewModel var body: some View { VStack { switch self.viewModel.mode { case .idle: Text("Idle") case .loading: Text("Loading") case .success(let starship): Text("Name: \(starship.name)") } } .onAppear(perform: { viewModel.fetchFromNetwork() }) } } struct ContentView: View { var body: some View { NavigationView { List { ForEach(fleet.registries, id: \.self) { registry in NavigationLink(destination: self.makeDestination(from: registry)) { Text(registry) } } } } } private func makeDestination(from registry: String) -> StarshipDetails { let viewModel = StarshipViewModel(registry: registry) // Don't do it here, because a network request will be done for the whole list // viewModel.fetchFromNetwork() let view = StarshipDetails(viewModel: viewModel) return view } } How should I run the fetchFromNetwork() call when navigating, but without using onAppear?
Note: I can't just use a button with an action, because there's no way to get a reference to the viewModel.
