How do you forward opaque inputs to default implementation outputs without a derived protocol?

Use case: SwiftUI AdaptiveView Protocol: Default vs Custom Implementation Issue

I can reduce that to the following. Can it be done without the extra protocol?

protocol UselessProtocol {
  associatedtype Input
  static var input: Input { get }
}

protocol Derived: UselessProtocol {
  associatedtype Output
  static func method(_: Input) -> Output
}

extension Derived {
  static var input: some Any { "default" }
  static var property: Output { method(input) }
  static func method(_: Input) -> some Any { input }
}
protocol SingleProtocol {
  associatedtype Input
  associatedtype Output
  static var input: Input { get }
  static func method(_: Input) -> Output
}

extension SingleProtocol {
  static var input: some Any { "default" }
  static var property: Output { method(input) }
  static func method(_: Input) -> some Any { input }
}
enum DerivedDefault: Derived { }
enum DerivedOverrider: Derived {
  static func method(_: Input) -> some Any { "override" }
}

enum SingleProtocolDefault: SingleProtocol { }
enum SingleProtocolOverrider: SingleProtocol {
  static func method(_: Input) -> some Any { "override" }
}
func cast(_ property: some Any) -> String { property as! String }
#expect(cast(DerivedDefault.property) == "default") // ✅
#expect(cast(DerivedOverrider.property) == "override") // ✅
#expect(cast(SingleProtocolDefault.property) == "default") // ✅
#expect(cast(SingleProtocolOverrider.property) == "override") // ❌ It's still "default".
2 Likes

Does it work if you use where clauses? Something like this in place of your single extension block:

extension SingleProtocol where Input == String {
  static var input: String { "default" }
}

extension SingleProtocol {
  static var property: Output { method(input) }
}

extension SingleProtocol where Output == Input {
  static func method(_: Input) -> Output { input }
}

(Some of these extensions might be able to be combined by moving the where clauses, but you get the idea.)

That works here (if all opaque outputs are changed from some Any to Output).

But constraining extensions to a concrete type (String, there), does not address the question. For the original question, the type is not String, but some View from a ViewBuilder. The some allows for several constraints to be satisfied in a way that concrete known types don't permit.