summaryrefslogtreecommitdiffhomepage
path: root/ios/Operations/TransformOperation.swift
diff options
context:
space:
mode:
authorAndrej Mihajlov <and@mullvad.net>2022-09-25 16:34:23 +0200
committerAndrej Mihajlov <and@mullvad.net>2022-09-26 16:35:38 +0200
commitf389956c2cd884df142adbf00ff2ac7e2f69c2b2 (patch)
tree5f7d4869324760eb4ba0107c0356edc89efd1457 /ios/Operations/TransformOperation.swift
parent2e83b1ca27ff243a615ff10c94c20840b38dfd45 (diff)
downloadmullvadvpn-f389956c2cd884df142adbf00ff2ac7e2f69c2b2.tar.xz
mullvadvpn-f389956c2cd884df142adbf00ff2ac7e2f69c2b2.zip
Move AsyncOperation into Operations static library and add separate tests
Diffstat (limited to 'ios/Operations/TransformOperation.swift')
-rw-r--r--ios/Operations/TransformOperation.swift138
1 files changed, 138 insertions, 0 deletions
diff --git a/ios/Operations/TransformOperation.swift b/ios/Operations/TransformOperation.swift
new file mode 100644
index 0000000000..71cac93f4b
--- /dev/null
+++ b/ios/Operations/TransformOperation.swift
@@ -0,0 +1,138 @@
+//
+// TransformOperation.swift
+// AsyncOperationQueueTest
+//
+// Created by pronebird on 31/05/2022.
+//
+
+import Foundation
+
+public final class TransformOperation<Input, Output, Failure: Error>:
+ ResultOperation<Output, Failure>,
+ InputOperation
+{
+ public typealias ExecutionBlock = (Input, TransformOperation<Input, Output, Failure>) -> Void
+ public typealias ThrowingExecutionBlock = (Input) throws -> Output
+ public typealias InputBlock = () -> Input?
+
+ private let nslock = NSLock()
+
+ public var input: Input? {
+ return _input
+ }
+
+ private var __input: Input?
+ private var _input: Input? {
+ get {
+ nslock.lock()
+ defer { nslock.unlock() }
+ return __input
+ }
+ set {
+ nslock.lock()
+ __input = newValue
+ nslock.unlock()
+ }
+ }
+
+ private var inputBlock: InputBlock?
+
+ private var executionBlock: ExecutionBlock?
+ private var cancellationBlocks: [() -> Void] = []
+
+ public init(
+ dispatchQueue: DispatchQueue? = nil,
+ input: Input? = nil,
+ block: ExecutionBlock? = nil
+ ) {
+ __input = input
+ executionBlock = block
+
+ super.init(dispatchQueue: dispatchQueue)
+ }
+
+ public init(
+ dispatchQueue: DispatchQueue? = nil,
+ input: Input? = nil,
+ throwingBlock: @escaping ThrowingExecutionBlock
+ ) {
+ __input = input
+ executionBlock = Self.wrapThrowingBlock(throwingBlock)
+
+ super.init(dispatchQueue: dispatchQueue)
+ }
+
+ override public func main() {
+ let inputValue = inputBlock?()
+
+ _input = inputValue
+
+ guard let inputValue = inputValue, let executionBlock = executionBlock else {
+ finish(completion: .cancelled)
+ return
+ }
+
+ executionBlock(inputValue, self)
+ }
+
+ override public func operationDidCancel() {
+ let blocks = cancellationBlocks
+ cancellationBlocks.removeAll()
+
+ for block in blocks {
+ block()
+ }
+ }
+
+ override public func operationDidFinish() {
+ cancellationBlocks.removeAll()
+ executionBlock = nil
+ }
+
+ // MARK: - Block handlers
+
+ public func setExecutionBlock(_ block: @escaping ExecutionBlock) {
+ dispatchQueue.async {
+ assert(!self.isExecuting && !self.isFinished)
+ self.executionBlock = block
+ }
+ }
+
+ public func setExecutionBlock(_ block: @escaping ThrowingExecutionBlock) {
+ setExecutionBlock(Self.wrapThrowingBlock(block))
+ }
+
+ public func addCancellationBlock(_ block: @escaping () -> Void) {
+ dispatchQueue.async {
+ if self.isCancelled {
+ block()
+ } else {
+ self.cancellationBlocks.append(block)
+ }
+ }
+ }
+
+ // MARK: - Input injection
+
+ public func setInputBlock(_ block: @escaping () -> Input?) {
+ dispatchQueue.async {
+ self.inputBlock = block
+ }
+ }
+
+ private class func wrapThrowingBlock(_ executionBlock: @escaping ThrowingExecutionBlock)
+ -> ExecutionBlock
+ {
+ return { input, operation in
+ do {
+ let value = try executionBlock(input)
+
+ operation.finish(completion: .success(value))
+ } catch {
+ let castedError = error as! Failure
+
+ operation.finish(completion: .failure(castedError))
+ }
+ }
+ }
+}