Swift 命名空间

之前在使用 Kingfisher、RxSwift 的时候,一直对 .rx 及 .kf 的实现比较疑惑,不知道其实现原理。

这篇文章我们就来打造一个专属自己的命名空间。

介绍

Swift 的命名空间是基于 module 而不是在代码中显式地指明,每个 module 代表了 Swift 中的一个命名空间。也就是说,同一个 target 里的类型名称还是不能相同的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// MyFramework.swift
// 这个文件存在于 MyFramework.framework 中
public class MyClass {
public class func hello() {
print("hello from framework")
}
}

// MyApp.swift
// 这个文件存在于 app 的主 target 中
class MyClass {
class func hello() {
print("hello from app")
}
}

在可能出现冲突的地方,我们需要在类型名称前面加上 module 的名字 (也就是 target 的名字):

1
2
3
4
5
// 使用哪个 MyClass,取决于是在哪个 target 中调用
MyClass.hello()

// hello from framework
MyFramework.MyClass.hello()

自己的命名空间

上面的说明虽然为我们解决了命名冲突的问题,但是我们应该怎样实现类似于 .rx、.kf 这样自己的命名空间呢?

下面我们就来一步步讲解。

原理

原理: 对原类型进行一层封装。然后,对这个封装进行自定义的方法扩展。

现在你可能不太理解,下面我们就一步步的来实现自己的命名空间。

代码实现

  • 定义一个泛型类

对原类型进行一层封装

1
2
3
4
5
6
7
8
// 定义泛型类
// 首先定义一个泛型类 JTKit,使用泛型 Base
public struct JTKit<Base> {
private let base: Base
public init(_ base: Base) {
self.base = base
}
}
  • 定义泛型协议

定义了一个 JTWrappable 协议,这个协议代表了支持 namespace 形式的扩展。并紧接着给这个协议 extension 了默认实现。这样实现了这个协议的类型就不需要自行实现协议所约定的内容了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 定义泛型协议
// 定义支持泛型的协议 JTWrappable,并通过协议扩展提供协议的默认实现,返回实现泛型类 JTKit 的对象自身。
public protocol JTWrappable {
associatedtype WrappableType

var jt: WrappableType { get }
}

// 协议的扩展
public extension JTWrappable {
var jt: JTKit<Self> {
get { return JTKit(self) }
}
}

associatedtype: 相关类型。意思也就是被associatedtype关键字修饰的变量,相当于一个占位符,而不能表示具体的类型。具体的类型需要让实现的类来指定。

  • 实现命名空间 jt

需要实现命名空间的类提供 JTWrappable 协议扩展,并实现相关命名空间的对象方法(主要是扩展新的方法,如代码中的 testMethod 方法)

1
2
3
4
5
6
7
8
9
10
11
12
extension String: JTWrappable {}

// String 命名空间 jt 中的函数
extension JTKit where Base == String {
public var testMethod: String {
return base + "namespace"
}

public func method(str: String) -> String {
return base + str
}
}
  • 使用
1
2
3
4
5
6
7
let str = "Hello "
print(str.jt.testMethod)
print(str.jt.method(str: "namespace"))

---输出结果:---
Hello namespace
Hello namespace

参考资料

Swifter 100 个 SWift 必备 Tips

Versatile namespace in Swift