[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-openai-swiftui-view-refactor":3,"mdc-c8vv8e-key":39,"related-repo-openai-swiftui-view-refactor":1591,"related-org-openai-swiftui-view-refactor":1715},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":11,"stars":28,"repoUrl":29,"updatedAt":30,"license":31,"forks":32,"topics":33,"repo":34,"sourceUrl":37,"mdContent":38},"swiftui-view-refactor","refactor SwiftUI views and data flow","Refactor SwiftUI view files into stable, testable structure. Use when splitting large views, tightening data flow, or cleaning Observation ownership.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},"openai","OpenAI","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Fopenai.png",[12,16,19,22,25],{"name":13,"slug":14,"type":15},"SwiftUI","swiftui","tag",{"name":17,"slug":18,"type":15},"Architecture","architecture",{"name":20,"slug":21,"type":15},"iOS","ios",{"name":23,"slug":24,"type":15},"Code Review","code-review",{"name":26,"slug":27,"type":15},"Mobile","mobile",3992,"https:\u002F\u002Fgithub.com\u002Fopenai\u002Fplugins","2026-04-06T18:40:04.49393",null,465,[],{"repoUrl":29,"stars":28,"forks":32,"topics":35,"description":36},[],"OpenAI Plugins","https:\u002F\u002Fgithub.com\u002Fopenai\u002Fplugins\u002Ftree\u002FHEAD\u002Fplugins\u002Fbuild-ios-apps\u002Fskills\u002Fswiftui-view-refactor","---\nname: swiftui-view-refactor\ndescription: Refactor SwiftUI view files into stable, testable structure. Use when splitting large views, tightening data flow, or cleaning Observation ownership.\n---\n\n# SwiftUI View Refactor\n\n## Overview\nRefactor SwiftUI views toward small, explicit, stable view types. Default to vanilla SwiftUI: local state in the view, shared dependencies in the environment, business logic in services\u002Fmodels, and view models only when the request or existing code clearly requires one.\n\n## Core Guidelines\n\n### 1) View ordering (top → bottom)\n- Enforce this ordering unless the existing file has a stronger local convention you must preserve.\n- Environment\n- `private`\u002F`public` `let`\n- `@State` \u002F other stored properties\n- computed `var` (non-view)\n- `init`\n- `body`\n- computed view builders \u002F other view helpers\n- helper \u002F async functions\n\n### 2) Default to MV, not MVVM\n- Views should be lightweight state expressions and orchestration points, not containers for business logic.\n- Favor `@State`, `@Environment`, `@Query`, `.task`, `.task(id:)`, and `onChange` before reaching for a view model.\n- Inject services and shared models via `@Environment`; keep domain logic in services\u002Fmodels, not in the view body.\n- Do not introduce a view model just to mirror local view state or wrap environment dependencies.\n- If a screen is getting large, split the UI into subviews before inventing a new view model layer.\n\n### 3) Strongly prefer dedicated subview types over computed `some View` helpers\n- Flag `body` properties that are longer than roughly one screen or contain multiple logical sections.\n- Prefer extracting dedicated `View` types for non-trivial sections, especially when they have state, async work, branching, or deserve their own preview.\n- Keep computed `some View` helpers rare and small. Do not build an entire screen out of `private var header: some View`-style fragments.\n- Pass small, explicit inputs (data, bindings, callbacks) into extracted subviews instead of handing down the entire parent state.\n- If an extracted subview becomes reusable or independently meaningful, move it to its own file.\n\nPrefer:\n\n```swift\nvar body: some View {\n    List {\n        HeaderSection(title: title, subtitle: subtitle)\n        FilterSection(\n            filterOptions: filterOptions,\n            selectedFilter: $selectedFilter\n        )\n        ResultsSection(items: filteredItems)\n        FooterSection()\n    }\n}\n\nprivate struct HeaderSection: View {\n    let title: String\n    let subtitle: String\n\n    var body: some View {\n        VStack(alignment: .leading, spacing: 6) {\n            Text(title).font(.title2)\n            Text(subtitle).font(.subheadline)\n        }\n    }\n}\n\nprivate struct FilterSection: View {\n    let filterOptions: [FilterOption]\n    @Binding var selectedFilter: FilterOption\n\n    var body: some View {\n        ScrollView(.horizontal, showsIndicators: false) {\n            HStack {\n                ForEach(filterOptions, id: \\.self) { option in\n                    FilterChip(option: option, isSelected: option == selectedFilter)\n                        .onTapGesture { selectedFilter = option }\n                }\n            }\n        }\n    }\n}\n```\n\nAvoid:\n\n```swift\nvar body: some View {\n    List {\n        header\n        filters\n        results\n        footer\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### 3b) Extract actions and side effects out of `body`\n- Do not keep non-trivial button actions inline in the view body.\n- Do not bury business logic inside `.task`, `.onAppear`, `.onChange`, or `.refreshable`.\n- Prefer calling small private methods from the view, and move real business logic into services\u002Fmodels.\n- The body should read like UI, not like a view controller.\n\n```swift\nButton(\"Save\", action: save)\n    .disabled(isSaving)\n\n.task(id: searchText) {\n    await reload(for: searchText)\n}\n\nprivate func save() {\n    Task { await saveAsync() }\n}\n\nprivate func reload(for searchText: String) async {\n    guard !searchText.isEmpty else {\n        results = []\n        return\n    }\n    await searchService.search(searchText)\n}\n```\n\n### 4) Keep a stable view tree (avoid top-level conditional view swapping)\n- Avoid `body` or computed views that return completely different root branches via `if\u002Felse`.\n- Prefer a single stable base view with conditions inside sections\u002Fmodifiers (`overlay`, `opacity`, `disabled`, `toolbar`, etc.).\n- Root-level branch swapping causes identity churn, broader invalidation, and extra recomputation.\n\nPrefer:\n\n```swift\nvar body: some View {\n    List {\n        documentsListContent\n    }\n    .toolbar {\n        if canEdit {\n            editToolbar\n        }\n    }\n}\n```\n\nAvoid:\n\n```swift\nvar documentsListView: some View {\n    if canEdit {\n        editableDocumentsList\n    } else {\n        readOnlyDocumentsList\n    }\n}\n```\n\n### 5) View model handling (only if already present or explicitly requested)\n- Treat view models as a legacy or explicit-need pattern, not the default.\n- Do not introduce a view model unless the request or existing code clearly calls for one.\n- If a view model exists, make it non-optional when possible.\n- Pass dependencies to the view via `init`, then create the view model in the view's `init`.\n- Avoid `bootstrapIfNeeded` patterns and other delayed setup workarounds.\n\nExample (Observation-based):\n\n```swift\n@State private var viewModel: SomeViewModel\n\ninit(dependency: Dependency) {\n    _viewModel = State(initialValue: SomeViewModel(dependency: dependency))\n}\n```\n\n### 6) Observation usage\n- For `@Observable` reference types on iOS 17+, store them as `@State` in the owning view.\n- Pass observables down explicitly; avoid optional state unless the UI genuinely needs it.\n- If the deployment target includes iOS 16 or earlier, use `@StateObject` at the owner and `@ObservedObject` when injecting legacy observable models.\n\n## Workflow\n\n1. Reorder the view to match the ordering rules.\n2. Remove inline actions and side effects from `body`; move business logic into services\u002Fmodels and keep only thin orchestration in the view.\n3. Shorten long bodies by extracting dedicated subview types; avoid rebuilding the screen out of many computed `some View` helpers.\n4. Ensure stable view structure: avoid top-level `if`-based branch swapping; move conditions to localized sections\u002Fmodifiers.\n5. If a view model exists or is explicitly required, replace optional view models with a non-optional `@State` view model initialized in `init`.\n6. Confirm Observation usage: `@State` for root `@Observable` models on iOS 17+, legacy wrappers only when the deployment target requires them.\n7. Keep behavior intact: do not change layout or business logic unless requested.\n\n## Notes\n\n- Prefer small, explicit view types over large conditional blocks and large computed `some View` properties.\n- Keep computed view builders below `body` and non-view computed vars above `init`.\n- A good SwiftUI refactor should make the view read top-to-bottom as data flow plus layout, not as mixed layout and imperative logic.\n- For MV-first guidance and rationale, see `references\u002Fmv-patterns.md`.\n- In addition to the references above, use web search to consult current Apple Developer documentation when SwiftUI APIs, Observation behavior, or platform guidance may have changed.\n\n## Large-view handling\n\nWhen a SwiftUI view file exceeds ~300 lines, split it aggressively. Extract meaningful sections into dedicated `View` types instead of hiding complexity in many computed properties. Use `private` extensions with `\u002F\u002F MARK: -` comments for actions and helpers, but do not treat extensions as a substitute for breaking a giant screen into smaller view types. If an extracted subview is reused or independently meaningful, move it into its own file.\n",{"data":40,"body":41},{"name":4,"description":6},{"type":42,"children":43},"root",[44,52,59,65,71,78,171,177,256,270,328,333,689,694,814,825,877,1021,1027,1088,1092,1173,1177,1238,1244,1292,1297,1342,1348,1397,1403,1491,1497,1552,1558,1585],{"type":45,"tag":46,"props":47,"children":48},"element","h1",{"id":4},[49],{"type":50,"value":51},"text","SwiftUI View Refactor",{"type":45,"tag":53,"props":54,"children":56},"h2",{"id":55},"overview",[57],{"type":50,"value":58},"Overview",{"type":45,"tag":60,"props":61,"children":62},"p",{},[63],{"type":50,"value":64},"Refactor SwiftUI views toward small, explicit, stable view types. Default to vanilla SwiftUI: local state in the view, shared dependencies in the environment, business logic in services\u002Fmodels, and view models only when the request or existing code clearly requires one.",{"type":45,"tag":53,"props":66,"children":68},{"id":67},"core-guidelines",[69],{"type":50,"value":70},"Core Guidelines",{"type":45,"tag":72,"props":73,"children":75},"h3",{"id":74},"_1-view-ordering-top-bottom",[76],{"type":50,"value":77},"1) View ordering (top → bottom)",{"type":45,"tag":79,"props":80,"children":81},"ul",{},[82,88,93,119,130,143,152,161,166],{"type":45,"tag":83,"props":84,"children":85},"li",{},[86],{"type":50,"value":87},"Enforce this ordering unless the existing file has a stronger local convention you must preserve.",{"type":45,"tag":83,"props":89,"children":90},{},[91],{"type":50,"value":92},"Environment",{"type":45,"tag":83,"props":94,"children":95},{},[96,103,105,111,113],{"type":45,"tag":97,"props":98,"children":100},"code",{"className":99},[],[101],{"type":50,"value":102},"private",{"type":50,"value":104},"\u002F",{"type":45,"tag":97,"props":106,"children":108},{"className":107},[],[109],{"type":50,"value":110},"public",{"type":50,"value":112}," ",{"type":45,"tag":97,"props":114,"children":116},{"className":115},[],[117],{"type":50,"value":118},"let",{"type":45,"tag":83,"props":120,"children":121},{},[122,128],{"type":45,"tag":97,"props":123,"children":125},{"className":124},[],[126],{"type":50,"value":127},"@State",{"type":50,"value":129}," \u002F other stored properties",{"type":45,"tag":83,"props":131,"children":132},{},[133,135,141],{"type":50,"value":134},"computed ",{"type":45,"tag":97,"props":136,"children":138},{"className":137},[],[139],{"type":50,"value":140},"var",{"type":50,"value":142}," (non-view)",{"type":45,"tag":83,"props":144,"children":145},{},[146],{"type":45,"tag":97,"props":147,"children":149},{"className":148},[],[150],{"type":50,"value":151},"init",{"type":45,"tag":83,"props":153,"children":154},{},[155],{"type":45,"tag":97,"props":156,"children":158},{"className":157},[],[159],{"type":50,"value":160},"body",{"type":45,"tag":83,"props":162,"children":163},{},[164],{"type":50,"value":165},"computed view builders \u002F other view helpers",{"type":45,"tag":83,"props":167,"children":168},{},[169],{"type":50,"value":170},"helper \u002F async functions",{"type":45,"tag":72,"props":172,"children":174},{"id":173},"_2-default-to-mv-not-mvvm",[175],{"type":50,"value":176},"2) Default to MV, not MVVM",{"type":45,"tag":79,"props":178,"children":179},{},[180,185,234,246,251],{"type":45,"tag":83,"props":181,"children":182},{},[183],{"type":50,"value":184},"Views should be lightweight state expressions and orchestration points, not containers for business logic.",{"type":45,"tag":83,"props":186,"children":187},{},[188,190,195,197,203,204,210,211,217,218,224,226,232],{"type":50,"value":189},"Favor ",{"type":45,"tag":97,"props":191,"children":193},{"className":192},[],[194],{"type":50,"value":127},{"type":50,"value":196},", ",{"type":45,"tag":97,"props":198,"children":200},{"className":199},[],[201],{"type":50,"value":202},"@Environment",{"type":50,"value":196},{"type":45,"tag":97,"props":205,"children":207},{"className":206},[],[208],{"type":50,"value":209},"@Query",{"type":50,"value":196},{"type":45,"tag":97,"props":212,"children":214},{"className":213},[],[215],{"type":50,"value":216},".task",{"type":50,"value":196},{"type":45,"tag":97,"props":219,"children":221},{"className":220},[],[222],{"type":50,"value":223},".task(id:)",{"type":50,"value":225},", and ",{"type":45,"tag":97,"props":227,"children":229},{"className":228},[],[230],{"type":50,"value":231},"onChange",{"type":50,"value":233}," before reaching for a view model.",{"type":45,"tag":83,"props":235,"children":236},{},[237,239,244],{"type":50,"value":238},"Inject services and shared models via ",{"type":45,"tag":97,"props":240,"children":242},{"className":241},[],[243],{"type":50,"value":202},{"type":50,"value":245},"; keep domain logic in services\u002Fmodels, not in the view body.",{"type":45,"tag":83,"props":247,"children":248},{},[249],{"type":50,"value":250},"Do not introduce a view model just to mirror local view state or wrap environment dependencies.",{"type":45,"tag":83,"props":252,"children":253},{},[254],{"type":50,"value":255},"If a screen is getting large, split the UI into subviews before inventing a new view model layer.",{"type":45,"tag":72,"props":257,"children":259},{"id":258},"_3-strongly-prefer-dedicated-subview-types-over-computed-some-view-helpers",[260,262,268],{"type":50,"value":261},"3) Strongly prefer dedicated subview types over computed ",{"type":45,"tag":97,"props":263,"children":265},{"className":264},[],[266],{"type":50,"value":267},"some View",{"type":50,"value":269}," helpers",{"type":45,"tag":79,"props":271,"children":272},{},[273,285,298,318,323],{"type":45,"tag":83,"props":274,"children":275},{},[276,278,283],{"type":50,"value":277},"Flag ",{"type":45,"tag":97,"props":279,"children":281},{"className":280},[],[282],{"type":50,"value":160},{"type":50,"value":284}," properties that are longer than roughly one screen or contain multiple logical sections.",{"type":45,"tag":83,"props":286,"children":287},{},[288,290,296],{"type":50,"value":289},"Prefer extracting dedicated ",{"type":45,"tag":97,"props":291,"children":293},{"className":292},[],[294],{"type":50,"value":295},"View",{"type":50,"value":297}," types for non-trivial sections, especially when they have state, async work, branching, or deserve their own preview.",{"type":45,"tag":83,"props":299,"children":300},{},[301,303,308,310,316],{"type":50,"value":302},"Keep computed ",{"type":45,"tag":97,"props":304,"children":306},{"className":305},[],[307],{"type":50,"value":267},{"type":50,"value":309}," helpers rare and small. Do not build an entire screen out of ",{"type":45,"tag":97,"props":311,"children":313},{"className":312},[],[314],{"type":50,"value":315},"private var header: some View",{"type":50,"value":317},"-style fragments.",{"type":45,"tag":83,"props":319,"children":320},{},[321],{"type":50,"value":322},"Pass small, explicit inputs (data, bindings, callbacks) into extracted subviews instead of handing down the entire parent state.",{"type":45,"tag":83,"props":324,"children":325},{},[326],{"type":50,"value":327},"If an extracted subview becomes reusable or independently meaningful, move it to its own file.",{"type":45,"tag":60,"props":329,"children":330},{},[331],{"type":50,"value":332},"Prefer:",{"type":45,"tag":334,"props":335,"children":340},"pre",{"className":336,"code":337,"language":338,"meta":339,"style":339},"language-swift shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","var body: some View {\n    List {\n        HeaderSection(title: title, subtitle: subtitle)\n        FilterSection(\n            filterOptions: filterOptions,\n            selectedFilter: $selectedFilter\n        )\n        ResultsSection(items: filteredItems)\n        FooterSection()\n    }\n}\n\nprivate struct HeaderSection: View {\n    let title: String\n    let subtitle: String\n\n    var body: some View {\n        VStack(alignment: .leading, spacing: 6) {\n            Text(title).font(.title2)\n            Text(subtitle).font(.subheadline)\n        }\n    }\n}\n\nprivate struct FilterSection: View {\n    let filterOptions: [FilterOption]\n    @Binding var selectedFilter: FilterOption\n\n    var body: some View {\n        ScrollView(.horizontal, showsIndicators: false) {\n            HStack {\n                ForEach(filterOptions, id: \\.self) { option in\n                    FilterChip(option: option, isSelected: option == selectedFilter)\n                        .onTapGesture { selectedFilter = option }\n                }\n            }\n        }\n    }\n}\n","swift","",[341],{"type":45,"tag":97,"props":342,"children":343},{"__ignoreMap":339},[344,355,364,373,382,391,400,409,418,427,436,445,455,464,473,482,490,499,508,517,526,535,543,551,559,568,577,586,594,602,611,620,629,638,647,656,665,673,681],{"type":45,"tag":345,"props":346,"children":349},"span",{"class":347,"line":348},"line",1,[350],{"type":45,"tag":345,"props":351,"children":352},{},[353],{"type":50,"value":354},"var body: some View {\n",{"type":45,"tag":345,"props":356,"children":358},{"class":347,"line":357},2,[359],{"type":45,"tag":345,"props":360,"children":361},{},[362],{"type":50,"value":363},"    List {\n",{"type":45,"tag":345,"props":365,"children":367},{"class":347,"line":366},3,[368],{"type":45,"tag":345,"props":369,"children":370},{},[371],{"type":50,"value":372},"        HeaderSection(title: title, subtitle: subtitle)\n",{"type":45,"tag":345,"props":374,"children":376},{"class":347,"line":375},4,[377],{"type":45,"tag":345,"props":378,"children":379},{},[380],{"type":50,"value":381},"        FilterSection(\n",{"type":45,"tag":345,"props":383,"children":385},{"class":347,"line":384},5,[386],{"type":45,"tag":345,"props":387,"children":388},{},[389],{"type":50,"value":390},"            filterOptions: filterOptions,\n",{"type":45,"tag":345,"props":392,"children":394},{"class":347,"line":393},6,[395],{"type":45,"tag":345,"props":396,"children":397},{},[398],{"type":50,"value":399},"            selectedFilter: $selectedFilter\n",{"type":45,"tag":345,"props":401,"children":403},{"class":347,"line":402},7,[404],{"type":45,"tag":345,"props":405,"children":406},{},[407],{"type":50,"value":408},"        )\n",{"type":45,"tag":345,"props":410,"children":412},{"class":347,"line":411},8,[413],{"type":45,"tag":345,"props":414,"children":415},{},[416],{"type":50,"value":417},"        ResultsSection(items: filteredItems)\n",{"type":45,"tag":345,"props":419,"children":421},{"class":347,"line":420},9,[422],{"type":45,"tag":345,"props":423,"children":424},{},[425],{"type":50,"value":426},"        FooterSection()\n",{"type":45,"tag":345,"props":428,"children":430},{"class":347,"line":429},10,[431],{"type":45,"tag":345,"props":432,"children":433},{},[434],{"type":50,"value":435},"    }\n",{"type":45,"tag":345,"props":437,"children":439},{"class":347,"line":438},11,[440],{"type":45,"tag":345,"props":441,"children":442},{},[443],{"type":50,"value":444},"}\n",{"type":45,"tag":345,"props":446,"children":448},{"class":347,"line":447},12,[449],{"type":45,"tag":345,"props":450,"children":452},{"emptyLinePlaceholder":451},true,[453],{"type":50,"value":454},"\n",{"type":45,"tag":345,"props":456,"children":458},{"class":347,"line":457},13,[459],{"type":45,"tag":345,"props":460,"children":461},{},[462],{"type":50,"value":463},"private struct HeaderSection: View {\n",{"type":45,"tag":345,"props":465,"children":467},{"class":347,"line":466},14,[468],{"type":45,"tag":345,"props":469,"children":470},{},[471],{"type":50,"value":472},"    let title: String\n",{"type":45,"tag":345,"props":474,"children":476},{"class":347,"line":475},15,[477],{"type":45,"tag":345,"props":478,"children":479},{},[480],{"type":50,"value":481},"    let subtitle: String\n",{"type":45,"tag":345,"props":483,"children":485},{"class":347,"line":484},16,[486],{"type":45,"tag":345,"props":487,"children":488},{"emptyLinePlaceholder":451},[489],{"type":50,"value":454},{"type":45,"tag":345,"props":491,"children":493},{"class":347,"line":492},17,[494],{"type":45,"tag":345,"props":495,"children":496},{},[497],{"type":50,"value":498},"    var body: some View {\n",{"type":45,"tag":345,"props":500,"children":502},{"class":347,"line":501},18,[503],{"type":45,"tag":345,"props":504,"children":505},{},[506],{"type":50,"value":507},"        VStack(alignment: .leading, spacing: 6) {\n",{"type":45,"tag":345,"props":509,"children":511},{"class":347,"line":510},19,[512],{"type":45,"tag":345,"props":513,"children":514},{},[515],{"type":50,"value":516},"            Text(title).font(.title2)\n",{"type":45,"tag":345,"props":518,"children":520},{"class":347,"line":519},20,[521],{"type":45,"tag":345,"props":522,"children":523},{},[524],{"type":50,"value":525},"            Text(subtitle).font(.subheadline)\n",{"type":45,"tag":345,"props":527,"children":529},{"class":347,"line":528},21,[530],{"type":45,"tag":345,"props":531,"children":532},{},[533],{"type":50,"value":534},"        }\n",{"type":45,"tag":345,"props":536,"children":538},{"class":347,"line":537},22,[539],{"type":45,"tag":345,"props":540,"children":541},{},[542],{"type":50,"value":435},{"type":45,"tag":345,"props":544,"children":546},{"class":347,"line":545},23,[547],{"type":45,"tag":345,"props":548,"children":549},{},[550],{"type":50,"value":444},{"type":45,"tag":345,"props":552,"children":554},{"class":347,"line":553},24,[555],{"type":45,"tag":345,"props":556,"children":557},{"emptyLinePlaceholder":451},[558],{"type":50,"value":454},{"type":45,"tag":345,"props":560,"children":562},{"class":347,"line":561},25,[563],{"type":45,"tag":345,"props":564,"children":565},{},[566],{"type":50,"value":567},"private struct FilterSection: View {\n",{"type":45,"tag":345,"props":569,"children":571},{"class":347,"line":570},26,[572],{"type":45,"tag":345,"props":573,"children":574},{},[575],{"type":50,"value":576},"    let filterOptions: [FilterOption]\n",{"type":45,"tag":345,"props":578,"children":580},{"class":347,"line":579},27,[581],{"type":45,"tag":345,"props":582,"children":583},{},[584],{"type":50,"value":585},"    @Binding var selectedFilter: FilterOption\n",{"type":45,"tag":345,"props":587,"children":589},{"class":347,"line":588},28,[590],{"type":45,"tag":345,"props":591,"children":592},{"emptyLinePlaceholder":451},[593],{"type":50,"value":454},{"type":45,"tag":345,"props":595,"children":597},{"class":347,"line":596},29,[598],{"type":45,"tag":345,"props":599,"children":600},{},[601],{"type":50,"value":498},{"type":45,"tag":345,"props":603,"children":605},{"class":347,"line":604},30,[606],{"type":45,"tag":345,"props":607,"children":608},{},[609],{"type":50,"value":610},"        ScrollView(.horizontal, showsIndicators: false) {\n",{"type":45,"tag":345,"props":612,"children":614},{"class":347,"line":613},31,[615],{"type":45,"tag":345,"props":616,"children":617},{},[618],{"type":50,"value":619},"            HStack {\n",{"type":45,"tag":345,"props":621,"children":623},{"class":347,"line":622},32,[624],{"type":45,"tag":345,"props":625,"children":626},{},[627],{"type":50,"value":628},"                ForEach(filterOptions, id: \\.self) { option in\n",{"type":45,"tag":345,"props":630,"children":632},{"class":347,"line":631},33,[633],{"type":45,"tag":345,"props":634,"children":635},{},[636],{"type":50,"value":637},"                    FilterChip(option: option, isSelected: option == selectedFilter)\n",{"type":45,"tag":345,"props":639,"children":641},{"class":347,"line":640},34,[642],{"type":45,"tag":345,"props":643,"children":644},{},[645],{"type":50,"value":646},"                        .onTapGesture { selectedFilter = option }\n",{"type":45,"tag":345,"props":648,"children":650},{"class":347,"line":649},35,[651],{"type":45,"tag":345,"props":652,"children":653},{},[654],{"type":50,"value":655},"                }\n",{"type":45,"tag":345,"props":657,"children":659},{"class":347,"line":658},36,[660],{"type":45,"tag":345,"props":661,"children":662},{},[663],{"type":50,"value":664},"            }\n",{"type":45,"tag":345,"props":666,"children":668},{"class":347,"line":667},37,[669],{"type":45,"tag":345,"props":670,"children":671},{},[672],{"type":50,"value":534},{"type":45,"tag":345,"props":674,"children":676},{"class":347,"line":675},38,[677],{"type":45,"tag":345,"props":678,"children":679},{},[680],{"type":50,"value":435},{"type":45,"tag":345,"props":682,"children":684},{"class":347,"line":683},39,[685],{"type":45,"tag":345,"props":686,"children":687},{},[688],{"type":50,"value":444},{"type":45,"tag":60,"props":690,"children":691},{},[692],{"type":50,"value":693},"Avoid:",{"type":45,"tag":334,"props":695,"children":697},{"className":336,"code":696,"language":338,"meta":339,"style":339},"var body: some View {\n    List {\n        header\n        filters\n        results\n        footer\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",[698],{"type":45,"tag":97,"props":699,"children":700},{"__ignoreMap":339},[701,708,715,723,731,739,747,754,761,768,776,784,792,800,807],{"type":45,"tag":345,"props":702,"children":703},{"class":347,"line":348},[704],{"type":45,"tag":345,"props":705,"children":706},{},[707],{"type":50,"value":354},{"type":45,"tag":345,"props":709,"children":710},{"class":347,"line":357},[711],{"type":45,"tag":345,"props":712,"children":713},{},[714],{"type":50,"value":363},{"type":45,"tag":345,"props":716,"children":717},{"class":347,"line":366},[718],{"type":45,"tag":345,"props":719,"children":720},{},[721],{"type":50,"value":722},"        header\n",{"type":45,"tag":345,"props":724,"children":725},{"class":347,"line":375},[726],{"type":45,"tag":345,"props":727,"children":728},{},[729],{"type":50,"value":730},"        filters\n",{"type":45,"tag":345,"props":732,"children":733},{"class":347,"line":384},[734],{"type":45,"tag":345,"props":735,"children":736},{},[737],{"type":50,"value":738},"        results\n",{"type":45,"tag":345,"props":740,"children":741},{"class":347,"line":393},[742],{"type":45,"tag":345,"props":743,"children":744},{},[745],{"type":50,"value":746},"        footer\n",{"type":45,"tag":345,"props":748,"children":749},{"class":347,"line":402},[750],{"type":45,"tag":345,"props":751,"children":752},{},[753],{"type":50,"value":435},{"type":45,"tag":345,"props":755,"children":756},{"class":347,"line":411},[757],{"type":45,"tag":345,"props":758,"children":759},{},[760],{"type":50,"value":444},{"type":45,"tag":345,"props":762,"children":763},{"class":347,"line":420},[764],{"type":45,"tag":345,"props":765,"children":766},{"emptyLinePlaceholder":451},[767],{"type":50,"value":454},{"type":45,"tag":345,"props":769,"children":770},{"class":347,"line":429},[771],{"type":45,"tag":345,"props":772,"children":773},{},[774],{"type":50,"value":775},"private var header: some View {\n",{"type":45,"tag":345,"props":777,"children":778},{"class":347,"line":438},[779],{"type":45,"tag":345,"props":780,"children":781},{},[782],{"type":50,"value":783},"    VStack(alignment: .leading, spacing: 6) {\n",{"type":45,"tag":345,"props":785,"children":786},{"class":347,"line":447},[787],{"type":45,"tag":345,"props":788,"children":789},{},[790],{"type":50,"value":791},"        Text(title).font(.title2)\n",{"type":45,"tag":345,"props":793,"children":794},{"class":347,"line":457},[795],{"type":45,"tag":345,"props":796,"children":797},{},[798],{"type":50,"value":799},"        Text(subtitle).font(.subheadline)\n",{"type":45,"tag":345,"props":801,"children":802},{"class":347,"line":466},[803],{"type":45,"tag":345,"props":804,"children":805},{},[806],{"type":50,"value":435},{"type":45,"tag":345,"props":808,"children":809},{"class":347,"line":475},[810],{"type":45,"tag":345,"props":811,"children":812},{},[813],{"type":50,"value":444},{"type":45,"tag":72,"props":815,"children":817},{"id":816},"_3b-extract-actions-and-side-effects-out-of-body",[818,820],{"type":50,"value":819},"3b) Extract actions and side effects out of ",{"type":45,"tag":97,"props":821,"children":823},{"className":822},[],[824],{"type":50,"value":160},{"type":45,"tag":79,"props":826,"children":827},{},[828,833,867,872],{"type":45,"tag":83,"props":829,"children":830},{},[831],{"type":50,"value":832},"Do not keep non-trivial button actions inline in the view body.",{"type":45,"tag":83,"props":834,"children":835},{},[836,838,843,844,850,851,857,859,865],{"type":50,"value":837},"Do not bury business logic inside ",{"type":45,"tag":97,"props":839,"children":841},{"className":840},[],[842],{"type":50,"value":216},{"type":50,"value":196},{"type":45,"tag":97,"props":845,"children":847},{"className":846},[],[848],{"type":50,"value":849},".onAppear",{"type":50,"value":196},{"type":45,"tag":97,"props":852,"children":854},{"className":853},[],[855],{"type":50,"value":856},".onChange",{"type":50,"value":858},", or ",{"type":45,"tag":97,"props":860,"children":862},{"className":861},[],[863],{"type":50,"value":864},".refreshable",{"type":50,"value":866},".",{"type":45,"tag":83,"props":868,"children":869},{},[870],{"type":50,"value":871},"Prefer calling small private methods from the view, and move real business logic into services\u002Fmodels.",{"type":45,"tag":83,"props":873,"children":874},{},[875],{"type":50,"value":876},"The body should read like UI, not like a view controller.",{"type":45,"tag":334,"props":878,"children":880},{"className":336,"code":879,"language":338,"meta":339,"style":339},"Button(\"Save\", action: save)\n    .disabled(isSaving)\n\n.task(id: searchText) {\n    await reload(for: searchText)\n}\n\nprivate func save() {\n    Task { await saveAsync() }\n}\n\nprivate func reload(for searchText: String) async {\n    guard !searchText.isEmpty else {\n        results = []\n        return\n    }\n    await searchService.search(searchText)\n}\n",[881],{"type":45,"tag":97,"props":882,"children":883},{"__ignoreMap":339},[884,892,900,907,915,923,930,937,945,953,960,967,975,983,991,999,1006,1014],{"type":45,"tag":345,"props":885,"children":886},{"class":347,"line":348},[887],{"type":45,"tag":345,"props":888,"children":889},{},[890],{"type":50,"value":891},"Button(\"Save\", action: save)\n",{"type":45,"tag":345,"props":893,"children":894},{"class":347,"line":357},[895],{"type":45,"tag":345,"props":896,"children":897},{},[898],{"type":50,"value":899},"    .disabled(isSaving)\n",{"type":45,"tag":345,"props":901,"children":902},{"class":347,"line":366},[903],{"type":45,"tag":345,"props":904,"children":905},{"emptyLinePlaceholder":451},[906],{"type":50,"value":454},{"type":45,"tag":345,"props":908,"children":909},{"class":347,"line":375},[910],{"type":45,"tag":345,"props":911,"children":912},{},[913],{"type":50,"value":914},".task(id: searchText) {\n",{"type":45,"tag":345,"props":916,"children":917},{"class":347,"line":384},[918],{"type":45,"tag":345,"props":919,"children":920},{},[921],{"type":50,"value":922},"    await reload(for: searchText)\n",{"type":45,"tag":345,"props":924,"children":925},{"class":347,"line":393},[926],{"type":45,"tag":345,"props":927,"children":928},{},[929],{"type":50,"value":444},{"type":45,"tag":345,"props":931,"children":932},{"class":347,"line":402},[933],{"type":45,"tag":345,"props":934,"children":935},{"emptyLinePlaceholder":451},[936],{"type":50,"value":454},{"type":45,"tag":345,"props":938,"children":939},{"class":347,"line":411},[940],{"type":45,"tag":345,"props":941,"children":942},{},[943],{"type":50,"value":944},"private func save() {\n",{"type":45,"tag":345,"props":946,"children":947},{"class":347,"line":420},[948],{"type":45,"tag":345,"props":949,"children":950},{},[951],{"type":50,"value":952},"    Task { await saveAsync() }\n",{"type":45,"tag":345,"props":954,"children":955},{"class":347,"line":429},[956],{"type":45,"tag":345,"props":957,"children":958},{},[959],{"type":50,"value":444},{"type":45,"tag":345,"props":961,"children":962},{"class":347,"line":438},[963],{"type":45,"tag":345,"props":964,"children":965},{"emptyLinePlaceholder":451},[966],{"type":50,"value":454},{"type":45,"tag":345,"props":968,"children":969},{"class":347,"line":447},[970],{"type":45,"tag":345,"props":971,"children":972},{},[973],{"type":50,"value":974},"private func reload(for searchText: String) async {\n",{"type":45,"tag":345,"props":976,"children":977},{"class":347,"line":457},[978],{"type":45,"tag":345,"props":979,"children":980},{},[981],{"type":50,"value":982},"    guard !searchText.isEmpty else {\n",{"type":45,"tag":345,"props":984,"children":985},{"class":347,"line":466},[986],{"type":45,"tag":345,"props":987,"children":988},{},[989],{"type":50,"value":990},"        results = []\n",{"type":45,"tag":345,"props":992,"children":993},{"class":347,"line":475},[994],{"type":45,"tag":345,"props":995,"children":996},{},[997],{"type":50,"value":998},"        return\n",{"type":45,"tag":345,"props":1000,"children":1001},{"class":347,"line":484},[1002],{"type":45,"tag":345,"props":1003,"children":1004},{},[1005],{"type":50,"value":435},{"type":45,"tag":345,"props":1007,"children":1008},{"class":347,"line":492},[1009],{"type":45,"tag":345,"props":1010,"children":1011},{},[1012],{"type":50,"value":1013},"    await searchService.search(searchText)\n",{"type":45,"tag":345,"props":1015,"children":1016},{"class":347,"line":501},[1017],{"type":45,"tag":345,"props":1018,"children":1019},{},[1020],{"type":50,"value":444},{"type":45,"tag":72,"props":1022,"children":1024},{"id":1023},"_4-keep-a-stable-view-tree-avoid-top-level-conditional-view-swapping",[1025],{"type":50,"value":1026},"4) Keep a stable view tree (avoid top-level conditional view swapping)",{"type":45,"tag":79,"props":1028,"children":1029},{},[1030,1049,1083],{"type":45,"tag":83,"props":1031,"children":1032},{},[1033,1035,1040,1042,1048],{"type":50,"value":1034},"Avoid ",{"type":45,"tag":97,"props":1036,"children":1038},{"className":1037},[],[1039],{"type":50,"value":160},{"type":50,"value":1041}," or computed views that return completely different root branches via ",{"type":45,"tag":97,"props":1043,"children":1045},{"className":1044},[],[1046],{"type":50,"value":1047},"if\u002Felse",{"type":50,"value":866},{"type":45,"tag":83,"props":1050,"children":1051},{},[1052,1054,1060,1061,1067,1068,1074,1075,1081],{"type":50,"value":1053},"Prefer a single stable base view with conditions inside sections\u002Fmodifiers (",{"type":45,"tag":97,"props":1055,"children":1057},{"className":1056},[],[1058],{"type":50,"value":1059},"overlay",{"type":50,"value":196},{"type":45,"tag":97,"props":1062,"children":1064},{"className":1063},[],[1065],{"type":50,"value":1066},"opacity",{"type":50,"value":196},{"type":45,"tag":97,"props":1069,"children":1071},{"className":1070},[],[1072],{"type":50,"value":1073},"disabled",{"type":50,"value":196},{"type":45,"tag":97,"props":1076,"children":1078},{"className":1077},[],[1079],{"type":50,"value":1080},"toolbar",{"type":50,"value":1082},", etc.).",{"type":45,"tag":83,"props":1084,"children":1085},{},[1086],{"type":50,"value":1087},"Root-level branch swapping causes identity churn, broader invalidation, and extra recomputation.",{"type":45,"tag":60,"props":1089,"children":1090},{},[1091],{"type":50,"value":332},{"type":45,"tag":334,"props":1093,"children":1095},{"className":336,"code":1094,"language":338,"meta":339,"style":339},"var body: some View {\n    List {\n        documentsListContent\n    }\n    .toolbar {\n        if canEdit {\n            editToolbar\n        }\n    }\n}\n",[1096],{"type":45,"tag":97,"props":1097,"children":1098},{"__ignoreMap":339},[1099,1106,1113,1121,1128,1136,1144,1152,1159,1166],{"type":45,"tag":345,"props":1100,"children":1101},{"class":347,"line":348},[1102],{"type":45,"tag":345,"props":1103,"children":1104},{},[1105],{"type":50,"value":354},{"type":45,"tag":345,"props":1107,"children":1108},{"class":347,"line":357},[1109],{"type":45,"tag":345,"props":1110,"children":1111},{},[1112],{"type":50,"value":363},{"type":45,"tag":345,"props":1114,"children":1115},{"class":347,"line":366},[1116],{"type":45,"tag":345,"props":1117,"children":1118},{},[1119],{"type":50,"value":1120},"        documentsListContent\n",{"type":45,"tag":345,"props":1122,"children":1123},{"class":347,"line":375},[1124],{"type":45,"tag":345,"props":1125,"children":1126},{},[1127],{"type":50,"value":435},{"type":45,"tag":345,"props":1129,"children":1130},{"class":347,"line":384},[1131],{"type":45,"tag":345,"props":1132,"children":1133},{},[1134],{"type":50,"value":1135},"    .toolbar {\n",{"type":45,"tag":345,"props":1137,"children":1138},{"class":347,"line":393},[1139],{"type":45,"tag":345,"props":1140,"children":1141},{},[1142],{"type":50,"value":1143},"        if canEdit {\n",{"type":45,"tag":345,"props":1145,"children":1146},{"class":347,"line":402},[1147],{"type":45,"tag":345,"props":1148,"children":1149},{},[1150],{"type":50,"value":1151},"            editToolbar\n",{"type":45,"tag":345,"props":1153,"children":1154},{"class":347,"line":411},[1155],{"type":45,"tag":345,"props":1156,"children":1157},{},[1158],{"type":50,"value":534},{"type":45,"tag":345,"props":1160,"children":1161},{"class":347,"line":420},[1162],{"type":45,"tag":345,"props":1163,"children":1164},{},[1165],{"type":50,"value":435},{"type":45,"tag":345,"props":1167,"children":1168},{"class":347,"line":429},[1169],{"type":45,"tag":345,"props":1170,"children":1171},{},[1172],{"type":50,"value":444},{"type":45,"tag":60,"props":1174,"children":1175},{},[1176],{"type":50,"value":693},{"type":45,"tag":334,"props":1178,"children":1180},{"className":336,"code":1179,"language":338,"meta":339,"style":339},"var documentsListView: some View {\n    if canEdit {\n        editableDocumentsList\n    } else {\n        readOnlyDocumentsList\n    }\n}\n",[1181],{"type":45,"tag":97,"props":1182,"children":1183},{"__ignoreMap":339},[1184,1192,1200,1208,1216,1224,1231],{"type":45,"tag":345,"props":1185,"children":1186},{"class":347,"line":348},[1187],{"type":45,"tag":345,"props":1188,"children":1189},{},[1190],{"type":50,"value":1191},"var documentsListView: some View {\n",{"type":45,"tag":345,"props":1193,"children":1194},{"class":347,"line":357},[1195],{"type":45,"tag":345,"props":1196,"children":1197},{},[1198],{"type":50,"value":1199},"    if canEdit {\n",{"type":45,"tag":345,"props":1201,"children":1202},{"class":347,"line":366},[1203],{"type":45,"tag":345,"props":1204,"children":1205},{},[1206],{"type":50,"value":1207},"        editableDocumentsList\n",{"type":45,"tag":345,"props":1209,"children":1210},{"class":347,"line":375},[1211],{"type":45,"tag":345,"props":1212,"children":1213},{},[1214],{"type":50,"value":1215},"    } else {\n",{"type":45,"tag":345,"props":1217,"children":1218},{"class":347,"line":384},[1219],{"type":45,"tag":345,"props":1220,"children":1221},{},[1222],{"type":50,"value":1223},"        readOnlyDocumentsList\n",{"type":45,"tag":345,"props":1225,"children":1226},{"class":347,"line":393},[1227],{"type":45,"tag":345,"props":1228,"children":1229},{},[1230],{"type":50,"value":435},{"type":45,"tag":345,"props":1232,"children":1233},{"class":347,"line":402},[1234],{"type":45,"tag":345,"props":1235,"children":1236},{},[1237],{"type":50,"value":444},{"type":45,"tag":72,"props":1239,"children":1241},{"id":1240},"_5-view-model-handling-only-if-already-present-or-explicitly-requested",[1242],{"type":50,"value":1243},"5) View model handling (only if already present or explicitly requested)",{"type":45,"tag":79,"props":1245,"children":1246},{},[1247,1252,1257,1262,1280],{"type":45,"tag":83,"props":1248,"children":1249},{},[1250],{"type":50,"value":1251},"Treat view models as a legacy or explicit-need pattern, not the default.",{"type":45,"tag":83,"props":1253,"children":1254},{},[1255],{"type":50,"value":1256},"Do not introduce a view model unless the request or existing code clearly calls for one.",{"type":45,"tag":83,"props":1258,"children":1259},{},[1260],{"type":50,"value":1261},"If a view model exists, make it non-optional when possible.",{"type":45,"tag":83,"props":1263,"children":1264},{},[1265,1267,1272,1274,1279],{"type":50,"value":1266},"Pass dependencies to the view via ",{"type":45,"tag":97,"props":1268,"children":1270},{"className":1269},[],[1271],{"type":50,"value":151},{"type":50,"value":1273},", then create the view model in the view's ",{"type":45,"tag":97,"props":1275,"children":1277},{"className":1276},[],[1278],{"type":50,"value":151},{"type":50,"value":866},{"type":45,"tag":83,"props":1281,"children":1282},{},[1283,1284,1290],{"type":50,"value":1034},{"type":45,"tag":97,"props":1285,"children":1287},{"className":1286},[],[1288],{"type":50,"value":1289},"bootstrapIfNeeded",{"type":50,"value":1291}," patterns and other delayed setup workarounds.",{"type":45,"tag":60,"props":1293,"children":1294},{},[1295],{"type":50,"value":1296},"Example (Observation-based):",{"type":45,"tag":334,"props":1298,"children":1300},{"className":336,"code":1299,"language":338,"meta":339,"style":339},"@State private var viewModel: SomeViewModel\n\ninit(dependency: Dependency) {\n    _viewModel = State(initialValue: SomeViewModel(dependency: dependency))\n}\n",[1301],{"type":45,"tag":97,"props":1302,"children":1303},{"__ignoreMap":339},[1304,1312,1319,1327,1335],{"type":45,"tag":345,"props":1305,"children":1306},{"class":347,"line":348},[1307],{"type":45,"tag":345,"props":1308,"children":1309},{},[1310],{"type":50,"value":1311},"@State private var viewModel: SomeViewModel\n",{"type":45,"tag":345,"props":1313,"children":1314},{"class":347,"line":357},[1315],{"type":45,"tag":345,"props":1316,"children":1317},{"emptyLinePlaceholder":451},[1318],{"type":50,"value":454},{"type":45,"tag":345,"props":1320,"children":1321},{"class":347,"line":366},[1322],{"type":45,"tag":345,"props":1323,"children":1324},{},[1325],{"type":50,"value":1326},"init(dependency: Dependency) {\n",{"type":45,"tag":345,"props":1328,"children":1329},{"class":347,"line":375},[1330],{"type":45,"tag":345,"props":1331,"children":1332},{},[1333],{"type":50,"value":1334},"    _viewModel = State(initialValue: SomeViewModel(dependency: dependency))\n",{"type":45,"tag":345,"props":1336,"children":1337},{"class":347,"line":384},[1338],{"type":45,"tag":345,"props":1339,"children":1340},{},[1341],{"type":50,"value":444},{"type":45,"tag":72,"props":1343,"children":1345},{"id":1344},"_6-observation-usage",[1346],{"type":50,"value":1347},"6) Observation usage",{"type":45,"tag":79,"props":1349,"children":1350},{},[1351,1371,1376],{"type":45,"tag":83,"props":1352,"children":1353},{},[1354,1356,1362,1364,1369],{"type":50,"value":1355},"For ",{"type":45,"tag":97,"props":1357,"children":1359},{"className":1358},[],[1360],{"type":50,"value":1361},"@Observable",{"type":50,"value":1363}," reference types on iOS 17+, store them as ",{"type":45,"tag":97,"props":1365,"children":1367},{"className":1366},[],[1368],{"type":50,"value":127},{"type":50,"value":1370}," in the owning view.",{"type":45,"tag":83,"props":1372,"children":1373},{},[1374],{"type":50,"value":1375},"Pass observables down explicitly; avoid optional state unless the UI genuinely needs it.",{"type":45,"tag":83,"props":1377,"children":1378},{},[1379,1381,1387,1389,1395],{"type":50,"value":1380},"If the deployment target includes iOS 16 or earlier, use ",{"type":45,"tag":97,"props":1382,"children":1384},{"className":1383},[],[1385],{"type":50,"value":1386},"@StateObject",{"type":50,"value":1388}," at the owner and ",{"type":45,"tag":97,"props":1390,"children":1392},{"className":1391},[],[1393],{"type":50,"value":1394},"@ObservedObject",{"type":50,"value":1396}," when injecting legacy observable models.",{"type":45,"tag":53,"props":1398,"children":1400},{"id":1399},"workflow",[1401],{"type":50,"value":1402},"Workflow",{"type":45,"tag":1404,"props":1405,"children":1406},"ol",{},[1407,1412,1424,1436,1449,1467,1486],{"type":45,"tag":83,"props":1408,"children":1409},{},[1410],{"type":50,"value":1411},"Reorder the view to match the ordering rules.",{"type":45,"tag":83,"props":1413,"children":1414},{},[1415,1417,1422],{"type":50,"value":1416},"Remove inline actions and side effects from ",{"type":45,"tag":97,"props":1418,"children":1420},{"className":1419},[],[1421],{"type":50,"value":160},{"type":50,"value":1423},"; move business logic into services\u002Fmodels and keep only thin orchestration in the view.",{"type":45,"tag":83,"props":1425,"children":1426},{},[1427,1429,1434],{"type":50,"value":1428},"Shorten long bodies by extracting dedicated subview types; avoid rebuilding the screen out of many computed ",{"type":45,"tag":97,"props":1430,"children":1432},{"className":1431},[],[1433],{"type":50,"value":267},{"type":50,"value":1435}," helpers.",{"type":45,"tag":83,"props":1437,"children":1438},{},[1439,1441,1447],{"type":50,"value":1440},"Ensure stable view structure: avoid top-level ",{"type":45,"tag":97,"props":1442,"children":1444},{"className":1443},[],[1445],{"type":50,"value":1446},"if",{"type":50,"value":1448},"-based branch swapping; move conditions to localized sections\u002Fmodifiers.",{"type":45,"tag":83,"props":1450,"children":1451},{},[1452,1454,1459,1461,1466],{"type":50,"value":1453},"If a view model exists or is explicitly required, replace optional view models with a non-optional ",{"type":45,"tag":97,"props":1455,"children":1457},{"className":1456},[],[1458],{"type":50,"value":127},{"type":50,"value":1460}," view model initialized in ",{"type":45,"tag":97,"props":1462,"children":1464},{"className":1463},[],[1465],{"type":50,"value":151},{"type":50,"value":866},{"type":45,"tag":83,"props":1468,"children":1469},{},[1470,1472,1477,1479,1484],{"type":50,"value":1471},"Confirm Observation usage: ",{"type":45,"tag":97,"props":1473,"children":1475},{"className":1474},[],[1476],{"type":50,"value":127},{"type":50,"value":1478}," for root ",{"type":45,"tag":97,"props":1480,"children":1482},{"className":1481},[],[1483],{"type":50,"value":1361},{"type":50,"value":1485}," models on iOS 17+, legacy wrappers only when the deployment target requires them.",{"type":45,"tag":83,"props":1487,"children":1488},{},[1489],{"type":50,"value":1490},"Keep behavior intact: do not change layout or business logic unless requested.",{"type":45,"tag":53,"props":1492,"children":1494},{"id":1493},"notes",[1495],{"type":50,"value":1496},"Notes",{"type":45,"tag":79,"props":1498,"children":1499},{},[1500,1512,1530,1535,1547],{"type":45,"tag":83,"props":1501,"children":1502},{},[1503,1505,1510],{"type":50,"value":1504},"Prefer small, explicit view types over large conditional blocks and large computed ",{"type":45,"tag":97,"props":1506,"children":1508},{"className":1507},[],[1509],{"type":50,"value":267},{"type":50,"value":1511}," properties.",{"type":45,"tag":83,"props":1513,"children":1514},{},[1515,1517,1522,1524,1529],{"type":50,"value":1516},"Keep computed view builders below ",{"type":45,"tag":97,"props":1518,"children":1520},{"className":1519},[],[1521],{"type":50,"value":160},{"type":50,"value":1523}," and non-view computed vars above ",{"type":45,"tag":97,"props":1525,"children":1527},{"className":1526},[],[1528],{"type":50,"value":151},{"type":50,"value":866},{"type":45,"tag":83,"props":1531,"children":1532},{},[1533],{"type":50,"value":1534},"A good SwiftUI refactor should make the view read top-to-bottom as data flow plus layout, not as mixed layout and imperative logic.",{"type":45,"tag":83,"props":1536,"children":1537},{},[1538,1540,1546],{"type":50,"value":1539},"For MV-first guidance and rationale, see ",{"type":45,"tag":97,"props":1541,"children":1543},{"className":1542},[],[1544],{"type":50,"value":1545},"references\u002Fmv-patterns.md",{"type":50,"value":866},{"type":45,"tag":83,"props":1548,"children":1549},{},[1550],{"type":50,"value":1551},"In addition to the references above, use web search to consult current Apple Developer documentation when SwiftUI APIs, Observation behavior, or platform guidance may have changed.",{"type":45,"tag":53,"props":1553,"children":1555},{"id":1554},"large-view-handling",[1556],{"type":50,"value":1557},"Large-view handling",{"type":45,"tag":60,"props":1559,"children":1560},{},[1561,1563,1568,1570,1575,1577,1583],{"type":50,"value":1562},"When a SwiftUI view file exceeds ~300 lines, split it aggressively. Extract meaningful sections into dedicated ",{"type":45,"tag":97,"props":1564,"children":1566},{"className":1565},[],[1567],{"type":50,"value":295},{"type":50,"value":1569}," types instead of hiding complexity in many computed properties. Use ",{"type":45,"tag":97,"props":1571,"children":1573},{"className":1572},[],[1574],{"type":50,"value":102},{"type":50,"value":1576}," extensions with ",{"type":45,"tag":97,"props":1578,"children":1580},{"className":1579},[],[1581],{"type":50,"value":1582},"\u002F\u002F MARK: -",{"type":50,"value":1584}," comments for actions and helpers, but do not treat extensions as a substitute for breaking a giant screen into smaller view types. If an extracted subview is reused or independently meaningful, move it into its own file.",{"type":45,"tag":1586,"props":1587,"children":1588},"style",{},[1589],{"type":50,"value":1590},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"items":1592,"total":1714},[1593,1612,1628,1640,1660,1682,1702],{"slug":1594,"name":1594,"fn":1595,"description":1596,"org":1597,"tags":1598,"stars":28,"repoUrl":29,"updatedAt":1611},"accessibility-and-inclusive-visualization","make data visualizations accessible","Make data visualizations accessible and inclusive. Use when the user needs chart or diagram accessibility guidance, text alternatives for complex visuals, color and contrast review, keyboard support, reduced-motion behavior for animation or parallax, or an accessibility QA workflow for exported figures, UML-like diagrams, and dashboards.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1599,1602,1605,1608],{"name":1600,"slug":1601,"type":15},"Accessibility","accessibility",{"name":1603,"slug":1604,"type":15},"Charts","charts",{"name":1606,"slug":1607,"type":15},"Data Visualization","data-visualization",{"name":1609,"slug":1610,"type":15},"Design","design","2026-06-30T19:00:57.102",{"slug":1613,"name":1613,"fn":1614,"description":1615,"org":1616,"tags":1617,"stars":28,"repoUrl":29,"updatedAt":1627},"agent-browser","automate browser interactions for agents","Browser automation CLI for AI agents. Use when the user needs to interact with websites, verify dev server output, test web apps, navigate pages, fill forms, click buttons, take screenshots, extract data, or automate any browser task. Also triggers when a dev server starts so you can verify it visually.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1618,1621,1624],{"name":1619,"slug":1620,"type":15},"Agents","agents",{"name":1622,"slug":1623,"type":15},"Browser Automation","browser-automation",{"name":1625,"slug":1626,"type":15},"Testing","testing","2026-04-06T18:41:03.44016",{"slug":1629,"name":1629,"fn":1630,"description":1631,"org":1632,"tags":1633,"stars":28,"repoUrl":29,"updatedAt":1639},"agent-browser-verify","verify dev server output with automated browser","Automated browser verification for dev servers. Triggers when a dev server starts to run a visual gut-check with agent-browser — verifies the page loads, checks for console errors, validates key UI elements, and reports pass\u002Ffail before continuing.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1634,1635,1638],{"name":1622,"slug":1623,"type":15},{"name":1636,"slug":1637,"type":15},"Local Development","local-development",{"name":1625,"slug":1626,"type":15},"2026-04-06T18:41:17.526867",{"slug":1641,"name":1641,"fn":1642,"description":1643,"org":1644,"tags":1645,"stars":28,"repoUrl":29,"updatedAt":1659},"agents-sdk","build AI agents on Cloudflare Workers","Build AI agents on Cloudflare Workers using the Agents SDK. Load when creating stateful agents, durable workflows, real-time WebSocket apps, scheduled tasks, MCP servers, or chat applications. Covers Agent class, state management, callable RPC, Workflows integration, and React hooks. Biases towards retrieval from Cloudflare docs over pre-trained knowledge.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1646,1647,1650,1653,1656],{"name":1619,"slug":1620,"type":15},{"name":1648,"slug":1649,"type":15},"Cloudflare Workers","cloudflare-workers",{"name":1651,"slug":1652,"type":15},"SDK","sdk",{"name":1654,"slug":1655,"type":15},"Serverless","serverless",{"name":1657,"slug":1658,"type":15},"WebSockets","websockets","2026-04-06T18:39:51.717063",{"slug":1661,"name":1661,"fn":1662,"description":1663,"org":1664,"tags":1665,"stars":28,"repoUrl":29,"updatedAt":1681},"ai-elements","build chat UIs with AI Elements","AI Elements component library guidance — pre-built React components for AI interfaces built on shadcn\u002Fui. Use when building chat UIs, message displays, tool call rendering, streaming responses, reasoning panels, or any AI-native interface with the AI SDK.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1666,1669,1672,1675,1678],{"name":1667,"slug":1668,"type":15},"Frontend","frontend",{"name":1670,"slug":1671,"type":15},"React","react",{"name":1673,"slug":1674,"type":15},"shadcn\u002Fui","shadcn-ui",{"name":1676,"slug":1677,"type":15},"UI Components","ui-components",{"name":1679,"slug":1680,"type":15},"Vercel","vercel","2026-04-06T18:40:59.619419",{"slug":1683,"name":1683,"fn":1684,"description":1685,"org":1686,"tags":1687,"stars":28,"repoUrl":29,"updatedAt":1701},"ai-gateway","configure Vercel AI Gateway","Vercel AI Gateway expert guidance. Use when configuring model routing, provider failover, cost tracking, or managing multiple AI providers through a unified API.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1688,1691,1694,1697,1700],{"name":1689,"slug":1690,"type":15},"AI Infrastructure","ai-infrastructure",{"name":1692,"slug":1693,"type":15},"Cost Optimization","cost-optimization",{"name":1695,"slug":1696,"type":15},"LLM","llm",{"name":1698,"slug":1699,"type":15},"Performance","performance",{"name":1679,"slug":1680,"type":15},"2026-04-06T18:40:44.377464",{"slug":1703,"name":1703,"fn":1704,"description":1705,"org":1706,"tags":1707,"stars":28,"repoUrl":29,"updatedAt":1713},"ai-generation-persistence","implement persistence patterns for AI generations","AI generation persistence patterns — unique IDs, addressable URLs, database storage, and cost tracking for every LLM generation",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1708,1709,1712],{"name":1692,"slug":1693,"type":15},{"name":1710,"slug":1711,"type":15},"Database","database",{"name":1695,"slug":1696,"type":15},"2026-04-06T18:41:08.513425",600,{"items":1716,"total":1913},[1717,1738,1761,1778,1794,1811,1830,1842,1856,1870,1882,1897],{"slug":1718,"name":1718,"fn":1719,"description":1720,"org":1721,"tags":1722,"stars":1735,"repoUrl":1736,"updatedAt":1737},"prior-auth-packet-builder","build healthcare prior authorization packets","Build a concise prior authorization packet from local case files and payer policy docs.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1723,1726,1729,1732],{"name":1724,"slug":1725,"type":15},"Documents","documents",{"name":1727,"slug":1728,"type":15},"Healthcare","healthcare",{"name":1730,"slug":1731,"type":15},"Insurance","insurance",{"name":1733,"slug":1734,"type":15},"Regulatory Compliance","regulatory-compliance",28169,"https:\u002F\u002Fgithub.com\u002Fopenai\u002Fopenai-agents-python","2026-04-16T05:11:39.180399",{"slug":1739,"name":1739,"fn":1740,"description":1741,"org":1742,"tags":1743,"stars":1758,"repoUrl":1759,"updatedAt":1760},"aspnet-core","build ASP.NET Core web applications","Build, review, refactor, or architect ASP.NET Core web applications using current official guidance for .NET web development. Use when working on Blazor Web Apps, Razor Pages, MVC, Minimal APIs, controller-based Web APIs, SignalR, gRPC, middleware, dependency injection, configuration, authentication, authorization, testing, performance, deployment, or ASP.NET Core upgrades.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1744,1747,1749,1752,1755],{"name":1745,"slug":1746,"type":15},".NET","dotnet",{"name":1748,"slug":1739,"type":15},"ASP.NET Core",{"name":1750,"slug":1751,"type":15},"Blazor","blazor",{"name":1753,"slug":1754,"type":15},"C#","csharp",{"name":1756,"slug":1757,"type":15},"Web Development","web-development",23787,"https:\u002F\u002Fgithub.com\u002Fopenai\u002Fskills","2026-04-12T05:07:02.819491",{"slug":1762,"name":1762,"fn":1763,"description":1764,"org":1765,"tags":1766,"stars":1758,"repoUrl":1759,"updatedAt":1777},"chatgpt-apps","build ChatGPT Apps SDK applications","Build, scaffold, refactor, and troubleshoot ChatGPT Apps SDK applications that combine an MCP server and widget UI. Use when Codex needs to design tools, register UI resources, wire the MCP Apps bridge or ChatGPT compatibility APIs, apply Apps SDK metadata or CSP or domain settings, or produce a docs-aligned project scaffold. Prefer a docs-first workflow by invoking the openai-docs skill or OpenAI developer docs MCP tools before generating code.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1767,1770,1773,1776],{"name":1768,"slug":1769,"type":15},"Apps SDK","apps-sdk",{"name":1771,"slug":1772,"type":15},"ChatGPT","chatgpt",{"name":1774,"slug":1775,"type":15},"MCP","mcp",{"name":9,"slug":8,"type":15},"2026-04-12T05:07:05.468097",{"slug":1779,"name":1779,"fn":1780,"description":1781,"org":1782,"tags":1783,"stars":1758,"repoUrl":1759,"updatedAt":1793},"cli-creator","build CLIs from API docs","Build a composable CLI for Codex from API docs, an OpenAPI spec, existing curl examples, an SDK, a web app, an admin tool, or a local script. Use when the user wants Codex to create a command-line tool that can run from any repo, expose composable read\u002Fwrite commands, return stable JSON, manage auth, and pair with a companion skill.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1784,1787,1790],{"name":1785,"slug":1786,"type":15},"API Development","api-development",{"name":1788,"slug":1789,"type":15},"CLI","cli",{"name":1791,"slug":1792,"type":15},"Codex","codex","2026-04-12T05:07:04.132762",{"slug":1795,"name":1795,"fn":1796,"description":1797,"org":1798,"tags":1799,"stars":1758,"repoUrl":1759,"updatedAt":1810},"cloudflare-deploy","deploy projects to Cloudflare","Deploy applications and infrastructure to Cloudflare using Workers, Pages, and related platform services. Use when the user asks to deploy, host, publish, or set up a project on Cloudflare.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1800,1803,1806,1807],{"name":1801,"slug":1802,"type":15},"Cloudflare","cloudflare",{"name":1804,"slug":1805,"type":15},"Cloudflare Pages","cloudflare-pages",{"name":1648,"slug":1649,"type":15},{"name":1808,"slug":1809,"type":15},"Deployment","deployment","2026-04-12T05:07:14.275118",{"slug":1812,"name":1812,"fn":1813,"description":1814,"org":1815,"tags":1816,"stars":1758,"repoUrl":1759,"updatedAt":1829},"define-goal","define and set measurable project goals","Help the user define a concrete, measurable goal before starting work, especially when they ask to use the goal tool, create a goal, set an objective, clarify success criteria, or turn a fuzzy intention into a quantitative outcome. Use this skill for goal creation and goal refinement only; it does not manage durable snapshots, decision logs, or long-running execution artifacts.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1817,1820,1823,1826],{"name":1818,"slug":1819,"type":15},"Productivity","productivity",{"name":1821,"slug":1822,"type":15},"Project Management","project-management",{"name":1824,"slug":1825,"type":15},"Strategy","strategy",{"name":1827,"slug":1828,"type":15},"Task Management","task-management","2026-05-23T06:17:16.870838",{"slug":1831,"name":1831,"fn":1832,"description":1833,"org":1834,"tags":1835,"stars":1758,"repoUrl":1759,"updatedAt":1841},"figma","translate Figma designs into code","Use the Figma MCP server to fetch design context, screenshots, variables, and assets from Figma, and to translate Figma nodes into production code. Trigger when a task involves Figma URLs, node IDs, design-to-code implementation, or Figma MCP setup and troubleshooting.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1836,1837,1839,1840],{"name":1609,"slug":1610,"type":15},{"name":1838,"slug":1831,"type":15},"Figma",{"name":1667,"slug":1668,"type":15},{"name":1774,"slug":1775,"type":15},"2026-04-12T05:06:47.939943",{"slug":1843,"name":1843,"fn":1844,"description":1845,"org":1846,"tags":1847,"stars":1758,"repoUrl":1759,"updatedAt":1855},"figma-code-connect-components","connect Figma designs to code components","Connects Figma design components to code components using Code Connect mapping tools. Use when user says \"code connect\", \"connect this component to code\", \"map this component\", \"link component to code\", \"create code connect mapping\", or wants to establish mappings between Figma designs and code implementations. For canvas writes via `use_figma`, use `figma-use`.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1848,1849,1852,1853,1854],{"name":1609,"slug":1610,"type":15},{"name":1850,"slug":1851,"type":15},"Design System","design-system",{"name":1838,"slug":1831,"type":15},{"name":1667,"slug":1668,"type":15},{"name":1676,"slug":1677,"type":15},"2026-05-10T05:59:52.971881",{"slug":1857,"name":1857,"fn":1858,"description":1859,"org":1860,"tags":1861,"stars":1758,"repoUrl":1759,"updatedAt":1869},"figma-create-design-system-rules","generate design system rules from Figma","Generates custom design system rules for the user's codebase. Use when user says \"create design system rules\", \"generate rules for my project\", \"set up design rules\", \"customize design system guidelines\", or wants to establish project-specific conventions for Figma-to-code workflows. Requires Figma MCP server connection.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1862,1863,1864,1867,1868],{"name":1609,"slug":1610,"type":15},{"name":1850,"slug":1851,"type":15},{"name":1865,"slug":1866,"type":15},"Documentation","documentation",{"name":1838,"slug":1831,"type":15},{"name":1667,"slug":1668,"type":15},"2026-05-16T06:07:47.821474",{"slug":1871,"name":1871,"fn":1872,"description":1873,"org":1874,"tags":1875,"stars":1758,"repoUrl":1759,"updatedAt":1881},"figma-implement-design","translate Figma designs into application code","Translates Figma designs into production-ready application code with 1:1 visual fidelity. Use when implementing UI code from Figma files, when user mentions \"implement design\", \"generate code\", \"implement component\", provides Figma URLs, or asks to build components matching Figma specs. For Figma canvas writes via `use_figma`, use `figma-use`.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1876,1877,1878,1879,1880],{"name":1609,"slug":1610,"type":15},{"name":1838,"slug":1831,"type":15},{"name":1667,"slug":1668,"type":15},{"name":1676,"slug":1677,"type":15},{"name":1756,"slug":1757,"type":15},"2026-05-16T06:07:40.583615",{"slug":1883,"name":1883,"fn":1884,"description":1885,"org":1886,"tags":1887,"stars":1758,"repoUrl":1759,"updatedAt":1896},"hatch-pet","create animated pets for Codex","Create, repair, validate, visually QA, and package Codex-compatible animated pets and pet spritesheets from character art, generated images, company or prospect brand cues, or visual references. Use when a user wants a lightweight-worker Codex pet workflow, a non-pixel custom pet style, a prospect or company mascot pet, or a full 8x9 animated pet atlas with transparent unused cells, QA contact sheets, and pet.json packaging. This skill composes the installed $imagegen system skill for visual generation and uses bundled scripts for deterministic spritesheet assembly.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1888,1891,1892,1895],{"name":1889,"slug":1890,"type":15},"Animation","animation",{"name":1791,"slug":1792,"type":15},{"name":1893,"slug":1894,"type":15},"Creative","creative",{"name":1609,"slug":1610,"type":15},"2026-05-02T05:31:48.48485",{"slug":1898,"name":1898,"fn":1899,"description":1900,"org":1901,"tags":1902,"stars":1758,"repoUrl":1759,"updatedAt":1912},"imagegen","generate and edit raster images","Generate or edit raster images when the task benefits from AI-created bitmap visuals such as photos, illustrations, textures, sprites, mockups, or transparent-background cutouts. Use when Codex should create a brand-new image, transform an existing image, or derive visual variants from references, and the output should be a bitmap asset rather than repo-native code or vector. Do not use when the task is better handled by editing existing SVG\u002Fvector\u002Fcode-native assets, extending an established icon or logo system, or building the visual directly in HTML\u002FCSS\u002Fcanvas.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":8},[1903,1904,1905,1908,1911],{"name":1893,"slug":1894,"type":15},{"name":1609,"slug":1610,"type":15},{"name":1906,"slug":1907,"type":15},"Image Generation","image-generation",{"name":1909,"slug":1910,"type":15},"Images","images",{"name":9,"slug":8,"type":15},"2026-05-15T06:23:24.312127",675]