I’m getting this error in production and can’t find a way to reproduce it.
Fatal error > No ObservableObject of type PurchaseManager found. A
View.environmentObject(_:) for PurchaseManager may be missing as an
ancestor of this view. > PurchaseManager > SwiftUI
The crash comes from this view:
struct PaywallView: View {
@EnvironmentObject private var purchaseManager: PurchaseManager
var body: some View {
// Call to purchaseManager causing the crash
}
}
And this view is instantiated in subviews of the MainView
@main
struct MyApp: App {
let purchasesManager = PurchaseManager.shared
var body: some Scene {
WindowGroup {
MainView()
.environmentObject(purchasesManager)
}
}
}
}
or, when called from a UIKit controller, from this controler:
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")
}
}
I tested all the use cases that trigger the PaywallView to show up, and I never got a crash.
FWIW, the PurchaseManager looks like this:
public class PurchaseManager: ObservableObject {
static let shared = PurchaseManager()
init() {
setupRevenueCat()
fetchOfferings()
refreshPurchaserInfo()
}
}
Why would the ObservableObject go missing? In which circumstances?
2
Answers
Try not to use the singleton pattern here (
.shared
),EnvironmentObject
is meant to be a replacement for it. You should instantiate PurchasesManager in MyApp.without state object compiles fine but needed if you want child views to update automatically.
Doing those things with a dummy PurchasesManager runs fine for me.
The reason your problem is intermittent, is probably because the
PurchaseManager
init()
could finish
before all the data is setup properly, due to the "delays" of the
async functions in
init()
. So sometimes the data will be availablewhen the View wants it, and sometimes it will not be there and crash your app.
You could try the following approach that includes @atultw advice of using
StateObject.