Architecture Overview - MKS2508/MKS-IPTV-App GitHub Wiki
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
line-height: 1.6;
color: #333;
max-width: 960px;
margin: 0 auto;
padding: 20px;
}
h1, h2, h3, h4, h5, h6 {
font-weight: 600;
color: #111;
}
a {
color: #0366d6;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
code {
font-family: "SFMono-Regular", Consolas, "Liberation Mono", Menlo, Courier, monospace;
background-color: #f6f8fa;
padding: .2em .4em;
margin: 0;
font-size: 85%;
border-radius: 3px;
}
pre {
background-color: #f6f8fa;
padding: 16px;
overflow: auto;
line-height: 1.45;
border-radius: 3px;
}
pre code {
padding: 0;
margin: 0;
font-size: 100%;
background-color: transparent;
border: 0;
}
footer {
margin-top: 40px;
padding-top: 20px;
border-top: 1px solid #eee;
font-size: 12px;
color: #586069;
}
</style>
Home > Architecture > Architecture Overview
A comprehensive guide to MKS-IPTV-App's architecture, design patterns, and implementation strategies.
MKS-IPTV-App is built with modern Swift development principles, emphasizing:
- Type Safety with Swift 6's strict concurrency
- Reactive Programming using SwiftUI and Combine
- Actor-based Concurrency for thread-safe operations
- Platform Adaptation while maintaining code reuse
- Testability through dependency injection and protocols
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β UI Layer β
β βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ β
β β iOS Views β β macOS Views β β tvOS Views β β
β β (SwiftUI) β β (SwiftUI) β β (SwiftUI) β β
β βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β ViewModel Layer β
β βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ β
β β StreamViewModel β βDownloadViewModelβ β SearchViewModelβ β
β β(@MainActor) β β(@MainActor) β β (@MainActor) β β
β βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Service Layer β
β βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ β
β β StreamManager β β DownloadManager β β HTTPStreamServerβ β
β β (Actor) β β (Actor) β β (Actor) β β
β βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Data Layer β
β βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ β
β β Core Data β β UserDefaults β β File System β β
β β (Persistence) β β (Settings) β β (Downloads) β β
β βββββββββββββββββββ βββββββββββββββββββ βββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
Data structures and business logic
// Pure data models
struct IPTVStream: Codable, Identifiable {
let id: UUID
let name: String
let url: URL
let category: StreamCategory
}
// Business logic protocols
protocol StreamService {
func fetchStreams() async throws -> [IPTVStream]
func validateStream(_ stream: IPTVStream) async throws -> Bool
}
SwiftUI views with platform adaptations
struct StreamListView: View {
@StateObject private var viewModel = StreamListViewModel()
var body: some View {
PlatformNavigationView {
StreamGridView(streams: viewModel.streams)
.searchable(text: $viewModel.searchText)
}
.task {
await viewModel.loadStreams()
}
}
}
Observable objects managing view state
@MainActor
class StreamListViewModel: ObservableObject {
@Published var streams: [IPTVStream] = []
@Published var searchText: String = ""
@Published var isLoading: Bool = false
private let streamService: StreamService
init(streamService: StreamService = StreamManager.shared) {
self.streamService = streamService
}
func loadStreams() async {
isLoading = true
defer { isLoading = false }
do {
streams = try await streamService.fetchStreams()
} catch {
// Handle error
}
}
}
- Data isolation using actors for thread-safe operations
- Structured concurrency with async/await patterns
- MainActor for UI-related operations
- Global actors for shared resources
actor StreamManager {
private var activeStreams: [UUID: AVPlayer] = [:]
private var httpProxyServer: HTTPProxyServer?
func loadStream(_ url: URL) async throws -> UUID {
let streamId = UUID()
let proxiedURL = try await setupProxy(for: url)
let player = AVPlayer(url: proxiedURL)
activeStreams[streamId] = player
return streamId
}
nonisolated func getCurrentPlayer(for id: UUID) -> AVPlayer? {
// Safe access to actor-isolated data
return activeStreams[id]
}
}
actor DownloadManager {
private var activeDownloads: [UUID: URLSessionDownloadTask] = [:]
private var downloadProgress: [UUID: Double] = [:]
func startDownload(for item: DownloadableItem) async throws -> UUID {
let downloadId = UUID()
let task = URLSession.shared.downloadTask(with: item.downloadURL)
activeDownloads[downloadId] = task
task.resume()
return downloadId
}
func getProgress(for downloadId: UUID) async -> Double {
return downloadProgress[downloadId] ?? 0.0
}
}
actor HTTPProxyServer {
private var server: NWListener?
private let port: NWEndpoint.Port
func start() async throws {
let parameters = NWParameters.tcp
server = try NWListener(using: parameters, on: port)
server?.newConnectionHandler = { [weak self] connection in
await self?.handleConnection(connection)
}
server?.start(queue: .global())
}
private func handleConnection(_ connection: NWConnection) async {
// Handle incoming HTTP requests
// Proxy to IPTV endpoints with custom headers
}
}
extension StreamManager {
func resolveStreamURL(_ originalURL: URL) async throws -> URL {
// 1. Check for redirects
let resolvedURL = try await followRedirects(originalURL)
// 2. Determine if proxy is needed
if requiresProxy(resolvedURL) {
return try await setupProxy(for: resolvedURL)
}
// 3. Return direct URL if no proxy needed
return resolvedURL
}
private func requiresProxy(_ url: URL) -> Bool {
// Logic to determine if URL needs HTTP proxy
return url.scheme == "http" || hasCustomHeaders(url)
}
}
struct iOSNavigationView: View {
var body: some View {
TabView {
LiveTVView()
.tabItem { Label("Live TV", systemImage: "tv") }
MoviesView()
.tabItem { Label("Movies", systemImage: "film") }
DownloadsView()
.tabItem { Label("Downloads", systemImage: "arrow.down.circle") }
}
}
}
struct macOSNavigationView: View {
var body: some View {
NavigationSplitView {
SidebarView()
} detail: {
ContentView()
}
.toolbar {
TouchBarControls()
}
}
}
struct tvOSNavigationView: View {
@FocusState private var focusedTab: Tab?
var body: some View {
TabView(selection: $focusedTab) {
LiveTVView()
.tag(Tab.liveTV)
.focused($focusedTab, equals: .liveTV)
}
.focusGuide(.automatic)
}
}
extension NSTouchBarItem.Identifier {
static let playPause = NSTouchBarItem.Identifier("com.mks.iptv.playPause")
static let volume = NSTouchBarItem.Identifier("com.mks.iptv.volume")
static let search = NSTouchBarItem.Identifier("com.mks.iptv.search")
}
class TouchBarManager: NSObject, NSTouchBarDelegate {
@MainActor
func makeTouchBar() -> NSTouchBar {
let touchBar = NSTouchBar()
touchBar.delegate = self
touchBar.defaultItemIdentifiers = [.playPause, .volume, .search]
return touchBar
}
}
// Core Data for complex relationships
@Model
class DownloadedContent {
@Attribute(.unique) var id: UUID
var title: String
var localPath: URL
var originalURL: URL
var downloadDate: Date
var fileSize: Int64
}
// UserDefaults for simple settings
extension UserDefaults {
@objc dynamic var preferredQuality: String {
get { string(forKey: "preferredQuality") ?? "auto" }
set { set(newValue, forKey: "preferredQuality") }
}
}
// File system for downloaded content
actor FileManager {
static let downloadsDirectory = FileManager.default
.urls(for: .documentDirectory, in: .userDomainMask)
.first!
.appendingPathComponent("Downloads")
func saveDownload(_ data: Data, filename: String) async throws -> URL {
let fileURL = Self.downloadsDirectory.appendingPathComponent(filename)
try data.write(to: fileURL)
return fileURL
}
}
// Centralized app state
@MainActor
class AppState: ObservableObject {
@Published var selectedTab: Tab = .liveTV
@Published var isDownloading: Bool = false
@Published var currentlyPlaying: IPTVStream?
// Computed properties
var hasActiveDownloads: Bool {
// Logic to check for active downloads
}
}
// View models subscribe to app state
@MainActor
class ContentViewModel: ObservableObject {
@Published var content: [ContentItem] = []
private let appState: AppState
private var cancellables = Set<AnyCancellable>()
init(appState: AppState) {
self.appState = appState
// React to app state changes
appState.$selectedTab
.sink { [weak self] tab in
Task { await self?.loadContent(for: tab) }
}
.store(in: &cancellables)
}
}
// Service protocols for dependency injection
protocol StreamServiceProtocol {
func fetchStreams() async throws -> [IPTVStream]
}
// Mock implementations for testing
class MockStreamService: StreamServiceProtocol {
var mockStreams: [IPTVStream] = []
var shouldThrowError = false
func fetchStreams() async throws -> [IPTVStream] {
if shouldThrowError {
throw StreamError.networkError
}
return mockStreams
}
}
// Testable view models
@MainActor
class StreamListViewModel: ObservableObject {
private let streamService: StreamServiceProtocol
init(streamService: StreamServiceProtocol = StreamManager.shared) {
self.streamService = streamService
}
}
#if os(iOS)
// iOS-specific implementations
import UIKit
typealias PlatformViewController = UIViewController
#elseif os(macOS)
// macOS-specific implementations
import AppKit
typealias PlatformViewController = NSViewController
#elseif os(tvOS)
// tvOS-specific implementations
import TVUIKit
typealias PlatformViewController = UIViewController
#endif
// Shared interface
protocol PlatformAdaptable {
func configureForPlatform()
}
@MainActor
class ServiceContainer: ObservableObject {
static let shared = ServiceContainer()
lazy var streamManager = StreamManager()
lazy var downloadManager = DownloadManager()
lazy var httpServer = HTTPStreamServer()
private init() {}
// Factory methods for testing
func makeStreamService() -> StreamServiceProtocol {
#if DEBUG
if ProcessInfo.processInfo.environment["TESTING"] == "1" {
return MockStreamService()
}
#endif
return streamManager
}
}
- Weak references to prevent retain cycles
- Actor isolation to prevent data races
- Lazy loading for expensive operations
- Resource cleanup in deinitializers
- Task groups for parallel operations
- AsyncSequence for streaming data
- Sendable conformance for cross-actor communication
- MainActor isolation for UI updates
- Connection pooling for HTTP requests
- Request deduplication for identical URLs
- Caching strategies for frequently accessed data
- Bandwidth adaptation based on network conditions
This architecture ensures scalability, maintainability, and optimal performance across all Apple platforms while leveraging the latest Swift 6 concurrency features.
{{< include _Footer.md >}}