swiftswiftuiviewglobalfunc

How can I run a func that lives in a seperate View in SwiftUI?


I have an app wherein once navigated to a second page and shown some results, the user has the option of running the same function but with some different parameters.

I am stuck on figuring out how to run the func that is inside my initial screen from within my second...

I have tried several methods but seem to get stuck. I have tried moving the func to my @main struct as well and having it be global. seems there are some linking issues as I'm doing it wrong. I have placed ContentView.doFunction() as an example of where I need to run the func from, this obviously doesn't work and is a bit more pythonic..

I will provide a very basic reproduction of the code.

struct ContentView: View {
    
    @State private var readyToNavigate: Bool = false
    
    var body: some View {
        NavigationStack {
            VStack {
                Button(action: doFunction, label: {
                    Text("Button")
                })
                .navigationDestination(isPresented: $readyToNavigate) {
                    secondPage()}
                .navigationTitle("Test")
            }
        }
    }
    func doFunction() {
        Task {
            let example = await "This is running an async operation"
            print(example)
        }
        readyToNavigate = true
    }
}

#Preview {
    ContentView()
}

and from the other view which is in a seperate swift file...

struct secondPage: View {
    
    var body: some View {
        Button("run func from first page", action: {
//            ContentView.doFunction()
            print("running async func from ContentView")
        })
    }
}
#Preview {
    secondPage()
}

Solution

  • In SwiftUI use async/await with .task not Task {}, e.g.

    struct ContentView: View {
        
        @State private var readyToNavigate: Bool = false
        @State private var isRunning = false
    
        var body: some View {
            NavigationStack {
                VStack {
                    Button(isRunning ? "Stop" : "Start") {
                        isRunning.toggle()
                    }
                    .navigationDestination(isPresented: $readyToNavigate) {
                        SecondPage()
                    }
                    .navigationTitle("Test")
                }
            }
            .task(id: isRunning) { // runs on appear and cancelled and restarted when id changes, cancelled on dissapear
                 if isRunning == false { return }
                 await doFunction()
                 isRunning = false
            }
        }
    
        func doFunction() async {
            let example = await "This is running an async operation"
            print(example)
            readyToNavigate = true
        }
    }
    

    If you want to restart the function from a child View then you can pass a Binding in to allow write access to the same State that is used as the id in .task.