Initial commit

This commit is contained in:
Zhongwei Li
2025-11-29 18:28:37 +08:00
commit ccc65b3f07
180 changed files with 53970 additions and 0 deletions

View File

@@ -0,0 +1,145 @@
# Workflow: Add a Feature to an Existing App
<required_reading>
**Read these reference files NOW:**
1. references/app-architecture.md
2. references/swiftui-patterns.md
**Plus relevant refs based on feature type** (see Step 2).
</required_reading>
<process>
## Step 1: Understand the Feature
Ask the user:
- What should the feature do?
- Where in the app does it belong?
- Any specific requirements or constraints?
## Step 2: Read Relevant References
Based on feature type, read additional references:
| Feature Type | Additional References |
|--------------|----------------------|
| Data persistence | references/data-persistence.md |
| Networking/API | references/networking.md |
| File handling | references/document-apps.md |
| Background tasks | references/concurrency-patterns.md |
| System integration | references/system-apis.md |
| Menu bar | references/menu-bar-apps.md |
| Extensions | references/app-extensions.md |
| UI polish | references/design-system.md, references/macos-polish.md |
## Step 3: Understand Existing Code
Read the relevant parts of the existing codebase:
- App entry point (usually AppName.swift or AppNameApp.swift)
- State management (AppState, models)
- Existing views related to the feature area
Identify:
- How state flows through the app
- Existing patterns to follow
- Where the new feature fits
## Step 4: Plan the Implementation
Before writing code:
1. Identify new files/types needed
2. Identify existing files to modify
3. Plan the data flow
4. Consider edge cases
## Step 5: Implement with TDD
Follow test-driven development:
1. Write failing test for new behavior
2. Run → RED
3. Implement minimal code
4. Run → GREEN
5. Refactor
6. Repeat
## Step 6: Integrate
- Wire up new views to navigation
- Connect to existing state management
- Add menu items/shortcuts if applicable
- Handle errors gracefully
## Step 7: Build and Test
```bash
# Build
xcodebuild -project AppName.xcodeproj -scheme AppName build 2>&1 | xcsift
# Run tests
xcodebuild test -project AppName.xcodeproj -scheme AppName
# Launch for manual testing
open ./build/Build/Products/Debug/AppName.app
```
## Step 8: Polish
- Add keyboard shortcuts (references/macos-polish.md)
- Ensure accessibility
- Match existing UI patterns
</process>
<integration_patterns>
**Adding to state:**
```swift
// In AppState
@Observable
class AppState {
// Add new property
var newFeatureData: [NewType] = []
// Add new methods
func performNewFeature() { ... }
}
```
**Adding a new view:**
```swift
struct NewFeatureView: View {
@Environment(AppState.self) private var appState
var body: some View {
// Use existing patterns from app
}
}
```
**Adding to navigation:**
```swift
// In existing NavigationSplitView or similar
NavigationLink("New Feature", destination: NewFeatureView())
```
**Adding menu command:**
```swift
struct AppCommands: Commands {
var body: some Commands {
CommandGroup(after: .newItem) {
Button("New Feature Action") {
// action
}
.keyboardShortcut("N", modifiers: [.command, .shift])
}
}
}
```
</integration_patterns>
<success_criteria>
Feature is complete when:
- Functionality works as specified
- Tests pass
- Follows existing code patterns
- UI matches app style
- Keyboard shortcuts work
- No regressions in existing features
</success_criteria>

View File

