Carthage
brew install carthage
Cartfile
github "RedHandTech/Speedy"
carthage update
Cocoapods
pod 'Speedy', '~> 0.1'
添加到Podfile中,然后运行pod install
可观察
Inspectable
的实例。Inspectable
允许您筛选、映射,当然也可以检查值(请见下面的详细示例)。值
Value
类是创建可检查值的起点,简单地将您的值包装在一个Value
实例中。Value
中(请见下面的详细示例)。注意:以下示例假设了MVVM环境。Speedy在采用MVVM时特别有用。
class MyViewModel {
let myViewControllerTitle = Value("Hello, World!")
}
就是这样!myViewControllerTitle
的类型将是Value<String>
。
要读取Value
的值,只要访问value
属性即可
class MyViewController: UIViewController {
let viewModel: MyViewModel ....
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
title = viewModel.myViewControllerTitle.value
}
}
要设置响应式检查
class MyViewController: UIViewController {
let viewModel: MyViewModel ....
override func viewDidLoad() {
super.viewDidLoad()
// setup inspections
weak var weakSelf = self // IMPORTANT
viewModel.myViewControllerTitle.inspect { weakSelf?.title = $0 }
}
}
这将设置在viewModel
中对myViewControllerTitle
值进行回调和每次值更改时调用。这意味着视图模型只需要调用myViewControllerTitle.value = "Goodbye, World!"
,然后回调和闭包会被调用。**注意**创建自引用类型的self弱引用在闭包中使用的重要性。这是因为在这种情况下,视图控制器拥有视图模型,视图模型反过来拥有值,值又拥有闭包。
如果您有这样的视图模型
class MyViewModel {
let rows = Value(0)
}
在您的视图控制器中,您希望使用这个值来提供给一个 UITableView
数据。您想在值变化时每次都刷新表格,对吧?所以...
class MyViewController: UIViewController {
let viewModel: MyViewModel ...
override func viewDidLoad() {
super.viewDidLoad()
weak var weakSelf = self
viewModel.rows.inspect { _ in weakSelf?.tableView?.reloadData() }
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return viewModel.rows.value
}
}
太好了!但是如果 rows.value
是一个负数怎么办呢?我们并不希望在那时调用重新加载数据。Speedy 允许您过滤出负数,如下所示
class MyViewController: UIViewController {
let viewModel: MyViewModel ...
override func viewDidLoad() {
super.viewDidLoad()
weak var weakSelf = self
viewModel.rows.when { $0 >= 0 }
.inspect { _ in weakSelf?.tableView?.reloadData() }
}
}
只有在 when
闭包中的条件评估为 true
时,事件序列才会继续。
假设我们有一个基本的值设置如下
class MyViewModel {
let favoriteNumber = Value(7)
}
在我们的视图控制器中,我们想做一些很简单的事情,比如显示一个带有文本 "我最喜欢的数字是 7"
的标签,但我们希望这个标签是动态的,也就是说当 favoriteNumber
值变化时,标签需要更新。Speedy 允许您转换值,这可以非常简单,如下所示
class MyViewController: UIViewController {
let viewModel: MyViewModel ....
override func viewDidLoad() {
super.viewDidLoad()
weak var weakSelf = self
viewModel.favoriteNumber.map { String(format: "My favorite number is: %i", $0) }
.inspect { weakSelf?.label?.text = $0 }
}
}
现在假设如果 是 9
,您想将其更改为 8(别问我为什么...)
class MyViewController: UIViewController {
let viewModel: MyViewModel ....
override func viewDidLoad() {
super.viewDidLoad()
weak var weakSelf = self
viewModel.favoriteNumber.map { $0 == 9 ? 8 : $0 }
.map { String(format: "My favorite number is: %i", $0) }
.inspect { weakSelf?.label?.text = $0 }
}
}
现在假设 favoriteNumber.value
是一个可选值,并且您只希望在它不是 nil 时更新标签
class MyViewController: UIViewController {
let viewModel: MyViewModel ....
override func viewDidLoad() {
super.viewDidLoad()
weak var weakSelf = self
viewModel.favoriteNumber.when { $0 != nil }
.map { $0! }
.map { String(format: "My favorite number is: %i", $0) }
.inspect { weakSelf?.label?.text = $0 }
}
}
或者,如果您想始终更新标签,但如果值是 nil,则将其转换为大码
class MyViewController: UIViewController {
let viewModel: MyViewModel ....
override func viewDidLoad() {
super.viewDidLoad()
weak var weakSelf = self
viewModel.favoriteNumber.map { $0 ?? 0 }
.map { String(format: "My favorite number is: %i", $0) }
.inspect { weakSelf?.label?.text = $0 }
}
}
可能性是无限的!
正如您可能已经注意到的,Speedy 支持序列的 Inspectable
,每次调用 where
、map
和 watch
都会返回另一个 Inspectable
的实例,因此您可以继续操作这些值。
这允许您做以下这样的事情
class MyViewController: UIViewController {
let viewModel: MyViewModel ....
override func viewDidLoad() {
super.viewDidLoad()
weak var weakSelf = self
viewModel.coolValue.inspect { weakSelf?.doSomethingCool($0) }
}
}
到这样复杂的事情
class MyViewModel {
let repeatable: Value<(String?, Int)> = Value(("Hello, World!", 10))
}
class MyViewController: UIViewController {
let viewModel: MyViewModel ....
var tableData: [String] = []
override func viewDidLoad() {
super.viewDidLoad()
weak var weakSelf = self
viewModel.repeatable.when { $0.0 != nil }
.map { ($0.0!, $0.1) }
.watch { weakSelf?.doCoolThingWithString($0.0) }
.when { $0.0 != "Goodbye, World!" }
.when { $0.1 >= 0 }
.map { (t: (String, Int)) -> [String]
var array: [String] = []
for _ in 1...t.1 {
arr.append(t.0)
}
return array
}.inspect { array in
weakSelf?.tableData = array
weakSelf?.tableView?.reloadData()
}
}
}
您可以通过访问旧值的属性(很有趣,不是吗)来访问给定值的旧值。
let myVal = Value(10)
print(myVal.oldValue) // output nil
myVal.value = 1
print(myVal.oldValue) // output 10
还有一些围绕旧值的函数
let myVal = Value(0)
myVal.compare { $0 == ($1 ?? 0) + 1 }
.inspect { print("Incremented \($0 - 1) by 1 is now \($0)") }
myVal.value += 1 // outputs: "Incremented 0 by 1 is now 1."
myVal.value += 1 // outputs: "Incremented 1 by 1 is now 2"
myVal.value += 2 // no output
let val = Value("Hello")
val.distinct()
.inspect { print("New distinct value: \($0)") }
val.value = "Hello" // no output
val.value = "Hello, World!" // output: "New distinct value: Hello, World!"
您可以使用 Speedy 执行多个时间相关的任务。
tick
函数let value = Value(0)
let metadata = TimerMetadata(interval: 1)
value.tick(metadata)
.do { $0 + 1 }
.inspect {
print($0)
if $0 >= 10 {
metadata.stop()
}
}
上面的代码将输出以下值:1, 2, 3, 4, 5, 6, 7, 8, 9, 10
Wait
类Wait(5).then {
print("Hello, World!")
}
上面的代码在 5 秒后打印 Hello, World!
。
var count = 0
Wait(1).repeat {
count += 1
print("Hello, World!")
if count >= 10 {
$0 = true
}
}
上面的代码将打印 Hello, World!
,直到 count 变量等于 10。
酷吧?