Sorry if this is a newbie question. I've been studying existential generics (I know try to avoid them for performance reasons). But this is more of a curiosity question than an actual problem. I'm just trying to understand generics a bit more.
As an example I look at UIViewRepresentable and I see that makeUIView() takes a Context typealias. This "context" argument is passed by SwiftUI, but I'm curious how SwiftUI creates this context given that it doesn't know the associated type for Coordinator.
public struct UIViewRepresentableContext<Representable> where Representable : UIViewRepresentable {
public let coordinator: Representable.Coordinator
// ... etc
}
public protocol UIViewRepresentable: View where Body == Never {
associatedtype UIViewType: UIView
associatedtype Coordinator = Void
func makeUIView(context: Context) -> UIViewType
// ... etc
typealias Context = UIViewRepresentableContext<Self>
}
extension UIViewRepresentable where Coordinator == Void {
public func makeCoordinator() -> Coordinator {
return
}
}
When I come along and create my own extension of UIViewRepresentable with my own custom Coordinator. My "makeUIView" function will be passed a Context (UIViewRepresentableContext) which captures the coordinator returned by makeCoordinator() which is probably not Void. My question is just what is the syntax in swift to instantiate this "Context" for "any UIViewRepresentable"? Or is Xcode creating a code snippet behind my back to create this Context?
Hopefully I'm clear enough. I'm not actually trying to create this Context. I'm just using UIViewRepresentable as an example of generics syntax that I'm not quite grasping.
Thank you for the snippet. It gives me some ideas, but is not quite right as Coordinator are UIViewType are members of UIViewRepresentable not View. Also I'm still not clear how you can create the context for "any UIViewRepresentable"?
UIViewRepresentableContext<any UIViewRepresentable> makes no sense, because UIViewRepresentableContext requires its generic argument to conform to UIViewRepresentable and any UIViewRepresentable does not conform to UIViewRepresentable.
In theory you could get something like any<V: UIViewRepresentable> UIViewRepresentableContext<V>, but Swift does not support it yet. As a workaround you could make a custom protocol, and declare conformance of UIViewRepresentableContext to this protocol in extension, which has effect of making UIViewRepresentableContext<V> conform to the protocol for every value of V.
protocol MyUIViewRepresentableContext {
var viewType: any UIViewRepresentable.Type
var uiViewType: UIView.Type
var myCoordinator: Any { get }
var transaction: Transaction { get }
var environment: EnvironmentValues { get }
}
extension UIViewRepresentableContext: MyUIViewRepresentableContext {
var viewType: any UIViewRepresentable.Type { Representable.self }
var uiViewType: UIView.Type { Representable.UIViewType.self }
var myCoordinator: Any { self.coordinator }
}
var typedContext: MyView.Context = ...
var erasedContext: any MyUIViewRepresentableContext = typedContext
print(erasedContext.myCoordinator)
I'm just trying to push my understanding of Swift and SwiftUI underpinnings a bit more. I realize now that SwiftUI is probably not using 'any UIViewRepresentable' since any custom extension of UIViewRepresentable will be created through ViewBuilder so it would be a compile time call to makeCoordinator() instead of a blind runtime call through 'any'. I probably just lean on my object-oriented 'any' crutch a bit too easily. Protocols and associated types are certainly powerful features that I'm still trying to wrap my head around.
Thank you for the code snippets I'll continue to study them.
After some further experimentation I came up with this solution (only works in 5.7):
extension UIViewRepresentable {
func _createContext() -> Any {
Context(coordinator: makeCoordinator())
}
func _createUIView(_ context: Any) -> UIView {
makeUIView(context: context as! Context)
}
}
// ...
if let representable = someView as? any UIViewRepresentable {
let uiView = representable._createUIView(representable._createContext())
print(uiView)
}
Granted this doesn't do anything useful. It was more an experiment trying to figure out how to call protocol functions with associated types on arbitrary implementations of those protocols. One trick seems to be to hide the associated types behind utility extension functions.