Xcode报错: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.
Xcode报错: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.

Xcode报错: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.

注意问题

请尝试Command+Q强退Xcode项目,然后重新打开。如果问题仍然存在,请查看下面的教程。

问题描述

在Xcode中,发现定义的单例shared报错:

class IAPManager:ObservableObject {
    static let shared = IAPManager()    // 报错行
    @Published var products: [Product] = []    // 存储从 App Store 获取的内购商品信息
    ...
}

报错代码为:

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.

报错原因:ObservableObject 发布(更新)其 @Published 属性的变化时发生在后台线程,而 SwiftUI 要求这些更新必须在主线程上进行。

IAPManager 中,有一些操作是异步执行的,例如网络请求或者其他后台任务。如果这些操作修改了 @Published 属性(比如 products 或 purchaseResult),但没有切换到主线程,就会引发这个错误。

解决方法

需要确保在后台线程完成操作后,将结果切换回主线程来更新 @Published 属性。这可以通过 DispatchQueue.main.async 或者 Task { @MainActor in } 来实现。例如:

func loadProduct() async {
    do {
        print("加载产品中...")
        let fetchedProducts = try await fetchProduct() 
        DispatchQueue.main.async {  // 确保主线程执行代码块,这样@Published属性更新不会引起异常
            self.products = fetchedProducts  // 闭包注意使用self
        }
    } catch {
        print("加载产品失败:\(error)")    
    }
}

或者使用 @MainActor 来显式地标记代码在主线程上运行:

@MainActor
func updateProducts(_ products: [Product]) {
    self.products = products
}

func loadProduct() async {
    do {
        print("加载产品中...")
        let fetchedProducts = try await fetchProduct()
        await updateProducts(fetchedProducts)
        print("成功加载产品: \(products)")
    } catch {
        print("加载产品失败:\(error)")
    }
}

DispatchQueue.main.async { }:确保在主线程上执行代码块,这样 @Published 属性的更新不会引发异常。

@MainActor:将整个方法或任务标记为主线程执行,简化线程管理。

确保所有 @Published 属性的更新都是在主线程上进行,就可以解决这个问题。

总结

@MainActor
class IAPManager:ObservableObject {...}

	我们需要确保类声明的@MainActor,每个更新操作都使用DispatchQueue.main.async。
func handlePurchase(num: Int) {
    Task { @MainActor in
        DispatchQueue.main.async {
            self.savePurchasedState(for: self.products[num].id)
        }
        ...
    }
}

此外我们也可以将

DispatchQueue.main.async 的地方改为直接 await MainActor.run,可以确保这些操作都在主线程上(这是个替代方案)。

涉及Task部分使用@MainActor:

func handlePurchase(num: Int) {
    Task { @MainActor in
        ...
    }
}

注意:以上操作全部完成之后,强退Xcode重新打开项目,报错消失。

如果您认为这篇文章给您带来了帮助,您可以在此通过支付宝或者微信打赏网站开放者。

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注