Architecture Guidelines
Relevant source files
The following files were used as context for generating this wiki page:
The putio-ios project is a native iOS application built using UIKit and follows a modularized directory structure. The architecture prioritizes standard Apple patterns, leveraging storyboards, presenters, and view models to manage complexity while maintaining a legacy UIKit foundation. Shared logic is centralized in a Common directory, while business logic is isolated within feature-specific folders under Features.
Sources: CLAUDE.md:4-6, CONTRIBUTING.md:73-75
Core Architectural Patterns
The codebase adheres to a set of specific patterns designed to maintain consistency across the application.
Feature-Based Organization
Code is divided into two primary directories:
- Putio/Features/
<Area>: Contains code specific to a feature (e.g., Files, History, Settings). This includes ViewControllers, ViewModels, and associated Storyboards. - Putio/Common: Contains genuinely shared code, including API wrappers, managers (e.g.,
DeeplinkManager), and helpers (e.g.,PutioRealm).
Sources: CLAUDE.md:32-34
Threading and Concurrency
- Main Thread: Exclusively for UI updates.
- Background Threads: Reserved for expensive operations such as network response parsing, image decoding, and PDF parsing.
- Lifecycle Management: Async loading paths must handle success, failure, cancellation, and back navigation cleanly.
Sources: CLAUDE.md:38-40
Error Handling
Instead of silent failures, the application uses the InternalFailurePresenter to surface unexpected internal errors to the user or logs.
Sources: CLAUDE.md:37
Data Persistence (Realm)
The application uses Realm for local data persistence. The PutioRealm helper class centralizes configuration, migration, and session management.
Persistence Lifecycle
The application utilizes a specific migration strategy to handle schema updates and ensures that old database formats (from Realm 3.x) are cleaned up to prevent crashes with newer versions of the SDK.
The diagram shows the setup flow for the Realm database, including handling incompatible file formats.
Sources: Putio/Common/Helpers/Realm.swift:58-85
Data Model Management
| Feature | Implementation | Description |
|---|---|---|
| Migrations | migrate(_:_:) | Handles schema versioning (currently version 12). |
| User Session | replaceUserSession | Deletes old user/config objects and adds new ones to ensure a clean state on login. |
| Recovery | recoverDownloadsIfNeeded | Rebuilds download records from file references stored in UserDefaults if the database was wiped. |
Sources: Putio/Common/Helpers/Realm.swift:9-10, Putio/Common/Helpers/Realm.swift:49-56, Putio/Common/Helpers/Realm.swift:145-151
Feature Implementation: Files and History
Features typically follow a View-ViewModel pattern, often utilizing StatefulViewController to manage different UI states (loading, empty, error, offline).
State Management in ViewControllers
The FilesViewController demonstrates the integration of a state machine to handle various network and data conditions.
This diagram illustrates the transitions between different UI states based on API response outcomes.
Sources: Putio/Features/Files/FilesViewController.swift:90-113, Putio/Features/Files/FilesViewController.swift:145-167
ViewModel Responsibilities
ViewModels like HistoryViewModel manage the internal state and provide representable data to the ViewControllers via delegates or direct property access.
The sequence shows the interaction between the ViewController, ViewModel, and the underlying API SDK.
Sources: Putio/Features/Files/FilesViewController.swift:135-150, Putio/Features/History/HistoryViewModel.swift:49-65
Authentication and App Lifecycle
The AppDelegate manages the high-level application lifecycle, including SDK initialization (Sentry, Intercom, Realm) and the authentication state.
Bootstrapping Flow
- Logger Setup: Configures SwiftyBeaver.
- SDK Configuration: Initializes Realm, Sentry (if enabled), and Intercom (if enabled).
- UI Configuration: Applies global styling via
Stylize.UIKitand configures the audio session. - Authentication Check: Validates the existing keychain token and either fetches user info or redirects to the login screen.
Sources: Putio/AppDelegate.swift:39-50, Putio/AppDelegate.swift:207-216
Authentication Process
The app uses a PutioKeychain to store access tokens. If a token is found, it attempts to fetch account info and configuration simultaneously using a DispatchGroup.
Sources: Putio/AppDelegate.swift:218-261
Navigation and Deeplinking
Navigation is managed via a RootContainerViewController and a MainTabBarController. The DeeplinkManager handles routing for both URL schemes and Universal Links.
| Route Pattern | Target Destination |
|---|---|
/files/{id} | Files Tab -> Specific File Page |
/downloads/{id} | Downloads Tab -> Specific Download |
/history | History Tab |
/settings / /account | Account Tab |
/link | Account Tab -> Link Device Page |
Sources: Putio/Common/Managers/DeeplinkManager.swift:65-95
Development and Verification
Architectural integrity is maintained through a strict verification matrix and specific command-line tools.
make bootstrap: Installs dependencies and prepares the environment.make verify: Runs the primary test suite (should be run before any PR).make e2e-simulator: Runs XCUITests against mocked API responses usingPutioE2EMockURLProtocol.make run-simulator: Launches a signed build for interactive testing of persistence and keychain logic.
Sources: CLAUDE.md:43-48, CONTRIBUTING.md:45-48, Putio/Common/API/PutioE2EMockURLProtocol.swift:5-15
Local Configuration
Teammates use a Config/Local.xcconfig (derived from 1Password) to manage private service keys and signing identities without committing them to the repository.
Sources: CLAUDE.md:20-25, CONTRIBUTING.md:23-28