mirror of
https://github.com/pointfreeco/swift-composable-architecture.git
synced 2025-12-24 12:14:25 +01:00
* Converted voice memos back to identified array
* update deps
* update docs for DismissEffect
* wip
* Add Sendable conformance to PresentationState (#2086)
* wip
* swift-format
* wip
* wip
* fix some warnings
* docs
* wip
* wip
* Catch some typos in Articles (#2088)
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* docs
* wip
* wip
* docs
* wip
* wip
* wip
* wip
* docs
* docs
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* Fix invalid states count for 3 optionals and typos (#2094)
* wip
* wip
* more dismisseffect docs
* fixed some references
* navigation doc corrections
* more nav docs
* fix cancellation tests in release mode
* wrap some tests in #if DEBUG since they are testing expected failures
* update UUIDs in tests to use shorter initializer
* fixed a todo
* wip
* fix merge errors
* wip
* fix
* wip
* wip
* fixing a bunch of todos
* get rid of rawvalue in StackElementID
* more todos
* NavLinkStore docs
* fix swift 5.6 stuff
* fix some standups tests
* fix
* clean up
* docs fix
* fixes
* wip
* 5.6 fix
* wip
* wip
* dont parallelize tests
* updated demo readmes
* wip
* Use ObservedObject instead of StateObject for alert/dialog modifiers.
* integration tests for bad dismissal behavior
* check for runtime warnings in every integration test
* wip
* wip
* wip
* fix
* wip
* wip
* wip
* wip
* wip
* wip
* Drop a bunch of Hashables.
* some nav bug fixes
* wip
* wip
* wip
* fix
* fix
* wip
* wip
* Simplify recording test.
* add concurrent async test
* fix
* wip
* Refact how detail dismisses itself.
* fix
* 5.6 fix
* wip
* wip
* wip
* wip
* Add TestStore.assert.
* Revert "Add TestStore.assert."
This reverts commit a892cccc66.
* add Ukrainian Readme.md (#2121)
* Add TestStore.assert. (#2123)
* Add TestStore.assert.
* wip
* Update Sources/ComposableArchitecture/TestStore.swift
Co-authored-by: Stephen Celis <stephen@stephencelis.com>
* Update Sources/ComposableArchitecture/Documentation.docc/Extensions/TestStore.md
Co-authored-by: Stephen Celis <stephen@stephencelis.com>
* fix tests
---------
Co-authored-by: Stephen Celis <stephen@stephencelis.com>
* Run swift-format
* push for store.finish and presentation
* wip
* move docs around
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* Add case subscripts
* wip
* wip
* wip
* 5.7-only
* wip
* wip
* wip
* wip
* fix
* revert store.finish task cancellation
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* add test for presentation scope
* wip
* wip
* wip
* wip
* wip
* cleanup
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* Rename ReducerProtocol.swift to Reducer.swift (#2206)
* Hard-deprecate old SwitchStore initializers/overloads
* wip
* wip
* Resolve CaseStudies crash (#2258)
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* wip
* Bump timeout for CI
* wip
* wip
---------
Co-authored-by: Jackson Utsch <jutechs@gmail.com>
Co-authored-by: Stephen Celis <stephen@stephencelis.com>
Co-authored-by: 유재호 <y73447jh@gmail.com>
Co-authored-by: Dmytro <barabashdmyto@gmail.com>
Co-authored-by: mbrandonw <mbrandonw@users.noreply.github.com>
129 lines
3.1 KiB
Swift
129 lines
3.1 KiB
Swift
import ComposableArchitecture
|
|
@preconcurrency import SwiftUI
|
|
|
|
private let readMe = """
|
|
This application demonstrates how to make use of SwiftUI's `refreshable` API in the Composable \
|
|
Architecture. Use the "-" and "+" buttons to count up and down, and then pull down to request \
|
|
a fact about that number.
|
|
|
|
There is an overload of the `.send` method that allows you to suspend and await while a piece \
|
|
of state is true. You can use this method to communicate to SwiftUI that you are \
|
|
currently fetching data so that it knows to continue showing the loading indicator.
|
|
"""
|
|
|
|
// MARK: - Feature domain
|
|
|
|
struct Refreshable: Reducer {
|
|
struct State: Equatable {
|
|
var count = 0
|
|
var fact: String?
|
|
}
|
|
|
|
enum Action: Equatable {
|
|
case cancelButtonTapped
|
|
case decrementButtonTapped
|
|
case factResponse(TaskResult<String>)
|
|
case incrementButtonTapped
|
|
case refresh
|
|
}
|
|
|
|
@Dependency(\.factClient) var factClient
|
|
private enum CancelID { case factRequest }
|
|
|
|
func reduce(into state: inout State, action: Action) -> Effect<Action> {
|
|
switch action {
|
|
case .cancelButtonTapped:
|
|
return .cancel(id: CancelID.factRequest)
|
|
|
|
case .decrementButtonTapped:
|
|
state.count -= 1
|
|
return .none
|
|
|
|
case let .factResponse(.success(fact)):
|
|
state.fact = fact
|
|
return .none
|
|
|
|
case .factResponse(.failure):
|
|
// NB: This is where you could do some error handling.
|
|
return .none
|
|
|
|
case .incrementButtonTapped:
|
|
state.count += 1
|
|
return .none
|
|
|
|
case .refresh:
|
|
state.fact = nil
|
|
return .run { [count = state.count] send in
|
|
await send(
|
|
.factResponse(TaskResult { try await self.factClient.fetch(count) }),
|
|
animation: .default
|
|
)
|
|
}
|
|
.cancellable(id: CancelID.factRequest)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - Feature view
|
|
|
|
struct RefreshableView: View {
|
|
@State var isLoading = false
|
|
let store: StoreOf<Refreshable>
|
|
|
|
var body: some View {
|
|
WithViewStore(self.store, observe: { $0 }) { viewStore in
|
|
List {
|
|
Section {
|
|
AboutView(readMe: readMe)
|
|
}
|
|
|
|
HStack {
|
|
Button {
|
|
viewStore.send(.decrementButtonTapped)
|
|
} label: {
|
|
Image(systemName: "minus")
|
|
}
|
|
|
|
Text("\(viewStore.count)")
|
|
.monospacedDigit()
|
|
|
|
Button {
|
|
viewStore.send(.incrementButtonTapped)
|
|
} label: {
|
|
Image(systemName: "plus")
|
|
}
|
|
}
|
|
.frame(maxWidth: .infinity)
|
|
.buttonStyle(.borderless)
|
|
|
|
if let fact = viewStore.fact {
|
|
Text(fact)
|
|
.bold()
|
|
}
|
|
if self.isLoading {
|
|
Button("Cancel") {
|
|
viewStore.send(.cancelButtonTapped, animation: .default)
|
|
}
|
|
}
|
|
}
|
|
.refreshable {
|
|
self.isLoading = true
|
|
defer { self.isLoading = false }
|
|
await viewStore.send(.refresh).finish()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: - SwiftUI previews
|
|
|
|
struct Refreshable_Previews: PreviewProvider {
|
|
static var previews: some View {
|
|
RefreshableView(
|
|
store: Store(initialState: Refreshable.State()) {
|
|
Refreshable()
|
|
}
|
|
)
|
|
}
|
|
}
|