Skip to content

Commit 3b0a22d

Browse files
Move promise to separate file. Write unit tests for everything.
1 parent d089435 commit 3b0a22d

File tree

5 files changed

+319
-98
lines changed

5 files changed

+319
-98
lines changed

Tests/swift-sdk-swift-tests/IterableActionRunnerTests.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import OHHTTPStubs
1010

1111
@testable import IterableSDK
1212

13-
let testExpectationTimeout = 15.0
13+
let testExpectationTimeout = 15.0 // How long to wait when we expect to succeed
14+
let testExpectationTimeoutForInverted = 1.0 // How long to wait when we expect to fail
1415

1516
class IterableActionRunnerTests: XCTestCase {
1617
override func setUp() {
Lines changed: 191 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,191 @@
1+
//
2+
//
3+
// Created by Tapash Majumder on 10/26/18.
4+
// Copyright © 2018 Iterable. All rights reserved.
5+
//
6+
7+
import XCTest
8+
9+
@testable import IterableSDK
10+
11+
class PromiseTests: XCTestCase {
12+
struct MyError : Error, CustomStringConvertible {
13+
let message: String
14+
15+
var description: String {
16+
return message
17+
}
18+
}
19+
20+
func testMap() {
21+
let expectation1 = expectation(description: "test map")
22+
let expectation2 = expectation(description: "test map, inverted")
23+
expectation2.isInverted = true
24+
25+
let f1 = createSucessfulFuture(withValue: "zeeString")
26+
let f2 = f1.map {$0.count}
27+
28+
f2.onSuccess = { (value) in
29+
XCTAssertEqual(value, "zeeString".count)
30+
expectation1.fulfill()
31+
}
32+
f2.onFailure = {_ in
33+
expectation2.fulfill()
34+
}
35+
36+
wait(for: [expectation1], timeout: testExpectationTimeout)
37+
wait(for: [expectation2], timeout: testExpectationTimeoutForInverted)
38+
}
39+
40+
func testMapFailure() {
41+
let expectation1 = expectation(description: "test map failure, inverted")
42+
expectation1.isInverted = true
43+
let expectation2 = expectation(description: "test map failure")
44+
45+
let f1: Future<String, MyError> = createFailureFuture(withError: MyError(message: "zeeErrorMessage"))
46+
let f2 = f1.map {$0.count}
47+
48+
f2.onSuccess = { (value) in
49+
expectation1.fulfill()
50+
}
51+
f2.onFailure = {error in
52+
XCTAssertEqual(error.message, "zeeErrorMessage")
53+
expectation2.fulfill()
54+
}
55+
56+
wait(for: [expectation1], timeout: testExpectationTimeoutForInverted)
57+
wait(for: [expectation2], timeout: testExpectationTimeout)
58+
}
59+
60+
61+
func testFlatMap() {
62+
let expectation1 = expectation(description: "test flatMap")
63+
let expectation2 = expectation(description: "test flatMap, inverted")
64+
expectation2.isInverted = true
65+
66+
let f1 = createSucessfulFuture(withValue: "zeeString")
67+
68+
let f2 = f1.flatMap { (firstValue) in
69+
return self.createSucessfulFuture(withValue: firstValue + firstValue)
70+
}
71+
72+
f2.onSuccess = { (secondValue) in
73+
XCTAssertEqual(secondValue, "zeeStringzeeString")
74+
expectation1.fulfill()
75+
}
76+
f2.onFailure = {_ in
77+
expectation2.fulfill()
78+
}
79+
80+
wait(for: [expectation1], timeout: testExpectationTimeout)
81+
wait(for: [expectation2], timeout: testExpectationTimeoutForInverted)
82+
}
83+
84+
// The first future fails
85+
func testFlatMapFailure1() {
86+
let expectation1 = expectation(description: "test flatMap failure, inverted")
87+
expectation1.isInverted = true
88+
let expectation2 = expectation(description: "test flatMap failure")
89+
90+
let f1: Future<String, MyError> = createFailureFuture(withError: MyError(message: "zeeErrorMessage"))
91+
92+
let f2 = f1.flatMap { (firstValue) -> Future<String, MyError> in
93+
return self.createSucessfulFuture(withValue: "zeeString")
94+
}
95+
96+
f2.onSuccess = { (secondValue) in
97+
expectation1.fulfill()
98+
}
99+
f2.onFailure = {(error) in
100+
XCTAssertEqual(error.message, "zeeErrorMessage")
101+
expectation2.fulfill()
102+
}
103+
104+
wait(for: [expectation1], timeout: testExpectationTimeoutForInverted)
105+
wait(for: [expectation2], timeout: testExpectationTimeout)
106+
}
107+
108+
// The second future fails
109+
func testFlatMapFailure2() {
110+
let expectation1 = expectation(description: "test flatMap success, inverted")
111+
expectation1.isInverted = true
112+
let expectation2 = expectation(description: "test flatMap failure")
113+
114+
let f1 = createSucessfulFuture(withValue: "zeeString")
115+
116+
let f2 = f1.flatMap { (firstValue) -> Future<String, MyError> in
117+
return self.createFailureFuture(withError: MyError(message: "zeeErrorMessage"))
118+
}
119+
120+
f2.onSuccess = { (secondValue) in
121+
expectation1.fulfill()
122+
}
123+
f2.onFailure = {(error) in
124+
XCTAssertEqual(error.message, "zeeErrorMessage")
125+
expectation2.fulfill()
126+
}
127+
128+
wait(for: [expectation1], timeout: testExpectationTimeoutForInverted)
129+
wait(for: [expectation2], timeout: testExpectationTimeout)
130+
}
131+
132+
func testFutureInitWithSuccess() {
133+
let expectation1 = expectation(description: "test future init with success")
134+
let expectation2 = expectation(description: "test future init with success, inverted")
135+
expectation2.isInverted = true
136+
137+
let f1: Future<String, MyError> = Promise<String, MyError>(value: "zeeValue")
138+
139+
f1.onSuccess = { (value) in
140+
XCTAssertEqual(value, "zeeValue")
141+
expectation1.fulfill()
142+
}
143+
f1.onFailure = {_ in
144+
expectation2.fulfill()
145+
}
146+
147+
wait(for: [expectation1], timeout: testExpectationTimeout)
148+
wait(for: [expectation2], timeout: testExpectationTimeoutForInverted)
149+
}
150+
151+
func testFutureInitWithFailure() {
152+
let expectation1 = expectation(description: "test future init with failure, inverted")
153+
expectation1.isInverted = true
154+
let expectation2 = expectation(description: "test future init with failure")
155+
156+
let f1: Future<String, MyError> = Promise<String, MyError>(error: MyError(message: "zeeErrorMessage"))
157+
158+
f1.onSuccess = { (value) in
159+
expectation1.fulfill()
160+
}
161+
f1.onFailure = { error in
162+
XCTAssertEqual(error.message, "zeeErrorMessage")
163+
expectation2.fulfill()
164+
}
165+
166+
wait(for: [expectation1], timeout: testExpectationTimeoutForInverted)
167+
wait(for: [expectation2], timeout: testExpectationTimeout)
168+
}
169+
170+
171+
private func createSucessfulFuture<T>(withValue value: T) -> Future<T, MyError> {
172+
let future = Promise<T, MyError>()
173+
174+
DispatchQueue.main.async {
175+
future.resolve(with: value)
176+
}
177+
178+
return future
179+
}
180+
181+
private func createFailureFuture<T>(withError error: MyError) -> Future<T, MyError> {
182+
let future = Promise<T, MyError>()
183+
184+
DispatchQueue.main.async {
185+
future.reject(with: error)
186+
}
187+
188+
return future
189+
}
190+
}
191+

swift-sdk.xcodeproj/project.pbxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,8 @@
7777
ACE34AB5213776CB00691224 /* LocalStorageTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACE34AB4213776CB00691224 /* LocalStorageTests.swift */; };
7878
ACE34AB72139D70B00691224 /* LocalStorageProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACE34AB62139D70B00691224 /* LocalStorageProtocol.swift */; };
7979
ACED4C01213F50B30055A497 /* LoggingTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACED4C00213F50B30055A497 /* LoggingTests.swift */; };
80+
ACEDF41D2183C2EC000B9BFE /* Promise.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACEDF41C2183C2EC000B9BFE /* Promise.swift */; };
81+
ACEDF41F2183C436000B9BFE /* PromiseTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACEDF41E2183C436000B9BFE /* PromiseTests.swift */; };
8082
ACF560D620E443BF000AAC23 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACF560D520E443BF000AAC23 /* AppDelegate.swift */; };
8183
ACF560D820E443BF000AAC23 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ACF560D720E443BF000AAC23 /* ViewController.swift */; };
8284
ACF560DB20E443BF000AAC23 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = ACF560D920E443BF000AAC23 /* Main.storyboard */; };
@@ -236,6 +238,8 @@
236238
ACE34AB4213776CB00691224 /* LocalStorageTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalStorageTests.swift; sourceTree = "<group>"; };
237239
ACE34AB62139D70B00691224 /* LocalStorageProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalStorageProtocol.swift; sourceTree = "<group>"; };
238240
ACED4C00213F50B30055A497 /* LoggingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoggingTests.swift; sourceTree = "<group>"; };
241+
ACEDF41C2183C2EC000B9BFE /* Promise.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Promise.swift; sourceTree = "<group>"; };
242+
ACEDF41E2183C436000B9BFE /* PromiseTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PromiseTests.swift; sourceTree = "<group>"; };
239243
ACF560D320E443BF000AAC23 /* host-app.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "host-app.app"; sourceTree = BUILT_PRODUCTS_DIR; };
240244
ACF560D520E443BF000AAC23 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
241245
ACF560D720E443BF000AAC23 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
@@ -461,6 +465,7 @@
461465
AC2C667D20D3111900D46CC9 /* DateProvider.swift */,
462466
AC2C668120D32F2800D46CC9 /* IterableAppIntegrationInternal.swift */,
463467
ACD6116B2107D004003E7F6B /* NetworkHelper.swift */,
468+
ACEDF41C2183C2EC000B9BFE /* Promise.swift */,
464469
);
465470
path = Internal;
466471
sourceTree = "<group>";
@@ -483,6 +488,7 @@
483488
007960ED213F8B2300C53A6A /* DeferredDeeplinkTests.swift */,
484489
ACED4C00213F50B30055A497 /* LoggingTests.swift */,
485490
AC64626A2140AACF0046E1BD /* IterableAPIResponseTests.swift */,
491+
ACEDF41E2183C436000B9BFE /* PromiseTests.swift */,
486492
);
487493
path = "swift-sdk-swift-tests";
488494
sourceTree = "<group>";
@@ -955,6 +961,7 @@
955961
AC2C668220D32F2800D46CC9 /* IterableAppIntegrationInternal.swift in Sources */,
956962
AC72A0C820CF4CE2004D7997 /* ITBConsts.swift in Sources */,
957963
AC776DA6211A1B8A00C27C27 /* IterableRequestUtil.swift in Sources */,
964+
ACEDF41D2183C2EC000B9BFE /* Promise.swift in Sources */,
958965
AC7125EF20D4579E0043BBC1 /* IterableConfig.swift in Sources */,
959966
AC72A0B620CF4C53004D7997 /* IterableConstants.m in Sources */,
960967
AC72A0D120CF4D0B004D7997 /* IterableInAppManager.swift in Sources */,
@@ -986,6 +993,7 @@
986993
AC2C668420D3370600D46CC9 /* Mocks.swift in Sources */,
987994
AC64626B2140AACF0046E1BD /* IterableAPIResponseTests.swift in Sources */,
988995
ACD6116E21080564003E7F6B /* IterableAPITests.swift in Sources */,
996+
ACEDF41F2183C436000B9BFE /* PromiseTests.swift in Sources */,
989997
);
990998
runOnlyForDeploymentPostprocessing = 0;
991999
};

