【问题标题】:How to publish changes from the background thread Swift UI GET Request如何从后台线程 Swift UI GET 请求发布更改
【发布时间】:2020-06-09 19:44:39
【问题描述】:

我已经设置了我的应用程序,以便我使用 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!
                }
            }
        }
    }
}

在 Request 类中获取 store 调用

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

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()
    }
}

【问题讨论】:

    标签: ios networking get swiftui dispatch-queue


    【解决方案1】:

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

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

    【讨论】:

    • 哇,这个解决方案的旅程比我想要的要长得多。非常感谢!欣赏它
    • 如果我们想设置一个值或更新 UI。需要添加 DispatchQueue.main.async。成功了,谢谢!
    • 如果 dispatch 不是最后一个并且下面的代码依赖于 self,这是不安全的
    猜你喜欢
    • 1970-01-01
    • 2016-03-30
    • 2012-04-24
    • 1970-01-01
    • 2020-11-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多