System Architecture Overview
Minuta - https://minuta.tools
Layers
- Models - Pure data structures (Tag, TimeRecord, ExportOptions, ReportData)
- Services - Business logic with protocol abstractions
- Utilities - Shared helpers (DurationFormatter, ColorPalette, CSVParser, AppLogger)
- AppState - Unified state management via @EnvironmentObject
- StorageLocationManager - User folder selection with security-scoped bookmarks
- Views - SwiftUI views (unified codebase for iOS and Mac Catalyst)
Service Protocols
Services use protocol abstractions for testability:
// Core protocols
protocol FileStorageService { ... }
protocol TimeTrackingServiceProtocol: Sendable { ... }
protocol ReportService: Sendable { ... }
@MainActor protocol StorageLocationManagerProtocol { ... } Package Structure
Shared/- Swift Package with Models + Services + Utilities (MinutaShared)Minuta/- iOS + Mac Catalyst Xcode project
Mac Catalyst App
Single iOS app that runs on iPhone, iPad, and Mac via Catalyst:
Minuta.xcodeproj- Generated via XcodeGenMinutascheme - runs on iOS devices/simulators and “My Mac (Mac Catalyst)”- Unified UI codebase - same interface on all platforms
Data Flow
Views -> AppState -> Services -> FileSystem (JSON files)
\-> StorageLocationManager (folder selection) Storage Location
- Default:
~/Documents/Minuta/ - User can select custom folder via Settings
- Security-scoped bookmarks persist folder access across app launches
- StorageLocationManager handles bookmark creation/restoration
File Structure
Minuta/
+-- tags.json
+-- records/
+-- YYYY/
+-- MM/
+-- YYYY-MM-DD_HHmmss.json View Structure
ContentView
+-- RunningTimersSection
| +-- RunningTimerEditor (inline, per timer)
+-- TodaySectionView
| +-- PendingDeletionRow (soft-deleted)
| +-- EditableRecordRow (per record)
+-- HistorySection
+-- DateRangePickerView
+-- TagFilterView
+-- EditableRecordRow (per record)
SettingsSheet (modal)
+-- Tags section
+-- Data section (import/export/folder)
+-- About section Concurrency Model
Services use Swift’s actor model for thread safety:
LocalFileStorageService- Actor with in-memory record cachingTimeTrackingService- Actor wrapping storage with higher-level operations
This ensures all data access is serialized and safe for concurrent UI updates.
Performance Optimizations
Record Caching
LocalFileStorageService maintains an in-memory cache:
- Cache populated on first query
- Invalidated on save/update/delete operations
- Reduces disk I/O for repeated queries
Tag Lookup
AppState maintains tagsByID: [UUID: Tag] dictionary:
- O(1) tag lookup by ID
- Updated when tags are loaded/modified
Logging
Structured logging via OSLog (AppLogger):
AppLogger.storage.info("Loading records...")
AppLogger.tracking.error("Failed to save: \(error)")
AppLogger.app.debug("State updated")
AppLogger.intents.info("Starting timer via Shortcut") Categories: storage, tracking, app, intents
Report Generation
Pure Swift SVG rendering:
SVGReportRenderer- Generates SVG strings directlyWebKitReportService- Converts SVG to PDF/PNG via WKWebView- No JavaScript dependencies
Related
- 301-file-storage - File storage service details
- 305-storage-location - Storage location manager
- 306-report - Report generation
- 402-ios - UI architecture