Back to projects

mobile

Loyalty Cache Native iOS Loyalty Wallet

A native SwiftUI and SwiftData iOS app case study for scanning, storing, rendering, syncing, and quickly opening loyalty cards, with a share extension, favorites widget, App Shortcuts, and localized help.

Open Loyalty Cache
Technical focus
Native iOS architecture, feature implementation, and release-readiness perspective
Year
2026
Stack
Swift, SwiftUI, SwiftData, CloudKit, WidgetKit, App Intents, VisionKit, Vision, Core Image, Foundation Models, App Groups, Localization

Impact

  • Built a native iOS loyalty-card wallet with camera scanning, photo import, manual entry, duplicate handling, URL QR history, searchable card browsing, and checkout-ready barcode display.
  • Implemented system integrations including optional iCloud backup, App Group handoff, a Share Extension for importing cards from shared images, a favorites widget, deep links, and App Shortcuts.
  • Documented release-readiness work including multilingual release notes, privacy manifest configuration, localized app strings, performance guardrails, and focused XCTest coverage for the help assistant.

Loyalty Cache is a native iOS app for keeping loyalty cards on device and opening them quickly at checkout. The project is intentionally iOS-only: the technical scope is focused on platform-native scanning, persistence, widgets, shortcuts, localization, privacy, and release readiness rather than a cross-platform shell.

The app is not just a barcode list. It handles live camera capture, photo-based barcode extraction, manual entry, duplicate decisions, URL QR-code history, favorites, iCloud backup migration, widget snapshots, share-extension imports, localized help, and App Store release preparation.

Product Scope

The core workflow is built around saving real-world loyalty cards:

  • scan a card barcode with the camera
  • import a barcode or QR code from a photo
  • enter a code manually when scanning is unavailable
  • detect whether a QR payload is a web URL
  • save URLs into a separate URL history instead of treating them as loyalty cards
  • resolve duplicate cards and duplicate URLs with keep, merge, replace, or cancel paths
  • search, favorite, edit, delete, and restore cards
  • open a checkout view with a sharp regenerated barcode and temporary screen-brightness boost

The user-facing model keeps the privacy posture simple: cards stay on the device unless the user explicitly enables iCloud backup.

Native App Architecture

The app is organized as a SwiftUI and SwiftData Xcode project with separate feature modules:

  • the app module handles startup, routing, deep links, navigation state, and App Intents.
  • the scan module handles camera scan, photo import, manual entry, duplicate handling, and shared-import handoff.
  • the cards module handles saved-card browsing, search, favorites, URL history, editing, undo banners, checkout presentation, and barcode rendering.
  • the settings module handles backup, privacy, terms, help, and assistant controls.
  • the help module handles help-center content, deterministic help retrieval, optional on-device model generation, and chat state.
  • the data module handles SwiftData models, persistence, backup migration, and widget snapshots.
  • the share extension and favorites widget integrate with iOS outside the main app.

The app entry point creates shared storage and routing dependencies, injects the active SwiftData container, and handles deep links. The router supports direct navigation to scan, cards, favorites, and individual card checkout screens.

Scanning and Import

The camera scan path uses VisionKit’s DataScannerViewController through a SwiftUI wrapper. The scanner recognizes barcode and text items, ranks candidates, throttles processing, stops after a valid capture, and avoids duplicate scan emissions during rapid camera updates.

Photo import uses the Vision framework through VNDetectBarcodesRequest. Imported images are converted into ranked BarcodeScanCandidate values, preserving barcode descriptor data when the platform provides it. A shared classifier distinguishes loyalty card payloads from HTTP and HTTPS URLs, normalizes bare web URLs, and routes those results into URL history.

The Share Extension extends the same idea outside the app. It accepts shared images, extracts a barcode with Vision, asks the user for a card name, checks for duplicates, and passes the pending import back to the main app through the platform-supported extension handoff.

Persistence and Backup

