- Add Apple Sign-In backend (JWKS verification, account linking, session management) - Implement push notification deep linking with NotificationDeepLinkRouter - Add jailbreak detection, runtime integrity monitoring, secure enclave service - Implement OAuth social login, token refresh, and secure logout flows - Add image caching (memory/disk), optimizer, upload queue, async semaphore - Implement notification analytics, type preferences, and category setup - Expand UI test suite with UITestBase, accessibility, auth flow, performance tests - Add CI pipeline for iOS UI tests (3 device sizes) and performance benchmarks - Restructure Xcode project to manual groups with KordantWidgets target - Add SwiftLint, Swift Collections/Algorithms/GoogleSignIn dependencies - Update project.yml for XcodeGen with new targets and configurations
249 lines
10 KiB
Markdown
249 lines
10 KiB
Markdown
# Kordant Performance Baseline
|
|
|
|
> **Last Updated:** 2026-06-02
|
|
> **Owner:** iOS Team
|
|
> **Target Devices:** iPhone SE (2nd gen), iPhone 12, iPhone 15 Pro
|
|
|
|
---
|
|
|
|
## 1. Overview
|
|
|
|
This document defines performance budgets, baselines, and measurement methodology for the Kordant iOS app. Performance tests are implemented using XCTMetric (`XCTClockMetric`, `XCTCPUMetric`, `XCTMemoryMetric`, `XCTApplicationLaunchMetric`) in both unit and UI test targets.
|
|
|
|
### 1.1 Performance Goals
|
|
|
|
| Metric | Target | Device | Priority |
|
|
|--------|--------|--------|----------|
|
|
| Cold Launch Time | < 2.0s | iPhone 12 | P0 |
|
|
| Warm Launch Time | < 1.0s | iPhone 12 | P0 |
|
|
| Scroll FPS | 60fps | All lists | P0 |
|
|
| ViewModel Data Load | < 100ms | iPhone 12 | P1 |
|
|
| API Deserialization | < 50ms (1000 items) | iPhone 12 | P1 |
|
|
| Memory Usage (navigation) | < 150MB | iPhone 12 | P1 |
|
|
| Image Cache Key Generation | < 1ms | All devices | P2 |
|
|
| Security Checks | < 10ms | All devices | P2 |
|
|
|
|
---
|
|
|
|
## 2. Test Categories
|
|
|
|
### 2.1 Launch Performance (KordantUITests)
|
|
|
|
| Test | Metric | Baseline | Regression Threshold |
|
|
|------|--------|----------|---------------------|
|
|
| `testColdLaunchPerformance` | XCTApplicationLaunchMetric | < 2.0s | 10% (> 2.2s fails) |
|
|
| `testWarmLaunchPerformance` | XCTApplicationLaunchMetric | < 1.0s | 10% (> 1.1s fails) |
|
|
|
|
**Measurement:** Uses `XCTApplicationLaunchMetric` with `waitUntilResponsive: true`, waiting for the Dashboard navigation bar to appear. Tests run in UI testing mode with mocked data (populatedDashboard scenario).
|
|
|
|
### 2.2 Scroll Performance (KordantUITests)
|
|
|
|
| Test | Metric | Baseline | Regression Threshold |
|
|
|------|--------|----------|---------------------|
|
|
| `testDashboardScrollPerformance` | Clock, CPU, Memory | Smooth (no dropped frames) | 10% |
|
|
| `testAlertListScrollPerformance` | Clock, CPU, Memory | Smooth (no dropped frames) | 10% |
|
|
| `testServiceListScrollPerformance` | Clock, CPU, Memory | Smooth (no dropped frames) | 10% |
|
|
|
|
**Measurement:** Uses `XCTClockMetric`, `XCTCPUMetric`, `XCTMemoryMetric` in `measure(metrics:)` blocks. The test scrolls through lists programmatically and measures rendering performance. **Must run on physical devices** — simulators do not reflect real-world scroll performance.
|
|
|
|
### 2.3 Navigation Performance (KordantUITests)
|
|
|
|
| Test | Metric | Baseline | Regression Threshold |
|
|
|------|--------|----------|---------------------|
|
|
| `testTabNavigationPerformance` | Clock, CPU | < 500ms per transition | 10% |
|
|
| `testServiceDetailNavigationPerformance` | Clock, Memory | < 500ms per transition | 10% |
|
|
|
|
### 2.4 Data Loading Performance (KordantUITests)
|
|
|
|
| Test | Metric | Baseline | Regression Threshold |
|
|
|------|--------|----------|---------------------|
|
|
| `testDashboardDataLoadPerformance` | Clock, CPU, Memory | < 2s (includes network) | 10% |
|
|
| `testDarkWatchDataLoadPerformance` | Clock, Memory | < 2s (includes network) | 10% |
|
|
|
|
### 2.5 Memory Performance (KordantUITests)
|
|
|
|
| Test | Metric | Baseline | Regression Threshold |
|
|
|------|--------|----------|---------------------|
|
|
| `testMemoryUsageAcrossNavigationFlow` | XCTMemoryMetric | < 150MB peak | 10% |
|
|
| `testMemoryReturnsAfterNavigation` | XCTMemoryMetric | Stable (no leaks) | 10% increase |
|
|
|
|
### 2.6 Unit Performance (KordantTests)
|
|
|
|
| Test | Metric | Baseline | Regression Threshold |
|
|
|------|--------|----------|---------------------|
|
|
| `DeserializationPerformanceTests.decodeAlerts` | Manual timing | < 50ms (1000 items) | 10% |
|
|
| `DeserializationPerformanceTests.decodeExposures` | Manual timing | < 50ms (1000 items) | 10% |
|
|
| `ViewModelPerformanceTests.dashboardViewModelLoadTime` | Manual timing | < 100ms (50 items) | 10% |
|
|
| `ViewModelPerformanceTests.darkWatchViewModelLoadTime` | Manual timing | < 100ms (30 items) | 10% |
|
|
| `KeychainPerformanceTests.keychainStoreRetrievePerformance` | Manual timing | < 0.1ms per op | 10% |
|
|
| `SecurityPerformanceTests.jailbreakDetectionPerformance` | Manual timing | < 10ms | 10% |
|
|
| `SecurityPerformanceTests.runtimeIntegrityPerformance` | Manual timing | < 10ms | 10% |
|
|
|
|
### 2.7 XCTMetric Unit Performance (KordantTests)
|
|
|
|
| Test | Metric | Baseline | Regression Threshold |
|
|
|------|--------|----------|---------------------|
|
|
| `testJSONEncodingPerformance` | Clock, CPU, Memory | Established by 10 runs | 10% |
|
|
| `testJSONDecodingPerformance` | Clock, CPU, Memory | Established by 10 runs | 10% |
|
|
| `testThreatScoreCalculationPerformance` | Clock, CPU, Memory | Established by 10 runs | 10% |
|
|
| `testImageCacheMetadataPersistencePerformance` | Clock | Established by 10 runs | 10% |
|
|
| `testAlertSortingPerformance` | Clock | Established by 10 runs | 10% |
|
|
|
|
---
|
|
|
|
## 3. Baseline Establishment Process
|
|
|
|
1. **Initial Baseline:** Run each performance test 10 times on a reference device (iPhone 12).
|
|
2. **Record Results:** Xcode automatically records the baseline average.
|
|
3. **Document:** Record the baseline values in the "Baselines" column above.
|
|
4. **Accept:** Xcode compares future runs against the stored baseline.
|
|
5. **Review:** Baselines should be re-established after major OS updates or architecture changes.
|
|
|
|
### 3.1 Device-Specific Baselines
|
|
|
|
| Device | Cold Launch | Scroll FPS | Memory Peak |
|
|
|--------|-------------|------------|-------------|
|
|
| iPhone SE (2nd gen) | < 3.0s | 60fps (slower scroll) | < 120MB |
|
|
| iPhone 12 | < 2.0s | 60fps | < 150MB |
|
|
| iPhone 15 Pro | < 1.2s | 60fps | < 200MB |
|
|
|
|
---
|
|
|
|
## 4. Regression Detection
|
|
|
|
### 4.1 Threshold Configuration
|
|
|
|
Xcode's `measure(metrics:)` API automatically compares test results against stored baselines. A 10% regression threshold is configured for all tests.
|
|
|
|
```swift
|
|
// Example: Xcode flags this test if time exceeds baseline by > 10%
|
|
measure(metrics: [XCTClockMetric(), XCTCPUMetric(), XCTMemoryMetric()]) {
|
|
// code under test
|
|
}
|
|
```
|
|
|
|
### 4.2 CI Pipeline Integration
|
|
|
|
Performance tests run as part of the CI pipeline on every PR and release build:
|
|
|
|
1. **Unit performance tests:** Run on every PR (fast, no device needed)
|
|
2. **UI performance tests:** Run on device farm (iPhone SE, 12, 15 Pro) nightly
|
|
3. **Failure action:** PR cannot merge if any performance test regresses by > 10%
|
|
4. **Alert:** Performance degradation alerts sent to #ios-eng Slack channel
|
|
|
|
### 4.3 Manual Verification
|
|
|
|
In Xcode:
|
|
1. Open the test report (⌘9 → Tests tab)
|
|
2. Select a performance test
|
|
3. View the "Metrics" tab to see baseline vs. current results
|
|
4. Check "Performance Graph" for trend analysis across test runs
|
|
|
|
---
|
|
|
|
## 5. Optimization Techniques
|
|
|
|
### 5.1 Launch Time
|
|
|
|
| Technique | Location | Impact |
|
|
|-----------|----------|--------|
|
|
| Lazy service initialization | `KordantApp.swift` | ~500ms saved |
|
|
| Deferred setup after first frame | `ContentView.swift → task` | ~300ms saved |
|
|
| Minimal `didFinishLaunchingWithOptions` | `AppDelegate.swift` | ~200ms saved |
|
|
| Mock data in testing mode | `TestingMode.swift` | N/A (testing only) |
|
|
|
|
### 5.2 Scroll Performance
|
|
|
|
| Technique | Location | Impact |
|
|
|-----------|----------|--------|
|
|
| `LazyVStack` for lists | All list views | 60fps maintained |
|
|
| Image prefetching | `PaginatedListView.swift` | No loading jank |
|
|
| `ShieldSkeleton` placeholders | All loading states | Perceived performance |
|
|
| `CachedAsyncImage` with downsampling | `CachedAsyncImage.swift` | Memory efficient loading |
|
|
|
|
### 5.3 Memory
|
|
|
|
| Technique | Location | Impact |
|
|
|-----------|----------|--------|
|
|
| 50MB URLCache memory limit | `ImageCacheService.swift` | Prevents OOM |
|
|
| LRU disk cache eviction | `ImageCacheService.swift` | Disk quota enforced |
|
|
| Memory warning handling | `ImageCacheService.swift` | Clears cache on pressure |
|
|
| `@StateObject` lifecycle management | All ViewModels | No leaked view models |
|
|
|
|
### 5.4 Data Processing
|
|
|
|
| Technique | Location | Impact |
|
|
|-----------|----------|--------|
|
|
| Concurrent async data loading | `DashboardViewModel.swift` | 3x faster dashboard |
|
|
| JSON with iso8601 date strategy | `APIClient.swift` | Fast date parsing |
|
|
| Codable conformance | All models | Zero boilerplate parsing |
|
|
| Lazy metadata loading | `ImageCacheService.swift` | No disk I/O at launch |
|
|
|
|
---
|
|
|
|
## 6. Running Performance Tests
|
|
|
|
### 6.1 Local (Xcode)
|
|
|
|
```bash
|
|
# Run all performance tests
|
|
xcodebuild test \
|
|
-project Kordant.xcodeproj \
|
|
-scheme Kordant \
|
|
-testPlan KordantUITests \
|
|
-destination 'platform=iOS,name=iPhone 12' \
|
|
-only-testing:KordantUITests/LaunchPerformanceTests \
|
|
-only-testing:KordantUITests/ScrollPerformanceTests \
|
|
-only-testing:KordantUITests/NavigationPerformanceTests
|
|
|
|
# Run unit performance tests
|
|
xcodebuild test \
|
|
-project Kordant.xcodeproj \
|
|
-scheme Kordant \
|
|
-destination 'platform=iOS Simulator,name=iPhone 15 Pro' \
|
|
-only-testing:KordantTests/XCTMetricPerformanceTests
|
|
```
|
|
|
|
### 6.2 CI (Device Farm)
|
|
|
|
```bash
|
|
# Run on all target devices
|
|
xcodebuild test \
|
|
-project Kordant.xcodeproj \
|
|
-scheme Kordant \
|
|
-testPlan KordantUITests \
|
|
-destination 'platform=iOS,name=iPhone SE (2nd generation)' \
|
|
-destination 'platform=iOS,name=iPhone 12' \
|
|
-destination 'platform=iOS,name=iPhone 15 Pro' \
|
|
-resultBundlePath ./PerformanceResults.xcresult
|
|
```
|
|
|
|
### 6.3 Important Notes
|
|
|
|
- **Always run performance tests on physical devices.** Simulators do not reflect real-world CPU, GPU, or memory performance.
|
|
- Tests should be run with the device in **Airplane Mode** (for local stability tests) or with **real network conditions** (for data loading tests).
|
|
- Device brightness should be set to ~50% for consistency.
|
|
- Close all other apps before running performance tests.
|
|
- Tests should be run with a **Release build configuration** for accurate timing.
|
|
|
|
---
|
|
|
|
## 7. Troubleshooting
|
|
|
|
| Issue | Likely Cause | Resolution |
|
|
|-------|-------------|------------|
|
|
| Scroll test fails on device | Background processes | Restart device, close apps |
|
|
| Launch time > 2s | New dependency added in init | Move to deferred setup |
|
|
| Memory > 150MB | Image cache leak | Check URLCache eviction |
|
|
| JSON decode > 50ms | Large nested payload | Optimize API response shape |
|
|
| Baseline drift | iOS version change | Re-baseline after OS update |
|
|
|
|
---
|
|
|
|
## 8. Review Cadence
|
|
|
|
- **Weekly:** Review performance test results in CI dashboard
|
|
- **Monthly:** Full performance audit on reference device
|
|
- **Per Release:** Re-establish baselines before release branch cut
|
|
- **Per OS Update:** Re-run all tests after iOS beta/stable update
|