@@ -0,0 +1,98 @@
# Workflow: Build a New macOS 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 of app? (document-based, shoebox/library, menu bar utility, single-window)
- Any specific features needed? (persistence, networking, system integration)
## Step 2: Choose App Archetype
Based on requirements, select:
| Type | When to Use | Reference |
|------|-------------|-----------|
| Document-based | User creates/saves files | references/document-apps.md |
| Shoebox/Library | Internal database, no explicit save | references/shoebox-apps.md |
| Menu bar utility | Background functionality, quick actions | references/menu-bar-apps.md |
| Single-window | Focused task, simple UI | (use base patterns) |
Read the relevant app type reference if not single-window.
## Step 3: Scaffold Project
Use XcodeGen (recommended):
```bash
# Create project structure
mkdir -p AppName/Sources
cd AppName
# Create project.yml (see references/project-scaffolding.md for template)
# Create Swift files
# Generate xcodeproj
xcodegen generate
```
## Step 4: Implement with TDD
Follow test-driven development:
1. Write failing test
2. Run → RED
3. Implement minimal code
4. Run → GREEN
5. Refactor
6. Repeat
See references/testing-tdd.md for patterns.
## Step 5: Build and Verify
```bash
# Build
xcodebuild -project AppName.xcodeproj -scheme AppName build 2>&1 | xcsift
# Run
open ./build/Build/Products/Debug/AppName.app
```
## Step 6: Polish
Read references/macos-polish.md for:
- Keyboard shortcuts
- Menu bar integration
- Accessibility
- State restoration
</process>
<anti_patterns>
Avoid:
- Massive view models - views ARE the view model in SwiftUI
- Fighting SwiftUI - use declarative patterns
- Ignoring platform conventions - standard shortcuts, menus, windows
- Blocking main thread - async/await for all I/O
- Hard-coded paths - use FileManager APIs
- Retain cycles - use `[weak self]` in escaping closures
</anti_patterns>
<success_criteria>
A well-built macOS app:
- Follows macOS conventions (menu bar, shortcuts, window behavior)
- Uses SwiftUI for UI with AppKit integration where needed
- Manages state with @Observable and environment
- Persists data appropriately
- Handles errors gracefully
- Supports accessibility
- Builds and runs from CLI without opening Xcode
- Feels native and responsive
</success_criteria>

View File

@@ -0,0 +1,198 @@
# Workflow: Debug an Existing macOS App
<required_reading>
**Read these reference files NOW:**
1. references/cli-observability.md
2. references/testing-debugging.md
</required_reading>
<philosophy>
Debugging is iterative. Use whatever gets you to the 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
The goal is root cause, not following a ritual.
</philosophy>
<process>
## Step 1: Understand the Symptom
Ask the user or observe:
- What's the actual behavior vs expected?
- When does it happen? (startup, after action, under load)
- Is it reproducible?
- Any error messages?
## Step 2: Build and Check for Compile Errors
```bash
cd /path/to/app
xcodebuild -project AppName.xcodeproj -scheme AppName -derivedDataPath ./build build 2>&1 | xcsift
```
Fix any compile errors first. They're the easiest wins.
## Step 3: Choose Your Approach
**If you know roughly where the problem is:**
→ Read that code, form hypothesis, test it
**If you have no idea where to start:**
→ Use tools to narrow down (Step 4)
**If code looks correct but behavior is wrong:**
→ Runtime observation (Step 4) reveals what's actually happening
## Step 4: Runtime Diagnostics
Launch with log streaming:
```bash
# Terminal 1: stream logs
log stream --level debug --predicate 'subsystem == "com.company.AppName"'
# Terminal 2: launch
open ./build/Build/Products/Debug/AppName.app
```
**Match symptom to tool:**
| Symptom | Tool | Command |
|---------|------|---------|
| Memory growing / leak suspected | leaks | `leaks AppName` |
| UI freezes / hangs | spindump | `spindump AppName -o /tmp/hang.txt` |
| Crash | crash report | `cat ~/Library/Logs/DiagnosticReports/AppName_*.ips` |
| Slow performance | time profiler | `xcrun xctrace record --template 'Time Profiler' --attach AppName` |
| Race condition suspected | thread sanitizer | Build with `-enableThreadSanitizer YES` |
| Nothing happens / silent failure | logs | Check log stream output |
**Interact with the app** to trigger the issue. Use `cliclick` if available:
```bash
cliclick c:500,300 # click at coordinates
```
## Step 5: Interpret Tool Output
| Tool Shows | Likely Cause | Where to Look |
|------------|--------------|---------------|
| Leaked object: DataService | Retain cycle | Closures capturing self in DataService |
| Main thread blocked in computeX | Sync work on main | That function - needs async |
| Crash at force unwrap | Nil where unexpected | The unwrap site + data flow to it |
| Thread sanitizer warning | Data race | Shared mutable state without sync |
| High CPU in function X | Hot path | That function - algorithm or loop issue |
## Step 6: Read Relevant Code
Now you know where to look. Read that specific code:
- Understand what it's trying to do
- Identify the flaw
- Consider edge cases
## Step 7: Fix the Root Cause
Not the symptom. The actual cause.
**Bad:** Add nil check to prevent crash
**Good:** Fix why the value is nil in the first place
**Bad:** Add try/catch to swallow error
**Good:** Fix what's causing the error
## Step 8: Verify the Fix
Use the same diagnostic that found the issue:
```bash
# Rebuild
xcodebuild -project AppName.xcodeproj -scheme AppName build
# Launch and test
open ./build/Build/Products/Debug/AppName.app
# Run same diagnostic
leaks AppName # should show 0 leaks now
```
## Step 9: Prevent Regression
If the bug was significant, write a test:
```bash
xcodebuild test -project AppName.xcodeproj -scheme AppName
```
</process>
<common_patterns>
## Memory Leaks
**Symptom:** Memory grows over time, `leaks` shows retained objects
**Common causes:**
- Closure captures `self` strongly: `{ self.doThing() }`
- Delegate not weak: `var delegate: SomeProtocol`
- Timer not invalidated
**Fix:** `[weak self]`, `weak var delegate`, `timer.invalidate()`
## UI Freezes
**Symptom:** App hangs, spinning beachball, spindump shows main thread blocked
**Common causes:**
- Sync network call on main thread
- Heavy computation on main thread
- Deadlock from incorrect async/await usage
**Fix:** `Task { }`, `Task.detached { }`, check actor isolation
## Crashes
**Symptom:** App terminates, crash report generated
**Common causes:**
- Force unwrap of nil: `value!`
- Array index out of bounds
- Unhandled error
**Fix:** `guard let`, bounds checking, proper error handling
## Silent Failures
**Symptom:** Nothing happens, no error, no crash
**Common causes:**
- Error silently caught and ignored
- Async task never awaited
- Condition always false
**Fix:** Add logging, check control flow, verify async chains
## Performance Issues
**Symptom:** Slow, high CPU, laggy UI
**Common causes:**
- O(n²) or worse algorithm
- Unnecessary re-renders in SwiftUI
- Repeated expensive operations
**Fix:** Better algorithm, memoization, `let _ = Self._printChanges()`
</common_patterns>
<tools_quick_reference>
```bash
# Build errors (structured JSON)
xcodebuild build 2>&1 | xcsift
# Real-time logs
log stream --level debug --predicate 'subsystem == "com.company.App"'
# Memory leaks
leaks AppName
# UI hangs
spindump AppName -o /tmp/hang.txt
# Crash reports
cat ~/Library/Logs/DiagnosticReports/AppName_*.ips | head -100
# Memory regions
vmmap --summary AppName
# Heap analysis
heap AppName
# Attach debugger
lldb -n AppName
# CPU profiling
xcrun xctrace record --template 'Time Profiler' --attach AppName
# Thread issues (build flag)
xcodebuild build -enableThreadSanitizer YES
```
</tools_quick_reference>

