-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Rewrite promises to allow callback option (#19)
* Rewrite promises to allow callback option * setFailureType for Never * Add more error transformation * Add init that flattens PromiseError * Promise from Publisher as func, not var, so it can be made generic * Oops, fix wrong generic usage * Oops, fix wrong generics usage * More Promise extensions * Promises to use NonEmptyPublisher as upstream * Improve tests * Promise Zip to not automatically map * Promise timeout * Add generic constraint
- Loading branch information
Showing
14 changed files
with
847 additions
and
198 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
61 changes: 61 additions & 0 deletions
61
Sources/FoundationExtensions/Promise/NonEmptyPublisher.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
// | ||
// NonEmptyPublisher.swift | ||
// FoundationExtensions | ||
// | ||
// Created by Luiz Rodrigo Martins Barbosa on 19.04.21. | ||
// Copyright © 2021 Lautsprecher Teufel GmbH. All rights reserved. | ||
// | ||
|
||
#if canImport(Combine) | ||
import Combine | ||
import Foundation | ||
|
||
/// A Publisher that ensures that at least 1 value will be emitted before successful completion, but not necessarily in case of failure completion. | ||
/// This requires a fallback success or error in case the upstream decides to terminate empty. | ||
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) | ||
public struct NonEmptyPublisher<Upstream: Publisher>: Publisher { | ||
public typealias Output = Upstream.Output | ||
public typealias Failure = Upstream.Failure | ||
|
||
private let upstream: Upstream | ||
let fallback: () -> Result<Output, Failure> | ||
|
||
public init(upstream: Upstream, onEmpty fallback: @escaping () -> Result<Output, Failure>) { | ||
self.upstream = upstream | ||
self.fallback = fallback | ||
} | ||
|
||
private enum EmptyStream { | ||
case empty | ||
case someValue(Output) | ||
} | ||
|
||
public func receive<S>(subscriber: S) where S: Subscriber, Failure == S.Failure, Output == S.Input { | ||
upstream | ||
.map(EmptyStream.someValue) | ||
.replaceEmpty(with: EmptyStream.empty) | ||
.flatMap { valueType -> AnyPublisher<Output, Failure> in | ||
switch valueType { | ||
case .empty: | ||
switch fallback() { | ||
case let .success(fallbackValue): | ||
return Just(fallbackValue).setFailureType(to: Failure.self).eraseToAnyPublisher() | ||
case let .failure(fallbackError): | ||
return Fail(error: fallbackError).eraseToAnyPublisher() | ||
} | ||
case let .someValue(value): | ||
return Just(value).setFailureType(to: Failure.self).eraseToAnyPublisher() | ||
} | ||
} | ||
.subscribe(subscriber) | ||
} | ||
} | ||
|
||
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) | ||
extension NonEmptyPublisher where Upstream: PromiseType { | ||
public init(upstream: Upstream) { | ||
self.upstream = upstream | ||
self.fallback = { fatalError() } | ||
} | ||
} | ||
#endif |
46 changes: 46 additions & 0 deletions
46
Sources/FoundationExtensions/Promise/Promise+PerformInQueue.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
// | ||
// Promise+PerformInQueue.swift | ||
// FoundationExtensions | ||
// | ||
// Created by Luiz Rodrigo Martins Barbosa on 17.04.21. | ||
// Copyright © 2021 Lautsprecher Teufel GmbH. All rights reserved. | ||
// | ||
|
||
#if canImport(Combine) | ||
import Combine | ||
import Dispatch | ||
import Foundation | ||
|
||
extension DispatchWorkItem: Cancellable { } | ||
|
||
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) | ||
extension Publishers.Promise { | ||
public static func perform(in queue: DispatchQueue, operation: @escaping () throws -> Output) -> Self where Failure == Error { | ||
.init { completion in | ||
let workItem = DispatchWorkItem { | ||
do { | ||
let value = try operation() | ||
completion(.success(value)) | ||
} catch { | ||
completion(.failure(error)) | ||
} | ||
} | ||
|
||
queue.async(execute: workItem) | ||
return workItem | ||
} | ||
} | ||
|
||
public static func perform(in queue: DispatchQueue, operation: @escaping () -> Output) -> Self where Failure == Never { | ||
.init { completion in | ||
let workItem = DispatchWorkItem { | ||
let value = operation() | ||
completion(.success(value)) | ||
} | ||
|
||
queue.async(execute: workItem) | ||
return workItem | ||
} | ||
} | ||
} | ||
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.