Skip to content

Commit 78b0a5e

Browse files
authored
Merge pull request #1 from novasamatech/feature/async-operations
Feature async operations
2 parents 80481a2 + 67d207c commit 78b0a5e

File tree

8 files changed

+265
-285
lines changed

8 files changed

+265
-285
lines changed

Example/Operation-iOS.xcodeproj/project.pbxproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@
119119
0C8B0F6C2B84C15500A2A53F /* NetworkOperationTestHelper.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkOperationTestHelper.swift; sourceTree = "<group>"; };
120120
0C8B0F6E2B84C15500A2A53F /* CompoundOperationWrapperTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CompoundOperationWrapperTests.swift; sourceTree = "<group>"; };
121121
0C8B0F6F2B84C15500A2A53F /* OperationManagerTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OperationManagerTests.swift; sourceTree = "<group>"; };
122-
36E0D243064D53508C802EFF /* Operation-iOS.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = "Operation-iOS.podspec"; path = "../Operation-iOS.podspec"; sourceTree = "<group>"; };
122+
36E0D243064D53508C802EFF /* Operation-iOS.podspec */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = "Operation-iOS.podspec"; path = "../Operation-iOS.podspec"; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.ruby; };
123123
39B005BFF3A24E405D57B2D5 /* Pods_Operation_iOS_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Operation_iOS_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; };
124124
3C796FE43667E12EE37E6CB5 /* Pods_Operation_iOS_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Operation_iOS_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
125125
4BEF064EDF72965E12AD486A /* Pods-Operation-iOS_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Operation-iOS_Example.debug.xcconfig"; path = "Target Support Files/Pods-Operation-iOS_Example/Pods-Operation-iOS_Example.debug.xcconfig"; sourceTree = "<group>"; };

Operation-iOS.podspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Pod::Spec.new do |s|
22
s.name = 'Operation-iOS'
3-
s.version = '1.0.0'
3+
s.version = '2.0.0'
44
s.summary = 'Takes data from "rich" remote source and caches them in originaly "poor" local storage to speed up user interface.'
55

66
s.description = 'Library is aimed to solve a problem of providing persistent (cached) data while fresh one is being fetched from data source. Currently there are 3 types of data providers. DataProvider implementation is aimed to manage identifiable list of entities while SingleValueProvider deals with single objects. Finally, StreamableDataProvider is designed work with streamable data sources, for example, web sockets. Clients can subsribe for changes in data provider to update interface as soon as fresh data is fetched from the source. Currently, there is a single implementation of local storage based on Core Data. Interaction with the library occurs via native Operation concept to simplify chaining and dependency management.'

Operation-iOS/Classes/DataProvider/Repository/CoreData/CoreDataRepository+Protocol.swift

Lines changed: 78 additions & 147 deletions
Original file line numberDiff line numberDiff line change
@@ -3,190 +3,121 @@ import Foundation
33
extension CoreDataRepository: DataProviderRepositoryProtocol {
44
public func fetchOperation(by modelIdClosure: @escaping () throws -> String,
55
options: RepositoryFetchOptions) -> BaseOperation<Model?> {
6-
ClosureOperation {
7-
var model: Model?
8-
var error: Error?
9-
10-
let semaphore = DispatchSemaphore(value: 0)
11-
12-
self.fetch(by: modelIdClosure,
13-
options: options,
14-
runCompletionIn: nil) { (optionalModel, optionalError) in
15-
model = optionalModel
16-
error = optionalError
17-
18-
semaphore.signal()
6+
AsyncClosureOperation { completionClosure in
7+
self.fetch(
8+
by: modelIdClosure,
9+
options: options,
10+
runCompletionIn: nil
11+
) { (optionalModel, optionalError) in
12+
if let model = optionalModel {
13+
completionClosure(.success(model))
14+
} else if let error = optionalError {
15+
completionClosure(.failure(error))
16+
} else {
17+
completionClosure(.success(nil))
18+
}
1919
}
20-
21-
semaphore.wait()
22-
23-
if let existingModel = model {
24-
return existingModel
25-
}
26-
27-
if let existingError = error {
28-
throw existingError
29-
}
30-
31-
return nil
3220
}
3321
}
3422

3523
public func fetchAllOperation(with options: RepositoryFetchOptions) -> BaseOperation<[Model]> {
36-
ClosureOperation {
37-
var models: [Model]?
38-
var error: Error?
39-
40-
let semaphore = DispatchSemaphore(value: 0)
41-
42-
self.fetchAll(with: options,
43-
runCompletionIn: nil) { (optionalModels, optionalError) in
44-
models = optionalModels
45-
error = optionalError
46-
47-
semaphore.signal()
48-
}
49-
50-
semaphore.wait()
51-
52-
if let existingModels = models {
53-
return existingModels
54-
}
55-
56-
if let existingError = error {
57-
throw existingError
58-
} else {
59-
throw CoreDataRepositoryError.undefined
24+
AsyncClosureOperation { completionClosure in
25+
self.fetchAll(
26+
with: options,
27+
runCompletionIn: nil
28+
) { (optionalModels, optionalError) in
29+
if let models = optionalModels {
30+
completionClosure(.success(models))
31+
} else {
32+
let error = optionalError ?? CoreDataRepositoryError.undefined
33+
completionClosure(.failure(error))
34+
}
6035
}
6136
}
6237
}
6338

64-
public func fetchOperation(by request: RepositorySliceRequest,
65-
options: RepositoryFetchOptions) -> BaseOperation<[Model]> {
66-
ClosureOperation {
67-
var models: [Model]?
68-
var error: Error?
69-
70-
let semaphore = DispatchSemaphore(value: 0)
71-
72-
self.fetch(request: request,
73-
options: options,
74-
runCompletionIn: nil) { (optionalModels, optionalError) in
75-
models = optionalModels
76-
error = optionalError
77-
78-
semaphore.signal()
79-
}
80-
81-
semaphore.wait()
82-
83-
if let existingModels = models {
84-
return existingModels
85-
}
86-
87-
if let existingError = error {
88-
throw existingError
89-
} else {
90-
throw CoreDataRepositoryError.undefined
39+
public func fetchOperation(
40+
by request: RepositorySliceRequest,
41+
options: RepositoryFetchOptions
42+
) -> BaseOperation<[Model]> {
43+
AsyncClosureOperation { completionClosure in
44+
self.fetch(
45+
request: request,
46+
options: options,
47+
runCompletionIn: nil
48+
) { (optionalModels, optionalError) in
49+
if let models = optionalModels {
50+
completionClosure(.success(models))
51+
} else {
52+
let error = optionalError ?? CoreDataRepositoryError.undefined
53+
completionClosure(.failure(error))
54+
}
9155
}
9256
}
9357
}
9458

95-
public func saveOperation(_ updateModelsBlock: @escaping () throws -> [Model],
96-
_ deleteIdsBlock: @escaping () throws -> [String]) -> BaseOperation<Void> {
97-
ClosureOperation {
98-
var error: Error?
99-
59+
public func saveOperation(
60+
_ updateModelsBlock: @escaping () throws -> [Model],
61+
_ deleteIdsBlock: @escaping () throws -> [String]
62+
) -> BaseOperation<Void> {
63+
AsyncClosureOperation { completionClosure in
10064
let updatedModels = try updateModelsBlock()
10165
let deletedIds = try deleteIdsBlock()
10266

10367
if updatedModels.count == 0, deletedIds.count == 0 {
68+
completionClosure(.success(()))
10469
return
10570
}
106-
107-
let semaphore = DispatchSemaphore(value: 0)
108-
109-
self.save(updating: updatedModels,
110-
deleting: deletedIds,
111-
runCompletionIn: nil) { (optionalError) in
112-
error = optionalError
113-
semaphore.signal()
114-
}
115-
116-
semaphore.wait()
117-
118-
if let existingError = error {
119-
throw existingError
71+
72+
self.save(
73+
updating: updatedModels,
74+
deleting: deletedIds,
75+
runCompletionIn: nil
76+
) { (optionalError) in
77+
if let error = optionalError {
78+
completionClosure(.failure(error))
79+
} else {
80+
completionClosure(.success(()))
81+
}
12082
}
12183
}
12284
}
12385

124-
public func replaceOperation(_ newModelsBlock: @escaping () throws -> [Model])
125-
-> BaseOperation<Void> {
126-
ClosureOperation {
127-
var error: Error?
128-
86+
public func replaceOperation(_ newModelsBlock: @escaping () throws -> [Model]) -> BaseOperation<Void> {
87+
AsyncClosureOperation { completionClosure in
12988
let models = try newModelsBlock()
130-
131-
let semaphore = DispatchSemaphore(value: 0)
132-
89+
13390
self.replace(with: models, runCompletionIn: nil) { optionalError in
134-
error = optionalError
135-
136-
semaphore.signal()
137-
}
138-
139-
semaphore.wait()
140-
141-
if let existingError = error {
142-
throw existingError
91+
if let error = optionalError {
92+
completionClosure(.failure(error))
93+
} else {
94+
completionClosure(.success(()))
95+
}
14396
}
14497
}
14598
}
14699

147100
public func fetchCountOperation() -> BaseOperation<Int> {
148-
ClosureOperation {
149-
var count: Int?
150-
var error: Error?
151-
152-
let semaphore = DispatchSemaphore(value: 0)
153-
101+
AsyncClosureOperation { completionClosure in
154102
self.fetchCount(runCompletionIn: nil) { (optionalCount, optionalError) in
155-
count = optionalCount
156-
error = optionalError
157-
158-
semaphore.signal()
159-
}
160-
161-
semaphore.wait()
162-
163-
if let count = count {
164-
return count
165-
}
166-
167-
if let existingError = error {
168-
throw existingError
169-
} else {
170-
throw CoreDataRepositoryError.undefined
103+
if let count = optionalCount {
104+
completionClosure(.success(count))
105+
} else {
106+
let error = optionalError ?? CoreDataRepositoryError.undefined
107+
completionClosure(.failure(error))
108+
}
171109
}
172110
}
173111
}
174112

175113
public func deleteAllOperation() -> BaseOperation<Void> {
176-
ClosureOperation {
177-
var error: Error?
178-
179-
let semaphore = DispatchSemaphore(value: 0)
180-
114+
AsyncClosureOperation { completionClosure in
181115
self.deleteAll(runCompletionIn: nil) { (optionalError) in
182-
error = optionalError
183-
semaphore.signal()
184-
}
185-
186-
semaphore.wait()
187-
188-
if let existingError = error {
189-
throw existingError
116+
if let error = optionalError {
117+
completionClosure(.failure(error))
118+
} else {
119+
completionClosure(.success(()))
120+
}
190121
}
191122
}
192123
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import Foundation
2+
3+
open class AsyncClosureOperation<ResultType>: BaseOperation<ResultType> {
4+
let operationClosure: (@escaping (Result<ResultType, Error>) -> Void) throws -> Void
5+
let cancelationClosure: (() -> Void)?
6+
7+
public init(
8+
operationClosure: @escaping (@escaping (Result<ResultType, Error>) -> Void) throws -> Void,
9+
cancelationClosure: (() -> Void)? = nil
10+
) {
11+
self.cancelationClosure = cancelationClosure
12+
self.operationClosure = operationClosure
13+
}
14+
15+
open override func performAsync(_ callback: @escaping (Result<ResultType, Error>) -> Void) throws {
16+
try operationClosure(callback)
17+
}
18+
19+
open override func cancel() {
20+
cancelationClosure?()
21+
22+
super.cancel()
23+
}
24+
}

0 commit comments

Comments
 (0)