Namespace Wrapper Issue for protocol with associated types

We usually have some auxiliary methods to access resources such as colors/images/fonts in a massive project. And at the same time, it will be good to have automatic completion support in IDE.

So most people's first reaction is to use some extensions directly on such types eg. UIColor / UIImage / UIFont / Color / Image / Font.

extension UIImage {
    static var homepage: UIImage { xx }
}

However, this method may cause namespace conflicts, causing other business modules to be forced to write full qualified name(A.hello & B.hello) or even conflict to cause a compile error when importing two basic components at the same time.

The solution in ObjC world is to add namespace in the api name, such as UIImage.xig_add

Swift does not encourage the inclusion of namespace on naming, so we can implement it through namespace wrapper.

In the namespace of XIG we have the following way to add namespace.

Non-Protocol Type

public struct XIGWrapper<Base> {
  public let base: Base
  public init(_ base: Base) {
    self.base = base
  }
}
public protocol XIGProtocol {}
public extension XIGProtocol {
  var xig: XIGWrapper<Self> {
    return XIGWrapper(self)
  }
  static var xig: XIGWrapper<Self>.Type {
    return XIGWrapper.self
  }
}
// Usage
import SwiftUI
extension Color: XIGProtocol {}
extension XIGWrapper where Base == Color {
  public func opacity(_ value: Double): Color { ... }
  public static var red: Color { ... }
}
// Business Code
Color.xig.red
Color.clear.xig.opacity(0.5)

Protocol Type with AssociatedType

For protocol Type with associated type like SwiftUI.View and SwiftUI.ButtonStyle, things are different.

First, we can't using the above code to use a extension directly

import SwiftUI
extension SwiftUI.View: XIGProtocol {} // ❌ Extension of protocol 'View' cannot have an inheritance clause

But we could use a different way to warp it:

public struct XIG<Content> {
    public let content: Content
    public init(_ content: Content) {
        self.content = content
    }
}

For non-static method/property

If we'd like to use Text("a").xig.boarder(), we could just write the following code

extension View {
  public var xig: XIG<Self> { XIG(self) }
  public static var xig: XIG<Self>.Type { XIG<Self>.self }
}

extension XIG where Content: View {
  public func border() -> some View{ ... }
}

// Usage
Text("xx").xig.border()

For static method/property

There is currently not a great solution for me.

If we do not use namespace, we could write the following code after SE0299

struct HelloButtonStyle: ButtonStyle { ... }
extension ButtonStyle where Self == BorderedButtonStyle {
  static var hello: some ButtonStyle { HelloButtonStyle() }
}
// Business Code
Text("Hello").buttonStyle(.hello)

But if we try to use namespace way, we could still can't get the context of Self

extension XIG where Content == BorderedButtonStyle {
  static func hello: some ButtonStyle { HelloButtonStyle() }
}
struct HelloButtonStyle: ButtonStyle { ... }
// Business Code
Text("Hello").buttonStyle(.xig.hello) // ❌ Contextual member reference to static property 'xig' requires 'Self' constraint in the protocol extension
Text("Hello").buttonStyle(HelloButtonStyle.xig.hello) // Unless we use this. But maybe I'd rather to use HelloButtonStyle() directly instead if so.

I'm not very familiar with the implementation detail of SE0299. And I guess the problem is it can only infer 1 step search for generic type and in the final example of the problem there will be 2 search steps to resolve the generic type.

A potential ugly implementation is to block the spread of paradigms and make it two 1 step-search.

public struct XIGStatic<Content> {
    public init(_ content: Content.Type) {}
}
extension SwiftUI.ButtonStyle {
    public static var xig: XIGStatic<Self> { XIGStatic(Self.self) }
}

// Usage
public struct AnyButtonStyle: ButtonStyle {
    public func makeBody(configuration: Configuration) -> some View { ... } // Unimplement
}
extension ButtonStyle where Self == AnyButtonStyle {
    public static var xig: XIGStatic<AnyButtonStyle> { XIGStatic(AnyButtonStyle.self) }
}
struct HelloButtonStyle: ButtonStyle { ... }
extension XIGStatic where Content == AnyButtonStyle {
    var hello: some ButtonStyle { HelloButtonStyle() }
}
// Then we could final use the following code in business code 😂
Text("Hello").buttonStyle(.xig.hello) // 🎉

Looking forward to get your idea on this. cc @anandabits

Protocol metatype extensions (to better support SwiftUI)