如何从后台线程发布更改 Swift UI GET 请求

How to publish changes from the background thread Swift UI GET Request

我已经设置了我的应用程序,以便我使用 UserDefaults 来存储用户登录信息(isLoggedIn,帐户设置)。如果用户登录并退出应用程序,然后重新启动应用程序,我希望他们返回到主页选项卡。

此功能有效;然而,出于某种原因,在重新启动时,主页有一个应该执行的 getRequest。相反,屏幕变白。当我从登录导航时,此请求和涉及的加载有效,但在我重新启动应用程序时无效。我收到此警告:

Publishing changes from background threads is not allowed; make sure to publish values from the main thread (via operators like receive(on:)) on model updates.

在查看其他堆栈溢出帖子时,普遍的看法似乎是将任何类型的更改包装在 dispatchqueue.main.async 中;但是,这似乎对我不起作用。

import SwiftUI

struct StoresView: View {
    @ObservedObject var request = Request()
    @Environment(\.imageCache) var cache: ImageCache
    @EnvironmentObject var carts: Carts

    init() {
        getStores()
    }

    var body: some View {

        NavigationView {
            List(self.request.stores) { store in
                NavigationLink(destination: CategoryHome(store: store).environmentObject(self.carts)) {
                    VStack(alignment: .leading) {
                        Text(store.storeName)
                            .font(.system(size: 20))
                    }
                }
            }.navigationBarTitle(Text("Stores").foregroundColor(Color.black))
        }
    }

    func getStores() {
        DispatchQueue.main.async {
            self.request.getStoresList() { stores, status in
                if stores != nil {
                    self.request.stores = stores!
                }
            }
        }
    }
}

在请求中调用商店 class

class Request: ObservableObject {
@Published var stores = [Store]()
let rest = RestManager()


func getStoresList(completionHandler: @escaping ([Store]?, Int?)-> Void) {
    guard let url = URL(string: "@@@@@@@@@@@@@@@@@@@") else { return }

    self.rest.makeRequest(toURL: url, withHttpMethod: .GET, useSessionCookie: false) { (results) in
        guard let response = results.response else { return }
        if response.httpStatusCode == 200 {
            guard let data = results.data else { return}
            let decoder = JSONDecoder()
            guard let stores = try? decoder.decode([Store].self, from: data) else { return }
            completionHandler(stores, response.httpStatusCode)
        } else {
            completionHandler(nil, response.httpStatusCode)
        }
    }

}

从 RestManager 发出请求,我包含了发出请求,因为我看到其他一些人使用共享数据发布任务,但我在尝试使用它时可能没有正确使用它。任何建议或帮助将不胜感激。谢谢!

func makeRequest(toURL url: URL,
                 withHttpMethod httpMethod: HttpMethod, useSessionCookie: Bool?,
                 completion: @escaping (_ result: Results) -> Void) {

    DispatchQueue.main.async { [weak self] in
        let targetURL = self?.addURLQueryParameters(toURL: url)
        let httpBody = self?.getHttpBody()
        // fetches cookies and puts in appropriate header and body attributes
        guard let request = self?.prepareRequest(withURL: targetURL, httpBody: httpBody, httpMethod: httpMethod, useSessionCookie: useSessionCookie) else
        {
            completion(Results(withError: CustomError.failedToCreateRequest))
            return
        }

        let sessionConfiguration = URLSessionConfiguration.default
        let session = URLSession(configuration: sessionConfiguration)

        let task = session.dataTask(with: request) { (data, response, error) in
            print(response)
            completion(Results(withData: data,
                               response: Response(fromURLResponse: response),
                               error: error))
        }
        task.resume()
    }
}

您似乎在尝试调用 Main tread 中的函数,而不是设置商店 属性。调用 request. getStoresList 已经在主线程中,一旦调用您从那里进入后台线程,您需要在 URLSession 完成后返回主线程。您需要在主线程而不是后台线程中进行 UI 修改,因为错误已明确说明。要解决此问题,您需要执行以下操作:

    func getStores() {
        self.request.getStoresList() { stores, status in
            DispatchQueue.main.async {
                if stores != nil {
                    self.request.stores = stores!
                }
            }
        }
    }