How to Instantiate UIHostingController with CoreData??

As a long-time Obj-C dev (since iPhone OS 2) I decided that SwiftUI was the nudge to use Swift! ¯\_(ツ)_/¯
Thus, I am still trying to understand how a language can be so type-vague in code, and so type-pedantic in the compiler!!!

Pulling my hair out trying to get Swift/SwiftUI to instantiate a UIHostingController<>, for use with CoreData

Code Block
class MyCoreDataHostingController : UIHostingController<MyCoreDataView> {
required init?(coder: NSCoder) {//Instantiate From Storyboard
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let contentView = MyCoreDataView().environment(\.managedObjectContext, context)
super.init(coder: coder, rootView: contentView)
//Cannot convert value of type 'some View' to expected argument type 'MyCoreDataView'
}
}
struct MyCoreDataView: View {
var body: some View {
Text("Core Data View")
}
}

How can I FORCE Swift to INFER the correct/appropriate type?

What I have tried so far.
Code Block
5 let contentView = MyCoreDataView()

Compiles/runs, but does not include CoreData.

Code Block
6 super.init(coder: coder, rootView: contentView as! MyCoreDataView)

Compiles, but crashes at instantiation.

Could not cast value of type 'SwiftUI.ModifiedContent<MyHostingController.MyCoreDataView, SwiftUI.EnvironmentKeyWritingModifier<_C.NSManagedObjectContext>>' (...)
to 'MyHostingController.MyCoreDataView' (...).

... or is this simply not possible? (yet)

Accepted Reply

As you see, modifiers for SwiftUI View returns some opaque type some View which is not revealed to programmers but Swift compiler claims matching the type every here and there...

One possible solution is creating a wrapper View:
Code Block
class MyCoreDataHostingController : UIHostingController<MyCoreDataViewEnvironmentWrapper> {
required init?(coder: NSCoder) {//Instantiate From Storyboard
let contentView = MyCoreDataViewEnvironmentWrapper()
super.init(coder: coder, rootView: contentView)
}
}
struct MyCoreDataViewEnvironmentWrapper: View {
private let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
var body: some View {
MyCoreDataView().environment(\.managedObjectContext, context)
}
}


Or you can choose AnyView as the wrapper:
Code Block
class MyCoreDataHostingController : UIHostingController<AnyView> {
required init?(coder: NSCoder) {//Instantiate From Storyboard
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let contentView = AnyView(MyCoreDataView().environment(\.managedObjectContext, context))
super.init(coder: coder, rootView: contentView)
}
}


I'm not sure if this works for your case, but please try.

Replies

As you see, modifiers for SwiftUI View returns some opaque type some View which is not revealed to programmers but Swift compiler claims matching the type every here and there...

One possible solution is creating a wrapper View:
Code Block
class MyCoreDataHostingController : UIHostingController<MyCoreDataViewEnvironmentWrapper> {
required init?(coder: NSCoder) {//Instantiate From Storyboard
let contentView = MyCoreDataViewEnvironmentWrapper()
super.init(coder: coder, rootView: contentView)
}
}
struct MyCoreDataViewEnvironmentWrapper: View {
private let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
var body: some View {
MyCoreDataView().environment(\.managedObjectContext, context)
}
}


Or you can choose AnyView as the wrapper:
Code Block
class MyCoreDataHostingController : UIHostingController<AnyView> {
required init?(coder: NSCoder) {//Instantiate From Storyboard
let context = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
let contentView = AnyView(MyCoreDataView().environment(\.managedObjectContext, context))
super.init(coder: coder, rootView: contentView)
}
}


I'm not sure if this works for your case, but please try.
Thanks OOPer,
BOTH methods worked: I still have to check if the CoreData environment setting works with either/both, but very promising, and then decide the best/cleanest approach.

The <AnyView> (https://developer.apple.com/documentation/swiftui/anyview) solution was particularly inspired. And works with the ManagedObjectContext passing, so using that. 👍