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

Posted

技术标签:

【中文标题】如何从后台线程 Swift UI GET 请求发布更改【英文标题】:How to publish changes from the background thread Swift UI GET Request 【发布时间】: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()
    

【问题讨论】:

【参考方案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,这是不安全的

以上是关于如何从后台线程 Swift UI GET 请求发布更改的主要内容,如果未能解决你的问题,请参考以下文章

如何在swift中使用后台线程?

如何在后台线程swift中运行for循环

iOS / Swift(UI)始终运行的后台计算线程的高效实现-GCD案例?

从 GET 请求中解析 JSON 数组 Alamofire Swift 2

如何从 Swift 的后台线程中从 CoreData 中获取未保存的数据?

swift3 - 使用后台数据更新 ui