Skip to main content
Full technical details in your project at /docs/modules/Storage.md

Concurrency notes (v2.2.0)

  • MessageRepositoryImpl, ConversationRepositoryImpl, and SettingsRepositoryImpl are @MainActor-pinned. If downstream code constructs these from a non-main actor, you need an await at the construction site. The public protocol API is unchanged.
  • Swift 6 strict concurrency across the package.
If you hit MainActor-isolated errors on ModelContext after upgrading, it’s this change. Build the repository on the main thread or await it from a background context.

What You Get

  • Repository pattern - Never expose @Model types to Views
  • DTO pattern - Lightweight, Sendable data transfer objects
  • Keychain wrapper - Secure, tested token storage
  • Cursor pagination - Efficient infinite scroll
  • Batch operations - Optimized bulk updates/deletes
  • Migration framework - Safe schema evolution
  • Optional cloud sync - Supabase integration (feature-flagged)
Time saved: 16-24 hours of implementing and testing repositories, pagination, Keychain, and migrations.

Production Setup (From Real Code)

Here’s how the boilerplate actually sets up storage in CompositionRoot.swift:

Key Architecture Decisions

  • @Model types stay in the data layer - Views consume DTOs, never @Model objects
  • DTOs are public - lightweight, Sendable structs
  • Repositories on @MainActor - SwiftData requirement
  • Protocol-based - easy to test with mocks
  • Single ModelContext - thread-safe, no conflicts

Repository Pattern (Production Implementation)

The boilerplate uses protocol-based repositories that return lightweight DTOs:
Why This Pattern:
  • Views never see @Model types (maintains MVVM boundaries)
  • DTOs are Sendable (thread-safe)
  • Easy to mock for testing
  • Can swap implementations (local, cloud, hybrid)

Optional Cloud Sync

Sync conversations and messages across devices:Setup Guide: Chat Sync Setup
  1. Run SQL migration
  2. Enable feature flag
  3. Wire up hybrid repositories
  4. Test cross-device sync

Keychain Storage (Real Implementation)

The boilerplate includes a production-ready Keychain wrapper used for all secure storage:
Production Features:
  • ✅ iOS Security framework (kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly)
  • ✅ Automatic PII redaction in logs
  • ✅ Thread-safe operations
  • ✅ Comprehensive error handling
  • ✅ Works with AuthInterceptor via KeychainTokenProvider

Customization Examples

Add New SwiftData Model

Key Files

Dependencies

  • Core - Error handling, logging
  • Networking - For cloud sync (optional)

Used By

  • FeatureChat - Conversation and message storage
  • FeatureSettings - Settings persistence
  • Auth - Keychain for tokens
  • App target (profile) - ProfilePhotoStorageClient for profile photos (optional)

Best Practices

  • Use DTOs for passing data
  • @MainActor for ModelContext
  • Sendable for repositories
  • Unique IDs with @Attribute(.unique)
  • Protocol-based design
  • Return DTOs (not @Model objects)
  • Async/await throughout
  • Comprehensive error handling
  • Offline-first (local writes fast)
  • Background sync (non-blocking)
  • Graceful degradation
  • Optional (feature flag)

Learn More

Full Documentation

Complete Storage guide

Chat Sync

Enable cross-device sync

Photo Storage

Enable cloud photos

Building Guide

Add custom models

Test Coverage

StorageTests runs as part of the workspace suite — ~598 tests across 12 package test targets + the app test suites in one Boilerplate.xctestplan pass. Storage tests cover:
  • CRUD operations
  • Pagination
  • Error scenarios
  • Concurrent access
  • Keychain operations