Development Setup - 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 > Development > Development Setup
Complete guide for setting up a development environment for MKS-IPTV-App contributors.
Tool | Version | Purpose |
---|---|---|
Xcode | 16 Beta+ | Primary IDE and compiler |
macOS | 26 Beta+ | Development platform |
Swift | 6.0+ | Language runtime |
Git | 2.30+ | Version control |
- Free Account: Sufficient for simulator testing and basic development
- Paid Account: Required for device deployment and distribution
- Enterprise Account: For internal distribution (optional)
# Clone the main repository
git clone https://github.com/MKS2508/MKS-IPTV-App.git
cd MKS-IPTV-App
# Create your feature branch
git checkout -b feature/your-feature-name
# Open project in Xcode
open mks-multiplatform-iptv.xcodeproj
- Select Project โ Signing & Capabilities
- Team: Select your Apple Developer team
-
Bundle Identifier: Use unique identifier (e.g.,
com.yourname.mks-iptv
) - Automatic Signing: Enable for development
- mks-multiplatform-iptv: Main iOS/macOS app
- mks-multiplataforma-tvos-iptv: tvOS variant
- Debug: Development with debug symbols
- Release: Optimized builds for distribution
The project uses SPM for dependency management. Dependencies are defined in Package.swift
(if present) or integrated directly in Xcode.
Adding New Dependencies:
- File โ Add Package Dependencies
- Enter package URL
- Select version requirements
- Choose target integration
// Example Package.swift structure
// swift-tools-version:5.9
import PackageDescription
let package = Package(
name: "MKS-IPTV-App",
platforms: [
.iOS(.v17),
.macOS(.v14),
.tvOS(.v17)
],
dependencies: [
// Add external dependencies here
.package(url: "https://github.com/apple/swift-log.git", from: "1.0.0")
],
targets: [
.target(
name: "IPTVCore",
dependencies: [
.product(name: "Logging", package: "swift-log")
]
)
]
)
mks-multiplatform-iptv/
โโโ IPTVDownloader/ # Main app target
โ โโโ Core/ # Core functionality
โ โ โโโ Configuration/ # App configuration
โ โ โโโ Networking/ # Network layer
โ โ โโโ Player/ # Video player components
โ โโโ Features/ # Feature modules
โ โ โโโ Downloads/ # Download management
โ โ โโโ LiveChannels/ # Live TV streaming
โ โ โโโ Movies/ # VOD Movies
โ โ โโโ Series/ # VOD Series
โ โ โโโ TouchBar/ # macOS TouchBar
โ โโโ Models/ # Data models
โ โโโ Services/ # Business logic services
โ โโโ Utils/ # Utility functions
โ โโโ Views/ # SwiftUI views
โโโ mks-multiplataforma-tvos-iptv/ # tvOS target
โโโ Shared/ # Shared components
โโโ Tests/ # Unit tests
โโโ UITests/ # UI automation tests
// Model
struct IPTVStream: Codable, Identifiable {
let id: UUID
let name: String
let url: URL
}
// View
struct StreamListView: View {
@StateObject private var viewModel = StreamListViewModel()
var body: some View {
List(viewModel.streams) { stream in
StreamRowView(stream: stream)
}
}
}
// ViewModel
@MainActor
class StreamListViewModel: ObservableObject {
@Published var streams: [IPTVStream] = []
func loadStreams() async {
// Business logic here
}
}
actor StreamManager {
private var activeStreams: [UUID: AVPlayer] = [:]
func createStream(url: URL) async throws -> UUID {
let streamId = UUID()
let player = AVPlayer(url: url)
activeStreams[streamId] = player
return streamId
}
}
// Types: PascalCase
class StreamManager { }
struct IPTVConfiguration { }
enum PlaybackState { }
// Variables and functions: camelCase
var currentStream: IPTVStream?
func loadStream(_ url: URL) async throws
// Constants: camelCase
let maxRetryAttempts = 3
let defaultTimeout: TimeInterval = 30
// Private properties: leading underscore (optional)
private var _internalCache: [String: Any] = [:]
/// Loads and prepares an IPTV stream for playback
/// - Parameters:
/// - url: The stream URL to load
/// - metadata: Optional stream metadata
/// - Returns: Unique identifier for the stream session
/// - Throws: `StreamError` if the stream cannot be loaded
func loadStream(
_ url: URL,
metadata: StreamMetadata? = nil
) async throws -> UUID {
// Implementation
}
enum StreamError: LocalizedError {
case invalidURL(URL)
case networkFailure(Error)
case unsupportedFormat
var errorDescription: String? {
switch self {
case .invalidURL(let url):
return "Invalid stream URL: \(url)"
case .networkFailure(let error):
return "Network error: \(error.localizedDescription)"
case .unsupportedFormat:
return "Unsupported stream format"
}
}
}
// Break down complex views
struct ContentView: View {
var body: some View {
NavigationStack {
PlatformNavigationView {
MainContentArea()
}
}
.overlay(alignment: .bottom) {
PlayerControlsOverlay()
}
}
}
// Extract subviews
struct MainContentArea: View {
var body: some View {
TabView {
LiveTVView()
.tabItem { Label("Live TV", systemImage: "tv") }
MoviesView()
.tabItem { Label("Movies", systemImage: "film") }
}
}
}
// Use @StateObject for owned objects
struct ContentView: View {
@StateObject private var downloadManager = DownloadManager()
var body: some View {
// View content
}
}
// Use @ObservedObject for injected dependencies
struct DownloadListView: View {
@ObservedObject var downloadManager: DownloadManager
var body: some View {
// View content
}
}
import XCTest
@testable import IPTVDownloader
@MainActor
final class StreamManagerTests: XCTestCase {
var streamManager: StreamManager!
var mockNetworkService: MockNetworkService!
override func setUp() async throws {
mockNetworkService = MockNetworkService()
streamManager = StreamManager(networkService: mockNetworkService)
}
override func tearDown() async throws {
streamManager = nil
mockNetworkService = nil
}
func testStreamLoading() async throws {
// Given
let testURL = URL(string: "https://test.stream.url")!
mockNetworkService.shouldSucceed = true
// When
let streamId = try await streamManager.loadStream(testURL)
// Then
XCTAssertNotNil(streamId)
XCTAssertTrue(mockNetworkService.loadStreamCalled)
}
}
import XCTest
final class IPTVAppUITests: XCTestCase {
var app: XCUIApplication!
override func setUp() {
continueAfterFailure = false
app = XCUIApplication()
app.launchEnvironment["TESTING"] = "1"
app.launch()
}
func testDownloadFlow() throws {
// Navigate to Downloads
app.tabBars.buttons["Downloads"].tap()
// Verify empty state
XCTAssertTrue(app.staticTexts["No Downloads"].exists)
// Test download initiation
// (Implementation depends on UI flow)
}
}
#if DEBUG
class MockStreamService: StreamServiceProtocol {
var shouldSucceed = true
var loadStreamCalled = false
var mockStreams: [IPTVStream] = []
func loadStream(_ url: URL) async throws -> UUID {
loadStreamCalled = true
if !shouldSucceed {
throw StreamError.networkFailure(URLError(.networkConnectionLost))
}
return UUID()
}
func fetchStreams() async throws -> [IPTVStream] {
return mockStreams
}
}
#endif
// Debug preprocessor macros
#if DEBUG
let isDebugBuild = true
let logLevel: LogLevel = .debug
let apiBaseURL = URL(string: "https://dev-api.example.com")!
#else
let isDebugBuild = false
let logLevel: LogLevel = .error
let apiBaseURL = URL(string: "https://api.example.com")!
#endif
Development Scheme:
- Configuration: Debug
- Code Signing: Automatic
- Preprocessor Macros:
DEBUG=1
,DEVELOPMENT=1
Testing Scheme:
- Configuration: Debug
- Code Signing: Automatic
- Preprocessor Macros:
DEBUG=1
,TESTING=1
- Test Plans: All test targets
Release Scheme:
- Configuration: Release
- Code Signing: Manual (Distribution)
- Preprocessor Macros:
RELEASE=1
- Optimization: Aggressive
# Debug build for simulator
xcodebuild -project mks-multiplatform-iptv.xcodeproj \
-scheme mks-multiplatform-iptv \
-destination 'platform=iOS Simulator,name=iPhone 15 Pro' \
-configuration Debug \
build
# Device build (requires provisioning profile)
xcodebuild -project mks-multiplatform-iptv.xcodeproj \
-scheme mks-multiplatform-iptv \
-destination 'platform=iOS,name=My iPhone' \
-configuration Debug \
build
# Native macOS build
xcodebuild -project mks-multiplatform-iptv.xcodeproj \
-scheme mks-multiplatform-iptv \
-destination 'platform=macOS' \
-configuration Debug \
build
# Universal binary (Intel + Apple Silicon)
xcodebuild -project mks-multiplatform-iptv.xcodeproj \
-scheme mks-multiplatform-iptv \
-destination 'platform=macOS,arch=x86_64' \
-configuration Release \
ARCHS="arm64 x86_64" \
build
# tvOS simulator build
xcodebuild -project mks-multiplatform-iptv.xcodeproj \
-scheme mks-multiplataforma-tvos-iptv \
-destination 'platform=tvOS Simulator,name=Apple TV' \
-configuration Debug \
build
# Apple TV device build
xcodebuild -project mks-multiplatform-iptv.xcodeproj \
-scheme mks-multiplataforma-tvos-iptv \
-destination 'platform=tvOS,name=My Apple TV' \
-configuration Debug \
build
// Set in Xcode Scheme โ Run โ Environment Variables
TESTING=1 // Enable testing mode
DEBUG_NETWORKING=1 // Verbose network logging
MOCK_STREAMS=1 // Use mock stream data
LOG_LEVEL=DEBUG // Set logging level
// Set in Xcode Scheme โ Run โ Arguments Passed On Launch
-com.apple.CoreData.SQLDebug 1 // Core Data SQL logging
-com.apple.CoreData.Logging.stderr 1 // Core Data error logging
#if DEBUG
extension URLSession {
static let debug: URLSession = {
let config = URLSessionConfiguration.default
config.protocolClasses = [NetworkLoggingProtocol.self]
return URLSession(configuration: config)
}()
}
class NetworkLoggingProtocol: URLProtocol {
override func startLoading() {
print("๐ Network Request: \(request.url?.absoluteString ?? "Unknown")")
// Log request details
}
}
#endif
#if DEBUG
import os
class MemoryMonitor {
private let logger = Logger(subsystem: "com.mks.iptv", category: "memory")
func logMemoryUsage() {
let info = mach_task_basic_info()
var count = mach_msg_type_number_t(MemoryLayout<mach_task_basic_info>.size)/4
let kerr: kern_return_t = withUnsafeMutablePointer(to: &info) {
$0.withMemoryRebound(to: integer_t.self, capacity: 1) {
task_info(mach_task_self_,
task_flavor_t(MACH_TASK_BASIC_INFO),
$0,
&count)
}
}
if kerr == KERN_SUCCESS {
let memoryUsage = Float(info.resident_size) / 1024.0 / 1024.0
logger.debug("Memory usage: \(memoryUsage, format: .fixed(precision: 2)) MB")
}
}
}
#endif
name: CI
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
test:
runs-on: macos-14
steps:
- uses: actions/checkout@v4
- name: Select Xcode
run: sudo xcode-select -switch /Applications/Xcode_16.0.app/Contents/Developer
- name: Build and Test iOS
run: |
xcodebuild test \
-project mks-multiplatform-iptv.xcodeproj \
-scheme mks-multiplatform-iptv \
-destination 'platform=iOS Simulator,name=iPhone 15 Pro' \
-enableCodeCoverage YES
- name: Build and Test macOS
run: |
xcodebuild test \
-project mks-multiplatform-iptv.xcodeproj \
-scheme mks-multiplatform-iptv \
-destination 'platform=macOS' \
-enableCodeCoverage YES
- name: Upload Coverage
uses: codecov/codecov-action@v3
with:
file: ./coverage.lcov
Issue: "No such module 'IPTVCore'"
# Clean build folder
rm -rf ~/Library/Developer/Xcode/DerivedData
# Rebuild project
xcodebuild clean build
Issue: Code signing errors
- Check Apple Developer account status
- Verify provisioning profiles
- Update bundle identifier to unique value
- Enable "Automatically manage signing"
Issue: Network requests failing
- Check App Transport Security settings in Info.plist
- Verify network permissions
- Test with HTTP instead of HTTPS (development only)
Issue: AVPlayer not playing streams
- Verify stream URL format and accessibility
- Check audio session configuration
- Test with known working stream URLs
import os
struct Logger {
private let logger: os.Logger
init(subsystem: String, category: String) {
self.logger = os.Logger(subsystem: subsystem, category: category)
}
func debug(_ message: String, metadata: [String: Any] = [:]) {
#if DEBUG
let metadataString = metadata.map { "\($0.key)=\($0.value)" }.joined(separator: ", ")
logger.debug("\(message) [\(metadataString)]")
#endif
}
func error(_ message: String, error: Error? = nil) {
logger.error("\(message) \(error?.localizedDescription ?? "")")
}
}
This development setup ensures a productive and consistent development experience across all platforms while maintaining code quality and testing standards.
{{< include _Footer.md >}}