State of the Costco iOS App
An amber health status overall. Architecture, networking security, and test coverage are strong; app stability, lifecycle/memory, and accessibility need a focused remediation quarter.
Headline indicators
At a glance
Severity distribution
Health by area · sorted
Status by area
Twelve health areas, each with definition, current state, business impact, and next step. Color stripe encodes status — green / amber / red.
Likelihood of crashes on real iPhones / iPads in production.
13 critical force-try regex compilations; 18+ URL force-unwraps on Reviews API; 6 timer leaks in cart / cookies / ad views; pervasive ! force-unwrap pattern.
Each failed regex or URL parse takes down the feature on launch. Long sessions accumulate timer leaks → jetsam kills.
Stability strike-team — 4 weeks. Fix the 13 force-try sites; sweep BazaarVoice URL force-unwraps; add SwiftLint rules force_try / force_unwrapping.
HTTPS, Keychain, Cert Pinning, ATS exceptions, secret handling.
Strong baseline (CostcoServerTrustManager pinning, Keychain wrappers, Passkey via NokNok, SecRandomCopyBytes used). NSAllowsArbitraryLoads = true in Notification Service Extension Info.plist (both production and QA).
ATS bypass weakens platform-wide HTTPS posture; auditors and Apple Privacy Reports flag this.
Replace NSAllowsArbitraryLoads with NSExceptionDomains for specific hosts; document each pin and a renewal calendar.
App Store privacy nutrition label fidelity + iOS-specific permissions.
Permissions: camera, photo, location. Background location for geofencing. ThreatMetrix and Adobe analytics SDKs collect device + behavioral data.
Apple's annual privacy review can withhold review approval if disclosures are off.
Generate the Privacy Manifest (PrivacyInfo.xcprivacy) per Apple's 2024+ requirement; align with App Store privacy labels.
Cold start, scrolling, memory, battery.
Heavy synchronous work in viewDidLoad; UIImage(named:) repeated in cell config; cookie polling timer running every 5s; no Instruments-based dashboard.
iOS users perceive jank vividly. App Store reviews and crash-free metrics suffer.
Move heavy init to async Task; cache cell images; replace polling timer with event-based callback.
VoiceOver, Dynamic Type, contrast, traits.
Some accessibilityLabel/accessibilityHint usage; Dynamic Type not enforced (hardcoded UIFont.systemFont sizes); missing traits on buttons; VoiceOver gaps in onboarding flows.
AODA/ADA exposure same as Android; on iOS, App Store reviewers also check accessibility.
Mandate UIFont.preferredFont(forTextStyle:); SwiftLint a11y plugin; UI tests with Accessibility Inspector.
Languages and regional formatting.
4 locales (en, en-CA, fr-CA, fr); ~2,000 strings per locale; pseudo-localization not enabled; some hardcoded literals.
Same expansion exposure as Android.
Enable pseudo-locale (en-XA / ar-XB) in debug schemes; SwiftGen for compile-time string safety.
Module structure, MVVM, public-API discipline.
29 SPM packages with clear responsibilities; clean MVVM in feature ViewModels; CostcoNetworkClient + ServerTrustManager solid.
Strong foundation for parallel feature work.
Document SPM access-control conventions; tighten public API surface where possible.
XCTest + Snapshot + Mockey infrastructure.
680 test files; SnapshotTesting + iOSSnapshotTestCase; 6 XCTestPlans (Snapshot-Tests-EN_CA / EN_US / FR_CA + others). Coverage threshold not surfaced in CI.
Snapshot suite is a real strength.
Publish line + branch coverage to Codecov; fail PRs that drop coverage on changed files.
Xcode, Swift, Pods, SPM.
Xcode 15.x · Swift 5 · iOS 16 deployment · 29 SPM packages · CocoaPods + SwiftLint.
Modern toolchain; gradual migration to all-SPM is plausible.
Plan CocoaPods → SPM migration as legacy Pods become SPM-available.
Legacy Obj-C, hardcoded literals, duplicate stacks.
108 .m + 109 .h files in main app; hardcoded hex colors despite design system; RZVinyl custom networking fork; mixed UIKit/SwiftUI.
Each new feature pays a small Obj-C/UIKit interop tax.
Quarterly Obj-C → Swift LoC target; ban hex literals; assess RZVinyl replacement.
PR reviews, conventions, build times.
29 SPM packages buildable in parallel; Azure Pipelines CI with NowSecure; SwiftLint integrated; no CODEOWNERS evident.
Reviews unrouted; new engineer onboarding friction.
Add CODEOWNERS; enforce SwiftLint baseline on PRs; document SPM workflow.
OSLog, Crashlytics, signposts, dashboards.
print() usage in widget + test utils; no os.Logger adoption seen; Crashlytics integration unclear in scan.
Without structured logging, post-launch incident debug is harder.
Adopt os.Logger with signpost categories; ship structured-log dashboards.
Top risks (business framing)
Each risk maps to suggested owner team and rough effort.
Force-try regex / URL crashes
Memory leaks: timers, observers, retain cycles
ATS exceptions in Notification Service Extension
Accessibility: Dynamic Type missing
Hardcoded hex colors despite design system
print() / NSLog in production paths
Stale documentation
Quick reference
If you have 2 minutes
Look at the headline indicators. Critical defects (16) and accessibility (50/100) are where leadership attention is most useful.
If you have 10 minutes
Skim the Top Risks. Each maps to a recommended owner and effort estimate.
If you want to dig in
Tech Overview has charts and Excel export. Class-by-class gives engineers their working list.
For the board
Print-friendly: Cmd-P hides sidebar and toolbars.