Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions Sources/ComposableArchitecture/Effects/Animation.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import Combine
import SwiftUI

extension Effect {
/// Wraps the emission of each element with SwiftUI's `withAnimation`.
///
/// This publisher is most useful when using with ``Effect/task(priority:operation:)-2czg0``
///
/// ```swift
/// case .buttonTapped:
/// return .task {
/// .activityResponse(await environment.apiClient.fetchActivity())
/// }
/// .animation()
/// ```
///
/// - Parameter animation: An animation.
/// - Returns: A publisher.
public func animation(_ animation: Animation? = .default) -> Self {
AnimatedPublisher(upstream: self, animation: animation)
.eraseToEffect()
}
}

private struct AnimatedPublisher<Upstream: Publisher>: Publisher {
typealias Output = Upstream.Output
typealias Failure = Upstream.Failure

var upstream: Upstream
var animation: Animation?

func receive<S: Combine.Subscriber>(subscriber: S)
where S.Input == Output, S.Failure == Failure {
let conduit = Subscriber(downstream: subscriber, animation: self.animation)
self.upstream.receive(subscriber: conduit)
}

private class Subscriber<Downstream: Combine.Subscriber>: Combine.Subscriber {
typealias Input = Downstream.Input
typealias Failure = Downstream.Failure

let downstream: Downstream
let animation: Animation?

init(downstream: Downstream, animation: Animation?) {
self.downstream = downstream
self.animation = animation
}

func receive(subscription: Subscription) {
self.downstream.receive(subscription: subscription)
}

func receive(_ input: Input) -> Subscribers.Demand {
withAnimation(self.animation) {
self.downstream.receive(input)
}
}

func receive(completion: Subscribers.Completion<Failure>) {
self.downstream.receive(completion: completion)
}
}
}
4 changes: 2 additions & 2 deletions Sources/ComposableArchitecture/TestSupport/TestStore.swift
Original file line number Diff line number Diff line change
Expand Up @@ -176,8 +176,8 @@

/// The current state.
///
/// When read from a trailing closure assertion in ``send`` or ``receive``, it will equal the
/// `inout` state passed to the closure.
/// When read from a trailing closure assertion in ``send(_:_:file:line:)`` or
/// ``receive(_:_:file:line:)``, it will equal the `inout` state passed to the closure.
Comment on lines +179 to +180
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unrelated docc warning fixes.

public private(set) var state: State

private let file: StaticString
Expand Down