profile
viewpoint
Stephen Celis stephencelis @pointfreeco Brooklyn http://stephencelis.com Working on @pointfreeco: https://www.pointfree.co/

assaf/vanity 1447

Experiment Driven Development for Ruby

pointfreeco/swift-html 518

🗺 A Swift DSL for type-safe, extensible, and transformable HTML documents.

pointfreeco/swift-gen 203

🎱 Composable, transformable, controllable randomness.

stephencelis/app 95

Easy App config.

stephencelis/acts_as_singleton 48

A lightweight singleton library for your Active Record models.

pointfreeco/swift-html-vapor 47

💧 Vapor plugin for type-safe, transformable HTML views.

stephencelis/BRLOptionParser 47

A short wrapper for getopt_long(3). Command line options parsing for Objective-C.

mbrandonw/optics 22

A Swift lens library.

pointfreeco/swift-parser-printer 12

↔️ Parsing and printing

pointfreeco/swift-html-kitura 10

☁️ Kitura plugin for type-safe, transformable HTML views.

push eventpointfreeco/episode-code-samples

Stephen Celis

commit sha 320588e8eea595b442d1902c08cd88defa163f17

122

view details

push time in 2 days

push eventpointfreeco/pointfreeco

Stephen Celis

commit sha 25a61f543cfcc8a89f2c232a2fb46349aae7238c

122 prep

view details

push time in 2 days

push eventpointfreeco/episode-code-samples

Stephen Celis

commit sha 2ab06bf917fd5499c780328fa57e573f7a7e6bda

Fix

view details

push time in 5 days

push eventpointfreeco/pointfreeco

Stephen Celis

commit sha 70da9e0e9b61abadbf2b07ab85f61ca601a62b9a

Redactions collection

view details

push time in 5 days

PullRequestReviewEvent

push eventpointfreeco/swift-composable-architecture

Michael Brown

commit sha d06e4302a4729d48b4767b7b13cb1644fd0736dd

Don't depend on Equatable conformance of DispatchQueue (#311)

view details

push time in 9 days

PR merged pointfreeco/swift-composable-architecture

Don't depend on DispatchQueue's Equatable conformance

Among the changes I had to make in order to get the ReactiveSwift fork building for Linux was not depending on the Equatable conformance of DispatchQueue, and I realised that it probably isn't necessary at all.

Equatable conformance allows the switch self and the matching on .main, however, in Linux DispatchQueue does not conform to Equatable, so this doesn't work.

Since for the other queues we are doing name matching we could do the same for .main whose name is always "com.apple.main-thread"

+4 -4

0 comment

1 changed file

mluisbrown

pr closed time in 9 days

PullRequestReviewEvent

pull request commentpointfreeco/swift-composable-architecture

Add support for AccuracyAuthorization

Thanks for the PR, @mackoj! We haven't had time to dig into the failure yet but we'll try to get to it soon.

mackoj

comment created time in 9 days

push eventpointfreeco/episode-code-samples

Stephen Celis

commit sha 7ca0436bd61cb8a5caad3e4b333a3e23d1e4e6d6

121

view details

push time in 9 days

push eventpointfreeco/pointfreeco

Stephen Celis

commit sha ecbbe99224aef29cb6d1efe3cff79f028243c0f8

121 prep

view details

push time in 9 days

push eventpointfreeco/swift-web

Stephen Celis

commit sha 616f365ac7c94e1d9adbec3799afeeebea63913a

One more header fix

view details

push time in 12 days

push eventpointfreeco/swift-web

Stephen Celis

commit sha 5bae8a5c1e7ff20e1ad52783b2b83961dc46e84f

fix headers

view details

push time in 12 days

push eventpointfreeco/swift-nonempty

Maxim Krouk

commit sha b4f37767336e4bb98bffa3e05fad579d91c2b2d4

