Skip to content

Async await support#74

Open
stefanceriu wants to merge 3 commits into
ReactKit:swift/5.0from
stefanceriu:feature/async-await-support
Open

Async await support#74
stefanceriu wants to merge 3 commits into
ReactKit:swift/5.0from
stefanceriu:feature/async-await-support

Conversation

@stefanceriu

Copy link
Copy Markdown

We've been using SwiftState in Element X for quite some time now and we love it so thank you! There are places were we need to call async methods in the handlers though, so I've been toying with the idea of an async verion of the state machine.
This is what I came up with (heavily LLMed) and would love to hear your thoughts on it:

Adds AsyncStateMachine<S, E>, a thin wrapper around StateMachine that lets transition handlers be async. tryEvent and tryState are now async and return only after all matched handlers have finished — no more fire-and-forget Task {} workarounds.
All routing, conditions, orderin g, and state commits are delegated entirely to the wrapped StateMachine. The only addition is that handlers can now await work before the caller continues.

Thread safety is not provided, that's left to the caller and the machine must be driven from a single thread or actor.

Also makes two Package.swift changes:

  • declare platforms (iOS 13 / macOS 10.15 / tvOS 13 / watchOS 6) — required for async support
  • fix test target path: it pointed at Sources(now Tests/SwiftStateTests), which broke swift build because of overlapping sources

The SwiftStateTests test target pointed at `path:"Sources"`, which
overlapped the library target's sources and broke `swift build`,
running zero tests. Point it at `Tests/SwiftStateTests`.
Without an explicit `platforms:` floor, SPM assumes an ancient
deployment target and `async` code fails with "concurrency is only
available in iOS 13.0.0 or newer". Set iOS 13 / macOS 10.15 /
tvOS 13 / watchOS 6.
AsyncStateMachine<S, E> wraps a private StateMachine and exposes
`async` transition handlers plus `async` tryEvent/tryState that return
only after all matching handlers have finished. This lets consumers
await async work as part of a transition instead of fire-and-forget
`Task {}`.

Implemented as a thin wrapper so the existing sync API is unchanged:
each async handler is registered on the wrapped machine as a tiny
synchronous collector that enqueues the matched (handler, context); the
async drive then commits the transition via the wrapped machine (which
performs all routing, conditions, ordering and error matching) and
awaits the queued handlers in order. No routing logic is duplicated.

Conditions and route mappings remain synchronous. AsyncStateMachine is
not re-entrancy/concurrency safe; drive it from a single context.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant