iOS Swift
Platform: ios | swift6
name: iOS Swift
platform: ios
language: swift6
ui_framework: swiftui
build: xcode
project_gen: xcodegen (project.yml → .xcodeproj, never commit .xcodeproj)
package_manager: spm
auth: shared-auth (SharedAuth Swift Package)
payments: storekit2
validation: "Codable + custom validation (protocol-based)"
i18n: string_catalog (.xcstrings, Xcode 16 native)
linter: swiftlint (SPM plugin or Homebrew)
formatter: swift-format (Apple, SPM plugin)
testing: XCTest + Swift Testing
pre_commit: lefthook (swiftlint + swift-format hooks)
key_packages:
- SwiftUI
- SwiftData (local persistence, NOT Core Data for new projects)
- CoreML
- Vision (face detection)
- StoreKit 2 (IAP)
- SharedAuth (shared auth)
- SwiftLint (linter)
- PostHog (analytics, posthog-ios SPM)
optional_packages:
on_device_ai:
- "FoundationModelsKit (community tools for Apple Foundation Models — iOS 26+, @Generable, WebTool, Calendar, token mgmt) github:rudrankriyam/FoundationModelsTools"
- "VecturaKit (on-device vector DB — MLTensor/MLX/NaturalLanguage embedders, hybrid search, persistent storage) github:rryam/VecturaKit"
- "LumoKit (on-device RAG pipeline — PDF/Markdown parsing via PicoDocs + VecturaKit vectors) github:rryam/LumoKit"
- "MLX-Outil (tool calling via MLX Swift — Qwen 3 1.7B, WeatherKit/HealthKit/DuckDuckGo/Calendar/Contacts tools) github:rudrankriyam/MLX-Outil"
music:
- "MusadoraKit (MusicKit companion — simplified Apple Music API) github:rryam/MusadoraKit"
ui:
- "ErabiKit (customizable SwiftUI settings screen) github:rudrankriyam/ErabiKit"
- "LiquidGlasKit (liquid glass effects for SwiftUI) github:rudrankriyam/LiquidGlasKit"
deploy: app_store
asc_cli:
name: "App Store Connect CLI (asc)"
install: "brew tap rudrankriyam/tap && brew install rudrankriyam/tap/asc"
auth: "asc auth login --name {Name} --key-id KEY_ID --issuer-id ISSUER_ID --private-key ~/AuthKey.p8"
commands:
- "asc apps list"
- "asc testflight beta-groups list --app APP_ID"
- "asc crashes --app APP_ID"
- "asc versions list --app APP_ID"
- "asc submit --version VERSION_ID"
- "asc sales daily"
- "asc analytics report"
note: "If `asc` not installed, suggest: brew tap rudrankriyam/tap && brew install rudrankriyam/tap/asc"
infra: none (native store distribution)
ci_cd: github_actions (xcodebuild + fastlane)
monitoring: posthog (analytics + errors, posthog-ios SPM)
logs:
xcode: "Xcode → Window → Devices and Simulators → View Device Logs"
console: "log stream --predicate 'subsystem == \"com.{org}.{name}\"' --style compact"
testflight: "App Store Connect → TestFlight → Crashes"
posthog: "PostHog dashboard → Error tracking"
local_build: "xcodegen generate && xcodebuild -scheme {Name} -sdk iphonesimulator build 2>&1 | tail -30"
architecture: MVVM
# Patterns learned from real projects via SoloGraph
patterns:
viewmodel: |
@Observable @MainActor final class — NOT ObservableObject.
Init injection for services (DI). Use @State in views.
Example: @State private var viewModel = MyViewModel()
services: |
actor for heavy/ML services (thread safety for models).
@Observable @MainActor final class for UI-facing services (recording, audio).
Always create protocol: Services/Protocols/FooServiceProtocol.swift.
Protocol enables mocking in tests and swapping implementations.
models: |
SwiftData @Model for persistent data (NOT Core Data, NOT Codable struct).
Plain structs for transient data (configs, settings, API responses).
permissions: |
Always request permissions before use — never assume granted.
Pattern: requestPermission() async -> Bool in the service protocol.
Show alert with "Open Settings" button when denied.
Use #if os(iOS) for AVAudioSession (unavailable on macOS).
directory_structure: |
App/ — @main entry point
Models/ — @Model schemas (SwiftData)
Views/ — SwiftUI views
ViewModels/ — @Observable @MainActor view models
Services/ — Business logic (actors + @Observable classes)
Services/Protocols/ — Service protocols for DI
Extensions/ — String+, Date+, etc.
Resources/ — Assets.xcassets, etc.
concurrency: |
@MainActor on all ViewModels and UI-facing services.
actor for data/ML services (own isolation domain).
async/await everywhere, no Combine for new code.
Timer callbacks: Task { @MainActor in ... } for safety.
xcodegen_required:
info_plist:
UISupportedInterfaceOrientations:
- UIInterfaceOrientationPortrait
- UIInterfaceOrientationPortraitUpsideDown
- UIInterfaceOrientationLandscapeLeft
- UIInterfaceOrientationLandscapeRight
UILaunchScreen: {}
UIApplicationSceneManifest:
UIApplicationSupportsMultipleScenes: true
bundle_id: "<org_domain>.<name>"
build_settings:
PRODUCT_BUNDLE_IDENTIFIER: "<org_domain>.<name>"
MARKETING_VERSION: "1.0.0"
CURRENT_PROJECT_VERSION: "1"
DEVELOPMENT_TEAM: <apple_dev_team>
CODE_SIGN_STYLE: Automatic
archive_command: |
xcodegen generate
xcodebuild archive -scheme <Name> -destination 'generic/platform=iOS' -archivePath build/<Name>.xcarchive
open build/<Name>.xcarchive # → Distribute App
xcode_mcp:
description: "Xcode 26.3+ exposes 20 MCP tools via xcrun mcpbridge — Claude Code works natively"
enable: "Xcode Settings → Intelligence → Enable MCP Server"
bridge: "xcrun mcpbridge"
key_tools:
- "BuildProject — build from CLI via MCP"
- "RunAllTests / RunSomeTests — run tests"
- "RenderPreview — render SwiftUI preview as image (visual verification!)"
- "DocumentationSearch — Apple docs + WWDC transcripts (MLX embeddings)"
- "ExecuteSnippet — Swift REPL for testing snippets"
- "XcodeRead / XcodeWrite / XcodeUpdate — file operations"
- "GetBuildLog — read build errors"
reference: "https://rudrank.com/exploring-xcode-using-mcp-tools-cursor-external-clients"
visual_testing:
type: simulator
boot: "xcrun simctl boot 'iPhone 16' 2>/dev/null || true"
screenshot: "xcrun simctl io booted screenshot /tmp/sim-screenshot.png"
install: "xcrun simctl install booted"
launch: "xcrun simctl launch booted"
logs: "xcrun simctl spawn booted log stream --style compact --timeout 10"
checks:
- "Build and install on iOS Simulator"
- "Take screenshot after launch, verify main screen renders"
- "Check simulator logs for crashes or assertion failures"
notes: |
- Prefer SwiftUI over UIKit for new views
- Use async/await (Swift 6 strict concurrency)
- Use @Observable (NOT ObservableObject) — Swift 6 pattern
- @MainActor on ViewModels and UI services
- actor for ML/data services (thread safety)
- Protocol for every service (DI, testability)
- Local-first: SwiftData (NOT Core Data, NOT Firebase)
- XcodeGen project.yml — never commit .xcodeproj
- String Catalog for i18n (Xcode 16 native, .xcstrings)
- SwiftLint for linting (SPM plugin or build phase)
- swift-format for consistent formatting
- lefthook for pre-commit hooks (swiftlint + swift-format, language-agnostic, no Node.js)
- Swift Testing (@Test) for new tests, XCTest for legacy
- English first, then localize
- StoreKit 2 for in-app purchases and subscriptions
- #if os(iOS) for AVAudioSession, UIApplication (macOS compat)
- PostHog for analytics (privacy-respecting, EU hosting)
- App Store Connect CLI (asc) for TestFlight, submissions, crashes, analytics — install via brew if missing
- VecturaKit + LumoKit for on-device RAG (privacy-first local search — add when project needs semantic search)
- FoundationModelsKit for Apple Foundation Models (iOS 26+ — on-device AI with tools)
- Xcode MCP bridge (xcrun mcpbridge) — Claude Code can build, test, render previews natively (Xcode 26.3+)
- Reference blog: https://rudrank.com — iOS/MLX/Foundation Models guides by Rudrank Riyam