System Architecture Overview

Minuta - https://minuta.tools

Layers

  1. Models - Pure data structures (Tag, TimeRecord, ExportOptions, ReportData)
  2. Services - Business logic with protocol abstractions
  3. Utilities - Shared helpers (DurationFormatter, ColorPalette, CSVParser, AppLogger)
  4. AppState - Unified state management via @EnvironmentObject
  5. StorageLocationManager - User folder selection with security-scoped bookmarks
  6. 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 XcodeGen
  • Minuta scheme - 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 caching
  • TimeTrackingService - 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 directly
  • WebKitReportService - Converts SVG to PDF/PNG via WKWebView
  • No JavaScript dependencies

Related