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?