onmyway133 / blog

🍁 What you don't know is what you haven't learned
https://onmyway133.com/
MIT License
680 stars 33 forks source link

How to use EnvironmentObject in SwiftUI for watchOS #467

Open onmyway133 opened 5 years ago

onmyway133 commented 5 years ago

Declare top dependencies in ExtensionDelegate

class ExtensionDelegate: NSObject, WKExtensionDelegate {
    let storeContainer = StoreContainer()

    func applicationDidEnterBackground() {
        storeContainer.save()
    }
}

Reference that in HostingController. Note that we need to change from generic MainView to WKHostingController<AnyView> as environmentObject returns View protocol

class HostingController: WKHostingController<AnyView> {
    var storeContainer: StoreContainer!

    override func awake(withContext context: Any?) {
        super.awake(withContext: context)
        self.storeContainer = (WKExtension.shared().delegate as! ExtensionDelegate).storeContainer
    }

    override var body: AnyView {
        return AnyView(MainView()
            .environmentObject(storeContainer)
        )
    }
}

In theory, the environment object will be propagated down the view hierarchy, but in practice it throws error. So a workaround now is to just pass that environment object down manually

Fatal error: No ObservableObject of type SomeType found A View.environmentObject(_:) for StoreContainer.Type may be missing as an ancestor of this view

struct MainView: View {
    @EnvironmentObject var storeContainer: StoreContainer

    var body: some View {
        VStack {
            List(services.map({ AnyService($0) })) { anyService in
                NavigationLink(destination:
                    ItemsView(service: anyService.service)
                        .navigationBarTitle(anyService.service.name)
                        .onDisappear(perform: {
                            anyService.service.requestCancellable?.cancel()
                        })
                        .environmentObject(storeContainer)
                ) {
                    HStack {
                        Image(anyService.service.name)
                            .resizable()
                            .frame(width: 30, height: 30, alignment: .leading)
                        Text(anyService.service.name)
                    }
                }
            }.listStyle(CarouselListStyle())
        }
    }
}
alexeyismirnov commented 4 years ago

"In theory, the environment object will be propagated down the view hierarchy, but in practice it throws error." This seems to be fixed. Xcode Version 11.2.1 (11B53)