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
- Initial Baseline: Run each performance test 10 times on a reference device (iPhone 12).
- Record Results: Xcode automatically records the baseline average.
- Document: Record the baseline values in the "Baselines" column above.
- Accept: Xcode compares future runs against the stored baseline.
- 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.
4.2 CI Pipeline Integration
Performance tests run as part of the CI pipeline on every PR and release build:
- Unit performance tests: Run on every PR (fast, no device needed)
- UI performance tests: Run on device farm (iPhone SE, 12, 15 Pro) nightly
- Failure action: PR cannot merge if any performance test regresses by > 10%
- Alert: Performance degradation alerts sent to #ios-eng Slack channel
4.3 Manual Verification
In Xcode:
- Open the test report (⌘9 → Tests tab)
- Select a performance test
- View the "Metrics" tab to see baseline vs. current results
- 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)
6.2 CI (Device Farm)
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