{"id":"c9647616-95dc-41e4-9d6b-ae5607c2c7ee","shortId":"Qt39bJ","kind":"skill","title":"swiftui-view-refactor","tagline":"Refactor SwiftUI views into smaller components with stable, explicit data flow.","description":"# 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/models, and view models only when the request or existing code clearly requires one.\n\n## When to Use\n- When cleaning up a large SwiftUI view or splitting long `body` implementations.\n- When you need smaller subviews, explicit dependency injection, or better Observation usage.\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`/`public` `let`\n- `@State` / other stored properties\n- computed `var` (non-view)\n- `init`\n- `body`\n- computed view builders / other view helpers\n- helper / 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/models, 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/models.\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/else`.\n- Prefer a single stable base view with conditions inside sections/modifiers (`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/models 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/modifiers.\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/mv-patterns.md`.\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 `// 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\n## Limitations\n- Use this skill only when the task clearly matches the scope described above.\n- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.\n- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.","tags":["swiftui","view","refactor","antigravity","awesome","skills","sickn33","agent-skills","agentic-skills","ai-agent-skills","ai-agents","ai-coding"],"capabilities":["skill","source-sickn33","skill-swiftui-view-refactor","topic-agent-skills","topic-agentic-skills","topic-ai-agent-skills","topic-ai-agents","topic-ai-coding","topic-ai-workflows","topic-antigravity","topic-antigravity-skills","topic-claude-code","topic-claude-code-skills","topic-codex-cli","topic-codex-skills"],"categories":["antigravity-awesome-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/sickn33/antigravity-awesome-skills/swiftui-view-refactor","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add sickn33/antigravity-awesome-skills","source_repo":"https://github.com/sickn33/antigravity-awesome-skills","install_from":"skills.sh"}},"qualityScore":"0.700","qualityRationale":"deterministic score 0.70 from registry signals: · indexed on github topic:agent-skills · 34460 github stars · SKILL.md body (8,007 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-22T06:51:57.790Z","embedding":null,"createdAt":"2026-04-18T21:45:45.439Z","updatedAt":"2026-04-22T06:51:57.790Z","lastSeenAt":"2026-04-22T06:51:57.790Z","tsv":"'1':89,757 '16':741 '17':713,862 '2':133,766 '3':219,788 '300':959 '3b':436 '4':530,808 '5':607,826 '6':363,427,704,851 '7':872 'action':438,452,498,769,986 'aggress':963 'align':360,424 'alreadi':613 'ask':1054 'async':131,260,521 'avoid':407,536,543,596,677,726,797,813 'await':505,513,527 'base':560,688,818 'becom':313 'behavior':874 'better':84 'bind':296,379 'block':894 'bodi':73,123,184,231,327,356,384,410,444,457,485,544,587,774,791,906 'bootstrapifneed':678 'bottom':93,927 'boundari':1062 'branch':262,553,574,819 'break':998 'broader':579 'build':278 'builder':126,904 'buri':460 'busi':43,151,461,480,776,881 'button':451,496 'call':470,645 'callback':297 'canedit':594,603 'caus':576 'chang':878 'churn':578 'clarif':1056 'clean':64 'clear':57,644,1029 'code':56,643 'comment':984 'complet':550 'complex':974 'compon':10 'comput':117,124,226,269,546,804,897,902,911,977 'condit':540,563,822,893 'confirm':852 'contain':149,241 'control':494 'convent':105 'core':87 'creat':668 'criteria':1065 'data':14,295,929 'dedic':222,247,794,968 'default':29,134,631 'delay':682 'depend':39,81,200,661,696,697,702,703 'deploy':737,868 'describ':1033 'deserv':264 'differ':551 'disabl':500,568 'documentslistcont':591 'documentslistview':599 'domain':176 'earlier':743 'editabledocumentslist':604 'edittoolbar':595 'effect':441,772 'els':524,605 'enforc':94 'ensur':809 'entir':280,306 'environ':42,109,155,174,199,1045 'environment-specif':1044 'especi':255 'etc':570 'exampl':685 'exceed':958 'exist':55,99,642,652,831 'expert':1050 'explicit':13,25,80,293,616,626,725,834,888 'explicit-ne':625 'express':144 'extens':981,993 'extra':582 'extract':246,299,311,437,793,964,1008 'fals':390 'favor':153 'file':100,323,957,1020 'filter':415 'filterchip':398 'filtereditem':343 'filteropt':337,338,377,378,382,393 'filtersect':336,374 'first':943 'flag':230 'flow':15,930 'font':366,370,430,434 'footer':417 'footersect':344 'foreach':392 'fragment':290 'func':510,516 'function':132 'genuin':732 'get':205 'giant':1000 'good':916 'guard':522 'guidanc':944 'guidelin':88 'hand':303 'handl':610,952 'header':286,414,420 'headersect':331,347 'helper':129,130,229,272,807,988 'hide':973 'horizont':388 'hstack':391 'id':159,394,503 'ident':577 'if/else':555 'imper':938 'implement':74 'includ':739 'independ':316,1013 'init':122,666,676,695,850,914 'initi':848 'initialvalu':700 'inject':82,168,752 'inlin':453,768 'input':294,1059 'insid':463,564 'instead':301,971 'intact':875 'introduc':187,634 'invalid':580 'invent':213 'io':712,740,861 'issav':501 'isselect':401 'item':342 'keep':175,268,447,531,781,873,901 'larg':67,206,892,896,950 'large-view':949 'layer':218 'layout':879,932,936 'lead':361,425 'legaci':623,753,863 'let':112,349,352,376 'level':539,573,816 'lightweight':142 'like':488,491 'limit':1021 'line':960 'list':330,413,590 'local':33,104,194,824 'logic':44,152,177,243,462,481,777,882,939 'long':72,790 'longer':235 'make':653,920 'mani':803,976 'mark':983 'match':762,1030 'meaning':317,965,1014 'method':473 'mirror':193 'miss':1067 'mix':935 'model':49,167,172,190,217,609,620,637,651,671,755,830,839,847,859 'move':318,478,775,821,1015 'multipl':242 'must':107 'mv':136,942 'mv-first':941 'mvvm':138 'need':77,627,733 'new':215 'non':120,252,449,656,843,909 'non-opt':655,842 'non-trivi':251,448 'non-view':119,908 'note':885 'observ':85,687,705,708,723,754,853,858 'observation-bas':686 'observedobject':750 'onappear':465 'onchang':161,466 'one':59,238,647 'ontapgestur':404 'opac':567 'option':396,399,400,402,406,657,727,837,844 'orchestr':146,784 'order':91,96,764 'output':1039 'overlay':566 'overview':19 'own':720 'owner':748 'parent':307 'pass':291,660,722 'pattern':628,679 'permiss':1060 'plus':931 'point':147 'possibl':659 'prefer':221,245,324,469,556,584,886 'present':614 'preserv':108 'preview':267 'privat':110,284,345,372,418,472,509,515,691,980 'properti':116,232,900,978 'public':111 'queri':156 'rare':273 'rational':946 'reach':163 'read':487,923 'readonlydocumentslist':606 'real':479 'rebuild':798 'recomput':583 'refactor':4,5,18,20,918 'refer':709 'references/mv-patterns.md':948 'refresh':468 'reload':506,517 'remov':767 'reorder':758 'replac':836 'request':53,617,640,884 'requir':58,835,870,1058 'result':416,525 'resultssect':341 'return':526,549 'reus':1011 'reusabl':314 'review':1051 'root':552,572,857 'root-level':571 'rough':237 'rule':765 'safeti':1061 'save':497,499,511 'saveasync':514 'scope':1032 'screen':203,239,281,800,1001 'scrollview':387 'searchservice.search':528 'searchtext':504,508,519,529 'searchtext.isempty':523 'section':244,254,966 'sections/modifiers':565,825 'see':947 'selectedfilt':339,340,381,403,405 'self':395 'servic':169 'services/models':46,179,483,779 'setup':683 'share':38,171 'shorten':789 'showsind':389 'side':440,771 'singl':558 'skill':1024 'skill-swiftui-view-refactor' 'small':24,275,292,471,887 'smaller':9,78,1003 'someviewmodel':694,701 'source-sickn33' 'space':362,426 'specif':1046 'split':71,207,961 'stabl':12,26,533,559,810 'state':34,113,143,154,196,259,308,690,699,717,728,845,855 'stateobject':745 'stop':1052 'store':115,714 'string':351,354,520 'strong':220 'stronger':103 'struct':346,373 'structur':812 'style':289 'subheadlin':371,435 'substitut':996,1042 'subtitl':334,335,353,369,433 'subview':79,211,223,300,312,795,1009 'success':1064 'swap':542,575,820 'swift':325,408,495,585,597,689 'swiftui':2,6,16,21,32,68,917,955 'swiftui-view-refactor':1 'target':738,869 'task':157,158,464,502,512,1028 'test':1048 'text':364,368,428,432 'thin':783 'titl':332,333,350,365,429 'title2':367,431 'toolbar':569,592 'top':92,538,815,925 'top-level':537,814 'top-to-bottom':924 'topic-agent-skills' 'topic-agentic-skills' 'topic-ai-agent-skills' 'topic-ai-agents' 'topic-ai-coding' 'topic-ai-workflows' 'topic-antigravity' 'topic-antigravity-skills' 'topic-claude-code' 'topic-claude-code-skills' 'topic-codex-cli' 'topic-codex-skills' 'toward':23 'treat':618,992,1037 'tree':535 'trivial':253,450 'type':28,224,249,710,796,890,970,1005 'ui':209,489,731 'unless':97,638,729,883 'usag':86,706,854 'use':62,744,979,1022 'valid':1047 'vanilla':31 'var':118,285,326,355,380,383,409,419,586,598,692,912 'via':173,554,665 'view':3,7,17,22,27,37,48,69,90,121,125,128,139,166,183,189,195,216,228,248,271,288,329,348,358,375,386,412,422,456,476,493,534,541,547,561,589,601,608,619,636,650,664,670,674,721,760,787,806,811,829,838,846,889,899,903,910,922,951,956,969,1004 'viewmodel':693,698 'vstack':359,423 'work':261 'workaround':684 'workflow':756 'wrap':198 'wrapper':864","prices":[{"id":"43d39e35-0d64-4745-93f5-5070430185be","listingId":"c9647616-95dc-41e4-9d6b-ae5607c2c7ee","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"sickn33","category":"antigravity-awesome-skills","install_from":"skills.sh"},"createdAt":"2026-04-18T21:45:45.439Z"}],"sources":[{"listingId":"c9647616-95dc-41e4-9d6b-ae5607c2c7ee","source":"github","sourceId":"sickn33/antigravity-awesome-skills/swiftui-view-refactor","sourceUrl":"https://github.com/sickn33/antigravity-awesome-skills/tree/main/skills/swiftui-view-refactor","isPrimary":false,"firstSeenAt":"2026-04-18T21:45:45.439Z","lastSeenAt":"2026-04-22T06:51:57.790Z"}],"details":{"listingId":"c9647616-95dc-41e4-9d6b-ae5607c2c7ee","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"sickn33","slug":"swiftui-view-refactor","github":{"repo":"sickn33/antigravity-awesome-skills","stars":34460,"topics":["agent-skills","agentic-skills","ai-agent-skills","ai-agents","ai-coding","ai-workflows","antigravity","antigravity-skills","claude-code","claude-code-skills","codex-cli","codex-skills","cursor","cursor-skills","developer-tools","gemini-cli","gemini-skills","kiro","mcp","skill-library"],"license":"mit","html_url":"https://github.com/sickn33/antigravity-awesome-skills","pushed_at":"2026-04-22T06:40:00Z","description":"Installable GitHub library of 1,400+ agentic skills for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and more. Includes installer CLI, bundles, workflows, and official/community skill collections.","skill_md_sha":"cc35d0b885fc9e82fca66859017846d509115c4e","skill_md_path":"skills/swiftui-view-refactor/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/sickn33/antigravity-awesome-skills/tree/main/skills/swiftui-view-refactor"},"layout":"multi","source":"github","category":"antigravity-awesome-skills","frontmatter":{"name":"swiftui-view-refactor","description":"Refactor SwiftUI views into smaller components with stable, explicit data flow."},"skills_sh_url":"https://skills.sh/sickn33/antigravity-awesome-skills/swiftui-view-refactor"},"updatedAt":"2026-04-22T06:51:57.790Z"}}