【问题标题】:How to call function after async requests finish in SwiftUI?如何在 SwiftUI 中完成异步请求后调用函数?
【发布时间】:2020-01-06 22:11:52
【问题描述】:

我有一个函数调用 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,但我不知道该怎么做。


编辑 - 按照 cmets 的要求提供我的 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 请求中使用这个 sn-p 代码,它解决了我的问题,虽然我不明白为什么我需要这样做

    DispatchQueue.main.async {
        self.venueData.venuesdataarray.append(RESPONSE_DETAILS_HERE)
    }

最初我问过,有谁知道我如何在所有请求完成后更新我的@EnvironmentObject?

有人知道为什么我上面的 sn-p 可以让一切正常吗?

我只是想了解我在做什么,如果他们发现这个,也许有人可以学到一些东西

【问题讨论】:

  • 你能展示你的场地数据吗?
  • @Ernist Isabekov 是的,我在上面的编辑中包含了它

标签: swift asynchronous request swiftui completionhandler


【解决方案1】:

我尝试在我的第二个 api 请求中使用这个 sn-p 代码,它解决了我的问题,虽然我不明白为什么我需要这样做

DispatchQueue.main.async {
    self.venueData.venuesdataarray.append(RESPONSE_DETAILS_HERE)
}

最初我问过,有谁知道在所有请求完成后我如何更新我的@EnvironmentObject?

有人知道为什么我上面的 sn-p 可以使一切正常吗?我只是想了解我在做什么,如果他们发现这个,也许有人可以学到一些东西

【讨论】:

    【解决方案2】:

    有几件事情你不能从后台线程成功地完成。其中一些(如 UIKit 内容更改)不会产生错误,而是静默失败,这更糟。您有幸收到了相对具体的错误消息。

    错误消息是您无法从后台线程发布更改,需要从主线程发布。

    在“DispatchQueue.main.async”中包装你的追加使得那行代码在主线程上运行。

    就是这样。 这可能已经解释得更简洁了。

    【讨论】:

      猜你喜欢
      • 2020-04-23
      • 1970-01-01
      • 1970-01-01
      • 2014-05-12
      • 1970-01-01
      • 2016-09-05
      • 1970-01-01
      • 2019-11-25
      相关资源
      最近更新 更多