Speedy 0.2.1

Speedy 0.2.1

测试测试通过
语言语言 SwiftSwift
许可证 MIT
发布最后发布2017年4月
SwiftSwift版本3.0
SPM支持SPM

Rob Sanders维护。



Speedy 0.2.1

  • 作者
  • Rob Sanders

Speedy

欢迎使用Speedy!一个简洁的响应式Swift属性观察框架。
快速入门
  • 要开始使用Speedy,您可以使用Carthage、CocoaPods,或者简单地下载源并将其构建为所需的操作系统。

Carthage

  • 确保您已安装Carthage:brew install carthage
  • 在选择的目录中创建一个包含以下内容的Cartfile
github "RedHandTech/Speedy"
  • 运行carthage update

Cocoapods

  • pod 'Speedy', '~> 0.1'添加到Podfile中,然后运行pod install
文档

可观察

  • Speedy中的每一项均为类型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,每次调用 wheremapwatch 都会返回另一个 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。

酷吧?