Proper way of Navigation in SwiftUI after async task

Viewed 662

I have screen with List of Views one of them is NavigationLink that navigates to separate screen DocumentPreviewView where PDF document is presented.

This PDF document need to be downloaded asynchronously on the first screen after button is tapped and need to be passed to the DocumentPreviewView screen.

I came to working solution but I' m looking for more elegant way and more SwiftUI way. Maybe document should be some kind of observable object.

This is my current solution. As you can see I use hidden NavigationLink that is being triggered by callback from button action where I also download document which I need to present.

@State private var document = PDFDocument()
@State private var docState: DocState? = .setup

enum DocState: Int {
    case setup = 0
    case ready = 1
}

var body: some View {
    List {
    /// some other views

        Button(action: {
            someAsyncFunction { doc, error in 
              self.document = doc
              self.docState = .ready
            }
          }) {
          Text("Show Document")
        }

    /// some other views
    }

    NavigationLink(
            destination: DocumentPreviewView(pdfDocument: document), 
            tag: .ready, 
            selection: $docState) {
        EmptyView()
    }
}
1 Answers

Possible solution

struct ContentView: View {
    @State private var docState: DocState? = .setup
    
    enum DocState: Int {
        case setup = 0
        case ready = 1
    }
    
    
    var body: some View {
        NavigationView {
            ZStack {
                NavigationLink(
                    destination: Text("Preview"),
                    tag: .ready,
                    selection: $docState) {
                    Button(action: {
                        someTask() { isTaskDone in
                            docState = .ready
                        }
                    }) {
                        Text("Download")
                    }
                    }
            }
        }
    }
    
    func someTask(_ completion: @escaping ((Bool)->())) {
        DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
            completion(true)
        }
    }
}
Related