I'm attempting to generalise UIViewRepresentable and NSViewRepresentable in my codebase into a single protocol called ViewRepresentable.
The idea being that a struct can conform to ViewRepresentable, and implicitly conform to UIViewRepresentable on iOS, and NSViewRepresentable on macOS. Although this sounds specific to SwiftUI, it is in fact a general problem.
My approach is like so:
#if os(iOS) || os(watchOS) || os(tvOS)
import UIKit
public protocol ViewRepresentable: UIViewRepresentable {
associatedtype ViewType
@MainActor
func makeView(context: Context) -> ViewType
@MainActor
func updateView(_ view: ViewType, context: Context)
}
extension ViewRepresentable {
@MainActor
public func makeUIView(context: Context) -> UIViewType {
makeView(context: context)
}
@MainActor
public func updateUIView(_ uiView: UIViewType, context: Context) {
updateView(uiView, context: context)
}
}
#elseif os(macOS)
import AppKit
public protocol ViewRepresentable: NSViewRepresentable {
associatedtype ViewType
typealias NSViewType = ViewType
@MainActor
func makeView(context: Context) -> ViewType
@MainActor
func updateView(_ view: ViewType, context: Context)
}
extension ViewRepresentable {
@MainActor
public func makeNSView(context: Context) -> NSViewType {
makeView(context: context)
}
@MainActor
public func updateNSView(_ nsView: NSViewType, context: Context) {
updateView(nsView, context: context)
}
}
#endif
I want the struct that conforms to ViewRepresentable to not have to care if they are conforming to UIViewRepresentable or NSViewRepresentable, so they shouldn't have to specify the typealias for NSViewType or UIViewType, instead I want these to be inferred from them simply specifying the value of the ViewType typealias.
For example:
public struct SignInWithApple: ViewRepresentable {
public typealias ViewType = ASAuthorizationAppleIDButton
public func makeView(context: Context) -> ASAuthorizationAppleIDButton {
ASAuthorizationAppleIDButton()
}
public func updateView(_ view: ASAuthorizationAppleIDButton, context: Context) {
// . . .
}
}
At the moment, this example above fails to compile, because either NSViewType or UIViewType has not been specified by the struct SignInWithApple.
My initial approach was to specify the typealias on the protocol or the extension, so something like this (I've removed the code that hasn't changed):
public protocol ViewRepresentable: UIViewRepresentable {
typealias UIViewType = ViewType
/// . . . etc
}
public protocol ViewRepresentable: NSViewRepresentable {
typealias NSViewType = ViewType
/// . . . etc
}
This gets things compiling, and solves my problem, but I get warnings:
Typealias overriding associated type 'NSViewType' from protocol 'NSViewRepresentable' is better expressed as same-type constraint on the protocol
Typealias overriding associated type 'UIViewType' from protocol 'UIViewRepresentable' is better expressed as same-type constraint on the protocol
I used the fix it, and it made sense:
public protocol ViewRepresentable: UIViewRepresentable where UIViewType == ViewType {
/// . . . etc
}
public protocol ViewRepresentable: NSViewRepresentable where NSViewType == ViewType {
/// . . . etc
}
The warning goes away, but the build fails because it expects the SignInWithApple doesn't conform to ViewRepresentable, because it doesn't specify the typealias NSViewType or UIViewType.
What am I doing wrong here? I can get it to build, with warnings, but the fix it for the warning introduces build errors. Is there a warning free fix I'm missing?