Initial commit
This commit is contained in:
101
skills/expertise/iphone-apps/workflows/add-feature.md
Normal file
101
skills/expertise/iphone-apps/workflows/add-feature.md
Normal file
@@ -0,0 +1,101 @@
|
||||
# Workflow: Add a Feature to an Existing iOS App
|
||||
|
||||
<required_reading>
|
||||
**Read these NOW:**
|
||||
1. references/app-architecture.md
|
||||
2. references/swiftui-patterns.md
|
||||
|
||||
**Plus relevant refs based on feature** (see Step 2).
|
||||
</required_reading>
|
||||
|
||||
<process>
|
||||
## Step 1: Understand the Feature
|
||||
|
||||
Ask:
|
||||
- What should it do?
|
||||
- Where does it belong in the app?
|
||||
- Any constraints?
|
||||
|
||||
## Step 2: Read Relevant References
|
||||
|
||||
| Feature Type | Reference |
|
||||
|--------------|-----------|
|
||||
| Data persistence | references/data-persistence.md |
|
||||
| Networking/API | references/networking.md |
|
||||
| Push notifications | references/push-notifications.md |
|
||||
| In-app purchases | references/storekit.md |
|
||||
| Background tasks | references/background-tasks.md |
|
||||
| Navigation | references/navigation-patterns.md |
|
||||
| Polish/UX | references/polish-and-ux.md |
|
||||
|
||||
## Step 3: Understand Existing Code
|
||||
|
||||
Read:
|
||||
- App entry point
|
||||
- State management
|
||||
- Related views
|
||||
|
||||
Identify patterns to follow.
|
||||
|
||||
## Step 4: Implement with TDD
|
||||
|
||||
1. Write test for new behavior → RED
|
||||
2. Implement → GREEN
|
||||
3. Refactor
|
||||
4. Repeat
|
||||
|
||||
## Step 5: Integrate
|
||||
|
||||
- Wire up navigation
|
||||
- Connect to state
|
||||
- Handle errors
|
||||
|
||||
## Step 6: Build and Test
|
||||
|
||||
```bash
|
||||
xcodebuild -scheme AppName -destination 'platform=iOS Simulator,name=iPhone 16' build test
|
||||
xcrun simctl launch booted com.company.AppName
|
||||
```
|
||||
|
||||
## Step 7: Polish
|
||||
|
||||
- Haptic feedback for actions
|
||||
- Animations for transitions
|
||||
- Accessibility labels
|
||||
- Dynamic Type support
|
||||
</process>
|
||||
|
||||
<integration_patterns>
|
||||
**Adding state:**
|
||||
```swift
|
||||
@Observable
|
||||
class AppState {
|
||||
var newFeatureData: [NewType] = []
|
||||
func performNewFeature() { ... }
|
||||
}
|
||||
```
|
||||
|
||||
**Adding a view:**
|
||||
```swift
|
||||
struct NewFeatureView: View {
|
||||
@Environment(AppState.self) private var appState
|
||||
var body: some View { ... }
|
||||
}
|
||||
```
|
||||
|
||||
**Adding navigation:**
|
||||
```swift
|
||||
NavigationLink("New Feature", value: NewFeatureDestination())
|
||||
.navigationDestination(for: NewFeatureDestination.self) { _ in
|
||||
NewFeatureView()
|
||||
}
|
||||
```
|
||||
|
||||
**Adding a tab:**
|
||||
```swift
|
||||
TabView {
|
||||
NewFeatureView()
|
||||
.tabItem { Label("New", systemImage: "star") }
|
||||
}
|
||||
```
|
||||
</integration_patterns>
|
||||
111
skills/expertise/iphone-apps/workflows/build-new-app.md
Normal file
111
skills/expertise/iphone-apps/workflows/build-new-app.md
Normal file
@@ -0,0 +1,111 @@
|
||||
# Workflow: Build a New iOS App
|
||||
|
||||
<required_reading>
|
||||
**Read these reference files NOW before writing any code:**
|
||||
1. references/project-scaffolding.md
|
||||
2. references/cli-workflow.md
|
||||
3. references/app-architecture.md
|
||||
4. references/swiftui-patterns.md
|
||||
</required_reading>
|
||||
|
||||
<process>
|
||||
## Step 1: Clarify Requirements
|
||||
|
||||
Ask the user:
|
||||
- What does the app do? (core functionality)
|
||||
- What type? (single-screen, tab-based, navigation-based, data-driven)
|
||||
- Any specific features? (persistence, networking, push notifications, purchases)
|
||||
|
||||
## Step 2: Choose App Archetype
|
||||
|
||||
| Type | When to Use | Key Patterns |
|
||||
|------|-------------|--------------|
|
||||
| Single-screen utility | One primary function | Minimal navigation |
|
||||
| Tab-based (TabView) | Multiple equal sections | TabView with 3-5 tabs |
|
||||
| Navigation-based | Hierarchical content | NavigationStack |
|
||||
| Data-driven | User content library | SwiftData + @Query |
|
||||
|
||||
## Step 3: Scaffold Project
|
||||
|
||||
Use XcodeGen:
|
||||
```bash
|
||||
mkdir AppName && cd AppName
|
||||
# Create project.yml (see references/project-scaffolding.md)
|
||||
# Create Swift files in Sources/
|
||||
xcodegen generate
|
||||
```
|
||||
|
||||
## Step 4: Implement with TDD
|
||||
|
||||
1. Write failing test
|
||||
2. Run → RED
|
||||
3. Implement minimal code
|
||||
4. Run → GREEN
|
||||
5. Refactor
|
||||
6. Repeat
|
||||
|
||||
## Step 5: Build and Launch
|
||||
|
||||
```bash
|
||||
# Build
|
||||
xcodebuild -project AppName.xcodeproj -scheme AppName \
|
||||
-destination 'platform=iOS Simulator,name=iPhone 16' build 2>&1 | xcsift
|
||||
|
||||
# Launch in simulator
|
||||
xcrun simctl boot "iPhone 16" 2>/dev/null || true
|
||||
xcrun simctl install booted ./build/Build/Products/Debug-iphonesimulator/AppName.app
|
||||
xcrun simctl launch booted com.company.AppName
|
||||
```
|
||||
|
||||
## Step 6: Polish
|
||||
|
||||
Read references/polish-and-ux.md for:
|
||||
- Haptic feedback
|
||||
- Animations
|
||||
- Accessibility
|
||||
- Dynamic Type support
|
||||
</process>
|
||||
|
||||
<minimum_viable_app>
|
||||
```swift
|
||||
import SwiftUI
|
||||
|
||||
@main
|
||||
struct MyApp: App {
|
||||
@State private var appState = AppState()
|
||||
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
ContentView()
|
||||
.environment(appState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Observable
|
||||
class AppState {
|
||||
var items: [Item] = []
|
||||
}
|
||||
|
||||
struct ContentView: View {
|
||||
@Environment(AppState.self) private var appState
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
List(appState.items) { item in
|
||||
Text(item.name)
|
||||
}
|
||||
.navigationTitle("Items")
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
</minimum_viable_app>
|
||||
|
||||
<success_criteria>
|
||||
- Follows iOS Human Interface Guidelines
|
||||
- Builds and runs from CLI
|
||||
- Tests pass
|
||||
- Launches in simulator
|
||||
- User can verify UX manually
|
||||
</success_criteria>
|
||||
115
skills/expertise/iphone-apps/workflows/debug-app.md
Normal file
115
skills/expertise/iphone-apps/workflows/debug-app.md
Normal file
@@ -0,0 +1,115 @@
|
||||
# Workflow: Debug an Existing iOS App
|
||||
|
||||
<required_reading>
|
||||
**Read these reference files NOW:**
|
||||
1. references/cli-observability.md
|
||||
2. references/testing.md
|
||||
</required_reading>
|
||||
|
||||
<philosophy>
|
||||
Debugging is iterative. Use whatever gets you to root cause fastest:
|
||||
- Small app, obvious symptom → read relevant code
|
||||
- Large codebase, unclear cause → use tools to narrow down
|
||||
- Code looks correct but fails → tools reveal runtime behavior
|
||||
- After fixing → tools verify the fix
|
||||
</philosophy>
|
||||
|
||||
<process>
|
||||
## Step 1: Understand the Symptom
|
||||
|
||||
Ask:
|
||||
- What's happening vs expected?
|
||||
- When? (startup, after action, under load)
|
||||
- Reproducible?
|
||||
- Any error messages?
|
||||
|
||||
## Step 2: Build and Check for Compile Errors
|
||||
|
||||
```bash
|
||||
xcodebuild -scheme AppName -destination 'platform=iOS Simulator,name=iPhone 16' build 2>&1 | xcsift
|
||||
```
|
||||
|
||||
Fix compile errors first.
|
||||
|
||||
## Step 3: Choose Your Approach
|
||||
|
||||
**Know where problem is:** → Read that code
|
||||
**No idea where to start:** → Use tools (Step 4)
|
||||
**Code looks correct but fails:** → Runtime observation (Step 4)
|
||||
|
||||
## Step 4: Runtime Diagnostics
|
||||
|
||||
**Launch with logging:**
|
||||
```bash
|
||||
xcrun simctl boot "iPhone 16" 2>/dev/null || true
|
||||
xcrun simctl install booted ./build/Build/Products/Debug-iphonesimulator/AppName.app
|
||||
xcrun simctl launch --console booted com.company.AppName
|
||||
```
|
||||
|
||||
**Match symptom to tool:**
|
||||
|
||||
| Symptom | Tool | Command |
|
||||
|---------|------|---------|
|
||||
| Memory leak | leaks | `leaks AppName` (on simulator process) |
|
||||
| UI freeze | spindump | `spindump AppName` |
|
||||
| Crash | crash report | Check Console.app or `~/Library/Logs/DiagnosticReports/` |
|
||||
| Slow | profiler | `xcrun xctrace record --template 'Time Profiler'` |
|
||||
| Silent failure | console | `xcrun simctl launch --console booted ...` |
|
||||
|
||||
## Step 5: Interpret & Read Relevant Code
|
||||
|
||||
Tool output tells you WHERE. Now read THAT code.
|
||||
|
||||
## Step 6: Fix the Root Cause
|
||||
|
||||
Not the symptom. The actual cause.
|
||||
|
||||
## Step 7: Verify
|
||||
|
||||
```bash
|
||||
# Rebuild
|
||||
xcodebuild build ...
|
||||
|
||||
# Run same diagnostic
|
||||
# Confirm issue is resolved
|
||||
```
|
||||
|
||||
## Step 8: Regression Test
|
||||
|
||||
Write a test that would catch this bug in future.
|
||||
</process>
|
||||
|
||||
<common_patterns>
|
||||
## Memory Leaks
|
||||
**Cause:** Strong reference cycles in closures
|
||||
**Fix:** `[weak self]` capture
|
||||
|
||||
## UI Freezes
|
||||
**Cause:** Sync work on main thread
|
||||
**Fix:** `Task { }` or `Task.detached { }`
|
||||
|
||||
## Crashes
|
||||
**Cause:** Force unwrap, index out of bounds
|
||||
**Fix:** `guard let`, bounds checking
|
||||
|
||||
## Silent Failures
|
||||
**Cause:** Error swallowed, async not awaited
|
||||
**Fix:** Add logging, check async chains
|
||||
</common_patterns>
|
||||
|
||||
<ios_specific_tools>
|
||||
```bash
|
||||
# Console output from simulator
|
||||
xcrun simctl spawn booted log stream --predicate 'subsystem == "com.company.AppName"'
|
||||
|
||||
# Install and launch
|
||||
xcrun simctl install booted ./App.app
|
||||
xcrun simctl launch --console booted com.company.AppName
|
||||
|
||||
# Screenshot
|
||||
xcrun simctl io booted screenshot /tmp/screenshot.png
|
||||
|
||||
# Video recording
|
||||
xcrun simctl io booted recordVideo /tmp/video.mp4
|
||||
```
|
||||
</ios_specific_tools>
|
||||
125
skills/expertise/iphone-apps/workflows/optimize-performance.md
Normal file
125
skills/expertise/iphone-apps/workflows/optimize-performance.md
Normal file
@@ -0,0 +1,125 @@
|
||||
# Workflow: Optimize iOS App Performance
|
||||
|
||||
<required_reading>
|
||||
**Read NOW:**
|
||||
1. references/performance.md
|
||||
2. references/cli-observability.md
|
||||
</required_reading>
|
||||
|
||||
<philosophy>
|
||||
Measure first, optimize second. Never optimize based on assumptions.
|
||||
</philosophy>
|
||||
|
||||
<process>
|
||||
## Step 1: Define the Problem
|
||||
|
||||
Ask:
|
||||
- What feels slow?
|
||||
- Startup? Scrolling? Specific action?
|
||||
- When did it start?
|
||||
|
||||
## Step 2: Measure
|
||||
|
||||
**CPU Profiling:**
|
||||
```bash
|
||||
xcrun xctrace record \
|
||||
--template 'Time Profiler' \
|
||||
--device 'iPhone 16' \
|
||||
--attach AppName \
|
||||
--output profile.trace
|
||||
```
|
||||
|
||||
**Memory:**
|
||||
```bash
|
||||
xcrun xctrace record --template 'Allocations' ...
|
||||
```
|
||||
|
||||
**Launch time:**
|
||||
```bash
|
||||
# Add DYLD_PRINT_STATISTICS=1 to scheme environment
|
||||
```
|
||||
|
||||
## Step 3: Identify Bottlenecks
|
||||
|
||||
Look for:
|
||||
- Functions with high "self time"
|
||||
- Main thread blocking
|
||||
- Repeated expensive operations
|
||||
- Large allocations
|
||||
|
||||
**SwiftUI re-renders:**
|
||||
```swift
|
||||
var body: some View {
|
||||
let _ = Self._printChanges()
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## Step 4: Common Optimizations
|
||||
|
||||
### Main Thread
|
||||
```swift
|
||||
// Bad
|
||||
let data = expensiveWork() // blocks UI
|
||||
|
||||
// Good
|
||||
let data = await Task.detached { expensiveWork() }.value
|
||||
```
|
||||
|
||||
### SwiftUI
|
||||
```swift
|
||||
// Bad - rebuilds everything
|
||||
struct BigView: View {
|
||||
@State var a, b, c, d, e
|
||||
}
|
||||
|
||||
// Good - isolated state
|
||||
struct BigView: View {
|
||||
var body: some View {
|
||||
SubViewA() // has own @State
|
||||
SubViewB() // has own @State
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Lists
|
||||
```swift
|
||||
// Use LazyVStack for long lists
|
||||
ScrollView {
|
||||
LazyVStack {
|
||||
ForEach(items) { ... }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Images
|
||||
```swift
|
||||
AsyncImage(url: url) { image in
|
||||
image.resizable()
|
||||
} placeholder: {
|
||||
ProgressView()
|
||||
}
|
||||
```
|
||||
|
||||
## Step 5: Measure Again
|
||||
|
||||
Did it improve? If not, revert.
|
||||
|
||||
## Step 6: Performance Tests
|
||||
|
||||
```swift
|
||||
func testScrollPerformance() {
|
||||
measure(metrics: [XCTCPUMetric(), XCTMemoryMetric()]) {
|
||||
// scroll simulation
|
||||
}
|
||||
}
|
||||
```
|
||||
</process>
|
||||
|
||||
<targets>
|
||||
| Metric | Target | Unacceptable |
|
||||
|--------|--------|--------------|
|
||||
| Launch | < 1s | > 2s |
|
||||
| Scroll | 60 fps | < 30 fps |
|
||||
| Response | < 100ms | > 500ms |
|
||||
</targets>
|
||||
122
skills/expertise/iphone-apps/workflows/ship-app.md
Normal file
122
skills/expertise/iphone-apps/workflows/ship-app.md
Normal file
@@ -0,0 +1,122 @@
|
||||
# Workflow: Ship iOS App
|
||||
|
||||
<required_reading>
|
||||
**Read NOW:**
|
||||
1. references/app-store.md
|
||||
2. references/ci-cd.md
|
||||
</required_reading>
|
||||
|
||||
<process>
|
||||
## Step 1: Pre-Release Checklist
|
||||
|
||||
- [ ] Version/build numbers updated
|
||||
- [ ] No debug code or test data
|
||||
- [ ] Privacy manifest complete (PrivacyInfo.xcprivacy)
|
||||
- [ ] App icons all sizes (see references/app-icons.md)
|
||||
- [ ] Screenshots prepared
|
||||
- [ ] Release notes written
|
||||
|
||||
## Step 2: Archive
|
||||
|
||||
```bash
|
||||
xcodebuild archive \
|
||||
-project AppName.xcodeproj \
|
||||
-scheme AppName \
|
||||
-archivePath ./build/AppName.xcarchive \
|
||||
-destination 'generic/platform=iOS'
|
||||
```
|
||||
|
||||
## Step 3: Export for Distribution
|
||||
|
||||
**For TestFlight/App Store:**
|
||||
```bash
|
||||
xcodebuild -exportArchive \
|
||||
-archivePath ./build/AppName.xcarchive \
|
||||
-exportPath ./build/export \
|
||||
-exportOptionsPlist ExportOptions.plist
|
||||
```
|
||||
|
||||
ExportOptions.plist:
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>method</key>
|
||||
<string>app-store-connect</string>
|
||||
<key>signingStyle</key>
|
||||
<string>automatic</string>
|
||||
</dict>
|
||||
</plist>
|
||||
```
|
||||
|
||||
## Step 4: Upload to App Store Connect
|
||||
|
||||
```bash
|
||||
xcrun altool --upload-app \
|
||||
-f ./build/export/AppName.ipa \
|
||||
-t ios \
|
||||
--apiKey YOUR_KEY_ID \
|
||||
--apiIssuer YOUR_ISSUER_ID
|
||||
```
|
||||
|
||||
Or use `xcrun notarytool` with App Store Connect API.
|
||||
|
||||
## Step 5: TestFlight
|
||||
|
||||
1. Wait for processing in App Store Connect
|
||||
2. Add testers (internal or external)
|
||||
3. Gather feedback
|
||||
4. Iterate
|
||||
|
||||
## Step 6: App Store Submission
|
||||
|
||||
In App Store Connect:
|
||||
1. Complete app metadata
|
||||
2. Add screenshots for all device sizes
|
||||
3. Set pricing
|
||||
4. Submit for review
|
||||
|
||||
## Step 7: Post-Release
|
||||
|
||||
- Monitor crash reports
|
||||
- Respond to reviews
|
||||
- Plan next version
|
||||
</process>
|
||||
|
||||
<privacy_manifest>
|
||||
Required since iOS 17. Create `PrivacyInfo.xcprivacy`:
|
||||
```xml
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>NSPrivacyTracking</key>
|
||||
<false/>
|
||||
<key>NSPrivacyCollectedDataTypes</key>
|
||||
<array/>
|
||||
<key>NSPrivacyAccessedAPITypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>NSPrivacyAccessedAPIType</key>
|
||||
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
||||
<key>NSPrivacyAccessedAPITypeReasons</key>
|
||||
<array>
|
||||
<string>CA92.1</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
```
|
||||
</privacy_manifest>
|
||||
|
||||
<common_rejections>
|
||||
| Reason | Fix |
|
||||
|--------|-----|
|
||||
| Crash on launch | Test on real device, check entitlements |
|
||||
| Missing privacy descriptions | Add all NS*UsageDescription keys |
|
||||
| Broken links | Verify all URLs work |
|
||||
| Incomplete metadata | Fill all required fields |
|
||||
| Guideline 4.3 (spam) | Differentiate from existing apps |
|
||||
</common_rejections>
|
||||
101
skills/expertise/iphone-apps/workflows/write-tests.md
Normal file
101
skills/expertise/iphone-apps/workflows/write-tests.md
Normal file
@@ -0,0 +1,101 @@
|
||||
# Workflow: Write and Run Tests
|
||||
|
||||
<required_reading>
|
||||
**Read NOW:**
|
||||
1. references/testing.md
|
||||
</required_reading>
|
||||
|
||||
<philosophy>
|
||||
Tests are documentation that runs. They let the user verify correctness without reading code.
|
||||
</philosophy>
|
||||
|
||||
<process>
|
||||
## Step 1: Understand What to Test
|
||||
|
||||
**Claude tests (automated):**
|
||||
- Core logic
|
||||
- State management
|
||||
- Service layer
|
||||
- Edge cases
|
||||
|
||||
**User tests (manual):**
|
||||
- UX feel
|
||||
- Visual polish
|
||||
- Real device behavior
|
||||
|
||||
## Step 2: Write Tests
|
||||
|
||||
### Unit Tests
|
||||
```swift
|
||||
import Testing
|
||||
@testable import AppName
|
||||
|
||||
struct ItemTests {
|
||||
@Test func creation() {
|
||||
let item = Item(name: "Test")
|
||||
#expect(item.name == "Test")
|
||||
}
|
||||
|
||||
@Test func validation() {
|
||||
let empty = Item(name: "")
|
||||
#expect(!empty.isValid)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Async Tests
|
||||
```swift
|
||||
@Test func fetchItems() async throws {
|
||||
let service = MockService()
|
||||
let items = try await service.fetch()
|
||||
#expect(items.count > 0)
|
||||
}
|
||||
```
|
||||
|
||||
### State Tests
|
||||
```swift
|
||||
@Test func addItem() {
|
||||
let state = AppState()
|
||||
state.addItem(Item(name: "New"))
|
||||
#expect(state.items.count == 1)
|
||||
}
|
||||
```
|
||||
|
||||
## Step 3: Run Tests
|
||||
|
||||
```bash
|
||||
xcodebuild test \
|
||||
-project AppName.xcodeproj \
|
||||
-scheme AppName \
|
||||
-destination 'platform=iOS Simulator,name=iPhone 16' \
|
||||
-resultBundlePath TestResults.xcresult
|
||||
|
||||
# Summary
|
||||
xcrun xcresulttool get test-results summary --path TestResults.xcresult
|
||||
```
|
||||
|
||||
## Step 4: Coverage
|
||||
|
||||
```bash
|
||||
xcodebuild test -enableCodeCoverage YES ...
|
||||
xcrun xccov view --report TestResults.xcresult
|
||||
```
|
||||
|
||||
## Step 5: TDD Cycle
|
||||
|
||||
For new features:
|
||||
1. Write failing test
|
||||
2. Run → RED
|
||||
3. Implement minimum
|
||||
4. Run → GREEN
|
||||
5. Refactor
|
||||
6. Repeat
|
||||
</process>
|
||||
|
||||
<coverage_targets>
|
||||
| Code Type | Target |
|
||||
|-----------|--------|
|
||||
| Business logic | 80-100% |
|
||||
| State management | 70-90% |
|
||||
| Views | 0% (manual) |
|
||||
</coverage_targets>
|
||||
Reference in New Issue
Block a user