The data model uses SwiftData with two main records: LoyaltyCard and ScannedURL. LoyaltyCard stores display name, normalized sort key, favorite state, barcode value, normalized lookup key, detected type, optional barcode descriptor data, optional user override, notes, and creation time.

Storage is handled by StorageController. By default the app uses a local SwiftData store. If the user enables backup, the controller creates a CloudKit-backed container using the private iCloud container and migrates records from local storage into cloud storage.

The backup migration is designed as a recoverable operation:

  • migration runs away from the main actor
  • card and URL records are copied in batches
  • migrated IDs are tracked to avoid duplication
  • progress is checkpointed in a JSON file
  • local/cloud fallback errors keep the last usable store active
  • debug launch arguments can run backup toggle stress testing

That gives the app an operationally safer backup toggle instead of treating CloudKit as a one-way switch.

Card Browsing and Checkout

The saved-card UI uses SwiftData queries and derived snapshots instead of doing expensive grouping directly in view bodies. Cards can be filtered by favorites, searched by name, barcode, or notes, grouped into sections, and opened directly from app navigation, widget deep links, or search results.

Checkout display is handled by a dedicated rendering path. BarcodeRenderer uses Core Image where possible and includes custom renderers for linear barcode families such as EAN-13, EAN-8, UPC-A, Code 39, Code 93, ITF, and Codabar. It also handles QR, PDF417, Data Matrix, Aztec, and Code 128 fallbacks.

Rendering is asynchronous through BarcodeImageView, cached with NSCache, and shown with a placeholder while generation runs off the main actor. The checkout view temporarily raises screen brightness while the code is visible and restores the previous value when the user leaves the screen or the app moves into the background.

iOS System Integrations

Loyalty Cache uses several Apple platform surfaces:

  • App Groups share pending imports and widget snapshots between the app, Share Extension, and widget extension.
  • WidgetKit shows favorite cards in small, medium, large, and lock-screen accessory families.
  • Widget links deep-link directly into a selected card checkout view.
  • App Intents expose shortcuts for opening Scan, Cards, and Favorites.
  • Deep linking supports app navigation from extensions, widgets, and shortcuts.
  • Privacy configuration declares required platform storage access and no tracking.
  • Platform configuration covers camera usage and device-family behavior.

The project also includes app icon assets for iOS and macOS-style app icon slots, even though the product itself is maintained as an iOS app.

Help Assistant and Localization

The app includes both a help center and an in-app help assistant. The help assistant is grounded in a local HelpKnowledgeStore, ranks help items deterministically, and can optionally use Apple’s on-device Foundation Models API when available. If model generation is unavailable, disabled, or fails, the deterministic answer path remains active.

The assistant is scoped tightly to Loyalty Cache usage questions. It can suggest actions such as opening photo scan mode, manual scan mode, camera scan mode, URL history, backup settings, or a help article. The app also includes explicit transparency and privacy copy: chat questions stay in the current screen and are not persisted.

Localization is broad for a utility app. The project includes localized app and platform strings for 16 locales, including English, Turkish, German, Arabic, Simplified Chinese, Japanese, Korean, Spanish, Italian, French, Portuguese Brazil, Russian, Dutch, Indonesian, Hindi, and Afrikaans.

Release Readiness

The project includes App Store release-note preparation for version 1.3 across all supported locales. The release notes describe shared-image import, smoother scan flow, better camera transitions, more reliable capture, and card browsing/navigation stability.

Release-readiness work is also visible in the codebase:

  • scanner lifecycle guardrails for active/inactive scene transitions
  • duplicate-card and duplicate-URL decision paths
  • route handoff from extensions and widgets
  • migration checkpointing for backup changes
  • privacy and terms links inside settings
  • help assistant feature flags and launch arguments
  • XCTest coverage for help knowledge ranking and assistant action encoding

Technical Perspective

This case study demonstrates native iOS product engineering: SwiftUI architecture, SwiftData modeling, camera and photo barcode extraction, Core Image rendering, CloudKit backup migration, widget and share-extension integration, App Intents, localization, privacy configuration, App Store release preparation, and performance-focused quality work.