首页 > 解决方案 > 如何在 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?

有谁知道为什么我上面的代码片段使一切正常?

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

标签: swiftasynchronousrequestswiftuicompletionhandler

解决方案


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

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

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

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


推荐阅读