swift-sdk/Internal/NetworkHelper.swift

Lines changed: 0 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -28,103 +28,6 @@ extension SendRequestError : LocalizedError {
2828
}
2929
}
3030

31-
enum Result<Value, ErrorType> {
32-
case value(Value)
33-
case error(ErrorType)
34-
}
35-
36-
// This has only two public methods
37-
// either there is a success with result
38-
// or there is a failure with error
39-
// There is no way to set value a result in this class.
40-
class Future<Value, ErrorType> {
41-
var onSuccess: ((Value) -> Void)? = nil {
42-
didSet { result.map(report) }
43-
}
44-
45-
var onFailure : ((ErrorType) -> Void)? = nil {
46-
didSet { result.map(report) }
47-
}
48-
49-
fileprivate var result: Result<Value, ErrorType>? {
50-
// Observe whenever a result is assigned, and report it
51-
didSet { result.map(report) }
52-
}
53-
54-
// Report success or error based on result
55-
private func report(result: Result<Value, ErrorType>) {
56-
switch result {
57-
case .value(let value):
58-
onSuccess?(value)
59-
break
60-
case .error(let error):
61-
onFailure?(error)
62-
break
63-
}
64-
}
65-
}
66-
67-
extension Future {
68-
func flatMap<NextValue>(_ closure: @escaping (Value) -> Future<NextValue, ErrorType>) -> Future<NextValue, ErrorType> {
69-
let promise = Promise<NextValue, ErrorType>()
70-
71-
onSuccess = { value in
72-
let future = closure(value)
73-
74-
future.onSuccess = { futureValue in
75-
promise.resolve(with: futureValue)
76-
}
77-
78-
future.onFailure = { futureError in
79-
promise.reject(with: futureError)
80-
}
81-
}
82-
83-
onFailure = { error in
84-
promise.reject(with: error)
85-
}
86-
87-
return promise
88-
}
89-
90-
func map<NextValue>(_ closure: @escaping (Value) -> NextValue) -> Future<NextValue, ErrorType> {
91-
let promise = Promise<NextValue, ErrorType>()
92-
93-
onSuccess = { value in
94-
let nextValue = closure(value)
95-
promise.resolve(with: nextValue)
96-
}
97-
98-
onFailure = { error in
99-
promise.reject(with: error)
100-
}
101-
102-
return promise
103-
}
104-
}
105-
106-
107-
// This class takes the responsibility of setting value for Future
108-
class Promise<Value, ErrorType> : Future<Value, ErrorType> {
109-
init(value: Value? = nil) {
110-
super.init()
111-
result = value.map(Result.value)
112-
}
113-
114-
init(error: ErrorType) {
115-
super.init()
116-
result = Result.error(error)
117-
}
118-
119-
func resolve(with value: Value) {
120-
result = .value(value)
121-
}
122-
123-
func reject(with error: ErrorType) {
124-
result = .error(error)
125-
}
126-
}
127-
12831
protocol NetworkSessionProtocol {
12932
typealias CompletionHandler = (Data?, URLResponse?, Error?) -> Void
13033
func makeRequest(_ request: URLRequest, completionHandler: @escaping CompletionHandler)

0 commit comments

Comments
 (0)