Documentation Index
Fetch the complete documentation index at: https://mintlify.com/ShubhamPP04/Izzy/llms.txt
Use this file to discover all available pages before exploring further.
This guide outlines the coding conventions used in Izzy Music Player to maintain consistent, readable, and maintainable code.
Swift and SwiftUI conventions
File organization
Organize Swift files with clear MARK comments to separate logical sections:
// MARK: - Playback Manager
class PlaybackManager: ObservableObject {
// MARK: - Properties
@Published var currentTrack: Track?
@Published var playbackState: PlaybackState = .stopped
// MARK: - Initialization
private init() {
setupQueueManager()
setupRemoteCommandHandlers()
}
// MARK: - Playback Control
func play(track: Track) async {
// Implementation
}
// MARK: - Queue Management
func addToQueue(_ track: Track) {
// Implementation
}
}
Naming conventions
Classes and structs use PascalCase:
class PlaybackManager { }
struct MusicSearchResults { }
enum PlaybackState { }
Variables and functions use camelCase:
var currentTrack: Track?
func playCurrentTrack() async { }
let isBuffering = false
Constants use camelCase:
private let nowPlayingUpdateThrottle: TimeInterval = 2.0
let maxSearchResults = 50
Enums use PascalCase for types and camelCase for cases:
enum MusicSource: String, CaseIterable {
case youtubeMusic = "youtube_music"
case jioSaavn = "jiosaavn"
case tidal = "tidal"
}
Property declarations
Use explicit types when the type is not obvious from the initializer:
// Good
var currentTime: TimeInterval = 0
var playbackState: PlaybackState = .stopped
// Acceptable when type is obvious
var cancellables = Set<AnyCancellable>()
var isPlaying = false
Access control
Be explicit about access levels:
// Public API
public class PlaybackManager: ObservableObject {
// Published properties for SwiftUI
@Published var currentTrack: Track?
// Private implementation details
private var player: AVPlayer?
private var timeObserver: Any?
// Internal methods accessible within the module
func play(track: Track) async { }
// Private helper methods
private func setupPlayer() { }
}
Async/await usage
Prefer async/await over completion handlers:
// Good
func play(track: Track) async {
do {
let streamInfo = try await pythonService.getStreamInfo(videoId: track.videoId)
setupPlayer(with: streamInfo)
} catch {
handleError(error)
}
}
// Avoid
func play(track: Track, completion: @escaping (Result<Void, Error>) -> Void) {
pythonService.getStreamInfo(videoId: track.videoId) { result in
// Complex nested callbacks
}
}
SwiftUI view structure
Structure views with clear body composition:
struct PlaybackControlsView: View {
@ObservedObject var playbackManager: PlaybackManager
var body: some View {
VStack(spacing: 16) {
albumArtwork
trackInfo
controlButtons
progressBar
}
.padding()
}
// MARK: - View Components
private var albumArtwork: some View {
AsyncImage(url: URL(string: track.thumbnailURL ?? "")) { image in
image.resizable().aspectRatio(contentMode: .fill)
} placeholder: {
ProgressView()
}
.frame(width: 200, height: 200)
.cornerRadius(12)
}
private var trackInfo: some View {
VStack(spacing: 4) {
Text(track.title)
.font(.headline)
Text(track.artist)
.font(.subheadline)
.foregroundColor(.secondary)
}
}
private var controlButtons: some View {
HStack(spacing: 24) {
Button(action: { Task { await playbackManager.playPrevious() } }) {
Image(systemName: "backward.fill")
}
Button(action: togglePlayback) {
Image(systemName: playbackManager.isPlaying ? "pause.fill" : "play.fill")
}
Button(action: { Task { await playbackManager.playNext() } }) {
Image(systemName: "forward.fill")
}
}
}
// MARK: - Actions
private func togglePlayback() {
if playbackManager.isPlaying {
playbackManager.pause()
} else {
playbackManager.resume()
}
}
}
Use comments to explain why, not what. The code should be self-explanatory for what it does.
// Good - explains the reasoning
// π BATTERY OPTIMIZATION: Reduce time observer frequency from 0.5s to 1.0s
// This reduces CPU usage by 50% while maintaining smooth UI updates
let interval = CMTime(seconds: 1.0, preferredTimescale: 600)
// Good - explains battery optimization
// π FAST SEEK: Check cache first for instant playback
if let cached = streamCache.object(forKey: cacheKey), !cached.isExpired {
return StreamInfo(url: cached.url, title: cached.title, duration: cached.duration)
}
// Avoid - states the obvious
// Create a new player
let player = AVPlayer()
Use emoji prefixes for important optimization comments:
- π Battery optimization
- π Performance optimization
- π΅ Playback-related
- πΎ Persistence/storage
- π Search-related
Error handling
Handle errors gracefully with user-friendly messages:
do {
let streamInfo = try await pythonService.getStreamInfo(videoId: track.videoId)
setupPlayer(with: streamInfo)
} catch {
let errorMessage = error.localizedDescription
print("β Playback error: \(errorMessage)")
await MainActor.run {
self.playbackState = .error(errorMessage)
self.isBuffering = false
}
}
State management
Use @Published properties for observable state:
class PlaybackManager: ObservableObject {
@Published var currentTrack: Track?
@Published var playbackState: PlaybackState = .stopped
@Published var currentTime: TimeInterval = 0
@Published var volume: Float = 0.7 {
didSet {
player?.volume = volume
UserDefaults.standard.set(volume, forKey: "playerVolume")
}
}
}
Python conventions
File structure
Organize Python files with clear sections:
#!/usr/bin/env python3
"""
π BATTERY OPTIMIZED: YouTube Music Service for Izzy Music Player
Handles search, stream URL extraction, and YouTube Music API interactions.
"""
import sys
import json
import asyncio
from typing import Dict, List, Any, Optional
# Configure logging
logging.basicConfig(
level=logging.WARNING,
format='%(levelname)s: %(message)s'
)
# MARK: - Service Classes
class JioSaavnService:
"""JioSaavn music service integration using saavn.dev API"""
def __init__(self):
self.base_url = "https://saavn.sumit.co/api"
Function and method naming
Use snake_case for functions and methods:
def search_all(query: str, limit: int = 20) -> Dict[str, Any]:
"""Search across music library"""
pass
def get_stream_info(video_id: str) -> Dict[str, Any]:
"""Get stream info for a video"""
pass
def _format_song(song: Dict) -> Optional[Dict]:
"""Format song result (private helper)"""
pass
Type hints
Always use type hints for function parameters and return types:
from typing import Dict, List, Any, Optional
def search_all(self, query: str, limit: int = 20) -> Dict[str, Any]:
"""Search across JioSaavn music library"""
try:
results: Dict[str, List] = {
'songs': [],
'albums': [],
'artists': []
}
return {'success': True, 'data': results}
except Exception as e:
return {'success': False, 'error': str(e)}
Error handling
Use try-except blocks with specific error messages:
def get_stream_info(self, video_id: str) -> Dict[str, Any]:
"""Get stream info using API"""
try:
if not video_id:
return {
'success': False,
'error': 'Invalid video ID'
}
response = requests.get(f"{self.base_url}/songs/{video_id}", timeout=10)
if response.status_code != 200:
return {
'success': False,
'error': f'Failed to fetch: HTTP {response.status_code}'
}
return {'success': True, 'data': response.json()}
except Exception as e:
logger.error(f"Stream extraction failed: {e}")
return {'success': False, 'error': str(e)}
Logging
Use appropriate log levels:
import logging
logger = logging.getLogger(__name__)
# Error - something went wrong
logger.error(f"JioSaavn search failed: {e}")
# Warning - unexpected but handled
logger.warning("No lyrics found for this song")
# Info - useful runtime information (use sparingly for battery efficiency)
logger.info(f"Retrieved {len(songs)} songs")
# Debug - detailed debugging info (disabled in production)
logger.debug(f"Processing song: {song_data}")
# Use print with stderr for user-visible messages
print(f"β
Successfully imported yt-dlp", file=sys.stderr)
print(f"β Failed to import: {e}", file=sys.stderr)
Docstrings
Write clear docstrings for classes and public methods:
class JioSaavnService:
"""
JioSaavn music service integration using saavn.dev API
Provides search, streaming, and metadata retrieval for JioSaavn content.
"""
def search_all(self, query: str, limit: int = 20) -> Dict[str, Any]:
"""
Search across JioSaavn music library using saavn.dev API
Args:
query: Search query string
limit: Maximum number of results per category (default: 20)
Returns:
Dict with 'success' bool and 'data' containing search results,
or 'error' message if search failed
"""
pass
Use emoji prefixes for optimization-related code:
# π BATTERY OPTIMIZATION: Configure logging to reduce I/O
logging.basicConfig(
level=logging.WARNING, # Only log warnings and errors
format='%(levelname)s: %(message)s'
)
# π FAST SEARCH: Use parallel requests for better performance
async def search_parallel(queries: List[str]) -> List[Dict]:
tasks = [search_song(q) for q in queries]
return await asyncio.gather(*tasks)
Project organization
Directory structure
Maintain clear separation of concerns:
Izzy/
βββ Views/ # UI components (SwiftUI)
βββ Managers/ # Business logic and state management
βββ Models/ # Data structures and types
βββ Services/ # External API integrations
βββ ytmusic_service.py # Python backend
File naming
- Swift files:
PascalCase.swift (e.g., PlaybackManager.swift)
- View files:
DescriptiveNameView.swift (e.g., SearchResultsView.swift)
- Model files: Plural for collections (e.g.,
MusicModels.swift)
- Python files:
snake_case.py (e.g., ytmusic_service.py)
Version control
Git ignore patterns
Donβt commit:
- Build artifacts (
build/, DerivedData/)
- Python virtual environments (
venv/, music_env/)
- User-specific files (
.DS_Store, *.xcuserstate)
- API keys and secrets (
.env, credentials.json)
Commit practices
- Make atomic commits (one logical change per commit)
- Write clear commit messages
- Donβt commit commented-out code
- Remove debug print statements before committing
Battery efficiency
// Reduce observer frequency
let interval = CMTime(seconds: 1.0, preferredTimescale: 600) // Not 0.5s
// Throttle updates
private var lastUpdate: TimeInterval = 0
if currentTime - lastUpdate >= 2.0 {
updateUI()
lastUpdate = currentTime
}
// Stop timers when not needed
if playbackStarted {
timer.invalidate()
timer = nil
}
Memory management
// Use weak self in closures to prevent retain cycles
player.addPeriodicTimeObserver { [weak self] time in
guard let self = self else { return }
self.currentTime = time.seconds
}
// Configure cache limits
let cache = URLCache(
memoryCapacity: 50 * 1024 * 1024, // 50MB
diskCapacity: 100 * 1024 * 1024 // 100MB
)
Network efficiency
// Cache network responses
private var streamCache = NSCache<NSString, CachedStreamInfo>()
// Prefetch next track
Task {
try await Task.sleep(nanoseconds: 2_000_000_000)
_ = try await getStreamInfo(videoId: nextTrack.videoId)
}
Testing guidelines
When testing your changes:
- Test with all three music services (YouTube Music, Tidal, JioSaavn)
- Test playback controls (play, pause, seek, next, previous)
- Test queue management (add, remove, reorder)
- Test persistence (app restart, state restoration)
- Monitor console for errors and warnings
- Check memory usage in Instruments
- Test on different macOS versions if possible
Resources