Snapshot Storage for WidgetKit
How the app stores display-ready values in an App Group snapshot for WidgetKit to read first
A Hangangjari widget runs separately from the app and refreshes on a different cadence. Because the WidgetKit extension cannot share the app’s memory, widget-ready values are stored in an App Group snapshot that the extension can read directly.
Display Values Stored for the Widget
The WidgetKit extension first reads display values stored in the App Group and tries the network when needed.
The Hangangjari widget had to:
- Show recent values without opening the app.
- Keep the last confirmed value when the network fails.
- Provide purpose-specific information for parking and general park widgets.
- Deep link to the relevant park and screen when tapped.
- Mark stale information with its state.
The extension cannot use the main app’s memory at its independent execution time. An App Group snapshot provides shared storage between the app and widget.
flowchart LR App["Main app"] --> Repo["Repository"] Repo --> SwiftData["SwiftData cache"] App --> Builder["Snapshot builder"] Builder --> AppGroup["App Group snapshot"] Widget["WidgetKit extension"] --> Loader["Widget entry loader"] Loader --> AppGroup Loader --> API["/v2 home-summary<br/>state fallback"] Loader --> Entry["Widget display entry"]
Display Values Kept in the Snapshot
A snapshot is a small bundle containing only the values the widget displays.
A parking snapshot contains values such as park ID, short park name, parking lot list, remaining spaces, capacity, availability level, confirmation time, stale cutoff, and directions URL. A general park snapshot contains crowding, weather, fine dust, key signals, and forecast movement.
A parking widget and a general widget display different values for the same park, so snapshot keys are separated by purpose.
| Snapshot | Question | Storage unit |
|---|---|---|
| Parking snapshot | Is it okay to drive there now? | Park + display mode |
| General snapshot | Is it okay to visit this park today? | Park + general focus |
| Legacy snapshot | Backward compatibility | Single parking snapshot |
Purpose-specific keys prevent one widget configuration from overwriting another snapshot. Snapshot fields also follow the decision each widget supports: “Can I bring the car?” for parking and “Is this park okay today?” for a general park visit.
Stored Values Before the Network
The widget entry loader follows this order.
sequenceDiagram
autonumber
participant Widget as Widget provider
participant Loader as Widget entry loader
participant Snapshot as App Group snapshot
participant API as Hangangjari API
participant Builder as Snapshot builder
Widget->>Loader: Request timeline value
Loader->>Snapshot: Read stored value for this purpose
alt Snapshot is fresh and refresh is not forced
Snapshot-->>Loader: Return usable stored value
Loader-->>Widget: Return widget entry from stored value
else Stale or missing
Loader->>API: Request first-screen or status data
API-->>Loader: Return display read model
Loader->>Builder: Build widget display values
Builder-->>Snapshot: Store display values in shared area
Loader-->>Widget: Return widget entry from new response
end
The snapshot storage time and the inner data’s observed_at represent different forms of freshness.
- Snapshot freshness: should the widget entry be rebuilt?
- Data freshness: is the displayed data recent enough to call current?
Even if a widget opens quickly from a cache hit, the UI must show stale state if the inner parking value is stale.
Separate Roles for SwiftData and App Group
SwiftData is the app’s internal read cache. It lets app screens reuse parks, lots, statuses, forecasts, and home summaries.
The App Group snapshot is a small display-value bundle shared with widgets. Because the extension must be able to read it reliably, it stores only the values needed on screen as a Codable payload.
This role boundary isolates widgets from changes to the app’s internal data. To avoid duplicating the app cache, a snapshot is limited to the final display ingredients used to build widget entries.
Failure and Fallback States
The widget chooses a last-confirmed value, unavailable state, or placeholder according to the failure state.
| Situation | Display behavior |
|---|---|
| Fresh snapshot exists | Build entry from snapshot |
| Snapshot exists but is stale | Try network, then fall back with stale presentation if it fails |
| No snapshot exists | Try API, then show unavailable or placeholder if it fails |
| Nearby park selection cannot access location | Show unavailable with a location reason |
| API payload park mismatch | Treat as empty data |
The widget displays “no information,” “last confirmed information,” and “just refreshed information” as separate states. A user can see stale status even when acting on a single number.
Freshness Displayed on a Small Screen
The App Group snapshot is a display format shared by the app and WidgetKit extension. Parking and general park widgets keep question-specific bundles, and snapshot storage time is evaluated separately from the inner data’s observed_at. Fallback distinguishes the last confirmed value from unavailable information, while the snapshot stores only values needed for display and deep links.
Comments
No comments yet. Be the first to leave one.
Pending review