Skip to content

Commit e2301dd

Browse files
M0rtyMerrfreak4pc
authored andcommitted
[feature/fromAsync] add from async for single (#199)
1 parent 8562ff5 commit e2301dd

File tree

4 files changed

+147
-11
lines changed

4 files changed

+147
-11
lines changed

.circleci/config.yml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,6 @@ jobs:
1717
- v1-dep-{{ .Branch }}-
1818
- v1-dep-master-
1919
- v1-dep-
20-
- run:
21-
name: Update Homebrew
22-
command: brew update
23-
- run:
24-
name: Install Swiftlint
25-
command: brew install swiftlint
2620
- run:
2721
name: Bootstrap Carthage
2822
command: scripts/bootstrap-if-needed.sh

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ master
55
-----
66
- added `partition(_:)` operator
77
- added `bufferWithTrigger` operator
8+
- added `fromAsync` operator for `Single`
89

910
3.4.0
1011
-----

Source/RxSwift/fromAsync.swift

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,70 @@ extension Observable {
6666
return { (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J) in Observable.fromAsync(curry(asyncRequest)(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)) }
6767
}
6868
}
69+
70+
public enum FromAsyncError: Error {
71+
/// Both result & error can't be nil
72+
case inconsistentCompletionResult
73+
}
74+
75+
public extension PrimitiveSequenceType where TraitType == SingleTrait {
76+
/**
77+
Transforms an async function that returns data or error through a completionHandler in a function that returns data through a Single
78+
- The returned function will thake the same arguments than asyncRequest, minus the last one
79+
*/
80+
static func fromAsync<Er: Error>(_ asyncRequest: @escaping (@escaping (ElementType?, Er?) -> Void) -> Void) -> Single<ElementType> {
81+
return .create { single in
82+
asyncRequest { result, error in
83+
switch (result, error) {
84+
case let (.some(result), nil):
85+
single(.success(result))
86+
case let (nil, .some(error)):
87+
single(.error(error))
88+
default:
89+
single(.error(FromAsyncError.inconsistentCompletionResult))
90+
}
91+
}
92+
return Disposables.create()
93+
}
94+
}
95+
96+
static func fromAsync<A, Er: Error>(_ asyncRequest: @escaping (A, @escaping (ElementType?, Er?) -> Void) -> Void) -> (A) -> Single<ElementType> {
97+
return { (a: A) in Single.fromAsync(curry(asyncRequest)(a)) }
98+
}
99+
100+
static func fromAsync<A, B, Er: Error>(_ asyncRequest: @escaping (A, B, @escaping (ElementType?, Er?) -> Void) -> Void) -> (A, B) -> Single<ElementType> {
101+
return { (a: A, b: B) in Single.fromAsync(curry(asyncRequest)(a)(b)) }
102+
}
103+
104+
static func fromAsync<A, B, C, Er: Error>(_ asyncRequest: @escaping (A, B, C, @escaping (ElementType?, Er?) -> Void) -> Void) -> (A, B, C) -> Single<ElementType> {
105+
return { (a: A, b: B, c: C) in Single.fromAsync(curry(asyncRequest)(a)(b)(c)) }
106+
}
107+
108+
static func fromAsync<A, B, C, D, Er: Error>(_ asyncRequest: @escaping (A, B, C, D, @escaping (ElementType?, Er?) -> Void) -> Void) -> (A, B, C, D) -> Single<ElementType> {
109+
return { (a: A, b: B, c: C, d: D) in Single.fromAsync(curry(asyncRequest)(a)(b)(c)(d)) }
110+
}
111+
112+
static func fromAsync<A, B, C, D, E, Er: Error>(_ asyncRequest: @escaping (A, B, C, D, E, @escaping (ElementType?, Er?) -> Void) -> Void) -> (A, B, C, D, E) -> Single<ElementType> {
113+
return { (a: A, b: B, c: C, d: D, e: E) in Single.fromAsync(curry(asyncRequest)(a)(b)(c)(d)(e)) }
114+
}
115+
116+
static func fromAsync<A, B, C, D, E, F, Er: Error>(_ asyncRequest: @escaping (A, B, C, D, E, F, @escaping (ElementType?, Er?) -> Void) -> Void) -> (A, B, C, D, E, F) -> Single<ElementType> {
117+
return { (a: A, b: B, c: C, d: D, e: E, f: F) in Single.fromAsync(curry(asyncRequest)(a)(b)(c)(d)(e)(f)) }
118+
}
119+
120+
static func fromAsync<A, B, C, D, E, F, G, Er: Error>(_ asyncRequest: @escaping (A, B, C, D, E, F, G, @escaping (ElementType?, Er?) -> Void) -> Void) -> (A, B, C, D, E, F, G) -> Single<ElementType> {
121+
return { (a: A, b: B, c: C, d: D, e: E, f: F, g: G) in Single.fromAsync(curry(asyncRequest)(a)(b)(c)(d)(e)(f)(g)) }
122+
}
123+
124+
static func fromAsync<A, B, C, D, E, F, G, H, Er: Error>(_ asyncRequest: @escaping (A, B, C, D, E, F, G, H, @escaping (ElementType?, Er?) -> Void) -> Void) -> (A, B, C, D, E, F, G, H) -> Single<ElementType> {
125+
return { (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H) in Single.fromAsync(curry(asyncRequest)(a)(b)(c)(d)(e)(f)(g)(h)) }
126+
}
127+
128+
static func fromAsync<A, B, C, D, E, F, G, H, I, Er: Error>(_ asyncRequest: @escaping (A, B, C, D, E, F, G, H, I, @escaping (ElementType?, Er?) -> Void) -> Void) -> (A, B, C, D, E, F, G, H, I) -> Single<ElementType> {
129+
return { (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I) in Single.fromAsync(curry(asyncRequest)(a)(b)(c)(d)(e)(f)(g)(h)(i)) }
130+
}
131+
132+
static func fromAsync<A, B, C, D, E, F, G, H, I, J, Er: Error>(_ asyncRequest: @escaping (A, B, C, D, E, F, G, H, I, J, @escaping (ElementType?, Er?) -> Void) -> Void) -> (A, B, C, D, E, F, G, H, I, J) -> Single<ElementType> {
133+
return { (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, i: I, j: J) in Single.fromAsync(curry(asyncRequest)(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)) }
134+
}
135+
}

