design(app-creator): S6 — forfatt ios-app referanse-domain-pack + start claude-code-plugin-pakke

- domain-packs/ios-app/: komplett referanse-pakke (8 komponenter) — pack.json med
  components-map, conventions.md, 4 patterns/, gotchas.md, checklist.md splittet i 3
  (App Store-submission + security-privacy/MASVS 2.1 + accessibility/WCAG 2.2 AA),
  3 scaffold/-maler (PrivacyInfo.xcprivacy-plist, NSUsageDescription-inventar,
  ASC-metadata), eksempel-feature-brief i Voyage strict-mode-format, glossary.md.
  Hver teknisk påstand verifisert mot Apple Developer / W3C WAI / OWASP MAS.
- domain-packs/claude-code-plugin/: stub — pack.json + conventions.md + gotchas.md
  + checklist.md + glossary.md ferdige (ekstrahert fra ktg-privat-konvensjoner);
  patterns/scaffold/examples er stubs.
- docs/domain-pack-spec.md: § D2/D3/D4/D7 + restrisiko oppdatert — pack.json
  components-felt, core/supplementary låst til manifestet, checklist-splitt-konvensjon,
  snapshot-materialiserings-mekanikk presisert, iOS-versjons-korreksjon (iOS 26, ikke
  "iOS 18/19"), D7 → "forfattet i S6".
- prototype-run/friksjon.md: #9 (Akashic-briefen refererer "iOS 19" som ikke finnes —
  rettes i S7) + S6-prosessnotater (checklist-splitt bekreftet nødvendig, components-gap
  fylt, verifiserings-asymmetri ios-app vs claude-code-plugin notert).
- CLAUDE.md: peker til domain-packs/ under § Status.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-05-12 14:06:54 +02:00
commit 2011507ea1
27 changed files with 1059 additions and 17 deletions

View file

@ -0,0 +1,33 @@
<!-- domain-pack: ios-app · component: patterns · supplementary -->
<!-- verified 2026-05-12 against SwiftData docs (iOS 17+). -->
# Pattern: offline-first med SwiftData
**Når:** appen eier sine data lokalt, ingen backend (eller backend kun som sync-tillegg). Den enkleste personvern-posisjonen og det riktige valget for små single-purpose-apper.
## Form
- **`@Model`-klasser** definerer skjemaet. Ingen `NSManagedObject`, ingen `.xcdatamodeld`.
- **`ModelContainer`** opprettes ved app-start; `ModelContext` injiseres i view-treet via `.modelContainer(...)`.
- **`@Query`** i views for reaktiv henting; muter via `modelContext.insert/delete` + (auto)`save`.
- **Migrasjoner:** `SchemaMigrationPlan` med versjonerte `VersionedSchema`-er. Planlegg fra dag 1 — skjemaet *vil* endre seg.
## Offline-first-disiplin
- All lesning/skriving går mot lokal store; ingen view venter på nett.
- Hvis sync legges til senere: lokal store er sannheten, sync er en bakgrunnsoppgave som reconciler — ikke omvendt. Konflikt-strategi (last-write-wins / per-felt-merge) er en arkitektur-beslutning (fase 3-ADR).
- Backup: SwiftData-store ligger i app-containeren og dekkes av iCloud-enhets-backup automatisk. Eksplisitt iCloud-sync krever CloudKit-integrasjon (`ModelConfiguration(... , cloudKitDatabase: .automatic)`) — en bevisst tilleggsbeslutning, ikke default.
## Deling med widget / Live Activity
Store må ligge i en **App Group**-container for å være synlig for extensions. Se `patterns/widget-live-activities-shared-model.md` — dette pattern-et og det henger sammen for enhver app med widgets.
## Fallgruver
- **Ikke** anta at endringer i app-prosessen er øyeblikkelig synlige i widget-prosessen — refresh-timing er mer pålitelig på iOS 18+ enn iOS 17. ([Hacking with Swift — SwiftData i widgets](https://www.hackingwithswift.com/quick-start/swiftdata/how-to-access-a-swiftdata-container-from-widgets))
- **Ikke** behold en stor `ModelContainer` i minne i en widget-extension uten grunn — extension-minnebudsjettet er stramt.
- **Ikke** hopp over migrasjonsplanen "fordi appen er liten" — en uplanlagt skjemaendring etter lansering kan kreve destruktiv migrasjon.
## App-brief-signal som trigger dette pattern-et
"Ingen backend / all data lokal", "fungerer offline", "personvern-bevarende", "én bruker, egen enhet".