【问题标题】:A View.environmentObject(_:) may be missing as an ancestor of this view - but not always…View.environmentObject(_:) 作为该视图的祖先可能会丢失——但并非总是如此……
【发布时间】:2021-12-10 16:40:04
【问题描述】:

我在生产中遇到此错误,无法找到重现它的方法。

致命错误 > 未找到 PurchaseManager 类型的 ObservableObject。一种 PurchaseManager 的 View.environmentObject(_:) 可能作为 这种观点的始祖。 > PurchaseManager > SwiftUI

崩溃来自这个观点:

struct PaywallView: View {
    @EnvironmentObject private var purchaseManager: PurchaseManager
    var body: some View {
        // Call to purchaseManager causing the crash
    }
}

并且这个视图在 MainView 的子视图中实例化

@main
struct MyApp: App {
    let purchasesManager = PurchaseManager.shared
    var body: some Scene {
        WindowGroup {
            MainView()
                .environmentObject(purchasesManager)
            }
        }
    }
}

或者,当从 UIKit 控制器调用时,从这个控制器:

final class PaywallHostingController: UIHostingController<AnyView> {
    init() {
        super.init(rootView:
                    AnyView(
                        PaywallView()
                            .environmentObject(PurchaseManager.shared)
                    )
        )
    }
    @objc required dynamic init?(coder aDecoder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

我测试了所有触发 PaywallView 显示的用例,我从未遇到过崩溃。

FWIW,PurchaseManager 看起来像这样:

public class PurchaseManager: ObservableObject {   
    static let shared       = PurchaseManager()
    init() {
        setupRevenueCat()
        fetchOfferings()
        refreshPurchaserInfo()
    }
}

为什么 ObservableObject 会丢失?在什么情况下?

【问题讨论】:

  • 可能是因为private。或者你应该这样做:.environmentObject(PurchaseManager.shared)
  • 请添加你是如何实现PurchaseManager.shared的代码
  • @Asperi 为什么?你在找什么?
  • @Asperi 完成。我添加了实现。
  • 可能你的PurchaseManagerinit中的一些函数(比如fetchOfferings)是异步的。这可能是您的问题间歇性出现的原因。

标签: ios swift swiftui


【解决方案1】:

你的问题是间歇性的,可能是因为PurchaseManager init() 可以完成 在正确设置所有数据之前,由于“延迟” init() 中的异步函数。所以有时数据会可用 当视图需要它时,有时它会不存在并导致您的应用崩溃。

您可以尝试以下方法,其中包括使用 @atultw 的建议 状态对象。

import SwiftUI

@main
struct TestApp: App {
    @StateObject var purchaseManager = PurchaseManager() // <-- here
    
    var body: some Scene {
        WindowGroup {
            MainView()
                .onAppear {
                    purchaseManager.startMeUp()    // <-- here
                }
                .environmentObject(purchaseManager)
        }
    }
}

struct MainView: View {
    @EnvironmentObject var purchaseManager: PurchaseManager
    
    var body: some View {
        Text("testing")
        List {
            ForEach(purchaseManager.offerings, id: \.self) { offer in
                Text(offer)
            }
        }
    }
}

public class PurchaseManager: ObservableObject {
    @Published var offerings: [String] = []
    
    // -- here --
    func startMeUp() {
        // setupRevenueCat()
        fetchOfferings()
        // refreshPurchaserInfo()
    }
    
    func fetchOfferings() {
        DispatchQueue.main.asyncAfter(deadline: .now()+2) {
            self.offerings = ["offer 1","offer 2","offer 3","offer 4"]
        }
    }
}

【讨论】:

  • 1/ 由于产品有默认值,数据如何设置不正确?和 2/ 为什么是最后期限? Fwiw,当应用程序从 UIKit 重写为 SwiftUI 时,单例是简化事情的临时妥协。虽然我也不喜欢它,但我似乎看不出它与这次崩溃有关的任何明确原因。
  • 很难回答您的问题,当我们没有您问题的可重现示例代码时,我所能做的就是推测。是的,产品有一个默认值,但假设您正在使用索引进行循环,然后由于异步函数,索引发生变化,它可能会产生问题。您能否说明导致PaywallView 崩溃的原因,我猜可能是MainView。最好的办法是显示一个显示您的问题的示例代码。请注意,在我的简单测试中,单例对我来说不是主要问题。
  • deadline 只是一个简单的异步函数模拟,需要一些时间才能完成,因为您没有显示 fetchOfferings() 中的内容
  • 感谢@workingdod。我没有更多的工作要做,但你给了我很多。我认为 fetchOfferings/refreshPurchaserInfo 协同工作的方式存在问题。另外,我发现了 environmentObjects 的另一个问题,还有这个 useyourloaf.com/blog/swiftui-environment-when-presenting-views
【解决方案2】:

这里尽量不要使用单例模式(.shared),EnvironmentObject 是为了替代它。您应该在 MyApp 中实例化 PurchasesManager。

@main
struct MyApp: App {
    @StateObject var purchasesManager = PurchaseManager() 
    var body: some Scene {
        WindowGroup {
            MainView()
                .environmentObject(purchasesManager)
            }
        }
    }
}

没有状态对象可以很好地编译,但如果您希望子视图自动更新,则需要。

用一个虚拟的 PurchasesManager 做这些事情对我来说很好。

【讨论】:

  • 不是我需要的答案,但我同意它应该是一个单例,所以这仍然很有帮助并且值得一票。谢谢
猜你喜欢
  • 2021-07-08
  • 1970-01-01
  • 2020-05-11
  • 2021-06-24
  • 2021-05-22
  • 1970-01-01
  • 1970-01-01
  • 2020-05-11
  • 2021-12-30
相关资源
最近更新 更多