洞察探索如何利用兼容微信生态的小程序容器,实现跨平台开发,助力金融和车联网行业的数字化转型。
650
2022-10-21
Snail一个Swift快速观测框架
A lightweight observables framework, also available in Kotlin
Installation
Carthage
You can install Carthage with Homebrew using the following command:
brew updatebrew install carthage
To integrate Snail into your Xcode project using Carthage, specify it in your Cartfile where "x.x.x" is the current release:
github "UrbanCompass/Snail" "x.x.x"
Swift Package Manager
To install using Swift Package Manager have your Swift package set up, and add Snail as a dependency to your Package.swift.
dependencies: [ .Package(url: "https://github.com/UrbanCompass/Snail.git", majorVersion: 0)]
Manually
Add all the files from Snail/Snail to your project
Developing Locally
Run the setup script to install required dependencies ./scripts/setup.sh
Creating Observables
let observable = Observable
Disposer
A disposer is in charge of removing all the subscriptions. A disposer is usually located in a centralized place where most of the subscriptions happen (ie: UIViewController in an MVVM architecture). Since most of the subscriptions are to different observables, and those observables are tied to type, all the things that are going to be disposed need to comform to Disposable.
If the Disposer helps get rid of the closures and prevent retention cycles (see weak self section). For the sake of all the examples, let's have a disposer created:
let disposer = Disposer()
Closure Wrapper
The main usage for the Disposer is to get rid of subscription closures that we create on Observables, but the other usage that we found handy, is the ability to dispose of regular closures. As part of the library, we created a small Closure wrapper class that complies with Disposable. This way you can wrap simple closures to be disposed.
let closureCall = Closure { print("We ❤️ Snail")}.add(to: Disposer)
Please note that this would not dispose of the closureCall reference to closure, it would only Dispose the content of the Closure.
Subscribing to 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)
Closures are optional too...
observable.subscribe( onNext: { thing in ... } // do something with thing).add(to: disposer)
observable.subscribe( onError: { error in ... } // do something with error).add(to: disposer)
Creating Observables Variables
let variable = Variable
let optionalString = Variable
let int = Variable
Combining Observable Variables
let isLoaderAnimating = Variable
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 = trueisListLoading.value = true // triggers
Transforming Observable Variable Types
let variable = Variable
Miscellaneous 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 errorlet n = 5let replay = Replay(n) // replays the last N events when a new observer subscribes
Subscribing to Control Events
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)
Queues
You can specify which queue an observables will be notified on by using .subscribe(queue:
There are 3 scenarios:
You don't specify the queue. Your observer will be notified on the same thread as the observable published on. You specified main queue AND the observable published on the main queue. Your observer will be notified synchronously on the main queue. You specified a queue. Your observer will be notified async on the specified queue.
Examples
Subscribing on DispatchQueue.main
observable.subscribe(queue: .main, onNext: { thing in ... }).add(to: disposer)
Weak self is optional
You can use [weak self] if you want, but with the introduction of Disposer, retention cycles are destroyed when calling disposer.disposeAll().
One idea would be to call disposer.disposeAll() when you pop a view controller from the navigation stack.
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 }}
In Practice
Subscribing to Notifications
NotificationCenter.default.observeEvent(Notification.Name.UIKeyboardWillShow) .subscribe(queue: .main, onNext: { notification in self.keyboardWillShow(notification) }).add(to: disposer)
Subscribing to Gestures
let panGestureRecognizer = UIPanGestureRecognizer()panGestureRecognizer.asObservable() .subscribe(queue: .main, onNext: { sender in // Your code here }).add(to: disposer)view.addGestureRecognizer(panGestureRecognizer)
Subscribing to UIBarButton Taps
navigationItem.leftBarButtonItem?.tap .subscribe(onNext: { self.dismiss(animated: true, completion: nil) }).add(to: disposer)
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~