Overview › Quick Wins (iOS)

Quick Wins (iOS)

Generated 2026-05-07 · Costco iOS

Top 10 quick wins (ios)

Each row is a sub-1-day fix that reduces immediate risk. Copy-paste the After block; verify locally; ship.

Total quick wins
10
Estimated total effort
~1.5 days

spread across the team

Severity reduction
5

HIGH+ findings closed

CRITICAL

QW-i-01 · Remove NSAllowsArbitraryLoads from Notification Service Extension

30 min

Production NSE has cleartext traffic enabled — bypasses HTTPS-only policy of the main app.

Before
<!-- NotificationServiceExtension-Info.plist:25 -->
<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <true/>
</dict>
After
<!-- NotificationServiceExtension-Info.plist -->
<key>NSAppTransportSecurity</key>
<dict>
    <key>NSExceptionDomains</key>
    <dict>
        <key>cdn.costco.com</key>
        <dict>
            <key>NSExceptionAllowsInsecureHTTPLoads</key>
            <false/>
            <key>NSIncludesSubdomains</key>
            <true/>
        </dict>
    </dict>
</dict>
HIGH

QW-i-02 · Generate App Privacy Manifest

1 hour

Apple requires PrivacyInfo.xcprivacy for apps and SDKs accessing Required Reason APIs since 2024.

Before
# No PrivacyInfo.xcprivacy
After
<!-- Costco/Costco/PrivacyInfo.xcprivacy -->
<plist version="1.0">
<dict>
    <key>NSPrivacyAccessedAPITypes</key>
    <array>
        <dict>
            <key>NSPrivacyAccessedAPIType</key>
            <string>NSPrivacyAccessedAPICategoryUserDefaults</string>
            <key>NSPrivacyAccessedAPITypeReasons</key>
            <array><string>CA92.1</string></array>
        </dict>
        <!-- Add entries for FileTimestamp, DiskSpace, etc. -->
    </array>
    <key>NSPrivacyTracking</key>
    <false/>
</dict>
</plist>
HIGH

QW-i-03 · Replace try! NSRegularExpression with try?

30 min

Force-try on regex compilation crashes on syntax error. Use safe variant + fallback.

Before
// MyWarehouseAndDeliveryStateManager.swift:863
let regex = try! NSRegularExpression(
    pattern: "...",
    options: []
)
After
// MyWarehouseAndDeliveryStateManager.swift:863
guard let regex = try? NSRegularExpression(
    pattern: "...",
    options: []
) else {
    Logger.warehouse.error("Regex compile failed; falling back to substring match")
    return false  // or use a simpler matcher
}
HIGH

QW-i-04 · Replace force-unwrap on URL(string:) with optional pattern

1 hour

18+ instances in BazaarVoiceClient force-unwrap a hardcoded URL string. One typo crashes the entire reviews module.

Before
// BazaarVoiceClient.swift
static let baseURL = URL(string: "https://api.bazaarvoice.com/...")!
After
// BazaarVoiceClient.swift
static let baseURL: URL = {
    guard let u = URL(string: "https://api.bazaarvoice.com/...") else {
        Logger.bazaar.fault("Invalid base URL — Reviews disabled")
        return URL(string: "about:blank")!  // safe fallback; downstream still no-ops
    }
    return u
}()
HIGH

QW-i-05 · Invalidate cookieListenerTimer in deinit

30 min

Polling timer fires every 5 seconds for the app lifetime; doesn't get cancelled. Drains battery + memory.

Before
// CookiesManager.swift:691
self.cookieListenerTimer = Timer.scheduledTimer(
    withTimeInterval: 5.0, repeats: true
) { _ in
    self.checkCookies()
}
After
// CookiesManager.swift
self.cookieListenerTimer = Timer.scheduledTimer(
    withTimeInterval: 5.0, repeats: true
) { [weak self] _ in
    self?.checkCookies()
}

deinit {
    cookieListenerTimer?.invalidate()
    cookieListenerTimer = nil
}
MEDIUM

QW-i-06 · Replace print() with os.Logger

1 hour

30+ print() calls leak to console regardless of build config; Logger respects privacy + filtering.

Before
// DMCWidgetHelper.swift:84
print("Store is not Opend yet")
print("\(Date()): Get DMCWidget Timeline called")
After
// At top of file or in a Logger+ extension
import os
extension Logger {
    static let widget = Logger(subsystem: "com.costco.costco", category: "widget")
}

// Replace print with categorized Logger:
Logger.widget.notice("Store is not open yet")
Logger.widget.debug("Get DMCWidget Timeline called")
MEDIUM

QW-i-07 · Add SwiftLint custom rule banning hex outside design system

1 hour

Hex literals scattered across feature code defeat the design-token pipeline.

Before
# .swiftlint.yml — no custom rules
After
# .swiftlint.yml
custom_rules:
  no_hex_color_outside_design_system:
    name: "Hardcoded hex color"
    regex: 'Color\(hex:|UIColor\(hex:|init\(hex:'
    excluded:
      - ".*CostcoDesignSystem.*"
    severity: warning
  no_force_try_in_production:
    name: "Forbid try!"
    regex: 'try!'
    excluded:
      - ".*Tests.*"
      - ".*UITests.*"
    severity: error
MEDIUM

QW-i-08 · Add weak self to escaping closures

30 min

Closures stored on long-lived objects (NotificationCenter, Timer, dispatch queues) capture self strongly without [weak self].

Before
// CartViewModel.swift
NotificationCenter.default.addObserver(
    forName: .cartUpdated, object: nil, queue: .main
) { notification in
    self.refresh()  // strong capture
}
After
// CartViewModel.swift
NotificationCenter.default.addObserver(
    forName: .cartUpdated, object: nil, queue: .main
) { [weak self] notification in
    self?.refresh()
}

// Or migrate to Combine:
NotificationCenter.default.publisher(for: .cartUpdated)
    .receive(on: DispatchQueue.main)
    .sink { [weak self] _ in self?.refresh() }
    .store(in: &cancellables)
LOW

QW-i-09 · Add CODEOWNERS

15 min

29 SPM packages, no review routing.

Before
# No CODEOWNERS
After
# .github/CODEOWNERS
* @costco-ios/maintainers
/Costco-Digital/Features/Cart/        @costco-ios/cart-team
/Costco-Digital/Features/PDP/         @costco-ios/pdp-team
/Costco-Digital/Features/Authentication/  @costco-ios/security
/Costco-Digital/CostcoDesignSystem/   @costco-ios/design-system
/Costco-Digital/Core/                 @costco-ios/platform
MEDIUM

QW-i-10 · Bump Podfile fallback iOS to 16.0

30 min

Main app is iOS 16; Podfile fallback at 14 lets some pods ship for older OS than the app supports.

Before
# Costco/Podfile
platform :ios, '14.0'
After
# Costco/Podfile
platform :ios, '16.0'
Costco iOS · Code Review Report · Generated 2026-05-07 · 88 machine-curated findings