如何将 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
,而不是使用完成处理程序。
- 把你的函数变成异步的并且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
}
- 在您的主视图中,使用
Programme?
类型的 @State
变量,它将绑定到 ProgrammeDetailView
. 中的另一个变量
函数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
}
}
}
- 记得在
ProgrammeDetailView
中创建 @Binding
变量。下面的示例可以接收任何视图作为参数,但是因为 REST API 需要一些时间来响应,所以您需要处理 programme == nil
: 的情况
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!)
}
}
}
我有一个调用 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
,而不是使用完成处理程序。
- 把你的函数变成异步的并且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
}
- 在您的主视图中,使用
Programme?
类型的@State
变量,它将绑定到ProgrammeDetailView
. 中的另一个变量
函数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
}
}
}
- 记得在
ProgrammeDetailView
中创建@Binding
变量。下面的示例可以接收任何视图作为参数,但是因为 REST API 需要一些时间来响应,所以您需要处理programme == nil
: 的情况
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!)
}
}
}