View File

@@ -0,0 +1,244 @@
# Workflow: Optimize App Performance
<required_reading>
**Read these reference files NOW:**
1. references/cli-observability.md
2. references/concurrency-patterns.md
3. references/swiftui-patterns.md
</required_reading>
<philosophy>
Measure first, optimize second. Never optimize based on assumptions.
Profile → Identify bottleneck → Fix → Measure again → Repeat
</philosophy>
<process>
## Step 1: Define the Problem
Ask the user:
- What feels slow? (startup, specific action, scrolling, etc.)
- How slow? (seconds, milliseconds, "laggy")
- When did it start? (always, after recent change, with more data)
## Step 2: Measure Current Performance
**CPU Profiling:**
```bash
# Record 30 seconds of activity
xcrun xctrace record \
--template 'Time Profiler' \
--time-limit 30s \
--output profile.trace \
--launch -- ./build/Build/Products/Debug/AppName.app/Contents/MacOS/AppName
```
**Memory:**
```bash
# While app is running
vmmap --summary AppName
heap AppName
leaks AppName
```
**Startup time:**
```bash
# Measure launch to first frame
time open -W ./build/Build/Products/Debug/AppName.app
```
## Step 3: Identify Bottlenecks
**From Time Profiler:**
- Look for functions with high "self time"
- Check main thread for blocking operations
- Look for repeated calls that could be cached
**From memory tools:**
- Large allocations that could be lazy-loaded
- Objects retained longer than needed
- Duplicate data in memory
**SwiftUI re-renders:**
```swift
// Add to any view to see why it re-renders
var body: some View {
let _ = Self._printChanges()
// ...
}
```
## Step 4: Common Optimizations
### Main Thread
**Problem:** Heavy work on main thread
```swift
// Bad
func loadData() {
let data = expensiveComputation() // blocks UI
self.items = data
}
// Good
func loadData() async {
let data = await Task.detached {
expensiveComputation()
}.value
await MainActor.run {
self.items = data
}
}
```
### SwiftUI
**Problem:** Unnecessary re-renders
```swift
// Bad - entire view rebuilds when any state changes
struct ListView: View {
@State var items: [Item]
@State var searchText: String
// ...
}
// Good - extract subviews with their own state
struct ListView: View {
var body: some View {
VStack {
SearchBar() // has its own @State
ItemList() // only rebuilds when items change
}
}
}
```
**Problem:** Expensive computation in body
```swift
// Bad
var body: some View {
List(items.sorted().filtered()) // runs every render
// Good
var sortedItems: [Item] { // or use .task modifier
items.sorted().filtered()
}
var body: some View {
List(sortedItems)
}
```
### Data Loading
**Problem:** Loading all data upfront
```swift
// Bad
init() {
self.allItems = loadEverything() // slow startup
}
// Good - lazy loading
func loadItemsIfNeeded() async {
guard items.isEmpty else { return }
items = await loadItems()
}
```
**Problem:** No caching
```swift
// Bad
func getImage(for url: URL) async -> NSImage {
return await downloadImage(url) // downloads every time
}
// Good
private var imageCache: [URL: NSImage] = [:]
func getImage(for url: URL) async -> NSImage {
if let cached = imageCache[url] { return cached }
let image = await downloadImage(url)
imageCache[url] = image
return image
}
```
### Collections
**Problem:** O(n²) operations
```swift
// Bad - O(n) lookup in array
items.first { $0.id == targetId }
// Good - O(1) lookup with dictionary
itemsById[targetId]
```
**Problem:** Repeated filtering
```swift
// Bad
let activeItems = items.filter { $0.isActive } // called repeatedly
// Good - compute once, update when needed
@Published var activeItems: [Item] = []
func updateActiveItems() {
activeItems = items.filter { $0.isActive }
}
```
## Step 5: Measure Again
After each optimization:
```bash
# Re-run profiler
xcrun xctrace record --template 'Time Profiler' ...
# Compare metrics
```
Did it actually improve? If not, revert and try different approach.
## Step 6: Prevent Regression
Add performance tests:
```swift
func testStartupPerformance() {
measure {
// startup code
}
}
func testScrollingPerformance() {
measure(metrics: [XCTCPUMetric(), XCTMemoryMetric()]) {
// scroll simulation
}
}
```
</process>
<performance_targets>
| Metric | Target | Unacceptable |
|--------|--------|--------------|
| App launch | < 1 second | > 3 seconds |
| Button response | < 100ms | > 500ms |
| List scrolling | 60 fps | < 30 fps |
| Memory (idle) | < 100MB | > 500MB |
| Memory growth | Stable | Unbounded |
</performance_targets>
<tools_reference>
```bash
# CPU profiling
xcrun xctrace record --template 'Time Profiler' --attach AppName
# Memory snapshot
vmmap --summary AppName
heap AppName
# Allocations over time
xcrun xctrace record --template 'Allocations' --attach AppName
# Energy impact
xcrun xctrace record --template 'Energy Log' --attach AppName
# System trace (comprehensive)
xcrun xctrace record --template 'System Trace' --attach AppName
```
</tools_reference>

