SwiftUI - 不允许发布后台线程 - 在不更新 ui 的代码上

标签 swiftui thread-safety runtime-error

swiftui 的新手,不明白为什么第一个代码中的 JSONDecoder() 行会抛出

[SwiftUI] 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.

这对我来说并没有更新用户界面,那为什么会显示呢?

enter image description here

 do {
    // pass the request type, bearer token, and email / password ( which are sent as POST body params )
    let request = L.getRequest(requestType:"POST", token: token, email: self.username, password: self.psw)

    L.fetchData(from: request) { result in
        switch result {
        case .success(let data):
            // covert the binary data to a swift dictionary
            do {
                let response = try JSONDecoder().decode(WpJson.self, from: data)
                for (key, title) in response.allowedReadings {
                    let vimeoId = Int( key )!
                    let vimeoUri = self.buildVimeoUri(vimeoId: key)
                    self.addReadingEntity(vimeoUri: vimeoUri, vimeoId: vimeoId, title: title)
                }
                self.writeToKeychain(jwt:response.jwt, displayName: response.displayName)
                readings = self.fetchReadings()
            }
            catch {
                self.error = error.localizedDescription
            }
        case .failure(let error):
            self.error = error.localizedDescription
        }
    }
}

我尝试在 L.fetchData(from: request) { result in 中围绕 do-catch 包装一个主队列,但这没有帮助

DispatchQueue.main.async { [weak self] in 

这是登录协议(protocol),同样没有任何 ui 工作:

import Foundation
import SwiftUI

struct Login: Endpoint {
    var url: URL?
    init(url: URL?) {
        self.url = url
    }
}

protocol Endpoint {
    var url: URL? { get set }
    init(url: URL?)
}

extension Endpoint {
    func getRequestUrl() -> URLRequest {
        guard let requestUrl = url else { fatalError() }
        // Prepare URL Request Object
        return URLRequest(url: requestUrl)
    }
    
    func getRequest(requestType:String="POST", token:String, email:String="", password:String="") -> URLRequest {
        var request = self.getRequestUrl()
        request.httpMethod = "POST"
        request.setValue("application/json", forHTTPHeaderField: "Content-Type")
        request.addValue("Bearer \(token)", forHTTPHeaderField: "Authorization")
        
        if ( "" != email && "" != password && requestType == "POST") {
            let parameters:[String:String?] =  [
                "email": email,
                "password": password
            ]
            
            // Run the request
            do {
                // pass dictionary to nsdata object and set it as request body
                request.httpBody = try JSONSerialization.data(withJSONObject: parameters, options: .prettyPrinted)
            } catch let error {
                print(error.localizedDescription)
            }
        }
        return request;
    }
    
    func fetchData(from request: URLRequest, completion: @escaping (Result<Data, NetworkError>) -> Void) {
        
         URLSession.shared.dataTask(with: request) { data, response, error in
            if let data = data {
                completion(.success(data))
           } else if error != nil {
                // any sort of network failure
                completion(.failure(.requestFailed))
           } else {
                // this ought not to be possible, yet here we are
                completion(.failure(.unknown))
           }
        }.resume()
    }
}

extension URLSession {
    func dataTask(with request: URLRequest, completionHandler: @escaping (Result<(Data, HTTPURLResponse), Error>) -> Void) -> URLSessionDataTask {
        return dataTask(with: request, completionHandler: { (data, urlResponse, error) in
            if let error = error {
                completionHandler(.failure(error))
            } else if let data = data, let urlResponse = urlResponse as? HTTPURLResponse {
                completionHandler(.success((data, urlResponse)))
            }
        })
    }
}

你知道如何解决这个问题吗?

最佳答案

将其包装在赋值位置

        catch {
            DispatchQueue.main.async {
             self.error = error.localizedDescription
            }
        }
    case .failure(let error):
       DispatchQueue.main.async {
          self.error = error.localizedDescription
       }
    }

关于SwiftUI - 不允许发布后台线程 - 在不更新 ui 的代码上,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63692250/

相关文章:

SwiftUI 按钮文本居中

java - 线程 "Thread-8"java.lang.StackOverflowError...at java.util.Random.nextInt 中出现异常(来源未知)

java - notify() 如何按顺序或随机唤醒线程

c++ - 在C++中使用线程交替打印奇数和偶数打印

Go 的垃圾收集器在使用时删除 ZeroMQ 套接字

c - strcmp 在 c 中不工作

swift - 在 SwiftUI 中移动表行

ios - 在 ScrollView 中切换 VStack 的饱和度 - SwiftUI

c++ - 删除指向自动变量的指针

swiftui - @state 变量更改后 View 不更新