{"id":"33bc1806-df5e-4b32-8e8b-db59e900de8c","shortId":"n4rNCk","kind":"skill","title":"swiftui-patterns","tagline":"Build SwiftUI views with modern MV architecture, state management, and view composition patterns. Covers @Observable ownership rules, @State/@Bindable/@Environment wiring, view decomposition, custom ViewModifiers, environment values, async data loading with .task, iOS 26+ APIs, W","description":"# SwiftUI Patterns\n\nModern SwiftUI patterns targeting iOS 26+ with Swift 6.3. Covers architecture, state management, view composition, environment wiring, async loading, design polish, and platform/share integration. Navigation and layout patterns live in dedicated sibling skills. Patterns are backward-compatible to iOS 17 unless noted.\n\n## Contents\n\n- [Architecture: Model-View (MV) Pattern](#architecture-model-view-mv-pattern)\n- [State Management](#state-management)\n- [View Ordering Convention](#view-ordering-convention)\n- [View Composition](#view-composition)\n- [Environment](#environment)\n- [Async Data Loading](#async-data-loading)\n- [iOS 26+ New APIs](#ios-26-new-apis)\n- [Performance Guidelines](#performance-guidelines)\n- [HIG Alignment](#hig-alignment)\n- [Writing Tools (iOS 18+)](#writing-tools-ios-18)\n- [Common Mistakes](#common-mistakes)\n- [Review Checklist](#review-checklist)\n- [References](#references)\n\n**Scope boundary:** This skill covers architecture, state ownership, composition, environment wiring, async loading, and related SwiftUI app structure patterns. Detailed navigation patterns are covered in the `swiftui-navigation` skill, including `NavigationStack`, `NavigationSplitView`, sheets, tabs, and deep-linking patterns. Detailed layout, container, and component patterns are covered in the `swiftui-layout-components` skill, including stacks, grids, lists, scroll view patterns, forms, controls, search UI with `.searchable`, overlays, and related layout components.\n\n## Architecture: Model-View (MV) Pattern\n\nDefault to MV -- views are lightweight state expressions; models and services own business logic. Do not introduce view models unless the existing code already uses them.\n\n**Core principles:**\n- Favor `@State`, `@Environment`, `@Query`, `.task`, and `.onChange` for orchestration\n- Inject services and shared models via `@Environment`; keep views small and composable\n- Split large views into smaller subviews rather than introducing a view model\n- Test models, services, and business logic; keep views simple and declarative\n\n```swift\nstruct FeedView: View {\n    @Environment(FeedClient.self) private var client\n\n    enum ViewState {\n        case loading, error(String), loaded([Post])\n    }\n\n    @State private var viewState: ViewState = .loading\n\n    var body: some View {\n        List {\n            switch viewState {\n            case .loading:\n                ProgressView()\n            case .error(let message):\n                ContentUnavailableView(\"Error\", systemImage: \"exclamationmark.triangle\",\n                                       description: Text(message))\n            case .loaded(let posts):\n                ForEach(posts) { post in\n                    PostRow(post: post)\n                }\n            }\n        }\n        .task { await loadFeed() }\n        .refreshable { await loadFeed() }\n    }\n\n    private func loadFeed() async {\n        do {\n            let posts = try await client.getFeed()\n            viewState = .loaded(posts)\n        } catch {\n            viewState = .error(error.localizedDescription)\n        }\n    }\n}\n```\n\nFor MV pattern rationale, app wiring, and lightweight client examples, see [references/architecture-patterns.md](references/architecture-patterns.md).\n\n## State Management\n\n### @Observable Ownership Rules\n\n**Important:** Always annotate `@Observable` view model classes with `@MainActor` to ensure UI-bound state is updated on the main thread. Required for Swift 6 concurrency safety.\n\n| Wrapper | When to Use |\n|---------|-------------|\n| `@State` | View owns the object or value. Creates and manages lifecycle. |\n| `let` | View receives an `@Observable` object. Read-only observation -- no wrapper needed. |\n| `@Bindable` | View receives an `@Observable` object and needs two-way bindings (`$property`). |\n| `@Environment(Type.self)` | Access shared `@Observable` object from environment. |\n| `@State` (value types) | View-local simple state: toggles, counters, text field values. Always `private`. |\n| `@Binding` | Two-way connection to parent's `@State` or `@Bindable` property. |\n\n### Ownership Pattern\n\n```swift\n// @Observable view model -- always @MainActor\n@MainActor\n@Observable final class ItemStore {\n    var title = \"\"\n    var items: [Item] = []\n}\n\n// View that OWNS the model\nstruct ParentView: View {\n    @State var viewModel = ItemStore()\n\n    var body: some View {\n        ChildView(store: viewModel)\n            .environment(viewModel)\n    }\n}\n\n// View that READS (no wrapper needed for @Observable)\nstruct ChildView: View {\n    let store: ItemStore\n\n    var body: some View { Text(store.title) }\n}\n\n// View that BINDS (needs two-way access)\nstruct EditView: View {\n    @Bindable var store: ItemStore\n\n    var body: some View {\n        TextField(\"Title\", text: $store.title)\n    }\n}\n\n// View that reads from ENVIRONMENT\nstruct DeepView: View {\n    @Environment(ItemStore.self) var store\n\n    var body: some View {\n        @Bindable var s = store\n        TextField(\"Title\", text: $s.title)\n    }\n}\n```\n\n**Granular tracking:** SwiftUI only re-renders views that read properties that changed. If a view reads `items` but not `isLoading`, changing `isLoading` does not trigger a re-render. This is a major performance advantage over `ObservableObject`.\n\n### Legacy ObservableObject\n\nOnly use if supporting iOS 16 or earlier. `@StateObject` → `@State`, `@ObservedObject` → `let`, `@EnvironmentObject` → `@Environment(Type.self)`.\n\n## View Ordering Convention\n\nOrder members top to bottom: 1) `@Environment` 2) `let` properties 3) `@State` / stored properties 4) computed `var` 5) `init` 6) `body` 7) view builders / helpers 8) async functions\n\n## View Composition\n\n### Extract Subviews\n\nBreak views into focused subviews. Each should have a single responsibility.\n\n```swift\nvar body: some View {\n    VStack {\n        HeaderSection(title: title, isPinned: isPinned)\n        DetailsSection(details: details)\n        ActionsSection(onSave: onSave, onCancel: onCancel)\n    }\n}\n```\n\n### Computed View Properties\n\nKeep related subviews as computed properties in the same file; extract to a standalone `View` struct when reuse is intended or the subview carries its own state.\n\n```swift\nvar body: some View {\n    List {\n        header\n        filters\n        results\n    }\n}\n\nprivate var header: some View {\n    VStack(alignment: .leading, spacing: 6) {\n        Text(title).font(.title2)\n        Text(subtitle).font(.subheadline)\n    }\n}\n```\n\n### ViewBuilder Functions\n\nFor conditional logic that does not warrant a separate struct:\n\n```swift\n@ViewBuilder\nprivate func statusBadge(for status: Status) -> some View {\n    switch status {\n    case .active: Text(\"Active\").foregroundStyle(.green)\n    case .inactive: Text(\"Inactive\").foregroundStyle(.secondary)\n    }\n}\n```\n\n### Custom View Modifiers\n\nExtract repeated styling into `ViewModifier`:\n\n```swift\nstruct CardStyle: ViewModifier {\n    func body(content: Content) -> some View {\n        content\n            .padding()\n            .background(.background)\n            .clipShape(RoundedRectangle(cornerRadius: 12))\n            .shadow(radius: 2)\n    }\n}\nextension View { func cardStyle() -> some View { modifier(CardStyle()) } }\n```\n\n### Stable View Tree\n\nAvoid top-level conditional view swapping. Prefer a single stable base view with conditions inside sections or modifiers. When a view file exceeds ~300 lines, split with extensions and `// MARK: -` comments.\n\n## Environment\n\n### Custom Environment Values\n\n```swift\nprivate struct ThemeKey: EnvironmentKey {\n    static let defaultValue: Theme = .default\n}\n\nextension EnvironmentValues {\n    var theme: Theme {\n        get { self[ThemeKey.self] }\n        set { self[ThemeKey.self] = newValue }\n    }\n}\n\n// Usage\n.environment(\\.theme, customTheme)\n@Environment(\\.theme) var theme\n```\n\n### Common Built-in Environment Values\n\n```swift\n@Environment(\\.dismiss) var dismiss\n@Environment(\\.colorScheme) var colorScheme\n@Environment(\\.dynamicTypeSize) var dynamicTypeSize\n@Environment(\\.horizontalSizeClass) var sizeClass\n@Environment(\\.isSearching) var isSearching\n@Environment(\\.openURL) var openURL\n@Environment(\\.modelContext) var modelContext\n```\n\n## Async Data Loading\n\nAlways use `.task` -- it cancels automatically on view disappear:\n\n```swift\nstruct ItemListView: View {\n    @State var store = ItemStore()\n\n    var body: some View {\n        List(store.items) { item in\n            ItemRow(item: item)\n        }\n        .task { await store.load() }\n        .refreshable { await store.refresh() }\n    }\n}\n```\n\nUse `.task(id:)` to re-run when a dependency changes:\n\n```swift\n.task(id: searchText) {\n    guard !searchText.isEmpty else { return }\n    await search(query: searchText)\n}\n```\n\nNever create manual `Task` in `onAppear` unless you need to store a reference for cancellation. Exception: `Task {}` is acceptable in synchronous action closures (e.g., Button actions) for immediate state updates before async work.\n\n## iOS 26+ New APIs\n\n- **`.scrollEdgeEffectStyle(.soft, for: .top)`** -- fading edge effect on scroll edges\n- **`.backgroundExtensionEffect()`** -- mirror/blur at safe area edges\n- **`@Animatable`** macro -- synthesizes `AnimatableData` conformance automatically (see `swiftui-animation` skill)\n- **`TextEditor`** -- now accepts `AttributedString` for rich text\n\n## Performance Guidelines\n\n- **Lazy stacks/grids:** Use `LazyVStack`, `LazyHStack`, `LazyVGrid`, `LazyHGrid` for large collections. Regular stacks render all children immediately.\n- **Stable IDs:** All items in `List`/`ForEach` must conform to `Identifiable` with stable IDs. Never use array indices.\n- **Avoid body recomputation:** Move filtering and sorting to computed properties or the model, not inline in `body`.\n- **Equatable views:** For complex views that re-render unnecessarily, conform to `Equatable`.\n\n## HIG Alignment\n\nFollow Apple Human Interface Guidelines for layout, typography, color, and accessibility. Key rules:\n\n- Use semantic colors (`Color.primary`, `.secondary`, `Color(uiColor: .systemBackground)`) for automatic light/dark mode\n- Use system font styles (`.title`, `.headline`, `.body`, `.caption`) for Dynamic Type support\n- Use `ContentUnavailableView` for empty and error states\n- Support adaptive layouts via `horizontalSizeClass`\n- Provide VoiceOver labels (`.accessibilityLabel`) and support Dynamic Type accessibility sizes by switching layout orientation\n\nSee [references/design-polish.md](references/design-polish.md) for HIG, theming, haptics, focus, transitions, and loading patterns.\n\n## Writing Tools (iOS 18+)\n\nControl the Apple Intelligence Writing Tools experience on text views with `.writingToolsBehavior(_:)`.\n\n| Level | Effect | When to use |\n|-------|--------|-------------|\n| `.complete` | Full inline rewriting (proofread, rewrite, transform) | Notes, email, documents |\n| `.limited` | Overlay panel only — original text untouched | Code editors, validated forms |\n| `.disabled` | Writing Tools hidden entirely | Passwords, search bars |\n| `.automatic` | System chooses based on context (default) | Most views |\n\n```swift\nTextEditor(text: $body)\n    .writingToolsBehavior(.complete)\nTextField(\"Search…\", text: $query)\n    .writingToolsBehavior(.disabled)\n```\n\n**Detecting active sessions:** Read `isWritingToolsActive` on `UITextView` (UIKit) to defer validation or suspend undo grouping until a rewrite finishes.\n\n> **Docs:** [WritingToolsBehavior](https://sosumi.ai/documentation/swiftui/writingtoolsbehavior) · [writingToolsBehavior(_:)](https://sosumi.ai/documentation/swiftui/view/writingtoolsbehavior(_:))\n\n## Common Mistakes\n\n1. Using `@ObservedObject` to create objects -- use `@StateObject` (legacy) or `@State` (modern)\n2. Heavy computation in view `body` -- move to model or computed property\n3. Not using `.task` for async work -- manual `Task` in `onAppear` leaks if not cancelled\n4. Array indices as `ForEach` IDs -- causes incorrect diffing and UI bugs\n5. Forgetting `@Bindable` -- `$property` syntax on `@Observable` requires `@Bindable`\n6. Over-using `@State` -- only for view-local state; shared state belongs in `@Observable`\n7. Not extracting subviews -- long body blocks are hard to read and optimize\n8. Using `NavigationView` -- deprecated; use `NavigationStack`\n9. Inline closures in body -- extract complex closures to methods\n10. `.sheet(isPresented:)` when state represents a model -- use `.sheet(item:)` instead\n11. **Using `AnyView` for type erasure** -- causes identity resets and disables diffing. Use `@ViewBuilder`, `Group`, or generics instead. See [references/deprecated-migration.md](references/deprecated-migration.md)\n\n## Review Checklist\n\n- [ ] `@Observable` used for shared state models (not `ObservableObject` on iOS 17+)\n- [ ] `@State` owns objects; `let`/`@Bindable` receives them\n- [ ] `NavigationStack` used (not `NavigationView`)\n- [ ] `.task` modifier for async data loading\n- [ ] `LazyVStack`/`LazyHStack` for large collections\n- [ ] Stable `Identifiable` IDs (not array indices)\n- [ ] Views decomposed into focused subviews\n- [ ] No heavy computation in view `body`\n- [ ] Environment used for deeply shared state\n- [ ] Custom `ViewModifier` for repeated styling\n- [ ] `.sheet(item:)` preferred over `.sheet(isPresented:)`\n- [ ] Sheets own their actions and call `dismiss()` internally\n- [ ] MV pattern followed -- no unnecessary view models\n- [ ] `@Observable` view model classes are `@MainActor`-isolated\n- [ ] Model types passed across concurrency boundaries are `Sendable`\n\n## References\n\n- Architecture, app wiring, and lightweight clients: [references/architecture-patterns.md](references/architecture-patterns.md)\n- Design polish (HIG, theming, haptics, transitions, loading, focus): [references/design-polish.md](references/design-polish.md)\n- Deprecated API migration: [references/deprecated-migration.md](references/deprecated-migration.md)\n- Platform and sharing patterns (Transferable, media, menus, macOS settings): [references/platform-and-sharing.md](references/platform-and-sharing.md)","tags":["swiftui","patterns","swift","ios","skills","dpearson2699","accessibility","agent-skills","ai-coding","apple","claude-code","codex-skills"],"capabilities":["skill","source-dpearson2699","skill-swiftui-patterns","topic-accessibility","topic-agent-skills","topic-ai-coding","topic-apple","topic-claude-code","topic-codex-skills","topic-cursor-skills","topic-ios","topic-ios-development","topic-liquid-glass","topic-localization","topic-mapkit"],"categories":["swift-ios-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/dpearson2699/swift-ios-skills/swiftui-patterns","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add dpearson2699/swift-ios-skills","source_repo":"https://github.com/dpearson2699/swift-ios-skills","install_from":"skills.sh"}},"qualityScore":"0.684","qualityRationale":"deterministic score 0.68 from registry signals: · indexed on github topic:agent-skills · 468 github stars · SKILL.md body (13,996 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-04-22T00:53:45.544Z","embedding":null,"createdAt":"2026-04-18T22:01:26.725Z","updatedAt":"2026-04-22T00:53:45.544Z","lastSeenAt":"2026-04-22T00:53:45.544Z","tsv":"'-26':129 '/documentation/swiftui/view/writingtoolsbehavior(_:))':1346 '/documentation/swiftui/writingtoolsbehavior)':1342 '1':683,1349 '10':1454 '11':1466 '12':858 '16':665 '17':82,1499 '18':146,151,1251 '2':685,861,1361 '26':37,47,125,1068 '3':688,1373 '300':897 '4':692,1388 '5':695,1400 '6':435,697,788,1409 '6.3':50 '7':699,1425 '8':703,1438 '9':1444 'accept':1052,1100 'access':481,580,1183,1230 'accessibilitylabel':1225 'across':1581 'action':1055,1059,1559 'actionssect':735 'activ':822,824,1320 'adapt':1218 'advantag':655 'align':139,142,785,1172 'alreadi':266 'alway':412,500,520,977 'anim':1096 'animat':1087 'animatabledata':1090 'annot':413 'anyview':1468 'api':38,127,132,1070,1606 'app':180,397,1588 'appl':1174,1254 'architectur':10,52,86,93,169,237,1587 'architecture-model-view-mv-pattern':92 'area':1085 'array':1139,1389,1526 'async':31,59,117,121,175,379,704,974,1065,1378,1514 'async-data-load':120 'attributedstr':1101 'automat':982,1092,1195,1298 'avoid':873,1141 'await':371,374,384,1006,1009,1030 'background':853,854 'backgroundextensioneffect':1081 'backward':78 'backward-compat':77 'bar':1297 'base':884,1301 'belong':1422 'bind':477,502,575 'bindabl':22,466,512,584,612,1402,1408,1504 'block':1431 'bodi':339,545,568,589,609,698,723,772,846,995,1142,1157,1204,1310,1366,1430,1448,1538 'bottom':682 'bound':424 'boundari':165,1583 'break':710 'bug':1399 'build':4 'builder':701 'built':941 'built-in':940 'busi':255,308 'button':1058 'call':1561 'cancel':981,1048,1387 'caption':1205 'cardstyl':843,865,869 'carri':766 'case':326,345,348,359,821,827 'catch':389 'caus':1394,1472 'chang':632,641,1021 'checklist':158,161,1488 'children':1121 'childview':548,562 'choos':1300 'class':417,525,1574 'client':323,401,1592 'client.getfeed':385 'clipshap':855 'closur':1056,1446,1451 'code':265,1286 'collect':1116,1521 'color':1181,1188,1191 'color.primary':1189 'colorschem':951,953 'comment':904 'common':152,155,939,1347 'common-mistak':154 'compat':79 'complet':1269,1312 'complex':1161,1450 'compon':208,217,236 'compos':291 'composit':15,56,111,114,172,707 'comput':693,740,747,1149,1363,1371,1535 'concurr':436,1582 'condit':800,877,887 'conform':1091,1131,1168 'connect':506 'contain':206 'content':85,847,848,851 'contentunavailableview':352,1211 'context':1303 'control':227,1252 'convent':105,109,677 'core':269 'cornerradius':857 'counter':496 'cover':17,51,168,187,211 'creat':449,1035,1353 'custom':27,833,906,1545 'customthem':934 'data':32,118,122,975,1515 'declar':314 'decompos':1529 'decomposit':26 'dedic':72 'deep':201 'deep-link':200 'deepli':1542 'deepview':602 'default':243,918,1304 'defaultvalu':916 'defer':1328 'depend':1020 'deprec':1441,1605 'descript':356 'design':61,1595 'detail':183,204,733,734 'detailssect':732 'detect':1319 'dif':1396,1477 'disabl':1290,1318,1476 'disappear':985 'dismiss':947,949,1562 'doc':1338 'document':1278 'dynam':1207,1228 'dynamictypes':955,957 'e.g':1057 'earlier':667 'edg':1076,1080,1086 'editor':1287 'editview':582 'effect':1077,1265 'els':1028 'email':1277 'empti':1213 'ensur':421 'entir':1294 'enum':324 'environ':23,29,57,115,116,173,273,286,319,479,486,551,600,604,673,684,905,907,932,935,943,946,950,954,958,962,966,970,1539 'environmentkey':913 'environmentobject':672 'environmentvalu':920 'equat':1158,1170 'erasur':1471 'error':328,349,353,391,1215 'error.localizeddescription':392 'exampl':402 'exceed':896 'except':1049 'exclamationmark.triangle':355 'exist':264 'experi':1258 'express':250 'extens':862,901,919 'extract':708,753,836,1427,1449 'fade':1075 'favor':271 'feedclient.self':320 'feedview':317 'field':498 'file':752,895 'filter':777,1145 'final':524 'finish':1337 'focus':713,1243,1531,1602 'follow':1173,1566 'font':791,795,1200 'foreach':363,1129,1392 'foregroundstyl':825,831 'forget':1401 'form':226,1289 'full':1270 'func':377,812,845,864 'function':705,798 'generic':1482 'get':924 'granular':620 'green':826 'grid':221 'group':1333,1480 'guard':1026 'guidelin':134,137,1106,1177 'haptic':1242,1599 'hard':1433 'header':776,781 'headersect':727 'headlin':1203 'heavi':1362,1534 'helper':702 'hidden':1293 'hig':138,141,1171,1240,1597 'hig-align':140 'horizontalsizeclass':959,1221 'human':1175 'id':1013,1024,1124,1136,1393,1524 'ident':1473 'identifi':1133,1523 'immedi':1061,1122 'import':411 'inact':828,830 'includ':194,219 'incorrect':1395 'indic':1140,1390,1527 'init':696 'inject':280 'inlin':1155,1271,1445 'insid':888 'instead':1465,1483 'integr':65 'intellig':1255 'intend':762 'interfac':1176 'intern':1563 'introduc':259,300 'io':36,46,81,124,128,145,150,664,1067,1250,1498 'isload':640,642 'isol':1577 'ispin':730,731 'ispres':1456,1555 'issearch':963,965 'iswritingtoolsact':1323 'item':530,531,637,1000,1003,1004,1126,1464,1551 'itemlistview':988 'itemrow':1002 'itemstor':526,543,566,587,993 'itemstore.self':605 'keep':287,310,743 'key':1184 'label':1224 'larg':293,1115,1520 'layout':68,205,216,235,1179,1219,1234 'lazi':1107 'lazyhgrid':1113 'lazyhstack':1111,1518 'lazyvgrid':1112 'lazyvstack':1110,1517 'lead':786 'leak':1384 'legaci':658,1357 'let':350,361,381,453,564,671,686,915,1503 'level':876,1264 'lifecycl':452 'light/dark':1196 'lightweight':248,400,1591 'limit':1279 'line':898 'link':202 'list':222,342,775,998,1128 'live':70 'load':33,60,119,123,176,327,330,337,346,360,387,976,1246,1516,1601 'loadfe':372,375,378 'local':492,1418 'logic':256,309,801 'long':1429 'maco':1617 'macro':1088 'main':430 'mainactor':419,521,522,1576 'major':653 'manag':12,54,99,102,407,451 'manual':1036,1380 'mark':903 'media':1615 'member':679 'menus':1616 'messag':351,358 'method':1453 'migrat':1607 'mirror/blur':1082 'mistak':153,156,1348 'mode':1197 'model':88,94,239,251,261,284,303,305,416,519,536,1153,1369,1461,1494,1570,1573,1578 'model-view':87,238 'modelcontext':971,973 'modern':8,42,1360 'modifi':835,868,891,1512 'move':1144,1367 'must':1130 'mv':9,90,96,241,245,394,1564 'navig':66,184,192 'navigationsplitview':196 'navigationstack':195,1443,1507 'navigationview':1440,1510 'need':465,473,558,576,1042 'never':1034,1137 'new':126,131,1069 'new-api':130 'newvalu':930 'note':84,1276 'object':446,458,471,484,1354,1502 'observ':18,408,414,457,462,470,483,517,523,560,1406,1424,1489,1571 'observableobject':657,659,1496 'observedobject':670,1351 'onappear':1039,1383 'oncancel':738,739 'onchang':277 'onsav':736,737 'openurl':967,969 'optim':1437 'orchestr':279 'order':104,108,676,678 'orient':1235 'origin':1283 'over-us':1410 'overlay':232,1280 'own':444,534,1501 'ownership':19,171,409,514 'pad':852 'panel':1281 'parent':508 'parentview':538 'pass':1580 'password':1295 'pattern':3,16,41,44,69,75,91,97,182,185,203,209,225,242,395,515,1247,1565,1613 'perform':133,136,654,1105 'performance-guidelin':135 'platform':1610 'platform/share':64 'polish':62,1596 'post':331,362,364,365,368,369,382,388 'postrow':367 'prefer':880,1552 'principl':270 'privat':321,333,376,501,779,811,910 'progressview':347 'proofread':1273 'properti':478,513,630,687,691,742,748,1150,1372,1403 'provid':1222 'queri':274,1032,1316 'radius':860 'rather':298 'rational':396 're':625,648,1016,1165 're-rend':624,647,1164 're-run':1015 'read':460,555,598,629,636,1322,1435 'read-on':459 'receiv':455,468,1505 'recomput':1143 'refer':162,163,1046,1586 'references/architecture-patterns.md':404,405,1593,1594 'references/deprecated-migration.md':1485,1486,1608,1609 'references/design-polish.md':1237,1238,1603,1604 'references/platform-and-sharing.md':1619,1620 'refresh':373,1008 'regular':1117 'relat':178,234,744 'render':626,649,1119,1166 'repeat':837,1548 'repres':1459 'requir':432,1407 'reset':1474 'respons':720 'result':778 'return':1029 'reus':760 'review':157,160,1487 'review-checklist':159 'rewrit':1272,1274,1336 'rich':1103 'roundedrectangl':856 'rule':20,410,1185 'run':1017 's.title':619 'safe':1084 'safeti':437 'scope':164 'scroll':223,1079 'scrolledgeeffectstyl':1071 'search':228,1031,1296,1314 'searchabl':231 'searchtext':1025,1033 'searchtext.isempty':1027 'secondari':832,1190 'section':889 'see':403,1093,1236,1484 'self':925,928 'semant':1187 'sendabl':1585 'separ':807 'servic':253,281,306 'session':1321 'set':927,1618 'shadow':859 'share':283,482,1420,1492,1543,1612 'sheet':197,1455,1463,1550,1554,1556 'sibl':73 'simpl':312,493 'singl':719,882 'size':1231 'sizeclass':961 'skill':74,167,193,218,1097 'skill-swiftui-patterns' 'small':289 'smaller':296 'soft':1072 'sort':1147 'sosumi.ai':1341,1345 'sosumi.ai/documentation/swiftui/view/writingtoolsbehavior(_:))':1344 'sosumi.ai/documentation/swiftui/writingtoolsbehavior)':1340 'source-dpearson2699' 'space':787 'split':292,899 'stabl':870,883,1123,1135,1522 'stack':220,1118 'stacks/grids':1108 'standalon':756 'state':11,21,53,98,101,170,249,272,332,406,425,442,487,494,510,540,669,689,769,990,1062,1216,1359,1413,1419,1421,1458,1493,1500,1544 'state-manag':100 'stateobject':668,1356 'static':914 'status':815,816,820 'statusbadg':813 'store':549,565,586,607,615,690,992,1044 'store.items':999 'store.load':1007 'store.refresh':1010 'store.title':572,595 'string':329 'struct':316,537,561,581,601,758,808,842,911,987 'structur':181 'style':838,1201,1549 'subheadlin':796 'subtitl':794 'subview':297,709,714,745,765,1428,1532 'support':663,1209,1217,1227 'suspend':1331 'swap':879 'swift':49,315,434,516,721,770,809,841,909,945,986,1022,1307 'swiftui':2,5,40,43,179,191,215,622,1095 'swiftui-anim':1094 'swiftui-layout-compon':214 'swiftui-navig':190 'swiftui-pattern':1 'switch':343,819,1233 'synchron':1054 'syntax':1404 'synthes':1089 'system':1199,1299 'systembackground':1193 'systemimag':354 'tab':198 'target':45 'task':35,275,370,979,1005,1012,1023,1037,1050,1376,1381,1511 'test':304 'text':357,497,571,594,618,789,793,823,829,1104,1260,1284,1309,1315 'texteditor':1098,1308 'textfield':592,616,1313 'theme':917,922,923,933,936,938,1241,1598 'themekey':912 'themekey.self':926,929 'thread':431 'titl':528,593,617,728,729,790,1202 'title2':792 'toggl':495 'tool':144,149,1249,1257,1292 'top':680,875,1074 'top-level':874 'topic-accessibility' 'topic-agent-skills' 'topic-ai-coding' 'topic-apple' 'topic-claude-code' 'topic-codex-skills' 'topic-cursor-skills' 'topic-ios' 'topic-ios-development' 'topic-liquid-glass' 'topic-localization' 'topic-mapkit' 'track':621 'transfer':1614 'transform':1275 'transit':1244,1600 'tree':872 'tri':383 'trigger':645 'two':475,504,578 'two-way':474,503,577 'type':489,1208,1229,1470,1579 'type.self':480,674 'typographi':1180 'ui':229,423,1398 'ui-bound':422 'uicolor':1192 'uikit':1326 'uitextview':1325 'undo':1332 'unless':83,262,1040 'unnecessari':1568 'unnecessarili':1167 'untouch':1285 'updat':427,1063 'usag':931 'use':267,441,661,978,1011,1109,1138,1186,1198,1210,1268,1350,1355,1375,1412,1439,1442,1462,1467,1478,1490,1508,1540 'valid':1288,1329 'valu':30,448,488,499,908,944 'var':322,334,338,527,529,541,544,567,585,588,606,608,613,694,722,771,780,921,937,948,952,956,960,964,968,972,991,994 'via':285,1220 'view':6,14,25,55,89,95,103,107,110,113,224,240,246,260,288,294,302,311,318,341,415,443,454,467,491,518,532,539,547,553,563,570,573,583,591,596,603,611,627,635,675,700,706,711,725,741,757,774,783,818,834,850,863,867,871,878,885,894,984,989,997,1159,1162,1261,1306,1365,1417,1528,1537,1569,1572 'view-composit':112 'view-loc':490,1416 'view-ordering-convent':106 'viewbuild':797,810,1479 'viewmodel':542,550,552 'viewmodifi':28,840,844,1546 'viewstat':325,335,336,344,386,390 'voiceov':1223 'vstack':726,784 'w':39 'warrant':805 'way':476,505,579 'wire':24,58,174,398,1589 'work':1066,1379 'wrapper':438,464,557 'write':143,148,1248,1256,1291 'writing-tools-io':147 'writingtoolsbehavior':1263,1311,1317,1339,1343","prices":[{"id":"d533a673-be24-451d-9dfb-dc01d77d4e79","listingId":"33bc1806-df5e-4b32-8e8b-db59e900de8c","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"dpearson2699","category":"swift-ios-skills","install_from":"skills.sh"},"createdAt":"2026-04-18T22:01:26.725Z"}],"sources":[{"listingId":"33bc1806-df5e-4b32-8e8b-db59e900de8c","source":"github","sourceId":"dpearson2699/swift-ios-skills/swiftui-patterns","sourceUrl":"https://github.com/dpearson2699/swift-ios-skills/tree/main/skills/swiftui-patterns","isPrimary":false,"firstSeenAt":"2026-04-18T22:01:26.725Z","lastSeenAt":"2026-04-22T00:53:45.544Z"}],"details":{"listingId":"33bc1806-df5e-4b32-8e8b-db59e900de8c","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"dpearson2699","slug":"swiftui-patterns","github":{"repo":"dpearson2699/swift-ios-skills","stars":468,"topics":["accessibility","agent-skills","ai-coding","apple","claude-code","codex-skills","cursor-skills","ios","ios-development","liquid-glass","localization","mapkit","networking","storekit","swift","swift-concurrency","swiftdata","swiftui","widgetkit","xcode"],"license":"other","html_url":"https://github.com/dpearson2699/swift-ios-skills","pushed_at":"2026-04-21T19:26:16Z","description":"Agent Skills for iOS 26+, Swift 6.3, SwiftUI, and modern Apple frameworks","skill_md_sha":"2a63fd8760aa21773c726370d4e366d0a790f048","skill_md_path":"skills/swiftui-patterns/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/dpearson2699/swift-ios-skills/tree/main/skills/swiftui-patterns"},"layout":"multi","source":"github","category":"swift-ios-skills","frontmatter":{"name":"swiftui-patterns","description":"Build SwiftUI views with modern MV architecture, state management, and view composition patterns. Covers @Observable ownership rules, @State/@Bindable/@Environment wiring, view decomposition, custom ViewModifiers, environment values, async data loading with .task, iOS 26+ APIs, Writing Tools, and performance guidelines. Use when structuring a SwiftUI app, managing state with @Observable, composing view hierarchies, or applying SwiftUI best practices."},"skills_sh_url":"https://skills.sh/dpearson2699/swift-ios-skills/swiftui-patterns"},"updatedAt":"2026-04-22T00:53:45.544Z"}}