View File

@@ -0,0 +1,159 @@
# Workflow: Ship/Release a macOS App
<required_reading>
**Read these reference files NOW:**
1. references/security-code-signing.md
2. references/cli-workflow.md
</required_reading>
<process>
## Step 1: Prepare for Release
Ensure the app is ready:
- All features complete and tested
- No debug code or test data
- Version and build numbers updated in Info.plist
- App icon and assets finalized
```bash
# Verify build succeeds
xcodebuild -project AppName.xcodeproj -scheme AppName -configuration Release build
```
## Step 2: Choose Distribution Method
| Method | Use When | Requires |
|--------|----------|----------|
| Direct distribution | Sharing with specific users, beta testing | Developer ID signing + notarization |
| App Store | Public distribution, paid apps | App Store Connect account, review |
| TestFlight | Beta testing at scale | App Store Connect |
## Step 3: Code Signing
**For Direct Distribution (Developer ID):**
```bash
# Archive
xcodebuild -project AppName.xcodeproj \
-scheme AppName \
-configuration Release \
-archivePath ./build/AppName.xcarchive \
archive
# Export with Developer ID
xcodebuild -exportArchive \
-archivePath ./build/AppName.xcarchive \
-exportPath ./build/export \
-exportOptionsPlist ExportOptions.plist
```
ExportOptions.plist for Developer ID:
```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>developer-id</string>
<key>signingStyle</key>
<string>automatic</string>
</dict>
</plist>
```
**For App Store:**
```xml
<key>method</key>
<string>app-store</string>
```
## Step 4: Notarization (Direct Distribution)
Required for apps distributed outside the App Store:
```bash
# Submit for notarization
xcrun notarytool submit ./build/export/AppName.app.zip \
--apple-id "your@email.com" \
--team-id "TEAMID" \
--password "@keychain:AC_PASSWORD" \
--wait
# Staple the ticket
xcrun stapler staple ./build/export/AppName.app
```
## Step 5: Create DMG (Direct Distribution)
```bash
# Create DMG
hdiutil create -volname "AppName" \
-srcfolder ./build/export/AppName.app \
-ov -format UDZO \
./build/AppName.dmg
# Notarize the DMG too
xcrun notarytool submit ./build/AppName.dmg \
--apple-id "your@email.com" \
--team-id "TEAMID" \
--password "@keychain:AC_PASSWORD" \
--wait
xcrun stapler staple ./build/AppName.dmg
```
## Step 6: App Store Submission
```bash
# Validate
xcrun altool --validate-app \
-f ./build/export/AppName.pkg \
-t macos \
--apiKey KEY_ID \
--apiIssuer ISSUER_ID
# Upload
xcrun altool --upload-app \
-f ./build/export/AppName.pkg \
-t macos \
--apiKey KEY_ID \
--apiIssuer ISSUER_ID
```
Then complete submission in App Store Connect.
## Step 7: Verify Release
**For direct distribution:**
```bash
# Verify signature
codesign -dv --verbose=4 ./build/export/AppName.app
# Verify notarization
spctl -a -vv ./build/export/AppName.app
```
**For App Store:**
- Check App Store Connect for review status
- Test TestFlight build if applicable
</process>
<checklist>
Before shipping:
- [ ] Version number incremented
- [ ] Release notes written
- [ ] Debug logging disabled or minimized
- [ ] All entitlements correct and minimal
- [ ] Privacy descriptions in Info.plist
- [ ] App icon complete (all sizes)
- [ ] Screenshots prepared (if App Store)
- [ ] Tested on clean install
</checklist>
<common_issues>
| Issue | Cause | Fix |
|-------|-------|-----|
| Notarization fails | Unsigned frameworks, hardened runtime issues | Check all embedded binaries are signed |
| "App is damaged" | Not notarized or stapled | Run notarytool and stapler |
| Gatekeeper blocks | Missing Developer ID | Sign with Developer ID certificate |
| App Store rejection | Missing entitlement descriptions, privacy issues | Add usage descriptions to Info.plist |
</common_issues>

