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>
624 lines
23 KiB
Markdown
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.
|