swift - 如何在 SwiftUI 中完成异步请求后调用函数?
问题描述
我有一个函数可以调用 2 种类型的 api 请求来获取我的应用程序中需要的一堆数据。在函数中,我请求位置,然后对于响应中的每个位置,我发出不同的请求以获取该特定位置的详细信息。(例如,如果请求 1 返回 20 个位置,我的第二个请求将被调用 20 次,每个位置调用一次)
我的功能代码在这里:
func requestAndCombineGData(location: CLLocation, radius: Int) {
// Clears map of markers
self.mapView.clear()
// Calls 'Nearby Search' request
googleClient.getGooglePlacesData(location: location, withinMeters: radius) { (response) in
print("Made Nearby Search request. Returned response here:", response)
// loops through each result from the above Nearby Request response
for location in response.results {
// Calls 'Place Details' request
self.googleClient.getGooglePlacesDetailsData(place_id: location.place_id) { (detailsResponse) in
print("GMV returned - detailsResponse.result - ", detailsResponse.result)
}
}
}
}
我上面引用的请求函数在这里:
func getGooglePlacesData(location: CLLocation, withinMeters radius: Int, using completionHandler: @escaping (GooglePlacesResponse) -> ()) {
for category in categoriesArray {
let url = googlePlacesNearbyDataURL(forKey: googlePlacesKey, location: location, radius: radius, type: category)
let task = session.dataTask(with: url) { (responseData, _, error) in
if let error = error {
print(error.localizedDescription)
return
}
guard let data = responseData, let response = try? JSONDecoder().decode(GooglePlacesResponse.self, from: data) else {
print("Could not decode JSON response")
completionHandler(GooglePlacesResponse(results:[]))
return
}
if response.results.isEmpty {
print("GC - response returned empty", response)
} else {
print("GC - response contained content", response)
completionHandler(response)
}
}
task.resume()
}
}
func getGooglePlacesDetailsData(place_id: String, using completionHandler: @escaping (GooglePlacesDetailsResponse) -> ()) {
let url = googlePlacesDetailsURL(forKey: googlePlacesKey, place_ID: place_id)
let task = session.dataTask(with: url) { (responseData, _, error) in
if let error = error {
print(error.localizedDescription)
return
}
guard let data = responseData, let detailsResponse = try? JSONDecoder().decode(GooglePlacesDetailsResponse.self, from: data) else {
print("Could not decode JSON response. responseData was: ", responseData)
return
}
print("GPD response - detailsResponse.result: ", detailsResponse.result)
completionHandler(detailsResponse)
}
task.resume()
}
在我得到所有我请求的数据之后(或者甚至在数据进入时)我想将它附加到我在我的 SceneDelegate.swift 文件中设置的@EnvironmentObject(数组)。我在我的应用程序的多个位置使用数据,因此@EnvironmentObject 充当“事实来源”。
我尝试使用下面的代码完成此操作,但不断收到错误消息 - “不允许从后台线程发布更改;确保在模型更新时从主线程发布值(通过接收(on :) 等运算符)。”
func requestAndCombineGData(location: CLLocation, radius: Int) {
// Clears map of markers
self.mapView.clear()
// Calls 'Nearby Search' request
googleClient.getGooglePlacesData(location: location, withinMeters: radius) { (response) in
print("Made Nearby Search request. Returned response here:", response)
// loops through each result from the above Nearby Request response
for location in response.results {
// Calls 'Place Details' request
self.googleClient.getGooglePlacesDetailsData(place_id: location.place_id) { (detailsResponse) in
print("GMV returned - detailsResponse.result - ", detailsResponse.result)
// THIS IS WHERE I TRY TO UPDATE MY @ENVIROMETOBJECT
self.venueData.venuesdataarray.append(detailsRespose.result)
}
}
}
}
我相信我需要确保请求完成然后尝试更新我的@EnvironmentObject,但我不知道该怎么做。
编辑 - 按照评论中的要求提供我的 VenueData 结构:
struct VenueData : Identifiable {
var id = UUID()
var name : String
var geometry : Location?
var rating : String?
var price_level : String?
var types : [String]?
var formatted_address : String?
var formatted_phone_number : String?
var website : String?
var photo_reference : String?
enum CodingKeysDetails : String, CodingKey {
case geometry = "geometry"
case name = "name"
case rating = "rating"
case price_level = "price_level"
case types = "types"
case opening_hours = "opening_hours"
case formatted_address = "formatted_address"
case formatted_phone_number = "formatted_phone_number"
case website = "website"
}
// Location struct
struct Location : Codable {
var location : LatLong
enum CodingKeys : String, CodingKey {
case location = "location"
}
// LatLong struct
struct LatLong : Codable {
var latitude : Double
var longitude : Double
enum CodingKeys : String, CodingKey {
case latitude = "lat"
case longitude = "lng"
}
}
}
}
class VenueDataArray: ObservableObject {
@Published var venuesdataarray : [VenueData] = [
VenueData(name: "test_name")
]
}
解决方案编辑 - 我尝试在我的第二个 api 请求中使用这段代码,它解决了我的问题,虽然我不明白为什么我需要这样做
DispatchQueue.main.async {
self.venueData.venuesdataarray.append(RESPONSE_DETAILS_HERE)
}
最初我问过,有谁知道在所有请求完成后我如何更新我的@EnvironmentObject?
有谁知道为什么我上面的代码片段使一切正常?
我只是想了解我在做什么,如果他们发现这个,也许有人可以学到一些东西
解决方案
我尝试在我的第二个 api 请求中使用这段代码,它解决了我的问题,虽然我不明白为什么我需要这样做
DispatchQueue.main.async {
self.venueData.venuesdataarray.append(RESPONSE_DETAILS_HERE)
}
最初我问过,有谁知道在所有请求完成后我如何更新我的@EnvironmentObject?
有谁知道为什么我上面的代码片段使一切正常?我只是想了解我在做什么,如果他们发现这个,也许有人可以学到一些东西
推荐阅读
- python-3.x - pandas tz_convert 为同一时区提供不同的偏移量
- python - 编译包含 boost/python 的 C++ 文件时出错
- delphi - Delphi在两个项目中共享单元
- python - 使用 json 将字符串转换为 dict 时出错
- python - 在比较值时将来自一个数据帧的字符串添加到第二个数据帧的新列中
- flutter - Flutter,在另一个类中修改后发送并获取值
- sql - 如何优化高排序成本和扫描计数
- mediatr - 请求处理程序的 Mediatr 多态调度不起作用
- python - PyAutoGui 和 PyScreez
- git - VSCODE 中的源代码控制未显示我要添加到暂存状态的更改