Update Package.swift (#34)

view details

push time in 12 days

PR merged pointfreeco/swift-nonempty

Update Package.swift

This PR fixes import for swift-tools-version 5.3

SwiftPM was unable to resolve package at https://github.com/pointfreeco/swift-nonempty

Maybe it's an SPM bug, but it saw neither "NonEmpty" nor "swift-nonempty" package names (now the second one works).

Also as far as I know (from some TCA issue and basic logic 🙂) you try to follow Apple's package naming guidelines like "swift-log", "swift-algorithms" or "swift-composable-architecture", so "swift-notempty" should fit this style more.

+1 -1

0 comment

1 changed file

maximkrouk

pr closed time in 12 days

PullRequestReviewEvent

push eventpointfreeco/swift-web

Stephen Celis

commit sha 2b2d8b38b61df99dfbd11e5ec01e0a0487de3aef

fix header

view details

push time in 12 days

push eventpointfreeco/swift-web

Brandon Williams

commit sha ca2e202a5a556f48f184f8f15520a62b4e63a8ab

Parse headers for requests. (#198) * Parse headers for requests. * wip

view details

push time in 12 days

delete branch pointfreeco/swift-web

delete branch : header-parser

delete time in 12 days

PR merged pointfreeco/swift-web

Parse headers for requests.

I think we'll need to parse stuff from headers soon and apparently this parser doesn't support it yet...

+50 -5

0 comment

4 changed files

mbrandonw

pr closed time in 12 days

push eventpointfreeco/swift-composable-architecture

Stephen Celis

commit sha 9d4d62ed633b141d538bb5c7e7cec25a3516f509

Rename overload

view details

push time in 12 days

push eventpointfreeco/swift-composable-architecture

Jeffrey Macko

commit sha 767e1d9553fcee5a95af10e0352f20fb03b98352

Update ci.yml (#308)

view details

push time in 13 days

PullRequestReviewEvent

push eventpointfreeco/episode-code-samples

Stephen Celis

commit sha f59fe8f271c5582b7dd4e2077a433daa47729a00

120

view details

push time in 13 days

issue commentpointfreeco/swift-composable-architecture

Support for accuracyAuthorization API in ComposableCoreLocation

Thanks for the report, @mackoj! Did you wanna try to PR? If not we'll try to get to this sometime soon.

mackoj

comment created time in 14 days

issue commentpointfreeco/swift-composable-architecture

@ObservedObject doesn't get notified from computed properties on iOS14

@barksten Definitely appears to be a change in iOS behavior and not a TCA bug. I think you'd see the same behavior in vanilla SwiftUI if you kept this state in a view model. You need to subscribe to state changes and call setNeedsStatusBarAppearanceUpdate to ensure that iOS knows to check the preferredStatusBarStyle property. In your code you can achieve this by adding a sink to your initializer:

class MyHostingController<Content>: UIHostingController<Content> where Content: View {
  var viewStore: ViewStore<AppState, AppAction>
  // 1️⃣ Add a cancellables set to your hosting controller to maintain view store subscriptions
  var cancellables: Set<AnyCancellable> = []
  
  init(viewStore: ViewStore<AppState, AppAction>, rootView: Content) {
    self.viewStore = viewStore
    super.init(rootView: rootView)

    // 2️⃣ Sink on activeColorScheme changes to call setNeedsStatusBarAppearanceUpdate
    self.viewStore.publisher.activeColorScheme
      .receive(on: DispatchQueue.main)
      .sink { [weak self] _ in self?.setNeedsStatusBarAppearanceUpdate() }
      .store(in: &self.cancellables)
  }
  // ...
}
barksten

comment created time in 15 days

push eventpointfreeco/pointfreeco

Stephen Celis

commit sha 4e59fa86b4af05602a55c2a7b8e3ee8159ff56fc

move

view details

push time in 15 days

push eventpointfreeco/pointfreeco

Stephen Celis

commit sha 53fadffde0737d7b823673a3006d57d87dd2d59b

120

view details

push time in 16 days

push eventpointfreeco/pointfreeco

Stephen Celis

commit sha 51a585aeff4178b87fecfd4bf3502bf5cc812084

bump

view details

push time in 19 days

delete branch pointfreeco/pointfreeco

delete branch : filter-subscriptionless-invoices

delete time in 19 days

push eventpointfreeco/pointfreeco

Stephen Celis

commit sha 28c34e378a31ef3decbcd25b505c310c98f2517a

Filter subscriptionless invoices (#682) * Filter subscriptionless invoices * Fix logic

view details

push time in 19 days

push eventpointfreeco/pointfreeco

Stephen Celis

commit sha ccad01dcaf44f2d0b4416a3f42142b60ae336f37

Fix logic

view details

push time in 19 days

create barnchpointfreeco/pointfreeco

branch : filter-subscriptionless-invoices

created branch time in 19 days

push eventpointfreeco/swift-composable-architecture

Danny Hertz

commit sha 98d5e88f0ff1f5da9993af9113ced57a1c04df68

Implment heading methods for core location library (#303)

view details

push time in 19 days

PR merged pointfreeco/swift-composable-architecture

Implement heading methods for core location library

The start/stop heading methods were not implemented in the live core location manager. I added them!

+12 -0

1 comment

1 changed file

dannyhertz

pr closed time in 19 days

pull request commentpointfreeco/swift-composable-architecture

Implement heading methods for core location library

Good find, thanks!

dannyhertz

comment created time in 19 days

push eventpointfreeco/episode-code-samples

Stephen Celis

commit sha e3f15b33816a6c1416f7c70716f00c35562957c3

119

view details

push time in 19 days

issue openedvapor/postgres-kit

Improve pool use documentation?

The README currently shows how to get a database from a pool:

let postgres = pool.database(logger: ...) // PostgresDatabase
let rows = try postgres.simpleQuery("SELECT version();").wait()

Should you go through pool whenever you want to query? Or can you keep postgres around and use it for all of your queries?

created time in 21 days

push eventpointfreeco/swift-snapshot-testing

James Sherlock

commit sha eeeeab8f2aae518d0e0b250f432e875269752111

Update AccessibilitySnapshot plugin link (#395)

view details

push time in 21 days

PR merged pointfreeco/swift-snapshot-testing

Update AccessibilitySnapshot plugin link

As of https://github.com/cashapp/AccessibilitySnapshot/pull/21 AccessibilitySnapshot now supports SnapshotTesting in the official repository using my code. Updating the link here to point people in the right direction.

+1 -1

0 comment

1 changed file

Sherlouk

pr closed time in 21 days

PullRequestReviewEvent

issue commentpointfreeco/swift-composable-architecture

Strange List Behavior

@m-housh Haven't encountered this particular issue yet. Have you tried reproducing in vanilla SwiftUI?

m-housh

comment created time in 21 days

issue commentpointfreeco/swift-composable-architecture

Making ComposableArchitecture dynamic

@Atimca Since this is an SPM bug/limitation that's being tracked, can we close this issue out?

Atimca

comment created time in 21 days

push eventpointfreeco/pointfreeco

Stephen Celis

commit sha 1e69e9b859d47bf62437f9082d205332b5f48811

Prep

view details

push time in 23 days

issue commentpointfreeco/swift-composable-architecture

Example issue with Todo

Ah yes, thanks! Luckily in this case we weren't (yet) working with child effects that would be problematic, but it's a general rule to follow. Fix in #301!

fatlazycat

comment created time in 24 days

create barnchpointfreeco/swift-composable-architecture

branch : todos-foreach

created branch time in 24 days

issue commentpointfreeco/swift-case-paths

Equatable CasePath

Unfortunately not. While it would be possible to add an identifier field to equate against, it'd be a breaking change and there's no way as far as I can tell to automatically derive an identifier from an enum case embed function. Open to ideas! But it might not be possible and all the more reason to introduce case paths at the language level so they can be hashable like key paths.

Alternatively, you could wrap case paths in a type that is equatable by providing the strategy for comparing paths. Not ideal, but could unblock you.

mycroftcanner

comment created time in 25 days

push eventpointfreeco/swift-composable-architecture

Michael Brown

commit sha 74017427edbfdc52775b008e614f511f1f042297

Fixes bug with LocalizedStringKeyTests (#299) * * Fix locale issues with LocalizedStringKeyTests * Use the locale parameter in LocalizedStringKey.formatted() * Changed test locale to "en_US_POSIX"

view details

push time in a month

PR merged pointfreeco/swift-composable-architecture

Fixes bug with LocalizedStringKeyTests

Currently, when you run the LocalizedStringKeyTests on a machine which is not using the en_US locale, they will fail:

("You are 1 000th in line.") is not equal to ("You are 1,000th in line.")

I currently have a custom locale where the number grouping symbol is a space, but many European locales use . for grouping and , for decimals. It would also fail for these locales.

The fix I made was to force the en_US locale for the NumberFormatter used in the test.

Whilst I was investigating this, I also found that the formatted() function in the LocalizedStringKey extension ignores the locale parameter passed, so I changed it to not ignore this parameter.

+2 -1

1 comment

2 changed files

mluisbrown

pr closed time in a month

PullRequestReviewEvent
PullRequestReviewEvent

Pull request review commentpointfreeco/swift-composable-architecture

Fixes bug with LocalizedStringKeyTests

 extension LocalizedStringKey: CustomDebugOutputConvertible {         return formatter?.string(for: value) ?? value as! CVarArg       } -    return String(format: key, locale: nil, arguments: arguments)+    return String(format: key, locale: locale, arguments: arguments)

Ha good catch!

mluisbrown

comment created time in a month

pull request commentpointfreeco/swift-composable-architecture

Better support enum state and struct actions with optional paths

  • I might be missing something as I wasn't able to implement it purely additively - the changes to Reducer.swift couldn't be implemented outside of the library (even when providing different names for otherwise conflicting properties/functions) as they make use of reducer which is private. I opted to change my SPM dependency to target this branch instead, but that also again means that we'd potentially be stuck on an out of date branch (unless it's periodically rebased on the latest).

Ah yeah, as @AlexisQapa notes, you can update reducer to run and you should be good to go.

  • It's possible for parent reducers to get actions for states they are not currently in. This could be viewed as a feature or a bug I suppose. But if you're considering these to be truly mutually exclusive, it might be preferable to throw an error/warning if the reducer receives unexpected actions, particularly since the child reducer for the unexpected action doesn't receive it (from what I can tell) ** Can be worked around by having the parent reducer switch on a combination of the state and the action, and ignore actions that are not allowed for the current state.

This should probably follow the pattern found in optional higher-order reducer, which currently assert in DEBUG mode:

https://github.com/pointfreeco/swift-composable-architecture/blob/fe06d95cff0dd7e68aa29a46e325340788f668c9/Sources/ComposableArchitecture/Reducer.swift#L305-L325

We have a branch changing this behavior with the introduction of an explicit breakpointOnNil parameter in #296, but we want to take a fresh pass at documentation to make things clearer before merging.

  • Would a SwitchStore be possible/desirable? In my example I was able to get the desired behaviour with a bunch of IfLetStores, but that doesn't make it obvious to a reader that these are mutually exclusive conditions. I could also wrap the IfLetStores in a normal switch statement, but that results in some redundancy since I am then unwrapping the state even though I know it is non-nil. There might be some obvious workaround for this that I am missing.

The nice thing about an explicit switch is that it lets you exhaustively switch over an enum, though I guess it could be problematic to introduce the whole enum of state to be observable just for that ability. That's why I think your solution is probably the best for now.

You might be able to do a SwitchStore that looks something like this, which just uses IfLetStore under the hood:

SwitchStore(
  self.store,
  .case(/AppState.loggedIn, then: LoggedInView.init(store:)),
  .case(/AppState.loggedOut, then: LoggedOutView.init(store:))
)

// vs.
IfLetStore(self.store.scope(state: /AppState.loggedIn), then: LoggedInView.init(store:)),
IfLetStore(self.store.scope(state: /AppState.loggedOut), then: LoggedOutView.init(store:))

But we still can't get compile-time exhaustivity (we could maybe do a runtime assertion when hitting an unhandled case), so I'm wondering whether it's worth it or not. Thoughts?

stephencelis

comment created time in a month

PR opened antranapp/awesome-tca

Add link to Construct
+1 -0

0 comment

1 changed file

pr created time in a month

push eventstephencelis/awesome-tca

Stephen Celis

commit sha c443308458f6162713c95c6bd3006b62ffbcd987

Update README.md

view details

push time in a month

fork stephencelis/awesome-tca

Awesome list for The Composable Architecture

fork in a month

pull request commentpointfreeco/swift-composable-architecture

Better support enum state and struct actions with optional paths

@nmccann Totally understandable. My hope is that you wouldn't get into this kind of situation, since the code is entirely additive. Once we come to a happy place where we can incorporate this functionality into TCA itself, you'll ideally be able to simply remove the code you paste in.

stephencelis

comment created time in a month

issue commentpointfreeco/swift-composable-architecture

Making ComposableArchitecture dynamic

@Atimca As long as you do all the dependency work in a single place (like the local package trick I mentioned), you should be able to introduce a TCA and a separate dependency that depends on swift-case-paths without the duplication error.

Atimca

comment created time in a month

issue commentpointfreeco/swift-composable-architecture

testScheduling causes EXC_BAD_INSTRUCTION on iOS 13.1 (simulator)

There have been a bunch of Combine bugs over the course of various versions of iOS, unfortunately 😞 In general we'd recommend running tests on a more recent version of iOS. We're definitely open to a workaround if anyone finds it, though it would need to be on the upstream dependency here: https://github.com/pointfreeco/combine-schedulers

Wanna close this and reopen the issue there if you think it's worth addressing?

andreyz

comment created time in a month

issue commentpointfreeco/swift-composable-architecture

Making ComposableArchitecture dynamic

TicTacToe example uses a hack with wrapping SPM into a dynamic framework.

According to some Apple engineers we spoke with, this is in fact the way to go right now for incorporating packages into a project. We've tried to document this in the README, although it is brief: https://github.com/pointfreeco/swift-composable-architecture#installation

Because we're dealing with an Apple bug here we think that introducing a .dynamic variant would be a hack of its own and complicate the library: we'd need to introduce an extra library name per target (like KingFisher does), document the workaround/bug, make all of TCA's upstream dependencies have dynamic variants, etc. The KingFisher PR links to some Swift forums discussion: https://github.com/onevcat/Kingfisher/pull/1426

One simplification we've experimented with in an application we're building is to power it with a single local Swift package that declares all of its dependencies in one place. Then the application can embed this single static library. It's worked well in Xcode 12, where some bugs around SPM, previews, and playgrounds were fixed.

Atimca

comment created time in a month

push eventpointfreeco/swift-snapshot-testing

Vincent Esche

commit sha 7b8e0b26dfc0730869fa62ac954e9547bfd47dc5

Made `.data` snapshotting strategy `public` (#391)

view details

push time in a month

PR merged pointfreeco/swift-snapshot-testing

Made `.data` snapshotting strategy `public`

For snapshot testing binary artifacts being able to test against the bare data is preferable over having to snapshot it as Encodable, which then might even end up getting encoded as strings (for .json at least), making it impossible to inspect with a hex editor.

+24 -1

0 comment

4 changed files

regexident

pr closed time in a month

PullRequestReviewEvent

push eventpointfreeco/swift-composable-architecture

Stephen Celis

commit sha cfc196e230331c3f7c421c5646f7a753c585c4a1

Fix

view details

push time in a month

push eventpointfreeco/swift-composable-architecture

Stephen Celis

commit sha 601154f4ab23febcb850c1bdd78b429097531a48

trap

view details

push time in a month

Pull request review commentpointfreeco/swift-composable-architecture

Breakpoint instead of assert in optional/forEach

 public struct Reducer<State, Action, Environment> {   ///       }   ///     )   ///-  /// Take care when combining optional reducers into parent domains, as order matters. Always-  /// combine optional reducers _before_ parent reducers that can `nil` out the associated optional-  /// state.+  /// Take care when combining optional reducers into parent domains. An optional reducer cannot+  /// process actions in its domain when its state is `nil`. If a child action is sent to an+  /// optional reducer when child state is `nil`, it is considered a logic error. There are a few+  /// ways in which these errors can sneak into a code base:+  ///+  ///   * A parent reducer sets child state to `nil` when processing a child action and runs+  ///     _before_ the child reducer:+  ///+  ///         let parentReducer = Reducer<ParentState, ParentAction, ParentEnvironment>.combine(+  ///           // When combining reducers, the parent reducer runs first+  ///           Reducer { state, action, environment in+  ///             switch action {+  ///             case .child(.didDisappear):+  ///               // And `nil`s out child state when processing a child action+  ///               state.child = nil+  ///               return .none+  ///             ...+  ///             }+  ///           },+  ///           // Before the child reducer runs+  ///           childReducer.optional().pullback(...)+  ///         )+  ///+  ///         let childReducer = Reducer<+  ///           ChildState, ChildAction, ChildEnvironment+  ///         > { state, action environment in+  ///           case .didDisappear:+  ///             // This action is never received here because child state is `nil` in the parent+  ///           ...+  ///         }+  ///+  ///     To ensure that a child reducer can process any action that a parent may use to `nil` out+  ///     its state, combine it _before_ the parent:+  ///+  ///         let parentReducer = Reducer<ParentState, ParentAction, ParentEnvironment>.combine(+  ///           // The child runs first+  ///           childReducer.optional().pullback(...),+  ///           // The parent runs after+  ///           Reducer { state, action, environment in+  ///             ...+  ///           }+  ///         )+  ///+  ///   * A child effect feeds a child action back into the store when child state is `nil`:+  ///+  ///         let childReducer = Reducer<+  ///           ChildState, ChildAction, ChildEnvironment+  ///         > { state, action environment in+  ///           switch action {+  ///           case .onAppear:+  ///             // An effect may want to feed its result back to the child domain in an action+  ///             return environment.apiClient+  ///               .request()+  ///               .map(ChildAction.response)+  ///+  ///           case let .response(response):+  ///             // But the child cannot process this action if its state is `nil` in the parent+  ///           ...+  ///           }+  ///         }+  ///+  ///     It is perfectly reasonable to ignore the result of an effect when child state is `nil`,+  ///     for example one-off effects that you don't want to cancel. However, many long-living+  ///     effects _should_ be explicitly canceled when tearing down a child domain:+  ///+  ///         let childReducer = Reducer<+  ///           ChildState, ChildAction, ChildEnvironment+  ///         > { state, action environment in+  ///           struct MotionId: Hashable {}+  ///+  ///           switch action {+  ///           case .onAppear:+  ///             // Mark long-living effects that shouldn't outlive their domain cancellable+  ///             return environment.motionClient+  ///               .start()+  ///               .map(ChildAction.motion)+  ///               .cancellable(id: MotionId())+  ///+  ///           case .onDisappear:+  ///             // And explicitly cancel them when the domain is torn down+  ///             return .cancel(id: MotionId())+  ///           ...+  ///           }+  ///         }+  ///+  ///   * A view store sends a child action when child state is `nil`:+  ///+  ///         WithViewStore(self.parentStore) { parentViewStore in+  ///           // If child state is `nil`, it cannot process this action.+  ///           Button("Child Action") { parentViewStore.send(.child(.action)) }+  ///           ...+  ///         }+  ///+  ///     Use `Store.scope` with`IfLetStore` or `Store.ifLet` to ensure that views can only send+  ///     child actions when the child domain is non-`nil`.+  ///+  ///         IfLetStore(+  ///           self.parentStore.scope(state: { $0.child }, action: { .child($0) }+  ///         ) { childStore in+  ///           // This destination only appears when child state is non-`nil`+  ///           WithViewStore(childStore) { childViewStore in+  ///             // So this action can only be sent when child state is non-`nil`+  ///             Button("Child Action") { childViewStore.send(.action) }+  ///           }+  ///           ...+  ///         }

If these additional docs look good I can create similar versions for forEach, or I can add a "see also" in forEach to read the optional documentation for more info (although I think the explanations are slightly different).

stephencelis

comment created time in a month

PullRequestReviewEvent

push eventpointfreeco/swift-composable-architecture

Stephen Celis

commit sha 9637b95803cd645207d484b95558871b0d19d3f1

Fix format

view details

push time in a month

Pull request review commentpointfreeco/swift-composable-architecture

Breakpoint instead of assert in optional/forEach

 public struct Reducer<State, Action, Environment> {   ///     `GlobalState`.   ///   - toLocalAction: A case path that can extract/embed `(Int, Action)` from `GlobalAction`.   ///   - toLocalEnvironment: A function that transforms `GlobalEnvironment` into `Environment`.+  ///   - breakpointOnNil: Raises `SIGTRAP` signal when an action is sent to the reducer but the+  ///     index is out of bounds. This is generally considered a logic error, as a child reducer+  ///     cannot process a child action for unavailable child state.

I dunno if breakpointOnNil is the right name for the forEach reducers...maybe we can come up with something better.

stephencelis

comment created time in a month

PullRequestReviewEvent

Pull request review commentpointfreeco/swift-composable-architecture

Breakpoint instead of assert in optional/forEach

 public struct Reducer<State, Action, Environment> {   ///       }   ///     )   ///-  /// Take care when combining optional reducers into parent domains, as order matters. Always-  /// combine optional reducers _before_ parent reducers that can `nil` out the associated optional-  /// state.+  /// Take care when combining optional reducers into parent domains. An optional reducer cannot+  /// process actions in its domain when its state is `nil`. If a child action is sent to an+  /// optional reducer when child state is `nil`, it is considered a logic error. There are a few+  /// ways in which these errors can sneak into a code base:+  ///+  ///   * A parent reducer sets child state to `nil` when processing a child action and runs+  ///     _before_ the child reducer:+  ///+  ///         let parentReducer = Reducer<ParentState, ParentAction, ParentEnvironment>.combine(+  ///           // When combining reducers, the parent reducer runs first+  ///           Reducer { state, action, environment in+  ///             switch action {+  ///             case .child(.didDisappear):+  ///               // And `nil`s out child state when processing a child action+  ///               state.child = nil+  ///               return .none+  ///             ...+  ///             }+  ///           },+  ///           // Before the child reducer runs+  ///           childReducer.optional().pullback(...)+  ///         )+  ///+  ///         let childReducer = Reducer<+  ///           ChildState, ChildAction, ChildEnvironment+  ///         > { state, action environment in+  ///           case .didDisappear:+  ///             // This action is never received here because child state is `nil` in the parent+  ///           ...+  ///         }+  ///+  ///     To ensure that a child reducer can process any action that a parent may use to `nil` out+  ///     its state, combine it _before_ the parent:+  ///+  ///         let parentReducer = Reducer<ParentState, ParentAction, ParentEnvironment>.combine(+  ///           // The child runs first+  ///           childReducer.optional().pullback(...),+  ///           // The parent runs after+  ///           Reducer { state, action, environment in+  ///             ...+  ///           }+  ///         )+  ///+  ///   * A child effect feeds a child action back into the store when child state is `nil`:+  ///+  ///         let childReducer = Reducer<+  ///           ChildState, ChildAction, ChildEnvironment+  ///         > { state, action environment in+  ///           switch action {+  ///           case .onAppear:+  ///             // An effect may want to feed its result back to the child domain in an action+  ///             return environment.apiClient+  ///               .request()+  ///               .map(ChildAction.response)+  ///+  ///           case let .response(response):+  ///             // But the child cannot process this action if its state is `nil` in the parent+  ///           ...+  ///           }+  ///         }+  ///+  ///     It is perfectly reasonable to ignore the result of an effect when child state is `nil`,+  ///     for example one-off effects that you don't want to cancel. However, many long-living+  ///     effects _should_ be explicitly canceled when tearing down a child domain:+  ///+  ///         let childReducer = Reducer<+  ///           ChildState, ChildAction, ChildEnvironment+  ///         > { state, action environment in+  ///           struct MotionId: Hashable {}+  ///+  ///           switch action {+  ///           case .onAppear:+  ///             // Mark long-living effects that shouldn't outlive their domain cancellable+  ///             return environment.motionClient+  ///               .start()+  ///               .map(ChildAction.motion)+  ///               .cancellable(id: MotionId())+  ///+  ///           case .onDisappear:+  ///             // And explicitly cancel them when the domain is torn down+  ///             return .cancel(id: MotionId())+  ///           ...+  ///           }+  ///         }+  ///+  ///   * A view store sends a child action when child state is `nil`:+  ///+  ///         WithViewStore(self.parentStore) { parentViewStore in+  ///           // If child state is `nil`, it cannot process this action.+  ///           Button("Child Action") { parentViewStore.send(.child(.action)) }+  ///           ...+  ///         }+  ///+  ///     Use `Store.scope` with`IfLetStore` or `Store.ifLet` to ensure that views can only send+  ///     child actions when the child domain is non-`nil`.+  ///+  ///         IfLetStore(+  ///           self.parentStore.scope(state: { $0.child }, action: { .child($0) }+  ///         ) { childStore in+  ///           // This destination only appears when child state is non-`nil`+  ///           WithViewStore(childStore) { childViewStore in+  ///             // So this action can only be sent when child state is non-`nil`+  ///             Button("Child Action") { childViewStore.send(.action) }+  ///           }+  ///           ...+  ///         }   ///   /// - See also: `IfLetStore`, a SwiftUI helper for transforming a store on optional state into a   ///   store on non-optional state.   /// - See also: `Store.ifLet`, a UIKit helper for doing imperative work with a store on optional   ///   state.-  public func optional(_ file: StaticString = #file, _ line: UInt = #line) -> Reducer<+  ///+  /// - Parameter breakpointOnNil: Raises `SIGTRAP` signal when an action is sent to the reducer+  ///   but state is `nil`. This is generally considered a logic error, as a child reducer cannot+  ///   process a child action for unavailable child state.+  /// - Returns: A reducer that works on optional state.+  public func optional(+    breakpointOnNil: Bool = true,+    _ file: StaticString = #file,+    _ line: UInt = #line+  ) -> Reducer<     State?, Action, Environment   > {     .init { state, action, environment in       guard state != nil else {-        assertionFailure(-          """-          "\(debugCaseOutput(action))" was received by an optional reducer when its state was \-          "nil". This can happen for a few reasons:--          * The optional reducer was combined with or run from another reducer that set \-          "\(State.self)" to "nil" before the optional reducer ran. Combine or run optional \-          reducers before reducers that can set their state to "nil". This ensures that optional \-          reducers can handle their actions while their state is still non-"nil".--          * An active effect emitted this action while state was "nil". Make sure that effects for \-          this optional reducer are canceled when optional state is set to "nil".--          * This action was sent to the store while state was "nil". Make sure that actions for \-          this reducer can only be sent to a view store when state is non-"nil". In SwiftUI \-          applications, use "IfLetStore".-          """,-          file: file,-          line: line-        )+        #if DEBUG+          if breakpointOnNil {+            fputs(+              """+              ---+              Warning: Reducer.optional@\(file):\(line))++              "\(debugCaseOutput(action))" was received by an optional reducer when its state was \+              "nil". This is generally considered an application logic error, and can happen for a \+              few reasons:++              * The optional reducer was combined with or run from another reducer that set \+              "\(State.self)" to "nil" before the optional reducer ran. Combine or run optional \+              reducers before reducers that can set their state to "nil". This ensures that \+              optional reducers can handle their actions while their state is still non-"nil".++              * An in-flight effect emitted this action while state was "nil". While it may be \+              perfectly reasonable to ignore this action, you may want to cancel the associated \+              effect before state is set to "nil", especially if it is a long-living effect.++              * This action was sent to the store while state was "nil". Make sure that actions \+              for this reducer can only be sent to a view store when state is non-"nil". In \+              SwiftUI applications, use "IfLetStore".+              ---+

This trailing line is needed since fputs doesn't print a trailing new line.

stephencelis

comment created time in a month

PullRequestReviewEvent

Pull request review commentpointfreeco/swift-composable-architecture

Breakpoint instead of assert in optional/forEach

 public struct Reducer<State, Action, Environment> {   ///       }   ///     )   ///-  /// Take care when combining optional reducers into parent domains, as order matters. Always-  /// combine optional reducers _before_ parent reducers that can `nil` out the associated optional-  /// state.+  /// Take care when combining optional reducers into parent domains. An optional reducer cannot+  /// process actions in its domain when its state is `nil`. If a child action is sent to an+  /// optional reducer when child state is `nil`, it is considered a logic error. There are a few+  /// ways in which these errors can sneak into a code base:+  ///+  ///   * A parent reducer sets child state to `nil` when processing a child action and runs+  ///     _before_ the child reducer:+  ///+  ///         let parentReducer = Reducer<ParentState, ParentAction, ParentEnvironment>.combine(+  ///           // When combining reducers, the parent reducer runs first+  ///           Reducer { state, action, environment in+  ///             switch action {+  ///             case .child(.didDisappear):+  ///               // And `nil`s out child state when processing a child action+  ///               state.child = nil+  ///               return .none+  ///             ...+  ///             }+  ///           },+  ///           // Before the child reducer runs+  ///           childReducer.optional().pullback(...)+  ///         )+  ///+  ///         let childReducer = Reducer<+  ///           ChildState, ChildAction, ChildEnvironment+  ///         > { state, action environment in+  ///           case .didDisappear:+  ///             // This action is never received here because child state is `nil` in the parent+  ///           ...+  ///         }+  ///+  ///     To ensure that a child reducer can process any action that a parent may use to `nil` out+  ///     its state, combine it _before_ the parent:+  ///+  ///         let parentReducer = Reducer<ParentState, ParentAction, ParentEnvironment>.combine(+  ///           // The child runs first+  ///           childReducer.optional().pullback(...),+  ///           // The parent runs after+  ///           Reducer { state, action, environment in+  ///             ...+  ///           }+  ///         )+  ///+  ///   * A child effect feeds a child action back into the store when child state is `nil`:+  ///+  ///         let childReducer = Reducer<+  ///           ChildState, ChildAction, ChildEnvironment+  ///         > { state, action environment in+  ///           switch action {+  ///           case .onAppear:+  ///             // An effect may want to feed its result back to the child domain in an action+  ///             return environment.apiClient+  ///               .request()+  ///               .map(ChildAction.response)+  ///+  ///           case let .response(response):+  ///             // But the child cannot process this action if its state is `nil` in the parent+  ///           ...+  ///           }+  ///         }+  ///+  ///     It is perfectly reasonable to ignore the result of an effect when child state is `nil`,+  ///     for example one-off effects that you don't want to cancel. However, many long-living+  ///     effects _should_ be explicitly canceled when tearing down a child domain:+  ///+  ///         let childReducer = Reducer<+  ///           ChildState, ChildAction, ChildEnvironment+  ///         > { state, action environment in+  ///           struct MotionId: Hashable {}+  ///+  ///           switch action {+  ///           case .onAppear:+  ///             // Mark long-living effects that shouldn't outlive their domain cancellable+  ///             return environment.motionClient+  ///               .start()+  ///               .map(ChildAction.motion)+  ///               .cancellable(id: MotionId())+  ///+  ///           case .onDisappear:+  ///             // And explicitly cancel them when the domain is torn down+  ///             return .cancel(id: MotionId())+  ///           ...+  ///           }+  ///         }+  ///+  ///   * A view store sends a child action when child state is `nil`:+  ///+  ///         WithViewStore(self.parentStore) { parentViewStore in+  ///           // If child state is `nil`, it cannot process this action.+  ///           Button("Child Action") { parentViewStore.send(.child(.action)) }+  ///           ...+  ///         }+  ///+  ///     Use `Store.scope` with`IfLetStore` or `Store.ifLet` to ensure that views can only send+  ///     child actions when the child domain is non-`nil`.+  ///+  ///         IfLetStore(+  ///           self.parentStore.scope(state: { $0.child }, action: { .child($0) }+  ///         ) { childStore in+  ///           // This destination only appears when child state is non-`nil`+  ///           WithViewStore(childStore) { childViewStore in+  ///             // So this action can only be sent when child state is non-`nil`+  ///             Button("Child Action") { childViewStore.send(.action) }+  ///           }+  ///           ...+  ///         }   ///   /// - See also: `IfLetStore`, a SwiftUI helper for transforming a store on optional state into a   ///   store on non-optional state.   /// - See also: `Store.ifLet`, a UIKit helper for doing imperative work with a store on optional   ///   state.-  public func optional(_ file: StaticString = #file, _ line: UInt = #line) -> Reducer<+  ///+  /// - Parameter breakpointOnNil: Raises `SIGTRAP` signal when an action is sent to the reducer+  ///   but state is `nil`. This is generally considered a logic error, as a child reducer cannot+  ///   process a child action for unavailable child state.+  /// - Returns: A reducer that works on optional state.+  public func optional(+    breakpointOnNil: Bool = true,+    _ file: StaticString = #file,+    _ line: UInt = #line+  ) -> Reducer<     State?, Action, Environment   > {     .init { state, action, environment in       guard state != nil else {-        assertionFailure(-          """-          "\(debugCaseOutput(action))" was received by an optional reducer when its state was \-          "nil". This can happen for a few reasons:--          * The optional reducer was combined with or run from another reducer that set \-          "\(State.self)" to "nil" before the optional reducer ran. Combine or run optional \-          reducers before reducers that can set their state to "nil". This ensures that optional \-          reducers can handle their actions while their state is still non-"nil".--          * An active effect emitted this action while state was "nil". Make sure that effects for \-          this optional reducer are canceled when optional state is set to "nil".--          * This action was sent to the store while state was "nil". Make sure that actions for \-          this reducer can only be sent to a view store when state is non-"nil". In SwiftUI \-          applications, use "IfLetStore".-          """,-          file: file,-          line: line-        )+        #if DEBUG+          if breakpointOnNil {+            fputs(

Using fputs to print to stderr.

stephencelis

comment created time in a month

PullRequestReviewEvent

Pull request review commentpointfreeco/swift-composable-architecture

Breakpoint instead of assert in optional/forEach

 public struct Reducer<State, Action, Environment> {   ///       }   ///     )   ///-  /// Take care when combining optional reducers into parent domains, as order matters. Always-  /// combine optional reducers _before_ parent reducers that can `nil` out the associated optional-  /// state.+  /// Take care when combining optional reducers into parent domains. An optional reducer cannot+  /// process actions in its domain when its state is `nil`. If a child action is sent to an+  /// optional reducer when child state is `nil`, it is considered a logic error. There are a few+  /// ways in which these errors can sneak into a code base:+  ///+  ///   * A parent reducer sets child state to `nil` when processing a child action and runs+  ///     _before_ the child reducer:+  ///+  ///         let parentReducer = Reducer<ParentState, ParentAction, ParentEnvironment>.combine(+  ///           // When combining reducers, the parent reducer runs first+  ///           Reducer { state, action, environment in+  ///             switch action {+  ///             case .child(.didDisappear):+  ///               // And `nil`s out child state when processing a child action+  ///               state.child = nil+  ///               return .none+  ///             ...+  ///             }+  ///           },+  ///           // Before the child reducer runs+  ///           childReducer.optional().pullback(...)+  ///         )+  ///+  ///         let childReducer = Reducer<+  ///           ChildState, ChildAction, ChildEnvironment+  ///         > { state, action environment in+  ///           case .didDisappear:+  ///             // This action is never received here because child state is `nil` in the parent+  ///           ...+  ///         }+  ///+  ///     To ensure that a child reducer can process any action that a parent may use to `nil` out+  ///     its state, combine it _before_ the parent:+  ///+  ///         let parentReducer = Reducer<ParentState, ParentAction, ParentEnvironment>.combine(+  ///           // The child runs first+  ///           childReducer.optional().pullback(...),+  ///           // The parent runs after+  ///           Reducer { state, action, environment in+  ///             ...+  ///           }+  ///         )+  ///+  ///   * A child effect feeds a child action back into the store when child state is `nil`:+  ///+  ///         let childReducer = Reducer<+  ///           ChildState, ChildAction, ChildEnvironment+  ///         > { state, action environment in+  ///           switch action {+  ///           case .onAppear:+  ///             // An effect may want to feed its result back to the child domain in an action+  ///             return environment.apiClient+  ///               .request()+  ///               .map(ChildAction.response)+  ///+  ///           case let .response(response):+  ///             // But the child cannot process this action if its state is `nil` in the parent+  ///           ...+  ///           }+  ///         }+  ///+  ///     It is perfectly reasonable to ignore the result of an effect when child state is `nil`,+  ///     for example one-off effects that you don't want to cancel. However, many long-living+  ///     effects _should_ be explicitly canceled when tearing down a child domain:+  ///+  ///         let childReducer = Reducer<+  ///           ChildState, ChildAction, ChildEnvironment+  ///         > { state, action environment in+  ///           struct MotionId: Hashable {}+  ///+  ///           switch action {+  ///           case .onAppear:+  ///             // Mark long-living effects that shouldn't outlive their domain cancellable+  ///             return environment.motionClient+  ///               .start()+  ///               .map(ChildAction.motion)+  ///               .cancellable(id: MotionId())+  ///+  ///           case .onDisappear:+  ///             // And explicitly cancel them when the domain is torn down+  ///             return .cancel(id: MotionId())+  ///           ...+  ///           }+  ///         }+  ///+  ///   * A view store sends a child action when child state is `nil`:+  ///+  ///         WithViewStore(self.parentStore) { parentViewStore in+  ///           // If child state is `nil`, it cannot process this action.+  ///           Button("Child Action") { parentViewStore.send(.child(.action)) }+  ///           ...+  ///         }+  ///+  ///     Use `Store.scope` with`IfLetStore` or `Store.ifLet` to ensure that views can only send+  ///     child actions when the child domain is non-`nil`.+  ///+  ///         IfLetStore(+  ///           self.parentStore.scope(state: { $0.child }, action: { .child($0) }+  ///         ) { childStore in+  ///           // This destination only appears when child state is non-`nil`+  ///           WithViewStore(childStore) { childViewStore in+  ///             // So this action can only be sent when child state is non-`nil`+  ///             Button("Child Action") { childViewStore.send(.action) }+  ///           }+  ///           ...+  ///         }   ///   /// - See also: `IfLetStore`, a SwiftUI helper for transforming a store on optional state into a   ///   store on non-optional state.   /// - See also: `Store.ifLet`, a UIKit helper for doing imperative work with a store on optional   ///   state.-  public func optional(_ file: StaticString = #file, _ line: UInt = #line) -> Reducer<+  ///+  /// - Parameter breakpointOnNil: Raises `SIGTRAP` signal when an action is sent to the reducer+  ///   but state is `nil`. This is generally considered a logic error, as a child reducer cannot+  ///   process a child action for unavailable child state.+  /// - Returns: A reducer that works on optional state.+  public func optional(+    breakpointOnNil: Bool = true,+    _ file: StaticString = #file,+    _ line: UInt = #line+  ) -> Reducer<     State?, Action, Environment   > {     .init { state, action, environment in       guard state != nil else {-        assertionFailure(-          """-          "\(debugCaseOutput(action))" was received by an optional reducer when its state was \-          "nil". This can happen for a few reasons:--          * The optional reducer was combined with or run from another reducer that set \-          "\(State.self)" to "nil" before the optional reducer ran. Combine or run optional \-          reducers before reducers that can set their state to "nil". This ensures that optional \-          reducers can handle their actions while their state is still non-"nil".--          * An active effect emitted this action while state was "nil". Make sure that effects for \-          this optional reducer are canceled when optional state is set to "nil".--          * This action was sent to the store while state was "nil". Make sure that actions for \-          this reducer can only be sent to a view store when state is non-"nil". In SwiftUI \-          applications, use "IfLetStore".-          """,-          file: file,-          line: line-        )+        #if DEBUG

Won't check for breakpoints on release builds.

stephencelis

comment created time in a month

PullRequestReviewEvent

Pull request review commentpointfreeco/swift-composable-architecture

Breakpoint instead of assert in optional/forEach

 public struct Reducer<State, Action, Environment> {   ///       }   ///     )   ///-  /// Take care when combining optional reducers into parent domains, as order matters. Always-  /// combine optional reducers _before_ parent reducers that can `nil` out the associated optional-  /// state.+  /// Take care when combining optional reducers into parent domains. An optional reducer cannot+  /// process actions in its domain when its state is `nil`. If a child action is sent to an+  /// optional reducer when child state is `nil`, it is considered a logic error. There are a few+  /// ways in which these errors can sneak into a code base:+  ///+  ///   * A parent reducer sets child state to `nil` when processing a child action and runs+  ///     _before_ the child reducer:+  ///+  ///         let parentReducer = Reducer<ParentState, ParentAction, ParentEnvironment>.combine(+  ///           // When combining reducers, the parent reducer runs first+  ///           Reducer { state, action, environment in+  ///             switch action {+  ///             case .child(.didDisappear):+  ///               // And `nil`s out child state when processing a child action+  ///               state.child = nil+  ///               return .none+  ///             ...+  ///             }+  ///           },+  ///           // Before the child reducer runs+  ///           childReducer.optional().pullback(...)+  ///         )+  ///+  ///         let childReducer = Reducer<+  ///           ChildState, ChildAction, ChildEnvironment+  ///         > { state, action environment in+  ///           case .didDisappear:+  ///             // This action is never received here because child state is `nil` in the parent+  ///           ...+  ///         }+  ///+  ///     To ensure that a child reducer can process any action that a parent may use to `nil` out+  ///     its state, combine it _before_ the parent:+  ///+  ///         let parentReducer = Reducer<ParentState, ParentAction, ParentEnvironment>.combine(+  ///           // The child runs first+  ///           childReducer.optional().pullback(...),+  ///           // The parent runs after+  ///           Reducer { state, action, environment in+  ///             ...+  ///           }+  ///         )+  ///+  ///   * A child effect feeds a child action back into the store when child state is `nil`:+  ///+  ///         let childReducer = Reducer<+  ///           ChildState, ChildAction, ChildEnvironment+  ///         > { state, action environment in+  ///           switch action {+  ///           case .onAppear:+  ///             // An effect may want to feed its result back to the child domain in an action+  ///             return environment.apiClient+  ///               .request()+  ///               .map(ChildAction.response)+  ///+  ///           case let .response(response):+  ///             // But the child cannot process this action if its state is `nil` in the parent+  ///           ...+  ///           }+  ///         }+  ///+  ///     It is perfectly reasonable to ignore the result of an effect when child state is `nil`,+  ///     for example one-off effects that you don't want to cancel. However, many long-living+  ///     effects _should_ be explicitly canceled when tearing down a child domain:+  ///+  ///         let childReducer = Reducer<+  ///           ChildState, ChildAction, ChildEnvironment+  ///         > { state, action environment in+  ///           struct MotionId: Hashable {}+  ///+  ///           switch action {+  ///           case .onAppear:+  ///             // Mark long-living effects that shouldn't outlive their domain cancellable+  ///             return environment.motionClient+  ///               .start()+  ///               .map(ChildAction.motion)+  ///               .cancellable(id: MotionId())+  ///+  ///           case .onDisappear:+  ///             // And explicitly cancel them when the domain is torn down+  ///             return .cancel(id: MotionId())+  ///           ...+  ///           }+  ///         }+  ///+  ///   * A view store sends a child action when child state is `nil`:+  ///+  ///         WithViewStore(self.parentStore) { parentViewStore in+  ///           // If child state is `nil`, it cannot process this action.+  ///           Button("Child Action") { parentViewStore.send(.child(.action)) }+  ///           ...+  ///         }+  ///+  ///     Use `Store.scope` with`IfLetStore` or `Store.ifLet` to ensure that views can only send+  ///     child actions when the child domain is non-`nil`.+  ///+  ///         IfLetStore(+  ///           self.parentStore.scope(state: { $0.child }, action: { .child($0) }+  ///         ) { childStore in+  ///           // This destination only appears when child state is non-`nil`+  ///           WithViewStore(childStore) { childViewStore in+  ///             // So this action can only be sent when child state is non-`nil`+  ///             Button("Child Action") { childViewStore.send(.action) }+  ///           }+  ///           ...+  ///         }   ///   /// - See also: `IfLetStore`, a SwiftUI helper for transforming a store on optional state into a   ///   store on non-optional state.   /// - See also: `Store.ifLet`, a UIKit helper for doing imperative work with a store on optional   ///   state.-  public func optional(_ file: StaticString = #file, _ line: UInt = #line) -> Reducer<+  ///+  /// - Parameter breakpointOnNil: Raises `SIGTRAP` signal when an action is sent to the reducer+  ///   but state is `nil`. This is generally considered a logic error, as a child reducer cannot+  ///   process a child action for unavailable child state.+  /// - Returns: A reducer that works on optional state.+  public func optional(+    breakpointOnNil: Bool = true,+    _ file: StaticString = #file,+    _ line: UInt = #line+  ) -> Reducer<     State?, Action, Environment   > {     .init { state, action, environment in       guard state != nil else {-        assertionFailure(-          """-          "\(debugCaseOutput(action))" was received by an optional reducer when its state was \-          "nil". This can happen for a few reasons:--          * The optional reducer was combined with or run from another reducer that set \-          "\(State.self)" to "nil" before the optional reducer ran. Combine or run optional \-          reducers before reducers that can set their state to "nil". This ensures that optional \-          reducers can handle their actions while their state is still non-"nil".--          * An active effect emitted this action while state was "nil". Make sure that effects for \-          this optional reducer are canceled when optional state is set to "nil".--          * This action was sent to the store while state was "nil". Make sure that actions for \-          this reducer can only be sent to a view store when state is non-"nil". In SwiftUI \-          applications, use "IfLetStore".-          """,-          file: file,-          line: line-        )+        #if DEBUG+          if breakpointOnNil {+            fputs(+              """+              ---+              Warning: Reducer.optional@\(file):\(line))++              "\(debugCaseOutput(action))" was received by an optional reducer when its state was \+              "nil". This is generally considered an application logic error, and can happen for a \

Qualified this with "generally" everywhere now and note that it is reasonable to ignore the result of some effects in a bullet point below.

stephencelis

comment created time in a month

PullRequestReviewEvent

push eventpointfreeco/swift-composable-architecture

Stephen Celis

commit sha d81bd827a6a203372ac3864cc52d1e4d24769cba

doc

view details

push time in a month

Pull request review commentpointfreeco/swift-composable-architecture

Breakpoint instead of assert in optional/forEach

 public struct Reducer<State, Action, Environment> {   /// Transforms a reducer that works on non-optional state into one that works on optional state by   /// only running the non-optional reducer when state is non-nil.   ///-  /// Often used in tandem with `pullback` to transform a reducer on a non-optional local domain-  /// into a reducer that can be combined with a reducer on a global domain that contains some-  /// optional local domain:+  /// Often used in tandem with `pullback` to transform a reducer on a non-optional child domain+  /// into a reducer that can be combined with a reducer on a parent domain that contains some+  /// optional child domain:

Any thoughts on updating our terminology from global-local to parent-child? It read better to me here. Global alone sounds like app-level domain, so you need to qualify as "more global" or "more local"...

stephencelis

comment created time in a month

PullRequestReviewEvent
PullRequestReviewEvent

Pull request review commentpointfreeco/swift-composable-architecture

Breakpoint instead of assert in optional/forEach

 import Combine /// /// Reducers have 3 generics: ///-/// * `State`: A type that holds the current state of the application-/// * `Action`: A type that holds all possible actions that cause the state of the application to-///   change.-/// * `Environment`: A type that holds all dependencies needed in order to produce `Effect`s, such-///   as API clients, analytics clients, random number generators, etc.+///   * `State`: A type that holds the current state of the application.

Just being consistent here. I added some code examples embedded in bullet points later on in this PR, which requires an indentation level of 8, so it looks best to make sure the text of bullets are indented by 4.

stephencelis

comment created time in a month

PR opened pointfreeco/swift-composable-architecture

Reviewers
Breakpoint instead of assert in optional/forEach

Based on feedback and earlier discussion.

+305 -137

0 comment

3 changed files

pr created time in a month

create barnchpointfreeco/swift-composable-architecture

branch : dont-assert-signal

created branch time in a month

push eventpointfreeco/episode-code-samples

Point-Free

commit sha 08c2fdd034ce21fa38abcde82b4547d4495c78db

wip

view details

push time in a month

push eventpointfreeco/episode-code-samples

Point-Free

commit sha 3a799be8479f01eea1906058647bcb0205818a4b

wip

view details

push time in a month

push eventpointfreeco/episode-code-samples

Stephen Celis

commit sha 42f7a34e73aec400ce78327d8253f9199b933f5c

prep

view details

push time in a month

issue closedpointfreeco/swift-composable-architecture

TicTacToe Crash When Interrupting 2FA Login

Describe the bug When running the TicTacToe application, it is possible to interrupt a long lived side effect on the Two Factor Authentication screen, which results in a crash due to an assertion: Fatal error: "LoginAction.twoFactorDismissed" was received by an optional reducer when its state was "nil".

To Reproduce

  • Run TicTacToe example project
  • Select SwiftUI (though the same bug affects the UIKit version)
  • Login with an email containing 2fa
  • Enter 1234 code
  • Tap Submit
  • Before request completes, press "< Login" in the top left corner
  • App crashes due to fatal error

Expected behavior App should return to login screen without crashing

Environment

  • Xcode 11.7

Additional context This sounds similar to #259 , but isn't quite the same error. This also sounds similar to the issue discussed in the forums here, and might be able to be fixed by the solution described there.

This issue covers one of the biggest unknowns I have with this architecture - you're incentivized to break up your code into reducers that focus on a particular task and child reducers don't need to know about their parents, however if the parent has an optional child, then that child needs to expose some way of cancelling all active effects so that the parent can clean up after them (which, as a parent, that is pretty realistic but not super convenient).

It'd be nice to see a canonical example of handling an optional child with long lived side effects that could get interrupted/cancelled. And if explicitly sending a "cancel" action to the child is the expected process, is there a better way to scale that? For example, if you have an optional child which itself has optional children with side effects that need to be canceled, it gets complicated.

closed time in a month

nmccann

issue commentpointfreeco/swift-composable-architecture

TicTacToe Crash When Interrupting 2FA Login

I think we can probably close it out. If we want to track a general solution we could open another issue. Feel free to in order to pester us if we lose track 😄

nmccann

comment created time in a month

issue commentpointfreeco/swift-composable-architecture

[Breaking change, 0.8.0] AlertState expects LocalizedStringKey instead of String values

@ohitsdaniel Any chance you can use LocalizedStringKey.init(verbatim:) for this? We can definitely consider the overloads in the future, but this should get things working for you in the meantime.

ohitsdaniel

comment created time in a month

push eventpointfreeco/episode-code-samples

Stephen Celis

commit sha ca28cd3b9b7738859230d7733650ac42e91bc660

118

view details

push time in a month

create barnchpointfreeco/pointfreeco

branch : preserve-coupons

created branch time in a month

pull request commentpointfreeco/pointfreeco

Fixes small typos and compilation issue in 117.1.

Ah sorry @jasdev! Missed this PR 😅 Thanks for taking the time, though!

jasdev

comment created time in a month

issue commentpointfreeco/swift-composable-architecture

TicTacToe Crash When Interrupting 2FA Login

Fixed the crash but will continue to explore more solutions to this common problem. We have a few ideas but wanna test them out to figure out and better understand the tools we should provide and their trade-offs.

nmccann

comment created time in a month

more