You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

627 lines
15 KiB

5 years ago
  1. [![CircleCI](https://img.shields.io/circleci/project/github/RxSwiftCommunity/RxSwiftExt/master.svg)](https://circleci.com/gh/RxSwiftCommunity/RxSwiftExt/tree/master)
  2. ![pod](https://img.shields.io/cocoapods/v/RxSwiftExt.svg)
  3. [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
  4. RxSwiftExt
  5. ===========
  6. If you're using [RxSwift](https://github.com/ReactiveX/RxSwift), you may have encountered situations where the built-in operators do not bring the exact functionality you want. The RxSwift core is being intentionally kept as compact as possible to avoid bloat. This repository's purpose is to provide additional convenience operators and Reactive Extensions.
  7. Installation
  8. ===========
  9. This branch of RxSwiftExt targets Swift 4.x and RxSwift 4.0.0 or later.
  10. * If you're looking for the Swift 3 version of RxSwiftExt, please use version `2.5.1` of the framework.
  11. * If your project is running on Swift 2.x, please use version `1.2` of the framework.
  12. #### CocoaPods
  13. Using Swift 4:
  14. ```ruby
  15. pod 'RxSwiftExt'
  16. ```
  17. This will install both the `RxSwift` and `RxCocoa` extensions.
  18. If you're interested in only installing the `RxSwift` extensions, without the `RxCocoa` extensions, simply use:
  19. ```ruby
  20. pod 'RxSwiftExt/Core'
  21. ```
  22. Using Swift 3:
  23. ```ruby
  24. pod 'RxSwiftExt', '2.5.1'
  25. ```
  26. If you use Swift 2.x:
  27. ```ruby
  28. pod 'RxSwiftExt', '1.2'
  29. ```
  30. #### Carthage
  31. Add this to your `Cartfile`
  32. ```
  33. github "RxSwiftCommunity/RxSwiftExt"
  34. ```
  35. Operators
  36. ===========
  37. RxSwiftExt is all about adding operators and Reactive Extensions to [RxSwift](https://github.com/ReactiveX/RxSwift)!
  38. ## Operators
  39. These operators are much like the RxSwift & RxCocoa core operators, but provide additional useful abilities to your Rx arsenal.
  40. * [unwrap](#unwrap)
  41. * [ignore](#ignore)
  42. * [ignoreWhen](#ignorewhen)
  43. * [Observable.once](#once)
  44. * [distinct](#distinct)
  45. * [map](#map)
  46. * [not](#not)
  47. * [and](#and)
  48. * [Observable.cascade](#cascade)
  49. * [pairwise](#pairwise)
  50. * [nwise](#nwise)
  51. * [retry](#retry)
  52. * [repeatWithBehavior](#repeatwithbehavior)
  53. * [catchErrorJustComplete](#catcherrorjustcomplete)
  54. * [pausable](#pausable)
  55. * [pausableBuffered](#pausablebuffered)
  56. * [apply](#apply)
  57. * [filterMap](#filtermap)
  58. * [Observable.fromAsync](#fromasync)
  59. * [Observable.zip(with:)](#zipwith)
  60. * [withUnretained](#withunretained)
  61. * [count](#count)
  62. There are two more available operators for `materialize()`'d sequences:
  63. * [errors](#errors-elements)
  64. * [elements](#errors-elements)
  65. Read below for details about each operator.
  66. ## Reactive Extensions
  67. RxSwift/RxCocoa Reactive Extensions are provided to enhance existing objects and classes from the Apple-ecosystem with Reactive abilities.
  68. * [UIViewPropertyAnimator.animate](#uiviewpropertyanimatoranimate)
  69. --------
  70. Operator details
  71. ===========
  72. #### unwrap
  73. Unwrap optionals and filter out nil values.
  74. ```swift
  75. Observable.of(1,2,nil,Int?(4))
  76. .unwrap()
  77. .subscribe { print($0) }
  78. ```
  79. ```
  80. next(1)
  81. next(2)
  82. next(4)
  83. ```
  84. #### ignore
  85. Ignore specific elements.
  86. ```swift
  87. Observable.from(["One","Two","Three"])
  88. .ignore("Two")
  89. .subscribe { print($0) }
  90. ```
  91. ```
  92. next(One)
  93. next(Three)
  94. completed
  95. ```
  96. #### ignoreWhen
  97. Ignore elements according to closure.
  98. ```swift
  99. Observable<Int>
  100. .of(1,2,3,4,5,6)
  101. .ignoreWhen { $0 > 2 && $0 < 6 }
  102. .subscribe { print($0) }
  103. ```
  104. ```
  105. next(1)
  106. next(2)
  107. next(6)
  108. completed
  109. ```
  110. #### once
  111. Send a next element exactly once to the first subscriber that takes it. Further subscribers get an empty sequence.
  112. ```swift
  113. let obs = Observable.once("Hello world")
  114. print("First")
  115. obs.subscribe { print($0) }
  116. print("Second")
  117. obs.subscribe { print($0) }
  118. ```
  119. ```
  120. First
  121. next(Hello world)
  122. completed
  123. Second
  124. completed
  125. ```
  126. #### distinct
  127. Pass elements through only if they were never seen before in the sequence.
  128. ```swift
  129. Observable.of("a","b","a","c","b","a","d")
  130. .distinct()
  131. .subscribe { print($0) }
  132. ```
  133. ```
  134. next(a)
  135. next(b)
  136. next(c)
  137. next(d)
  138. completed
  139. ```
  140. #### mapTo
  141. Replace every element with the provided value.
  142. ```swift
  143. Observable.of(1,2,3)
  144. .mapTo("Nope.")
  145. .subscribe { print($0) }
  146. ```
  147. ```
  148. next(Nope.)
  149. next(Nope.)
  150. next(Nope.)
  151. completed
  152. ```
  153. #### mapAt
  154. Transform every element to the value at the provided key path.
  155. ```swift
  156. struct Person {
  157. let name: String
  158. }
  159. Observable
  160. .of(
  161. Person(name: "Bart"),
  162. Person(name: "Lisa"),
  163. Person(name: "Maggie")
  164. )
  165. .mapAt(\.name)
  166. .subscribe { print($0) }
  167. ```
  168. ```
  169. next(Bart)
  170. next(Lisa)
  171. next(Maggie)
  172. completed
  173. ```
  174. #### not
  175. Negate booleans.
  176. ```swift
  177. Observable.just(false)
  178. .not()
  179. .subscribe { print($0) }
  180. ```
  181. ```
  182. next(true)
  183. completed
  184. ```
  185. #### and
  186. Verifies that every value emitted is `true`
  187. ```swift
  188. Observable.of(true, true)
  189. .and()
  190. .subscribe { print($0) }
  191. Observable.of(true, false)
  192. .and()
  193. .subscribe { print($0) }
  194. Observable<Bool>.empty()
  195. .and()
  196. .subscribe { print($0) }
  197. ```
  198. Returns a `Maybe<Bool>`:
  199. ```
  200. success(true)
  201. success(false)
  202. completed
  203. ```
  204. #### cascade
  205. Sequentially cascade through a list of observables, dropping previous subscriptions as soon as an observable further down the list starts emitting elements.
  206. ```swift
  207. let a = PublishSubject<String>()
  208. let b = PublishSubject<String>()
  209. let c = PublishSubject<String>()
  210. Observable.cascade([a,b,c])
  211. .subscribe { print($0) }
  212. a.onNext("a:1")
  213. a.onNext("a:2")
  214. b.onNext("b:1")
  215. a.onNext("a:3")
  216. c.onNext("c:1")
  217. a.onNext("a:4")
  218. b.onNext("b:4")
  219. c.onNext("c:2")
  220. ```
  221. ```
  222. next(a:1)
  223. next(a:2)
  224. next(b:1)
  225. next(c:1)
  226. next(c:2)
  227. ```
  228. #### pairwise
  229. Groups elements emitted by an Observable into arrays, where each array consists of the last 2 consecutive items; similar to a sliding window.
  230. ```swift
  231. Observable.from([1, 2, 3, 4, 5, 6])
  232. .pairwise()
  233. .subscribe { print($0) }
  234. ```
  235. ```
  236. next((1, 2))
  237. next((2, 3))
  238. next((3, 4))
  239. next((4, 5))
  240. next((5, 6))
  241. completed
  242. ```
  243. #### nwise
  244. Groups elements emitted by an Observable into arrays, where each array consists of the last N consecutive items; similar to a sliding window.
  245. ```swift
  246. Observable.from([1, 2, 3, 4, 5, 6])
  247. .nwise(3)
  248. .subscribe { print($0) }
  249. ```
  250. ```
  251. next([1, 2, 3])
  252. next([2, 3, 4])
  253. next([3, 4, 5])
  254. next([4, 5, 6])
  255. completed
  256. ```
  257. #### retry
  258. Repeats the source observable sequence using given behavior in case of an error or until it successfully terminated.
  259. There are four behaviors with various predicate and delay options: `immediate`, `delayed`, `exponentialDelayed` and
  260. `customTimerDelayed`.
  261. ```swift
  262. // in case of an error initial delay will be 1 second,
  263. // every next delay will be doubled
  264. // delay formula is: initial * pow(1 + multiplier, Double(currentAttempt - 1)), so multiplier 1.0 means, delay will doubled
  265. _ = sampleObservable.retry(.exponentialDelayed(maxCount: 3, initial: 1.0, multiplier: 1.0), scheduler: delayScheduler)
  266. .subscribe(onNext: { event in
  267. print("Receive event: \(event)")
  268. }, onError: { error in
  269. print("Receive error: \(error)")
  270. })
  271. ```
  272. ```
  273. Receive event: First
  274. Receive event: Second
  275. Receive event: First
  276. Receive event: Second
  277. Receive event: First
  278. Receive event: Second
  279. Receive error: fatalError
  280. ```
  281. #### repeatWithBehavior
  282. Repeats the source observable sequence using given behavior when it completes. This operator takes the same parameters as the [retry](#retry) operator.
  283. There are four behaviors with various predicate and delay options: `immediate`, `delayed`, `exponentialDelayed` and `customTimerDelayed`.
  284. ```swift
  285. // when the sequence completes initial delay will be 1 second,
  286. // every next delay will be doubled
  287. // delay formula is: initial * pow(1 + multiplier, Double(currentAttempt - 1)), so multiplier 1.0 means, delay will doubled
  288. _ = completingObservable.repeatWithBehavior(.exponentialDelayed(maxCount: 3, initial: 1.0, multiplier: 1.2), scheduler: delayScheduler)
  289. .subscribe(onNext: { event in
  290. print("Receive event: \(event)")
  291. })
  292. ```
  293. ```
  294. Receive event: First
  295. Receive event: Second
  296. Receive event: First
  297. Receive event: Second
  298. Receive event: First
  299. Receive event: Second
  300. ```
  301. #### catchErrorJustComplete
  302. Completes a sequence when an error occurs, dismissing the error condition
  303. ```swift
  304. let _ = sampleObservable
  305. .do(onError: { print("Source observable emitted error \($0), ignoring it") })
  306. .catchErrorJustComplete()
  307. .subscribe {
  308. print ("\($0)")
  309. }
  310. ```
  311. ```
  312. next(First)
  313. next(Second)
  314. Source observable emitted error fatalError, ignoring it
  315. completed
  316. ```
  317. #### pausable
  318. Pauses the elements of the source observable sequence unless the latest element from the second observable sequence is `true`.
  319. ```swift
  320. let observable = Observable<Int>.interval(1, scheduler: MainScheduler.instance)
  321. let trueAtThreeSeconds = Observable<Int>.timer(3, scheduler: MainScheduler.instance).map { _ in true }
  322. let falseAtFiveSeconds = Observable<Int>.timer(5, scheduler: MainScheduler.instance).map { _ in false }
  323. let pauser = Observable.of(trueAtThreeSeconds, falseAtFiveSeconds).merge()
  324. let pausedObservable = observable.pausable(pauser)
  325. let _ = pausedObservable
  326. .subscribe { print($0) }
  327. ```
  328. ```
  329. next(2)
  330. next(3)
  331. ```
  332. More examples are available in the project's Playground.
  333. #### pausableBuffered
  334. Pauses the elements of the source observable sequence unless the latest element from the second observable sequence is `true`. Elements emitted by the source observable are buffered (with a configurable limit) and "flushed" (re-emitted) when the observable resumes.
  335. Examples are available in the project's Playground.
  336. #### apply
  337. Apply provides a unified mechanism for applying transformations on Observable
  338. sequences, without having to extend ObservableType or repeating your
  339. transformations. For additional rationale for this see
  340. [discussion on github](https://github.com/RxSwiftCommunity/RxSwiftExt/issues/73)
  341. ```swift
  342. // An ordinary function that applies some operators to its argument, and returns the resulting Observable
  343. func requestPolicy(_ request: Observable<Void>) -> Observable<Response> {
  344. return request.retry(maxAttempts)
  345. .do(onNext: sideEffect)
  346. .map { Response.success }
  347. .catchError { error in Observable.just(parseRequestError(error: error)) }
  348. // We can apply the function in the apply operator, which preserves the chaining style of invoking Rx operators
  349. let resilientRequest = request.apply(requestPolicy)
  350. ```
  351. #### filterMap
  352. A common pattern in Rx is to filter out some values, then map the remaining ones to something else. `filterMap` allows you to do this in one step:
  353. ```swift
  354. // keep only odd numbers and double them
  355. Observable.of(1,2,3,4,5,6)
  356. .filterMap { number in
  357. (number % 2 == 0) ? .ignore : .map(number * 2)
  358. }
  359. ```
  360. The sequence above keeps even numbers 2, 4, 6 and produces the sequence 4, 8, 12.
  361. #### errors, elements
  362. These operators only apply to observable serquences that have been materialized with the `materialize()` operator (from RxSwift core). `errors` returns a sequence of filtered error events, ommitting elements. `elements` returns a sequence of filtered element events, ommitting errors.
  363. ```swift
  364. let imageResult = _chooseImageButtonPressed.asObservable()
  365. .flatMap { imageReceiver.image.materialize() }
  366. .share()
  367. let image = imageResult
  368. .elements()
  369. .asDriver(onErrorDriveWith: .never())
  370. let errorMessage = imageResult
  371. .errors()
  372. .map(mapErrorMessages)
  373. .unwrap()
  374. .asDriver(onErrorDriveWith: .never())
  375. ```
  376. #### fromAsync
  377. Turns simple asynchronous completion handlers into observable sequences. Suitable for use with existing asynchronous services which call a completion handler with only one parameter. Emits the result produced by the completion handler then completes.
  378. ```swift
  379. func someAsynchronousService(arg1: String, arg2: Int, completionHandler:(String) -> Void) {
  380. // a service that asynchronously calls
  381. // the given completionHandler
  382. }
  383. let observableService = Observable
  384. .fromAsync(someAsynchronousService)
  385. observableService("Foo", 0)
  386. .subscribe(onNext: { (result) in
  387. print(result)
  388. })
  389. .disposed(by: disposeBag)
  390. ```
  391. #### zipWith
  392. Convenience version of `Observable.zip(_:)`. Merges the specified observable sequences into one observable sequence by using the selector function whenever all
  393. of the observable sequences have produced an element at a corresponding index.
  394. ```swift
  395. let first = Observable.from(numbers)
  396. let second = Observable.from(strings)
  397. first.zip(with: second) { i, s in
  398. s + String(i)
  399. }.subscribe(onNext: { (result) in
  400. print(result)
  401. })
  402. ```
  403. ```
  404. next("a1")
  405. next("b2")
  406. next("c3")
  407. ```
  408. #### ofType
  409. The ofType operator filters the elements of an observable sequence, if that is an instance of the supplied type.
  410. ```swift
  411. Observable.of(NSNumber(value: 1),
  412. NSDecimalNumber(string: "2"),
  413. NSNumber(value: 3),
  414. NSNumber(value: 4),
  415. NSDecimalNumber(string: "5"),
  416. NSNumber(value: 6))
  417. .ofType(NSDecimalNumber.self)
  418. .subscribe { print($0) }
  419. ```
  420. ```
  421. next(2)
  422. next(5)
  423. completed
  424. ```
  425. This example emits 2, 5 (`NSDecimalNumber` Type).
  426. #### withUnretained
  427. The `withUnretained(_:resultSelector:)` operator provides an unretained, safe to use (i.e. not implicitly unwrapped), reference to an object along with the events emitted by the sequence.
  428. In the case the provided object cannot be retained successfully, the seqeunce will complete.
  429. ```swift
  430. class TestClass: CustomStringConvertible {
  431. var description: String { return "Test Class" }
  432. }
  433. Observable
  434. .of(1, 2, 3, 5, 8, 13, 18, 21, 23)
  435. .withUnretained(testClass)
  436. .do(onNext: { _, value in
  437. if value == 13 {
  438. // When testClass becomes nil, the next emission of the original
  439. // sequence will try to retain it and fail. As soon as it fails,
  440. // the sequence will complete.
  441. testClass = nil
  442. }
  443. })
  444. .subscribe()
  445. ```
  446. ```
  447. next((Test Class, 1))
  448. next((Test Class, 2))
  449. next((Test Class, 3))
  450. next((Test Class, 5))
  451. next((Test Class, 8))
  452. next((Test Class, 13))
  453. completed
  454. ```
  455. #### [count](http://reactivex.io/documentation/operators/count.html)
  456. Emits the number of items emitted by an Observable once it terminates with no errors. If a predicate is given, only elements matching the predicate will be counted.
  457. ```swift
  458. Observable.from([1, 2, 3, 4, 5, 6])
  459. .count { $0 % 2 == 0 }
  460. .subscribe()
  461. ```
  462. ```
  463. next(3)
  464. completed
  465. ```
  466. Reactive Extensions details
  467. ===========
  468. #### UIViewPropertyAnimator.animate
  469. The `animate(afterDelay:)` operator provides a Completable that triggers the animation upon subscription and completes when the animation ends.
  470. ```swift
  471. button.rx.tap
  472. .flatMap {
  473. animator1.rx.animate()
  474. .andThen(animator2.rx.animate(afterDelay: 0.15))
  475. .andThen(animator3.rx.animate(afterDelay: 0.1))
  476. }
  477. ```
  478. #### UIViewPropertyAnimator.fractionComplete
  479. The `fractionComplete` binder provides a reactive way to bind to `UIViewPropertyAnimator.fractionComplete`.
  480. ```swift
  481. slider.rx.value.map(CGFloat.init)
  482. .bind(to: animator.rx.fractionComplete)
  483. ```
  484. ## License
  485. This library belongs to _RxSwiftCommunity_.
  486. RxSwiftExt is available under the MIT license. See the LICENSE file for more info.