Tests/RxSwift/fromAsyncTests.swift

Lines changed: 79 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,19 @@ import RxSwiftExt
1313
import RxTest
1414

1515
class FromAsyncTests: XCTestCase {
16-
private func service(arg1: String, arg2: Int, completionHandler: (String) -> Void) {
17-
completionHandler("Result")
16+
private var scheduler: TestScheduler!
17+
private var observer: TestableObserver<String>!
18+
19+
override func setUp() {
20+
super.setUp()
21+
scheduler = TestScheduler(initialClock: 0)
22+
observer = scheduler.createObserver(String.self)
23+
}
24+
25+
override func tearDown() {
26+
scheduler = nil
27+
observer = nil
28+
super.tearDown()
1829
}
1930

2031
func testResultEquality() {
@@ -25,9 +36,6 @@ class FromAsyncTests: XCTestCase {
2536
correct.append(completed(0))
2637
}
2738

28-
let scheduler = TestScheduler(initialClock: 0)
29-
let observer = scheduler.createObserver(String.self)
30-
3139
_ = Observable
3240
.fromAsync(service(arg1:arg2:completionHandler:))("Foo", 2)
3341
.subscribe(observer)
@@ -36,4 +44,70 @@ class FromAsyncTests: XCTestCase {
3644

3745
XCTAssertEqual(observer.events, correct)
3846
}
47+
48+
func testSingleResultEqualitySuccessCase() {
49+
// given
50+
let result = "result"
51+
let expectedEvents: [Recorded<Event<String>>] = [.next(0, result), .completed(0)]
52+
// when
53+
_ = Single<String>
54+
.fromAsync(serviceWithError)(result)
55+
.asObservable()
56+
.subscribe(observer)
57+
scheduler.start()
58+
// then
59+
XCTAssertEqual(observer.events, expectedEvents)
60+
}
61+
62+
func testSingleOptionalResultEqualitySuccessCase() {
63+
// given
64+
let result: String? = nil
65+
let expectedEvents: [Recorded<Event<String?>>] = [.next(0, result), .completed(0)]
66+
let observer = scheduler.createObserver(Optional<String>.self)
67+
// when
68+
_ = Single<String?>
69+
.fromAsync(serviceWithOptionalResult)(result)
70+
.asObservable()
71+
.subscribe(observer)
72+
scheduler.start()
73+
// then
74+
XCTAssertEqual(observer.events, expectedEvents)
75+
}
76+
77+
func testSingleResultEqualityErrorCase() {
78+
// given
79+
let expectedEvents: [Recorded<Event<String>>] = [.error(0, TestError())]
80+
// when
81+
_ = Single<String>
82+
.fromAsync(serviceThrowingError)
83+
.asObservable()
84+
.subscribe(observer)
85+
scheduler.start()
86+
// then
87+
XCTAssertEqual(observer.events, expectedEvents)
88+
}
89+
90+
// MARK: - Private utils
91+
private struct TestError: Error {
92+
}
93+
94+
private func service(arg1: String, arg2: Int, completionHandler: (String) -> Void) {
95+
completionHandler("Result")
96+
}
97+
98+
private func serviceWithError(result: String, completionHandler: (String?, TestError?) -> Void) {
99+
completionHandler(result, nil)
100+
}
101+
102+
private func serviceWithOptionalResult(result: String?, completionHandler: (String??, TestError?) -> Void) {
103+
completionHandler(result, nil)
104+
}
105+
106+
private func serviceThrowingError(completionHandler: (String?, TestError?) -> Void) {
107+
completionHandler(nil, TestError())
108+
}
109+
110+
private func serviceWithOptionalResult(completionHandler: (String??, TestError?) -> Void) {
111+
completionHandler(.some(nil), nil)
112+
}
39113
}

0 commit comments

Comments
 (0)