View File

@@ -0,0 +1,258 @@
# Workflow: Write and Run Tests
<required_reading>
**Read these reference files NOW:**
1. references/testing-tdd.md
2. references/testing-debugging.md
</required_reading>
<philosophy>
Tests are documentation that runs. Write tests that:
- Describe what the code should do
- Catch regressions before users do
- Enable confident refactoring
</philosophy>
<process>
## Step 1: Understand What to Test
Ask the user:
- New tests for existing code?
- Tests for new feature (TDD)?
- Fix a bug with regression test?
**What Claude tests (automated):**
- Core logic (data transforms, calculations, algorithms)
- State management (models, relationships)
- Service layer (mocked dependencies)
- Edge cases (nil, empty, boundaries)
**What user tests (manual):**
- UX feel and visual polish
- Real hardware/device integration
- Performance under real conditions
## Step 2: Set Up Test Target
If tests don't exist yet:
```bash
# Add test target to project.yml (XcodeGen)
targets:
AppNameTests:
type: bundle.unit-test
platform: macOS
sources:
- path: Tests
dependencies:
- target: AppName
```
Or create test files manually in Xcode's test target.
## Step 3: Write Tests
### Unit Tests (Logic)
```swift
import Testing
@testable import AppName
struct ItemTests {
@Test func itemCreation() {
let item = Item(name: "Test", value: 42)
#expect(item.name == "Test")
#expect(item.value == 42)
}
@Test func itemValidation() {
let emptyItem = Item(name: "", value: 0)
#expect(!emptyItem.isValid)
}
@Test(arguments: [0, -1, 1000001])
func invalidValues(value: Int) {
let item = Item(name: "Test", value: value)
#expect(!item.isValid)
}
}
```
### State Tests
```swift
struct AppStateTests {
@Test func addItem() {
let state = AppState()
let item = Item(name: "New", value: 10)
state.addItem(item)
#expect(state.items.count == 1)
#expect(state.items.first?.name == "New")
}
@Test func deleteItem() {
let state = AppState()
let item = Item(name: "ToDelete", value: 1)
state.addItem(item)
state.deleteItem(item)
#expect(state.items.isEmpty)
}
}
```
### Async Tests
```swift
struct NetworkTests {
@Test func fetchItems() async throws {
let service = MockDataService()
service.mockItems = [Item(name: "Fetched", value: 5)]
let items = try await service.fetchItems()
#expect(items.count == 1)
}
@Test func fetchHandlesError() async {
let service = MockDataService()
service.shouldFail = true
await #expect(throws: NetworkError.self) {
try await service.fetchItems()
}
}
}
```
### Edge Cases
```swift
struct EdgeCaseTests {
@Test func emptyList() {
let state = AppState()
#expect(state.items.isEmpty)
#expect(state.selectedItem == nil)
}
@Test func nilHandling() {
let item: Item? = nil
#expect(item?.name == nil)
}
@Test func boundaryValues() {
let item = Item(name: String(repeating: "a", count: 10000), value: Int.max)
#expect(item.isValid) // or test truncation behavior
}
}
```
## Step 4: Run Tests
```bash
# Run all tests
xcodebuild test \
-project AppName.xcodeproj \
-scheme AppName \
-resultBundlePath TestResults.xcresult
# Run specific test
xcodebuild test \
-project AppName.xcodeproj \
-scheme AppName \
-only-testing:AppNameTests/ItemTests/testItemCreation
# View results
xcrun xcresulttool get test-results summary --path TestResults.xcresult
```
## Step 5: Coverage Report
```bash
# Generate coverage
xcodebuild test \
-project AppName.xcodeproj \
-scheme AppName \
-enableCodeCoverage YES \
-resultBundlePath TestResults.xcresult
# View coverage
xcrun xccov view --report TestResults.xcresult
# Coverage as JSON
xcrun xccov view --report --json TestResults.xcresult > coverage.json
```
## Step 6: TDD Cycle
For new features:
1. **Red:** Write failing test for desired behavior
2. **Green:** Write minimum code to pass
3. **Refactor:** Clean up while keeping tests green
4. **Repeat:** Next behavior
</process>
<test_patterns>
### Arrange-Act-Assert
```swift
@Test func pattern() {
// Arrange
let state = AppState()
let item = Item(name: "Test", value: 1)
// Act
state.addItem(item)
// Assert
#expect(state.items.contains(item))
}
```
### Mocking Dependencies
```swift
protocol DataServiceProtocol {
func fetchItems() async throws -> [Item]
}
class MockDataService: DataServiceProtocol {
var mockItems: [Item] = []
var shouldFail = false
func fetchItems() async throws -> [Item] {
if shouldFail { throw TestError.mock }
return mockItems
}
}
```
### Testing SwiftUI State
```swift
@Test func viewModelState() {
let state = AppState()
state.items = [Item(name: "A", value: 1), Item(name: "B", value: 2)]
state.selectedItem = state.items.first
#expect(state.selectedItem?.name == "A")
}
```
</test_patterns>
<what_not_to_test>
- SwiftUI view rendering (use previews + manual testing)
- Apple framework behavior
- Simple getters/setters with no logic
- Private implementation details (test via public interface)
</what_not_to_test>
<coverage_targets>
| Code Type | Target Coverage |
|-----------|-----------------|
| Business logic | 80-100% |
| State management | 70-90% |
| Utilities/helpers | 60-80% |
| Views | 0% (manual test) |
| Generated code | 0% |
</coverage_targets>