Awesome-omni-skill apple-dev-best-practices
Apple platform development best practices for Swift 6, SwiftUI, SwiftData, and iOS/macOS apps. Use when building any iOS or macOS app, writing Swift code, designing SwiftUI views, working with Xcode projects, implementing navigation, state management, concurrency, networking, persistence, or testing on Apple platforms. Triggers on Swift, SwiftUI, iOS, macOS, Xcode, UIKit, SwiftData, Core Data, XCTest, StoreKit, CloudKit, MapKit, HealthKit, or any Apple framework. Also use when reviewing Swift code, debugging iOS apps, migrating UIKit to SwiftUI, or planning Apple platform architecture.
git clone https://github.com/diegosouzapw/awesome-omni-skill
T=$(mktemp -d) && git clone --depth=1 https://github.com/diegosouzapw/awesome-omni-skill "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/development/apple-dev-best-practices" ~/.claude/skills/diegosouzapw-awesome-omni-skill-apple-dev-best-practices && rm -rf "$T"
skills/development/apple-dev-best-practices/SKILL.mdApple Development Best Practices
Modern Apple platform development using Swift 6 and SwiftUI as primary frameworks.
When to Use
Use this skill when:
- Building iOS or macOS apps with Swift 6 and SwiftUI
- Designing navigation, state management, or concurrency patterns for Apple platforms
- Working with SwiftData, Core Data, StoreKit, CloudKit, or other Apple frameworks
- Reviewing Swift code or planning Apple platform architecture
When NOT to Use
Do NOT use this skill when:
- Building cross-platform mobile apps (Flutter, React Native, Kotlin Multiplatform) — use a cross-platform mobile persona instead, because Apple-specific patterns like
and@Observable
don't applyNavigationStack - Writing server-side Swift (Vapor, Hummingbird) — use a backend engineering persona instead, because server-side Swift has different concurrency, deployment, and architecture concerns
Core Philosophy
Build apps that are previewable, testable, and maintainable. A previewable app is a testable app. A testable app is a maintainable app.
Swift 6 Standards
- Strict concurrency enabled — treat all warnings as errors
over@Observable
(iOS 17+)ObservableObject
for all asynchronous operationsasync/await- Value types (structs) preferred over reference types (classes) unless identity semantics needed
for early exits, never deeply nestedguard
chainsif let- Typed errors via
conformance — no raw stringsLocalizedError - No force unwrapping (
) without documented justification! - Follow Apple's Swift API Design Guidelines for naming
SwiftUI Architecture
State Management — Single Source of Truth (SSOT)
// Local view state → @State @State private var isExpanded = false // Observable model → @State with @Observable class @State private var viewModel = RecipeViewModel() // Shared across view tree → @Environment @Environment(\.recipeStore) private var store // Bindings to @Observable → @Bindable @Bindable var viewModel: RecipeViewModel
Rules:
for view-local state only — never shared across views@State
classes for ViewModels (replaces@Observable
+ObservableObject
)@Published
for dependency injection (services, stores, settings)@Environment- Never pass view models more than 2 levels deep — use Environment instead
Navigation — NavigationStack with Type-Safe Routing
enum Route: Hashable { case recipeDetail(Recipe) case settings case profile(User) } @Observable final class Router { var path = NavigationPath() func navigate(to route: Route) { path.append(route) } }
Rules:
only — never deprecatedNavigationStackNavigationView- Type-safe routing via
enumHashable - Router as
class in@Observable@Environment - Sheet presentation via optional ViewModel on parent
View Composition
- Extract subviews at 50+ lines or when reusable
- Max 100 lines per view file before mandatory extraction
- Custom ViewModifiers for shared styling — not repeated inline styles
- Never use
— destroys diffing performance and identityAnyView - Prefer
closures over@ViewBuilder
for type erasureAnyView
Performance
/LazyVStack
inside ScrollView — never eager stacks for large listsLazyHStack
wrapper for complex views that rarely changeEquatableView- Keep view body pure — no side effects, no network calls
- Use
modifier for async work, not.task
with TaskonAppear - Profile with SwiftUI Performance Instrument (Xcode 16+)
Project Structure
AppName/ ├── App/ # App entry, lifecycle, configuration │ ├── AppNameApp.swift │ └── AppDelegate.swift # Only if needed for UIKit integration ├── Features/ # Feature modules (self-contained) │ ├── Recipes/ │ │ ├── Views/ # SwiftUI views │ │ ├── ViewModels/ # @Observable classes │ │ └── Models/ # Data models (structs) │ ├── MealPlanning/ │ └── Community/ ├── Core/ # Shared infrastructure │ ├── Extensions/ │ ├── Services/ # Networking, auth, analytics │ ├── Persistence/ # SwiftData / Core Data │ └── Components/ # Reusable UI components ├── Resources/ # Assets, Localizations, Fonts └── Tests/ ├── UnitTests/ # ViewModel + Service tests └── UITests/ # Critical user flow tests
Rules:
- Features are self-contained — no cross-feature imports
- Shared code lives in
onlyCore/ - Each feature has its own Views, ViewModels, Models
- Feature folders mirror navigation structure
Concurrency
// Actor for thread-safe shared state actor RecipeStore { private var cache: [UUID: Recipe] = [:] func recipe(for id: UUID) -> Recipe? { cache[id] } } // @MainActor for UI-bound classes @MainActor @Observable final class RecipeListViewModel { var recipes: [Recipe] = [] var isLoading = false func loadRecipes() async { isLoading = true defer { isLoading = false } recipes = await recipeService.fetchAll() } }
Rules:
on all ViewModels@MainActor
for shared mutable stateactor
conformance for types crossing isolation boundariesSendable- Never
— useDispatchQueue.main.async
instead@MainActor
only insideTask
modifier or explicit user-initiated actions.task
for parallel independent workTaskGroup
Testing
- Swift Testing (
,@Test
) preferred over XCTest for new code#expect - Unit tests for all ViewModel logic — 80%+ coverage on business logic
- UI tests for critical user flows only (login, purchase, core CRUD)
- Dependency injection via protocols for testability
- No singletons in production code — inject via
@Environment - Preview-driven development: if a view is hard to preview, it's hard to test
Persistence
SwiftData (iOS 17+) is the default persistence layer:
@Model final class Recipe { var name: String var ingredients: [String] var instructions: String @Relationship(deleteRule: .cascade) var steps: [CookingStep] }
Rules:
classes for SwiftData — not structs@Model- Define
explicitly with delete rules@Relationship - Use
in views for automatic updates@Query - ModelContainer configured in App entry point
- Migration strategy documented before schema changes
Networking
protocol RecipeServiceProtocol: Sendable { func fetchAll() async throws -> [Recipe] func create(_ recipe: Recipe) async throws -> Recipe } struct RecipeService: RecipeServiceProtocol { private let session: URLSession private let decoder: JSONDecoder func fetchAll() async throws -> [Recipe] { let (data, response) = try await session.data(from: endpoint) guard let http = response as? HTTPURLResponse, (200...299).contains(http.statusCode) else { throw AppError.networkError(statusCode: http.statusCode) } return try decoder.decode([Recipe].self, from: data) } }
Rules:
- Protocol-based services for testability
conformance on all service typesSendable- Typed errors with
LocalizedError - No third-party HTTP libraries unless justified (URLSession is sufficient)
- Certificate pinning for sensitive data
Security
- Keychain for credentials, tokens, secrets — never UserDefaults
- App Transport Security enabled — HTTPS only
- No sensitive data in logs or crash reports
only for non-sensitive user preferences@AppStorage- Input validation on all user-provided data
- Privacy manifest (
) for App Store compliancePrivacyInfo.xcprivacy
Accessibility
- Every interactive element needs an
accessibilityLabel - Use semantic SwiftUI elements (Button, Toggle, Picker) — not
.onTapGesture - Support Dynamic Type — no hardcoded font sizes
- Minimum tap target 44x44pt
- Test with VoiceOver before shipping
Deep References
See
references/ for detailed guidance:
— Advanced view patterns, custom layouts, animationsreferences/swiftui-patterns.md
— Actor isolation, Sendable, structured concurrencyreferences/concurrency-guide.md
— XcodeBuildMCP setup, hooks, sandbox modesreferences/xcode-claude-integration.md
— UIKit → SwiftUI, CoreData → SwiftData pathsreferences/migration-guide.md