🐌 snail

一个轻量级的观察者框架,也提供 Kotlin。
安装
Carthage
您可以使用以下命令通过 Homebrew 安装 Carthage
brew update
brew install carthage
要使用 Carthage 将 Snail 集成到您的 Xcode 项目中,请在您的 Cartfile
中指定它,其中 "x.x.x"
是当前版本。
github "UrbanCompass/Snail" "x.x.x"
Swift 包管理器
使用 Swift 包管理器 安装,请确保您的 Swift 包已设置好,并将 Snail 添加为依赖项到您的 Package.swift
文件中。
dependencies: [
.Package(url: "https://github.com/UrbanCompass/Snail.git", majorVersion: 0)
]
手动
将 Snail/Snail
目录下的所有文件添加到您的项目中。
本地开发
- 运行设置脚本以安装必要的依赖项
./scripts/setup.sh
创建可观察对象
let observable = Observable<thing>()
Disposer
Disposer 负责移除所有订阅。Disposer 通常位于订阅发生最集中的地方(例如:在 MVVM 架构中的 UIViewController)。由于大多数订阅都绑定到不同的可观察对象,并且这些可观察对象都关联到类型,因此所有将要被清理的东西都需要符合 Disposable
。
如果 Disposer
有助于去除闭包并防止 retain cycle(请参阅 weak self 部分),那么为了所有示例,让我们创建一个 disposer
let disposer = Disposer()
Closure Wrapper
Disposer
的主要用途是用于消除我们在Observables
上创建的订阅闭包,但我们还发现它可以用来处理常规闭包。作为库的一部分,我们创建了一个小的Closure
包装类,它遵循Disposable
规范。这样,您可以包装简单闭包以便进行处理。
let closureCall = Closure {
print("We ❤️ Snail")
}.add(to: Disposer)
请注意,这不会消除闭包的closureCall
引用,它只会消除Closure
的内容。
订阅Observables
observable.subscribe(
onNext: { thing in ... }, // do something with thing
onError: { error in ... }, // do something with error
onDone: { ... } //do something when it's done
).add(to: disposer)
闭包也是可选的...
observable.subscribe(
onNext: { thing in ... } // do something with thing
).add(to: disposer)
observable.subscribe(
onError: { error in ... } // do something with error
).add(to: disposer)
创建Observables变量
let variable = Variable<whatever>(some initial value)
let optionalString = Variable<String?>(nil)
optionalString.asObservable().subscribe(
onNext: { string in ... } // do something with value changes
).add(to: disposer)
optionalString.value = "something"
let int = Variable<Int>(12)
int.asObservable().subscribe(
onNext: { int in ... } // do something with value changes
).add(to: disposer)
int.value = 42
组合Observables变量
let isLoaderAnimating = Variable<Bool>(false)
isLoaderAnimating.bind(to: viewModel.isLoading) // forward changes from one Variable to another
viewModel.isLoading = true
print(isLoaderAnimating.value) // true
Observable.merge([userCreated, userUpdated]).subscribe(
onNext: { user in ... } // do something with the latest value that got updated
}).add(to: disposer)
userCreated.value = User(name: "Russell") // triggers
userUpdated.value = User(name: "Lee") // triggers
Observable.combineLatest((isMapLoading, isListLoading)).subscribe(
onNext: { isMapLoading, isListLoading in ... } // do something when both values are set, every time one gets updated
}).add(to: disposer)
isMapLoading.value = true
isListLoading.value = true // triggers
其他Observables
let just = Just(1) // always returns the initial value (1 in this case)
enum TestError: Error {
case test
}
let failure = Fail(TestError.test) // always fail with error
let n = 5
let replay = Replay(n) // replays the last N events when a new observer subscribes
操作符
Snail提供了某些基本操作符,用于转换和操作可观察对象。
-
map
操作符允许将可观察值的值映射到另一个值。类似于对Collection
类型上的map
操作。let observable = Observable<Int>() let subject = observable.map { "Number: \($0)" } // -> subject emits `String` whenever `observable` emits.
-
filter
操作符允许过滤出可观察链中的某些值。类似于对Collection
类型上的filter
操作。如果值应该被发射,则返回true
,如果需要过滤掉该值,则返回false
。let observable = Observable<Int>() let subject = observable.filter { $0 % 2 == 0 } // -> subject will only emit even numbers.
-
flatMap
操作符允许将值映射到其他可观察值,例如,您可能希望在用户点击可观察值发射时创建一个针对网络请求的可观察值。let fetchTrigger = Observable<Void>() let subject = fetchTrigger.flatMap { Variable(100).asObservable() } // -> subject is an `Observable<Int>` that is created when `fetchTrigger` emits.
订阅控件事件
let control = UIControl()
control.controlEvent(.touchUpInside).subscribe(
onNext: { ... } // do something with thing
).add(to: disposer)
let button = UIButton()
button.tap.subscribe(
onNext: { ... } // do something with thing
).add(to: disposer)
队列
您可以使用.subscribe(queue: <desired queue>)
指定哪个队列将通知可观察值。如果没有指定,则可观察值将在.publish()时所在的队列上通知。
共有3种情况
-
您没有指定队列。您的观察者将在相同线程上收到与可观察值发布相同的通知。
-
您指定了
main
队列,并且可观察值也发布在main
队列上。您的观察者将在主队列上同步接收通知。 -
您指定了一个队列。您的观察者将在指定队列上异步接收通知。
示例
订阅在DispatchQueue.main
observable.subscribe(queue: .main,
onNext: { thing in ... }
).add(to: disposer)
弱引用是可选的
如果需要,可以使用[弱引用self],但有了Disposer
的引入,当调用disposer.disposeAll()
时,保留周期将被销毁。
一种想法是,当从导航栈中弹出视图控制器时调用disposer.disposeAll()
。
protocol HasDisposer {
var disposer: Disposer
}
class NavigationController: UINavigationController {
public override func popViewController(animated: Bool) -> UIViewController? {
let viewController = super.popViewController(animated: animated)
(viewController as? HasDisposer).disposer.disposeAll()
return viewController
}
}
实际上
订阅通知
NotificationCenter.default.observeEvent(Notification.Name.UIKeyboardWillShow)
.subscribe(queue: .main, onNext: { notification in
self.keyboardWillShow(notification)
}).add(to: disposer)
订阅手势
let panGestureRecognizer = UIPanGestureRecognizer()
panGestureRecognizer.asObservable()
.subscribe(queue: .main, onNext: { sender in
// Your code here
}).add(to: disposer)
view.addGestureRecognizer(panGestureRecognizer)
订阅 UIBarButton 点击
navigationItem.leftBarButtonItem?.tap
.subscribe(onNext: {
self.dismiss(animated: true, completion: nil)
}).add(to: disposer)