Context hooks
Call Swift actions, observe app state, and request changes from a Flick.
Use context hooks to call your app’s Swift actions and read or update its published state. Flicks opened through the Flick control receive the APIs allowed for their selected scope and declared capabilities.
Named views mounted with Flick(.card, title: "Hello") receive their explicit inputs as props. Context hooks access the host registry through a control-launched execution.
| Hook | Use it to | Returns |
|---|---|---|
useContextAction | Call a Swift action | A function with the action’s input and output types |
useContextObservation | Read a value published by Swift | The latest value |
useContextState | Read and update a value with a Swift setter | [value, setValue] |
These examples use MyApp from the Swift quick start. Its generated package, com.acme.myapp, gives TypeScript the types declared in Swift. Pass its descriptors, such as MyApp.states.count, to the matching hook.
useContextAction
Get a function that calls a Swift action. This button invokes the quick start’s increment handler:
import { Button, useContextAction } from '@flickfyi/core'
import { Effect } from 'effect'
// Generated from MyApp's Swift declarations by the SDK's parser
// flkr links this bundle-ID package to ../generated
import { MyApp } from 'com.acme.myapp'
export default () => {
// Swift: @FlickAction(failure: Never.self) case increment
// Returns () => Effect.Effect<void, never>
const increment = useContextAction(MyApp.actions.increment)
return (
<Button onClick={() => {
// increment() creates the Effect; runPromise executes the Swift handler
return Effect.runPromise(increment())
}}>
Increment
</Button>
)
}The action’s generated type determines its arguments and result. This action takes no input and returns an Effect that completes with void.
Effect.runPromise returns a Promise for that completion.
For typed inputs, results, failures, and streamed actions, see Expose actions. The hook preserves those types; execution depends on whether the action returns an Effect or Stream.
useContextObservation
Read a value the app publishes without a setter. The quick start’s case count(Int) becomes a TypeScript number:
import { Text, useContextObservation } from '@flickfyi/core'
import { MyApp } from 'com.acme.myapp'
export default () => {
// Swift publishes count through observeForFlick
const count = useContextObservation(MyApp.states.count)
return <Text>Count: {count}</Text>
}The hook returns the latest published value and rerenders the component when it changes. It subscribes while your component is mounted and unsubscribes when it unmounts.
useContextState
Read a value and request updates through the app’s Swift setter. Add set: to the quick start’s count registration:
// In your SwiftUI view, with @State var count = 0
// Other view modifiers…
.observeForFlick(count, for: \.count, set: { count = $0 }) {
Flick.Publish($0)
}import { Button, Flex, Text, useContextState } from '@flickfyi/core'
import { MyApp } from 'com.acme.myapp'
export default () => {
// Swift supplies both the published count and its setter
// For read access alone: const [count] = useContextState(MyApp.states.count)
const [count, setCount] = useContextState(MyApp.states.count)
return (
<Flex>
<Text>Count: {count}</Text>
<Button onClick={() => setCount(current => current + 1)}>
Increment
</Button>
<Button onClick={() => setCount(0)}>Reset</Button>
</Flex>
)
}The setter accepts a value (setCount(0)) or an updater (setCount(current => current + 1)) and returns void.
Swift handles each request and republishes its value. React displays that published value, including any validation or transformation the app applies.
See Publish state for more Swift setter examples, including selecting an item and transforming the published value.
The generator distinguishes read-only and writable state by the Swift registration. TypeScript rejects useContextState for a read-only value and useContextObservation for a writable one.
Handle unavailable context
Actions and state are available while their Swift registrations are mounted and exposed to the Flick. Required access is the default: an unavailable API throws Flick context is not mounted.
Pass { optional: true } when your component can render without that API. The hook returns undefined while it is unavailable and updates when availability changes:
import { Button, Flex, Text, useContextState } from '@flickfyi/core'
import { MyApp } from 'com.acme.myapp'
export default () => {
const counter = useContextState(MyApp.states.count, { optional: true })
// Call hooks before conditional returns
if (counter === undefined) {
return <Text>Open the counter in your app</Text>
}
// The whole tuple is undefined until the state is available
const [count, setCount] = counter
return (
<Flex>
<Text>Count: {count}</Text>
<Button onClick={() => setCount(current => current + 1)}>
Increment
</Button>
</Flex>
)
}All three context hooks accept this option:
| Hook | Result while unavailable |
|---|---|
useContextAction(action, { optional: true }) | undefined instead of the callable function |
useContextObservation(value, { optional: true }) | undefined instead of the published value |
useContextState(value, { optional: true }) | undefined instead of the [value, setter] tuple |
Runtime failures still throw with optional: true. Calling an action or setter after its Swift registration unmounts also throws. Action failures follow the action’s generated Effect or Stream type.
A published Swift optional maps to null when empty. undefined means the API itself is unavailable.
Component state and view inputs
Use React’s useState for values owned by your component, such as an expanded row or an input draft. Context hooks read and write values owned by the host app. Named-view inputs arrive as component props.
Call hooks at the top level of a component or custom hook, following the React rules of hooks. Invoke actions and setters from event handlers.