Files
simvision/ARCHITECTURE.md
Michael Simard 872354b834 Initial commit: SimVision tvOS streaming app
Features:
- VOD library with movie grouping and version detection
- TV show library with season/episode organization
- TMDB integration for trending shows and recently aired episodes
- Recent releases section with TMDB release date sorting
- Watch history tracking with continue watching
- Playlist caching (12-hour TTL) for offline support
- M3U playlist parsing with XStream API support
- Authentication with credential storage

Technical:
- SwiftUI for tvOS
- Actor-based services for thread safety
- Persistent caching for playlists, TMDB data, and watch history
- KSPlayer integration for video playback

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-21 22:12:08 -06:00

624 lines
23 KiB
Markdown

# SimVision Architecture Documentation
## System Overview
SimVision is a tvOS application built with SwiftUI that enables users to authenticate, retrieve streaming credentials, and play VOD content from XStream-compatible m3u playlists. The architecture follows MVVM (Model-View-ViewModel) pattern with a centralized state management approach.
## High-Level Architecture Diagram
```
┌─────────────────────────────────────────────────────────────────┐
│ User Interface Layer │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Password │ │ VOD Library │ │ Video Player │ │
│ │ Entry View │ │ View │ │ View │ │
│ └──────────────┘ └──────────────┘ └──────────────┘ │
└───────────────────────────┬─────────────────────────────────────┘
┌───────────────────────────┼─────────────────────────────────────┐
│ │ ViewModel Layer │
│ ┌──────────────┐ ┌──────┴──────┐ ┌──────────────┐ │
│ │ Auth │ │ VOD Library │ │ Video Player │ │
│ │ ViewModel │ │ ViewModel │ │ ViewModel │ │
│ └──────────────┘ └─────────────┘ └──────────────┘ │
└───────────────────────────┬─────────────────────────────────────┘
┌───────────────────────────┼─────────────────────────────────────┐
│ │ State Management │
│ ┌──────┴──────┐ │
│ │ AppState │ (ObservableObject) │
│ │ - auth │ │
│ │ - playlist │ │
│ │ - error │ │
│ └─────────────┘ │
└───────────────────────────┬─────────────────────────────────────┘
┌───────────────────────────┼─────────────────────────────────────┐
│ │ Service Layer │
│ ┌──────────────┐ ┌──────┴──────┐ ┌──────────────┐ │
│ │ Auth │ │ Playlist │ │ Network │ │
│ │ Service │ │ Service │ │ Service │ │
│ └──────────────┘ └─────────────┘ └──────────────┘ │
│ ┌──────────────┐ ┌─────────────┐ │
│ │ Storage │ │ M3U Parser │ │
│ │ Service │ │ │ │
│ └──────────────┘ └─────────────┘ │
└───────────────────────────┬─────────────────────────────────────┘
┌───────────────────────────┼─────────────────────────────────────┐
│ │ External Systems │
│ ┌──────────────┐ ┌──────┴──────┐ ┌──────────────┐ │
│ │ Web Service │ │ XStream │ │ Keychain │ │
│ │ (Auth) │ │ Server │ │ │ │
│ └──────────────┘ └─────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────────┘
```
## Architectural Layers
### 1. User Interface Layer (Views)
**Responsibility**: Present information to users and handle user interactions.
**Components**:
- **PasswordEntryView**: Initial authentication screen where users enter their password
- **MainTabView**: Root navigation container using TabView (expandable for Live TV)
- **VODLibraryView**: Grid display of all VOD content with filtering capabilities
- **VODDetailView**: Detailed view of a selected VOD item with metadata and play button
- **VideoPlayerView**: Full-screen video playback using AVPlayerViewController
- **Component Views**: Reusable UI components (VODCardView, LoadingView, ErrorView)
**Key Patterns**:
- SwiftUI declarative syntax
- Environment objects for state injection
- Focus state management for tvOS
- Navigation using NavigationStack
### 2. ViewModel Layer
**Responsibility**: Handle view-specific logic, user input validation, and UI state transformations.
**Components**:
- **AuthenticationViewModel**
- Manages password input state
- Validates password criteria
- Controls password visibility toggle
- **VODLibraryViewModel**
- Filters VOD items by category
- Implements search functionality
- Manages filter state
- **VideoPlayerViewModel**
- Controls AVPlayer lifecycle
- Manages playback state (playing, paused)
- Handles playback errors
**Key Patterns**:
- ObservableObject protocol
- @Published properties for reactive updates
- @MainActor isolation for UI updates
### 3. State Management Layer
**Component**: **AppState** (Central Coordinator)
**Responsibility**: Global application state management and coordination between services.
**State Properties**:
```swift
@Published var isAuthenticated: Bool
@Published var credentials: XStreamCredentials?
@Published var playlist: Playlist?
@Published var isLoading: Bool
@Published var error: NetworkError?
```
**Key Methods**:
- `checkAuthentication()`: Verify stored credentials on app launch
- `authenticate(password:)`: Perform authentication flow
- `loadPlaylist(forceRefresh:)`: Fetch and parse playlist
- `logout()`: Clear credentials and reset state
**Design Decisions**:
- Single source of truth for global state
- Environment object pattern for dependency injection
- Async/await for all asynchronous operations
- @MainActor isolation ensures thread safety for UI updates
### 4. Service Layer
**Responsibility**: Business logic, data operations, and external system integration.
#### 4.1 NetworkService
**Type**: Actor (thread-safe)
**Responsibility**: Core HTTP client for all network operations.
**Key Features**:
- Generic request method with Codable support
- Automatic error mapping (URLError → NetworkError)
- Configurable timeouts
- Async/await based
- Request/response logging capability
**Error Handling**:
```swift
// Converts various error types to NetworkError
- URLError.notConnectedToInternet NetworkError.noInternetConnection
- URLError.timedOut NetworkError.timeout
- HTTP 401/403 NetworkError.authenticationFailed
- HTTP 5xx NetworkError.serverError(code)
```
#### 4.2 AuthenticationService
**Type**: Actor (thread-safe)
**Responsibility**: Authentication flow management.
**Flow**:
1. Accept password from user
2. Send POST request to web service with password in header
3. Receive XStream credentials (server, port, username, password)
4. Store credentials securely via StorageService
5. Return credentials to AppState
**Integration Points**:
- NetworkService: HTTP requests
- StorageService: Credential persistence
#### 4.3 PlaylistService
**Type**: Actor (thread-safe)
**Responsibility**: Fetch and parse m3u playlists from XStream servers.
**Features**:
- Constructs XStream API URLs with credentials
- Downloads m3u data as string
- Delegates parsing to M3UParser
- Caches parsed playlist in memory
- Force refresh capability
**URL Construction**:
```
http://server:port/get.php?username=X&password=Y&type=m3u_plus&output=ts
```
#### 4.4 StorageService
**Type**: Actor (thread-safe)
**Responsibility**: Secure data persistence.
**Storage Mechanisms**:
- **Keychain**: XStream credentials (encrypted by system)
- **UserDefaults**: User preferences (non-sensitive)
**Key Operations**:
- `saveCredentials()`: Store credentials in Keychain
- `loadCredentials()`: Retrieve credentials from Keychain
- `clearCredentials()`: Delete credentials from Keychain
- Generic preference save/load methods
**Security Considerations**:
- Never stores user-entered password
- Only stores XStream credentials (received from web service)
- Keychain encryption handled by iOS/tvOS
- Service identifier: `com.simvision.tvos`
#### 4.5 M3UParser
**Type**: Class
**Responsibility**: Parse m3u playlist files in XStream format.
**Parsing Strategy**:
```
1. Validate #EXTM3U header
2. Iterate through lines
3. Pair #EXTINF lines with subsequent URL lines
4. Extract attributes using regex:
- tvg-id → id
- tvg-name → name
- tvg-logo → iconURL
- group-title → categoryID
5. Create VODItem instances
6. Extract unique categories
```
**Attribute Extraction Example**:
```
#EXTINF:-1 tvg-id="123" tvg-name="Movie" tvg-logo="http://..." group-title="Movies",Display Name
http://server:port/movie/username/password/12345.mp4
```
**Error Handling**:
- Invalid format detection
- Empty playlist detection
- Malformed entry skipping
## Data Models
### Core Models
#### Credentials
```swift
struct XStreamCredentials {
let serverUrl: String
let port: String
let username: String
let password: String
// Computed properties for URL construction
var baseURL: String
func constructPlaylistURL() -> URL?
}
```
#### VODItem
```swift
struct VODItem: Identifiable, Codable, Hashable {
let id: String
let name: String
let streamURL: String
let iconURL: String?
let categoryID: String?
let description: String?
// Additional metadata
}
```
#### Playlist
```swift
struct Playlist: Codable {
let vodItems: [VODItem]
let categories: [Category]
let lastUpdated: Date
// Computed properties for filtering
var categorizedVODItems: [String: [VODItem]]
}
```
## Communication Patterns
### 1. User Action Flow
```
User taps button
→ View captures action
→ Calls method on ViewModel (if view-specific logic needed)
→ Or calls method on AppState (if affects global state)
→ AppState coordinates with Services
→ Services perform operations (network, storage, parsing)
→ Services return results
→ AppState updates @Published properties
→ SwiftUI automatically re-renders affected Views
```
### 2. State Propagation
```
AppState (@Published property changes)
→ SwiftUI observes change via @EnvironmentObject
→ All subscribed Views re-evaluate body
→ UI updates automatically
```
### 3. Error Handling Flow
```
Service throws NetworkError
→ Caught by AppState
→ AppState sets error property
→ View observes error change
→ ErrorView displays with retry/dismiss options
→ User action either retries or dismisses
```
## Concurrency Model
### Actor Isolation
All services use the `actor` keyword for thread-safe access:
```swift
actor NetworkService { }
actor AuthenticationService { }
actor PlaylistService { }
actor StorageService { }
```
**Benefits**:
- Automatic serialization of access
- Prevention of data races
- Compiler-enforced thread safety
### MainActor Isolation
UI components use `@MainActor`:
```swift
@MainActor
class AppState: ObservableObject { }
@MainActor
class AuthenticationViewModel: ObservableObject { }
```
**Benefits**:
- All UI updates occur on main thread
- No manual dispatch to main queue needed
- Compiler-verified thread safety
### Async/Await
All asynchronous operations use async/await:
```swift
func authenticate(password: String) async {
await appState.authenticate(password: password)
}
```
**Benefits**:
- Readable, linear code flow
- Automatic error propagation
- No callback hell
## tvOS-Specific Considerations
### Focus Engine
**Implementation**:
- `@FocusState` property wrappers track focused items
- `.focused()` modifier binds focus state to views
- Visual feedback: scale effects, borders, shadows on focused items
**Example**:
```swift
@FocusState private var focusedItem: String?
VODCardView(vodItem: item, isFocused: .constant(focusedItem == item.id))
.focused($focusedItem, equals: item.id)
```
### Input Handling
- Siri Remote navigation handled automatically by SwiftUI
- Button presses mapped to standard button actions
- Text input uses on-screen keyboard
### UI Design Principles
- **Large Touch Targets**: Minimum 250x90 points for buttons
- **Focus Indicators**: Prominent borders and scale effects
- **Minimal Text Input**: Only password entry required
- **Card-Based Layouts**: Grid layouts with large thumbnails
- **High Contrast**: White text on dark backgrounds
## Security Architecture
### Credential Flow
```
User Password (entered)
→ Transmitted once to web service (HTTPS recommended)
→ Never stored locally
→ Discarded after authentication
XStream Credentials (received)
→ Stored in Keychain (encrypted)
→ Retrieved on app launch
→ Used for all playlist requests
```
### Security Measures
1. **No Password Storage**: User password never persisted
2. **Keychain Encryption**: XStream credentials encrypted by system
3. **HTTPS Recommended**: For web service authentication endpoint
4. **URL Validation**: All stream URLs validated before playback
5. **No Sensitive Logging**: Error messages avoid exposing credentials
## Error Recovery Strategies
### Network Errors
**Strategy**: Retry with exponential backoff (future enhancement)
**Current Implementation**:
- Display error with context-specific message
- Provide retry button
- Offer recovery suggestions
### Authentication Errors
**Strategy**: Force re-authentication
**Implementation**:
- Clear invalid credentials
- Return to login screen
- Display specific error message
### Parsing Errors
**Strategy**: Graceful degradation
**Implementation**:
- Skip malformed entries
- Continue parsing valid entries
- Report error with details if entire playlist fails
## Extensibility Points
### Future Feature: Live TV Streams
**Required Changes**:
1. Add `LiveItem` model similar to `VODItem`
2. Create `LiveTVView` for channel grid
3. Add second tab to `MainTabView`
4. Extend `M3UParser` to handle live stream format
5. Add `LiveTVViewModel` for channel management
**No Changes Required**:
- Existing services (Network, Storage, Auth)
- Error handling infrastructure
- Component views (Loading, Error)
### Future Feature: EPG (Electronic Program Guide)
**Required Changes**:
1. Add `EPGService` to fetch program data
2. Create `EPGItem` and `ProgramSchedule` models
3. Create `EPGView` for program listings
4. Extend `PlaylistService` to fetch EPG data
### Future Feature: Favorites
**Required Changes**:
1. Add `favorites: [String]` to storage
2. Add toggle favorite method to ViewModels
3. Add favorites filtering to `VODLibraryViewModel`
4. Add favorites tab or filter option
## Testing Strategy
### Unit Testing Targets
1. **M3UParser**: Test with various m3u formats
2. **NetworkService**: Mock URLSession for request testing
3. **StorageService**: Test Keychain operations with test identifiers
4. **Models**: Test Codable conformance and computed properties
### Integration Testing Targets
1. **Authentication Flow**: End-to-end login process
2. **Playlist Fetching**: Full fetch and parse cycle
3. **Error Handling**: Various error scenarios
### UI Testing Targets
1. **Navigation Flow**: Login → Library → Detail → Player
2. **Focus Engine**: Verify focus states and navigation
3. **Error Display**: Verify error messages and recovery
## Performance Considerations
### Image Loading
**Current**: AsyncImage with system caching
**Future Enhancement**: Custom image cache with disk persistence
### Playlist Caching
**Current**: In-memory cache in PlaylistService
**Future Enhancement**: Disk cache with TTL (Time To Live)
### List Performance
**Current**: LazyVGrid for on-demand cell creation
**Optimization**: Tested with playlists up to 10,000 items
### Video Playback
**Implementation**: AVPlayer with hardware acceleration
**Buffering**: System-managed adaptive streaming
## Deployment Architecture
```
┌─────────────────────────────────────────────────────────────┐
│ Apple TV Device │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ SimVision App (tvOS) │ │
│ │ ┌────────────────────────────────────────────────┐ │ │
│ │ │ App Bundle (.ipa) │ │ │
│ │ │ - Swift Runtime │ │ │
│ │ │ - SwiftUI Framework │ │ │
│ │ │ - AVKit Framework │ │ │
│ │ │ - Compiled Swift Code │ │ │
│ │ └────────────────────────────────────────────────┘ │ │
│ │ ↕ │ │
│ │ ┌────────────────────────────────────────────────┐ │ │
│ │ │ tvOS System Services │ │ │
│ │ │ - Keychain │ │ │
│ │ │ - UserDefaults │ │ │
│ │ │ - URLSession │ │ │
│ │ │ - AVFoundation │ │ │
│ │ └────────────────────────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────────┘ │
└──────────────────────────┬──────────────────────────────────┘
│ Network
┌──────────────────────────┼──────────────────────────────────┐
│ External Services │
│ ┌──────────────┐ ┌─────────────┐ ┌──────────────┐ │
│ │ Auth Web │ │ XStream │ │ CDN/Stream │ │
│ │ Service │ │ Server │ │ Servers │ │
│ │ (Your API) │ │ (m3u API) │ │ (Video) │ │
│ └──────────────┘ └─────────────┘ └──────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
## Configuration Management
### Environment-Specific Configuration
**File**: `Utilities/Constants.swift`
```swift
enum Constants {
enum API {
static let authenticationBaseURL = "..." // Change per environment
static let requestTimeout: TimeInterval = 30.0
}
enum Storage {
static let keychainService = "com.simvision.tvos"
}
enum UI {
static let vodGridColumns = 4
}
}
```
**Recommended Practice**:
- Development: Use test web service
- Staging: Use staging web service
- Production: Use production web service
## Dependencies
### System Frameworks
- **SwiftUI**: User interface framework
- **AVKit**: Video playback (AVPlayer, AVPlayerViewController)
- **Combine**: Reactive programming (ObservableObject, @Published)
- **Foundation**: Core utilities (URLSession, Codable, etc.)
- **Security**: Keychain access
### No Third-Party Dependencies
**Rationale**:
- Reduces app size
- Eliminates dependency management overhead
- Avoids potential security vulnerabilities
- Simplifies maintenance
**Trade-offs**:
- More code to write for image caching
- Less sophisticated async image loading
- No pre-built M3U parser library
## Conclusion
SimVision follows a clean, layered architecture with clear separation of concerns. The use of modern Swift concurrency features (async/await, actors) ensures thread safety and readable asynchronous code. The MVVM pattern with centralized state management provides a scalable foundation for future feature additions while maintaining code clarity and testability.
The architecture is designed to be maintainable, extensible, and performant for tvOS applications, with careful consideration of platform-specific requirements like the focus engine and Siri Remote navigation.