|
| 1 | +import Combine |
| 2 | +import SwiftUI |
| 3 | + |
| 4 | +extension Effect { |
| 5 | + /// Wraps the emission of each element with SwiftUI's `withAnimation`. |
| 6 | + /// |
| 7 | + /// This publisher is most useful when using with ``Effect/task(priority:operation:)-2czg0`` |
| 8 | + /// |
| 9 | + /// ```swift |
| 10 | + /// case .buttonTapped: |
| 11 | + /// return .task { |
| 12 | + /// .activityResponse(await environment.apiClient.fetchActivity()) |
| 13 | + /// } |
| 14 | + /// .animation() |
| 15 | + /// ``` |
| 16 | + /// |
| 17 | + /// - Parameter animation: An animation. |
| 18 | + /// - Returns: A publisher. |
| 19 | + public func animation(_ animation: Animation? = .default) -> Self { |
| 20 | + AnimatedPublisher(upstream: self, animation: animation) |
| 21 | + .eraseToEffect() |
| 22 | + } |
| 23 | +} |
| 24 | + |
| 25 | +private struct AnimatedPublisher<Upstream: Publisher>: Publisher { |
| 26 | + typealias Output = Upstream.Output |
| 27 | + typealias Failure = Upstream.Failure |
| 28 | + |
| 29 | + var upstream: Upstream |
| 30 | + var animation: Animation? |
| 31 | + |
| 32 | + func receive<S: Combine.Subscriber>(subscriber: S) |
| 33 | + where S.Input == Output, S.Failure == Failure { |
| 34 | + let conduit = Subscriber(downstream: subscriber, animation: self.animation) |
| 35 | + self.upstream.receive(subscriber: conduit) |
| 36 | + } |
| 37 | + |
| 38 | + private class Subscriber<Downstream: Combine.Subscriber>: Combine.Subscriber { |
| 39 | + typealias Input = Downstream.Input |
| 40 | + typealias Failure = Downstream.Failure |
| 41 | + |
| 42 | + let downstream: Downstream |
| 43 | + let animation: Animation? |
| 44 | + |
| 45 | + init(downstream: Downstream, animation: Animation?) { |
| 46 | + self.downstream = downstream |
| 47 | + self.animation = animation |
| 48 | + } |
| 49 | + |
| 50 | + func receive(subscription: Subscription) { |
| 51 | + self.downstream.receive(subscription: subscription) |
| 52 | + } |
| 53 | + |
| 54 | + func receive(_ input: Input) -> Subscribers.Demand { |
| 55 | + withAnimation(self.animation) { |
| 56 | + self.downstream.receive(input) |
| 57 | + } |
| 58 | + } |
| 59 | + |
| 60 | + func receive(completion: Subscribers.Completion<Failure>) { |
| 61 | + self.downstream.receive(completion: completion) |
| 62 | + } |
| 63 | + } |
| 64 | +} |
0 commit comments