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

23 KiB

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:

@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:

// 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

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

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

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:

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:

@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:

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:

@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

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.