如何将 completionHandler 的结果用于 Swift 中 NavigationLink 的目的地?

How to use the result of a completionHandler for the destination of a NavigationLink in Swift?

我有一个调用 REST API 和 returns 通过 completionHandler 结果的函数。

我想在按下 NavigationLink 时调用此函数,但使用结果作为传递到目的地的对象。无法完全弄清楚如何做到这一点,或者甚至可能。这是我当前的代码:

REST 函数

   func postProgramme(programmeName: String, programmeDays: Int, programmeDescription: String, completeionHandler: @escaping ProgrammeCompletionHandler) {
        struct PostRoutineData: Codable {
            let programmeName: String
            let programmeDays: Int
            let programmeDescription: String
        }
        
        let postProgrammeData = PostRoutineData(programmeName: programmeName, programmeDays: programmeDays, programmeDescription: programmeDescription)
        
        do {
            let jsonData = try JSONEncoder().encode(postProgrammeData)
            let jsonString = String(data: jsonData, encoding: .utf8)!
            let request = RESTRequest(path: "/workout/programme", body: jsonString.data(using: .utf8))
            Amplify.API.post(request: request) { result in
                switch result {
                case .success(let data):
                    do {
                        //let str = String(decoding: data, as: UTF8.self)
                        let programme = try self.decoder.decode(Programme.self, from: data)
                        completeionHandler(programme)
                    } catch {
                        print("[ERROR] Error within postProgrammes()", error)
                    }
                case .failure(let error):
                    print("[ERROR] Error within postProgrammes()", error)
                }
            }
        } catch {
            print("[ERROR] Error within postProgramme()", error)
        }
    }

NavigationLink,以及我解决问题的尝试:

NavigationLink(destination: ProgrammeDetailView(), isActive: $shouldTransit) {
    Text("Create Programme")
         .onTapGesture {
              self.createNewProgramme()
              self.shouldTransit = true
    }
}
func createNewProgramme() -> ProgrammeDetailView {
        sessionManager.postProgramme(programmeName: programmeName, programmeDays: programmeDays, programmeDescription: programmeDescription, completeionHandler: {(programme) -> ProgrammeDetailView in
            return ProgrammeDetailView(programme: programme)}
    }

假设它是一个 iOS 应用程序,如果你可以为 iOS 15 部署,你可以使用最近的异步/等待环境,通过使用异步函数和 returning Programme,而不是使用完成处理程序。

  1. 把你的函数变成异步的并且return一个Programme:
   func postProgramme(programmeName: String, programmeDays: Int, programmeDescription: String) async -> Programme? {

        // ...
        
                case .success(let data):
                    do {
                        let programme = try self.decoder.decode(Programme.self, from: data)

                        // No completion handler: return a Programme
                        // completionHandler(programme)
                        return programme
                    } catch {
                        print("[ERROR] Error within postProgrammes()", error)

                        // Return nil everywhere else
                        return nil
                    }
  1. 在您的主视图中,使用 Programme? 类型的 @State 变量,它将绑定到 ProgrammeDetailView.
  2. 中的另一个变量

函数createNewProgramme()将更新状态变量。

@State private var programme: Programme? = nil

var body: some View {
    NavigationView {

        // Pass the binding to ProgrammeDetailView
        NavigationLink(destination: ProgrammeDetailView(programme: $programme, content: { programme in
                  // A customised view
                  Text(programme?.name ?? "")
            }), isActive: $shouldTransit) {
            Text("Create Programme")
                 .onTapGesture {
                     self.createNewProgramme()
                     self.shouldTransit = true
                 }
        }
    }

func createNewProgramme() {

    // Task will allow working with async functions
    Task {
        let programme = await sessionManager.postProgramme(programmeName: programmeName, programmeDays: programmeDays, programmeDescription: programmeDescription)

        // Back to main thread to update the UI
        DispatchQueue.main.async {
             self.programme = programme
        }
    }
}
  1. 记得在 ProgrammeDetailView 中创建 @Binding 变量。下面的示例可以接收任何视图作为参数,但是因为 REST API 需要一些时间来响应,所以您需要处理 programme == nil:
  2. 的情况
struct ProgrammeDetailView<V: View> : View {

    // The binding with the parent view
    @Binding var programme: Programme?

    // Your customised view that receives a Programme as a parameter
    let content: (Programme)->V

    var body: some View {
        if programme == nil {
             ProgressView()
        } else {
             content(programme!)
        }
    }
}