Skip to content

Commit e607713

Browse files
Merge pull request #6 from novasamatech/feature/call-store
Added CancellableCallStore
2 parents fd512cd + 67f2c54 commit e607713

File tree

4 files changed

+167
-3
lines changed

4 files changed

+167
-3
lines changed

Operation-iOS/Classes/Helpers/BaseOperationExtensions.swift

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,22 @@ public extension BaseOperation {
2121
throw error
2222
}
2323
}
24+
25+
func extractNoCancellableResultData() throws -> ResultType {
26+
try extractResultData(throwing: BaseOperationError.parentOperationCancelled)
27+
}
28+
}
29+
30+
extension BaseOperation {
31+
public static func createWithError(_ error: Error) -> BaseOperation<ResultType> {
32+
let operation = BaseOperation<ResultType>()
33+
operation.result = .failure(error)
34+
return operation
35+
}
36+
37+
public static func createWithResult(_ result: ResultType) -> BaseOperation<ResultType> {
38+
let operation = BaseOperation<ResultType>()
39+
operation.result = .success(result)
40+
return operation
41+
}
2442
}
Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
import Foundation
2+
3+
public final class CancellableCallStore {
4+
private var cancellableCall: CancellableCall?
5+
6+
public init() {}
7+
8+
public var hasCall: Bool {
9+
cancellableCall != nil
10+
}
11+
12+
public func cancel() {
13+
let copy = cancellableCall
14+
cancellableCall = nil
15+
copy?.cancel()
16+
}
17+
18+
func store(call: CancellableCall) {
19+
cancellableCall = call
20+
}
21+
22+
func clear() {
23+
cancellableCall = nil
24+
}
25+
26+
func clearIfMatches(call: CancellableCall) -> Bool {
27+
guard matches(call: call) else {
28+
return false
29+
}
30+
31+
cancellableCall = nil
32+
33+
return true
34+
}
35+
36+
func matches(call: CancellableCall) -> Bool {
37+
cancellableCall === call
38+
}
39+
}
40+
41+
public func execute<T>(
42+
wrapper: CompoundOperationWrapper<T>,
43+
inOperationQueue operationQueue: OperationQueue,
44+
runningCallbackIn callbackQueue: DispatchQueue?,
45+
callbackClosure: @escaping (Result<T, Error>) -> Void
46+
) {
47+
wrapper.targetOperation.completionBlock = {
48+
dispatchInQueueWhenPossible(callbackQueue) {
49+
do {
50+
let value = try wrapper.targetOperation.extractNoCancellableResultData()
51+
callbackClosure(.success(value))
52+
} catch {
53+
callbackClosure(.failure(error))
54+
}
55+
}
56+
}
57+
58+
operationQueue.addOperations(wrapper.allOperations, waitUntilFinished: false)
59+
}
60+
61+
public func executeCancellable<T>(
62+
wrapper: CompoundOperationWrapper<T>,
63+
inOperationQueue operationQueue: OperationQueue,
64+
backingCallIn callStore: CancellableCallStore,
65+
runningCallbackIn callbackQueue: DispatchQueue?,
66+
mutex: NSLock? = nil,
67+
callbackClosure: @escaping (Result<T, Error>) -> Void
68+
) {
69+
wrapper.targetOperation.completionBlock = {
70+
dispatchInQueueWhenPossible(callbackQueue, locking: mutex) {
71+
guard callStore.clearIfMatches(call: wrapper) else {
72+
return
73+
}
74+
75+
do {
76+
let value = try wrapper.targetOperation.extractNoCancellableResultData()
77+
callbackClosure(.success(value))
78+
} catch {
79+
callbackClosure(.failure(error))
80+
}
81+
}
82+
}
83+
84+
callStore.store(call: wrapper)
85+
86+
operationQueue.addOperations(wrapper.allOperations, waitUntilFinished: false)
87+
}
88+
89+
public func execute<T>(
90+
operation: BaseOperation<T>,
91+
inOperationQueue operationQueue: OperationQueue,
92+
runningCallbackIn callbackQueue: DispatchQueue?,
93+
callbackClosure: @escaping (Result<T, Error>) -> Void
94+
) {
95+
operation.completionBlock = {
96+
dispatchInQueueWhenPossible(callbackQueue) {
97+
do {
98+
let value = try operation.extractNoCancellableResultData()
99+
callbackClosure(.success(value))
100+
} catch {
101+
callbackClosure(.failure(error))
102+
}
103+
}
104+
}
105+
106+
operationQueue.addOperations([operation], waitUntilFinished: false)
107+
}
108+
109+
public func execute<T>(
110+
operation: BaseOperation<T>,
111+
inOperationQueue operationQueue: OperationQueue,
112+
backingCallIn callStore: CancellableCallStore,
113+
runningCallbackIn callbackQueue: DispatchQueue?,
114+
callbackClosure: @escaping (Result<T, Error>) -> Void
115+
) {
116+
operation.completionBlock = {
117+
dispatchInQueueWhenPossible(callbackQueue) {
118+
guard callStore.clearIfMatches(call: operation) else {
119+
return
120+
}
121+
122+
do {
123+
let value = try operation.extractNoCancellableResultData()
124+
callbackClosure(.success(value))
125+
} catch {
126+
callbackClosure(.failure(error))
127+
}
128+
}
129+
}
130+
131+
callStore.store(call: operation)
132+
133+
operationQueue.addOperations([operation], waitUntilFinished: false)
134+
}
Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,19 @@
11
import Foundation
22

3-
func dispatchInQueueWhenPossible(_ queue: DispatchQueue?, block: @escaping () -> Void ) {
4-
if let queue = queue {
5-
queue.async(execute: block)
3+
public func dispatchInQueueWhenPossible(
4+
_ queue: DispatchQueue?,
5+
locking mutex: NSLock? = nil,
6+
block: @escaping () -> Void
7+
) {
8+
if let queue {
9+
queue.async {
10+
mutex?.lock()
11+
block()
12+
mutex?.unlock()
13+
}
614
} else {
15+
mutex?.lock()
716
block()
17+
mutex?.unlock()
818
}
919
}

Operation-iOS/Classes/Operations/Base/BaseOperation.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,3 +194,5 @@ open class BaseOperation<ResultType>: Operation {
194194
isFinished = true
195195
}
196196
}
197+
198+
extension BaseOperation: CancellableCall {}

0 commit comments

Comments
 (0)