Skip to content

Commit 0f026d3

Browse files
authored
Update Store.ifLet docs (#500)
* Update `Store.ifLet` docs * More docs * Update syntax for old CI * fix
1 parent c53a961 commit 0f026d3

File tree

4 files changed

+44
-55
lines changed

4 files changed

+44
-55
lines changed

Examples/TicTacToe/Sources/Views-UIKit/AppViewController.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,16 +37,16 @@ class AppViewController: UINavigationController {
3737

3838
self.store
3939
.scope(state: { $0.login }, action: AppAction.login)
40-
.ifLet { [weak self] loginStore in
40+
.ifLet(then: { [weak self] loginStore in
4141
self?.setViewControllers([LoginViewController(store: loginStore)], animated: false)
42-
}
42+
})
4343
.store(in: &self.cancellables)
4444

4545
self.store
4646
.scope(state: { $0.newGame }, action: AppAction.newGame)
47-
.ifLet { [weak self] newGameStore in
47+
.ifLet(then: { [weak self] newGameStore in
4848
self?.setViewControllers([NewGameViewController(store: newGameStore)], animated: false)
49-
}
49+
})
5050
.store(in: &self.cancellables)
5151
}
5252
}

Sources/ComposableArchitecture/SwiftUI/TextState.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,9 @@ import SwiftUI
4141
/// In the future, should `SwiftUI.Text` and `SwiftUI.LocalizedStringKey` reliably conform to
4242
/// `Equatable`, `TextState` may be deprecated.
4343
///
44-
/// - Note: `TextState` does not support _all_ `LocalizedStringKey` permutations at this time, in
45-
/// particular, for example interpolated `SwiftUI.Image`s. `TextState` also uses reflection to
46-
/// determine `LocalizedStringKey` equatability, so look out for edge cases.
44+
/// - Note: `TextState` does not support _all_ `LocalizedStringKey` permutations at this time
45+
/// (interpolated `SwiftUI.Image`s, for example. `TextState` also uses reflection to determine
46+
/// `LocalizedStringKey` equatability, so be mindful of edge cases.
4747
public struct TextState: Equatable, Hashable {
4848
fileprivate var modifiers: [Modifier] = []
4949
fileprivate let storage: Storage
Lines changed: 19 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,29 @@
11
import Combine
22

33
extension Store {
4-
/// Subscribes to updates when a store containing optional state goes from `nil` to non-`nil` or
5-
/// non-`nil` to `nil`.
4+
/// Calls one of two closures depending on whether a store's optional state is `nil` or not, and
5+
/// whenever this condition changes for as long as the cancellable lives.
66
///
7-
/// **NOTE:** one of the `unwrap` or `else` closures is always called based on the *initial* state of the
8-
/// optional state (`nil` or non-`nil`), in addition to subsequent changes of `nil` / non-`nil`.
7+
/// If the store's state is non-`nil`, it will safely unwrap the value and bundle it into a new
8+
/// store of non-optional state that is passed to the first closure. If the store's state is
9+
/// `nil`, the second closure is called instead.
910
///
10-
/// This is useful for handling navigation in UIKit. The state for a screen that you want to
11-
/// navigate to can be held as an optional value in the parent, and when that value switches
12-
/// from `nil` to non-`nil` you want to trigger a navigation and hand the detail view a `Store`
13-
/// whose domain has been scoped to just that feature:
11+
/// This method is useful for handling navigation in UIKit. The state for a screen the user wants
12+
/// to navigate to can be held as an optional value in the parent, and when that value goes from
13+
/// `nil` to non-`nil`, or non-`nil` to `nil`, you can update the navigation stack accordingly:
1414
///
15-
/// class MasterViewController: UIViewController {
16-
/// let store: Store<MasterState, MasterAction>
15+
/// class ParentViewController: UIViewController {
16+
/// let store: Store<ParentState, ParentAction>
1717
/// var cancellables: Set<AnyCancellable> = []
1818
/// ...
1919
/// func viewDidLoad() {
2020
/// ...
2121
/// self.store
22-
/// .scope(state: \.optionalDetail, action: MasterAction.detail)
22+
/// .scope(state: \.optionalChild, action: ParentAction.child)
2323
/// .ifLet(
24-
/// then: { [weak self] detailStore in
24+
/// then: { [weak self] childStore in
2525
/// self?.navigationController?.pushViewController(
26-
/// DetailViewController(store: detailStore),
26+
/// ChildViewController(store: childStore),
2727
/// animated: true
2828
/// )
2929
/// },
@@ -37,15 +37,15 @@ extension Store {
3737
/// }
3838
///
3939
/// - Parameters:
40-
/// - unwrap: A function that is called with a store of non-optional state whenever the store's
41-
/// optional state is initially non-`nil` or goes from `nil` to non-`nil`.
42-
/// - else: A function that is called whenever the store's optional state is initially `nil` or
40+
/// - unwrap: A function that is called with a store of non-optional state when the store's
41+
/// state is non-`nil`, or whenever it goes from `nil` to non-`nil`.
42+
/// - else: A function that is called when the store's optional state is `nil`, or whenever it
4343
/// goes from non-`nil` to `nil`.
44-
45-
/// - Returns: A cancellable associated with the underlying subscription.
44+
/// - Returns: A cancellable that maintains a subscription to updates whenever the store's state
45+
/// goes from `nil` to non-`nil` and vice versa, so that the caller can react to these changes.
4646
public func ifLet<Wrapped>(
4747
then unwrap: @escaping (Store<Wrapped, Action>) -> Void,
48-
else: @escaping () -> Void
48+
else: @escaping () -> Void = {}
4949
) -> Cancellable where State == Wrapped? {
5050
let elseCancellable =
5151
self
@@ -75,16 +75,4 @@ extension Store {
7575
unwrapCancellable.cancel()
7676
}
7777
}
78-
79-
/// An overload of `ifLet(then:else:)` for the times that you do not want to handle the `else`
80-
/// case.
81-
///
82-
/// - Parameter unwrap: A function that is called with a store of non-optional state whenever the
83-
/// store's optional state is initially non-`nil` or goes from `nil` to non-`nil`.
84-
/// - Returns: A cancellable associated with the underlying subscription.
85-
public func ifLet<Wrapped>(
86-
then unwrap: @escaping (Store<Wrapped, Action>) -> Void
87-
) -> Cancellable where State == Wrapped? {
88-
self.ifLet(then: unwrap, else: {})
89-
}
9078
}

Tests/ComposableArchitectureTests/StoreTests.swift

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -328,23 +328,24 @@ final class StoreTests: XCTestCase {
328328
environment: ()
329329
)
330330

331-
parentStore.ifLet { childStore in
332-
let vs = ViewStore(childStore)
333-
334-
vs
335-
.publisher
336-
.sink { _ in }
337-
.store(in: &self.cancellables)
338-
339-
vs.send(false)
340-
_ = XCTWaiter.wait(for: [.init()], timeout: 0.1)
341-
vs.send(false)
342-
_ = XCTWaiter.wait(for: [.init()], timeout: 0.1)
343-
vs.send(false)
344-
_ = XCTWaiter.wait(for: [.init()], timeout: 0.1)
345-
XCTAssertEqual(vs.state, 3)
346-
}
347-
.store(in: &self.cancellables)
331+
parentStore
332+
.ifLet(then: { childStore in
333+
let vs = ViewStore(childStore)
334+
335+
vs
336+
.publisher
337+
.sink { _ in }
338+
.store(in: &self.cancellables)
339+
340+
vs.send(false)
341+
_ = XCTWaiter.wait(for: [.init()], timeout: 0.1)
342+
vs.send(false)
343+
_ = XCTWaiter.wait(for: [.init()], timeout: 0.1)
344+
vs.send(false)
345+
_ = XCTWaiter.wait(for: [.init()], timeout: 0.1)
346+
XCTAssertEqual(vs.state, 3)
347+
})
348+
.store(in: &self.cancellables)
348349
}
349350

350351
func testActionQueuing() {

0 commit comments

Comments
 (0)