Architecture Pattern
r2Vault follows a modern MVVM (Model-View-ViewModel) architecture using Swift 6’s@Observable macro for reactive state management.
Key Architectural Principles
Unidirectional Data Flow
Views observe ViewModels, ViewModels coordinate Services, Services return data
Separation of Concerns
Models, Services, ViewModels, and Views are cleanly separated
Reactive UI
SwiftUI views automatically update when observable state changes
Async/Await First
All network and file operations use structured concurrency
MVVM Implementation
Observable ViewModels
r2Vault uses Swift 6’s@Observable macro for automatic change tracking:
ViewModels/AppViewModel.swift
The
@Observable macro eliminates the need for manual @Published properties and ObservableObject conformance, providing better performance and cleaner code.SwiftUI + AppKit Hybrid
r2Vault combines SwiftUI for the main interface with AppKit for menu bar functionality:Services/MenuBarManager.swift
Why the hybrid approach?SwiftUI’s menu bar support is limited. Using
NSStatusItem and NSPopover provides:- Persistent menu bar icon
- Control over popover behavior (
.applicationDefined) - Better window management
Concurrency Model
Structured Concurrency with async/await
All asynchronous operations use Swift’s modern concurrency:ViewModels/AppViewModel.swift
Parallel Uploads with TaskGroup
Multiple files upload concurrently usingwithTaskGroup:
ViewModels/AppViewModel.swift
Concurrent Deletion with TaskGroup
Bulk deletions happen in parallel:ViewModels/AppViewModel.swift
State Management
Single Source of Truth
AppViewModel is the single source of truth, created once at app launch:
FiaxeApp.swift
Environment Injection
The view model flows down the view hierarchy via SwiftUI’s environment:ContentView.swift
Per-Task Observable State
Each upload task is its own observable object:Models/UploadTask.swift
Data Flow
Example: File Upload Flow
- User drops files →
ContentViewcallsviewModel.handleDroppedURLs() - ViewModel creates tasks → Builds
FileUploadTaskobjects, adds to queue - ViewModel spawns upload → Calls
uploadPendingTasks()with TaskGroup - Service performs upload →
R2UploadService.upload()signs request, uploads via URLSession - Progress updates → Delegate calls
@MainActorclosure, updates task progress - View reacts → SwiftUI observes task changes, updates progress bar
- Completion → Task status changes to
.completed, view shows success
Thread Safety
MainActor for UI
All view models and UI state run on the main actor:Actor for Caching
The thumbnail cache is an actor for safe concurrent access:Services/ThumbnailCache.swift
Sendable Types
All data types crossing actor boundaries areSendable:
Models/R2Credentials.swift
Next Steps
Project Structure
Explore the directory layout and module organization
Tech Stack
Learn about the frameworks and dependencies used