Publish state
Publish Swift values to a Flick and accept changes through Swift setters.
Publish an item list, let a Flick select an item, and keep the selection in Swift. These examples use the SDK setup and Flick control from Quick start and build on the same ItemsView throughout.
Publish an item list
The native screen owns a list of items; the Flick displays their titles.
import FlickSDK
// Equatable detects changes; FlickObjectValue exposes these fields to React
struct Item: Equatable, FlickObjectValue {
let id: String
let title: String
}
@FlickScope(url: "https://acme-corp.com")
enum MyApp {
enum State {
case items([Item]) // React receives an array of { id: string, title: string }
}
}Publish the list from the SwiftUI view that owns it:
import FlickSDK
import SwiftUI
struct ItemsView: View {
@State var items = [
Item(id: "notes", title: "Notes"),
Item(id: "tasks", title: "Tasks"),
]
var body: some View {
Button("Reverse item order") { items.reverse() }
.observeForFlick(items, for: \.items) {
Flick.Publish($0) // Publish the current [Item], then its changes
}
}
}Mount ItemsView in the Quick start’s FlickSDK hierarchy. In a Flick opened there, read the list with useContextObservation:
import { Flex, Text, useContextObservation } from '@flickfyi/core'
import { MyApp } from 'com.acme.myapp'
export default function ItemList() {
const items = useContextObservation(MyApp.states.items) // ReadonlyArray<Item>
return (
<Flex>
{items.map(item => <Text key={item.id}>{item.title}</Text>)}
</Flex>
)
}The Flick initially shows Notes, then Tasks. Pressing the SwiftUI button reverses their order in the Flick. The generated Item has readonly id and title string properties.
Let the Flick select an item
Add selectedItemID to MyApp.State:
@FlickScope(url: "https://acme-corp.com")
enum MyApp {
enum State {
case items([Item])
case selectedItemID(String?) // Swift nil becomes React null
}
}Add the selection beside items in ItemsView:
@State var selectedItemID: String? = nilAppend this modifier after the existing items observation:
.observeForFlick(selectedItemID, for: \.selectedItemID, set: { selectedItemID = $0 }) {
Flick.Publish($0)
}set: receives the value requested by the Flick. Adding it makes this state writable, so use useContextState to read the selection and get its setter:
import { Button, Flex, Text, useContextObservation, useContextState } from '@flickfyi/core'
import { MyApp } from 'com.acme.myapp'
export default function ItemPicker() {
const items = useContextObservation(MyApp.states.items) // ReadonlyArray<Item>
const [selectedItemID, setSelectedItemID] = useContextState(MyApp.states.selectedItemID)
// selectedItemID: string | null; setSelectedItemID accepts the same type
return (
<Flex>
<Text>Selected: {selectedItemID ?? 'None'}</Text>
{items.map(item => (
<Button key={item.id} onClick={() => setSelectedItemID(item.id)}>
{item.title}
</Button>
))}
<Button onClick={() => setSelectedItemID(null)}>Clear selection</Button>
</Flex>
)
}Clicking Notes sends "notes" to the Swift setter. Swift stores it and publishes it back; the Flick then displays Selected: notes. Clear selection sends null, which Swift receives as nil.
Publish a different type from the one Swift stores
To store the selected Item in Swift, replace the selection property and observation below. case selectedItemID(String?) and the React ItemPicker stay the same.
@State var selectedItem: Item? = nil.observeForFlick(selectedItem, for: \.selectedItemID, set: { id in
// React sends "notes"; Swift finds and stores the corresponding Item
selectedItem = items.first { $0.id == id }
}) {
Flick.Publish($0?.id) // Swift Item? → published String? → React string | null
}The set: input and Flick.Publish output both use the state case’s String? type. Swift resolves the requested ID against its own items; null or an unknown ID clears the selection. Use an action when a request needs its own result or typed failure.
Handle unavailable state
A Flick opened elsewhere in MyApp can run while ItemsView is absent. Its selection state is then unavailable. Pass { optional: true } to show a fallback instead of throwing:
import { Button, Flex, Text, useContextState } from '@flickfyi/core'
import { MyApp } from 'com.acme.myapp'
export default function SelectionSummary() {
const selection = useContextState(MyApp.states.selectedItemID, { optional: true })
// The whole tuple is undefined when ItemsView is not mounted
if (selection === undefined) return <Text>Open the items screen</Text>
const [selectedItemID, setSelectedItemID] = selection
return (
<Flex>
<Text>Selected: {selectedItemID ?? 'None'}</Text>
<Button onClick={() => setSelectedItemID(null)}>Clear selection</Button>
</Flex>
)
}undefined means unavailable; null is a published value. The hook updates as the screen mounts, changes, or unmounts. useContextObservation also accepts { optional: true }, returning undefined when unavailable. Runtime failures still throw in either mode.