swiftship/internal/skills/data/features/storage/SKILL.md
Storage patterns: in-memory defaults, @AppStorage for flags, SwiftData for persistence. Use when implementing app features related to storage.
npx skillsauth add abdullah4ai/apple-dev-docs storageInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Unless the user explicitly requests persistence (words like "save", "persist", "database", "storage", "SwiftData"), use in-memory data with rich dummy data:
| Data Type | Storage | API |
|-----------|---------|-----|
| App data (notes, tasks, items) | In-memory (DEFAULT) | Plain struct, @Observable arrays |
| Simple flags and settings (sort order, preferred units) | UserDefaults | @AppStorage |
| Transient UI state (sheet shown, selected tab) | In-memory | @State |
Models are plain structs with a static sampleData array:
import Foundation
struct Note: Identifiable {
let id: UUID
var title: String
var content: String
var createdAt: Date
var isPinned: Bool
init(title: String, content: String = "", isPinned: Bool = false) {
self.id = UUID()
self.title = title
self.content = content
self.createdAt = Date()
self.isPinned = isPinned
}
static let sampleData: [Note] = [
Note(title: "Meeting Notes", content: "Discuss Q2 roadmap with the team", isPinned: true),
Note(title: "Recipe: Pasta Carbonara", content: "Eggs, pecorino, guanciale, black pepper"),
Note(title: "Book Recommendations", content: "The Midnight Library, Project Hail Mary"),
Note(title: "Workout Plan", content: "Mon: Upper body, Wed: Legs, Fri: Cardio"),
Note(title: "Gift Ideas", content: "Wireless earbuds, cookbook, plant pot"),
]
}
ViewModel holds data directly — no SwiftData, no modelContext:
import SwiftUI
@Observable
@MainActor
class NotesListViewModel {
var notes: [Note] = Note.sampleData
var searchText = ""
var filteredNotes: [Note] {
if searchText.isEmpty { return notes }
return notes.filter { $0.title.localizedCaseInsensitiveContains(searchText) }
}
func addNote(title: String) {
let note = Note(title: title)
notes.insert(note, at: 0)
}
func deleteNote(_ note: Note) {
notes.removeAll { $0.id == note.id }
}
}
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
Use for simple key-value settings:
@AppStorage("sortOrder") private var sortOrder = "date"
@AppStorage("showCompleted") private var showCompleted = true
Use @Model, @Query, .modelContainer ONLY if user says "save", "persist", "database", "storage", or "SwiftData":
import SwiftData
@Model
class Note {
var title: String
var content: String
var createdAt: Date
init(title: String, content: String = "") {
self.title = title
self.content = content
self.createdAt = Date()
}
}
testing
Use for 3D games: racing, 3D sports, board games, marble maze, tower defense, bowling. SceneKit + SceneView architecture, 3D scene hierarchy, physics, game loop, primitives, materials, cameras, particles, audio.
documentation
Game UI patterns: SwiftUI HUD overlays on SpriteKit, menus (main/pause/game-over), virtual joystick/d-pad, score displays, health bars, tutorial onboarding.
tools
Download free game sprites/textures/3D models and generate procedural assets. Covers nw_download_asset tool, texture factories, sprite atlas organization, 3D model loading, and programmatic asset creation.
testing
Use for 2D games: arcade, puzzle, sports, ping pong, platformer, shooter, 2D racing. SpriteKit + SpriteView architecture, scene hierarchy, physics, game loop, audio, particles, game feel.