如何从后台线程发布更改 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!
}
}
}
}
我已经设置了我的应用程序,以便我使用 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!
}
}
}
}