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.
- eg. SnapKit
aView.snp.xx
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.