LeadKit/TIFoundationUtils/AsyncOperation/Sources/AsyncOperation+FlatMap.swift

89 lines
3.3 KiB
Swift

//
// Copyright (c) 2023 Touch Instinct
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the Software), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED AS IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
import Foundation
private final class FlatMapAsyncOperation<Output, Failure: Error>: AsyncOperation<Output, Failure> {
private var dependencyObservation: NSKeyValueObservation?
private var flatMapObservation: NSKeyValueObservation?
private let dependency: Operation
private var flatMapDependency: AsyncOperation<Output, Failure>?
init<DependencyOutput, DependencyFailure>(dependency: AsyncOperation<DependencyOutput, DependencyFailure>,
flatMapOutput: ((DependencyOutput) -> AsyncOperation<Output, Failure>)?,
flatMapFailure: ((DependencyFailure) -> AsyncOperation<Output, Failure>)?)
where DependencyFailure: Error {
self.dependency = dependency
super.init()
dependency.cancelOnCancellation(of: self)
dependencyObservation = dependency.subscribe { [weak self] in
switch $0 {
case let .success(success):
self?.flatMapDependency = flatMapOutput?(success)
case let .failure(error):
self?.flatMapDependency = flatMapFailure?(error)
}
if let self {
self.flatMapDependency?.cancelOnCancellation(of: self)
}
self?.flatMapObservation = self?.flatMapDependency?.subscribe {
self?.handle(result: $0)
}
self?.flatMapDependency?.start()
}
state = .isReady
}
override func start() {
state = .isExecuting
dependency.start()
}
}
public extension AsyncOperation {
func flatMap<NewOutput>(_ transform: @escaping (Output) -> AsyncOperation<NewOutput, Failure>)
-> AsyncOperation<NewOutput, Failure> {
FlatMapAsyncOperation(dependency: self,
flatMapOutput: transform,
flatMapFailure: { .just(failure: $0) })
}
func flatMapError<NewFailure>(_ transform: @escaping (Failure) -> AsyncOperation<Output, NewFailure>)
-> AsyncOperation<Output, NewFailure> {
FlatMapAsyncOperation(dependency: self,
flatMapOutput: { .just(success: $0) },
flatMapFailure: transform)
}
}