Add state management templates#64
Open
ssestak wants to merge 6 commits into
Open
Conversation
added 5 commits
May 12, 2026 13:10
Add CacheProjection, ComponentState, ItemState, ArrayState and their corresponding SwiftUI views to the SwiftUI App template. Update the Example scene to demonstrate the full CacheProjection data flow with @observable computed properties. Add "Projection Scene" variant to the Scene template that scaffolds a Component, ComponentModel and CacheProjection stub.
Merge ErrorViewConfig and the missing EmptyViewConfig into a single StateInfoConfig carried inside ComponentState's empty/error cases. The config requires a config wherever the state is non-populated, which prevents the EmptyView() placeholder anti-pattern observed in production. StateInfoView is the baseline renderer for empty and error; a TODO asks each project to swap it for its design system. ComponentStateView keeps four generics but adds seven extension inits so callers can default empty/error to StateInfoView and only customize what they need. ItemStateView gains equivalent convenience inits. Drop ArrayStateView — production evidence (jt-assets) shows ArrayState is always converted to ItemState via a computed property on the Data struct, so a dedicated array view is dead weight. Use @ProxyMembers from futured-macros instead of the hand-rolled subscript(dynamicMember:) on cache projections. CacheModel is now inferred from the data(from:) signature; the default nil data(for:from:) is constrained to where ID == Void so projections with a real ID are forced to implement it. Extract the Example component's content into a private computed view and clarify the observation comment on the computed projection.
In Swift 6 default MainActor isolation, value types default to MainActor unless annotated otherwise. DataCacheModel must stay nonisolated (it crosses the DataCache boundary), so every type it transitively equates against also needs to be nonisolated — otherwise the synthesized Equatable conformance fails with "Main actor-isolated conformance cannot be used in nonisolated context". ExampleItem moves out of the extension so it sits alongside DataCacheModel at file scope with explicit nonisolated. Restore @dynamicMemberLookup on cache projections so @ProxyMembers can generate its subscript.
Without UILaunchScreen (or UILaunchStoryboardName) iOS runs the app in legacy compatibility mode and the UI is letterboxed instead of filling the screen on modern devices. A compile-time #warning in App.swift surfaces the requirement in the build log.
6 tasks
ArrayState was an unused state wrapper — render arrays via the CacheProjection -> ItemState -> ItemStateView pipeline instead, which keeps filtering and sorting out of views.
jmarek41
approved these changes
May 15, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Extends the SwiftUI App Xcode template with a reusable state-management layer (loading / empty / error / ready) and adds a new Projection Scene variant to the Scene template. Existing example files are updated to demonstrate the cache-projection pattern end-to-end. Also adds a build-time warning to remind developers to set
UILaunchScreeninInfo.plist.Architecture
flowchart LR subgraph Model["Model layer (new)"] CS[ComponentState] IS[ItemState] AS[ArrayState] SIC[StateInfoConfig] CP[CacheProjection] MK[Mockable] end subgraph Views["UI layer (new)"] SIV[StateInfoView] CSV[ComponentStateView] ISV[ItemStateView] end subgraph Scene["Example scene (updated)"] ECM[ExampleComponentModel] ECP[ExampleCacheProjection] EC[ExampleComponent] end DC[(DataCache<DataCacheModel>)] --> ECM ECM -->|projection| ECP ECP -->|state| CS EC -->|wraps content| CSV CSV --> CS CSV -.uses.-> SIV ISV --> IS SIV --> SIC CS -.config.-> SIC IS -.config.-> SIC AS -.config.-> SIC ECP -.conforms.-> CPKey Changes
ComponentState,ItemState,ArrayStateenums for screen / item / collection lifecycle, plusStateInfoConfigand a unifiedStateInfoViewfor empty/error presentation.ComponentStateViewandItemStateViewwrap content and switch on state.CacheProjectionprotocol with concreteExampleCacheProjection; the exampleComponentModelexposes a computedprojectionproperty that readsdataCache.value, so@Observablere-renders the view on cache changes (no Combine).Component,ComponentModel, andCacheProjectiontogether; adds aprojectionNametemplate option.nonisolatedso they can be used from any actor context.#warningto addUILaunchScreentoInfo.plist, preventing the legacy compatibility-mode pitfall on modern devices.Test Plan
ComponentStateViewrenders loading / empty / error / ready states correctly in previews