A Swift package that provides intelligent caching and preloading capabilities for AVFoundation-based media playback. It enables offline viewing, faster subsequent playback, and improved user experience for streaming media content.
- Automatic Caching: Seamlessly caches video content during playback for offline access
- Range-Aware Caching: Tracks downloaded byte ranges to detect and handle missing segments
- Preloading: Proactively download video segments to improve playback experience
- Cache Management: Comprehensive cache lifecycle management with size limits and cleanup
- Thread-Safe: Built with Swift Concurrency (actors) for safe concurrent operations
- AVFoundation Integration: Drop-in replacement for standard AVPlayerItem usage
Add the following to your Package.swift file:
dependencies: [
.package(url: "https://github.com/iankoex/AudioVisualService.git", from: "1.0.0")
]Or add it directly in Xcode:
- Go to File → Add Packages...
- Enter the repository URL:
https://github.com/iankoex/AudioVisualService.git
import AVFoundation
import AudioVisualService
let videoURL = URL(string: "https://example.com/video.mp4")!
let playerItem = CachingPlayerItem(url: videoURL)
let player = AVPlayer(playerItem: playerItem)
// Play the video - content will be cached automatically
player.play()import AudioVisualService
class MyViewController: UIViewController, AudioVisualServiceDelegate {
func didCacheData(url: URL, totalBytesCached: Int) {
print("Cached \(totalBytesCached) bytes for \(url)")
// Update UI with caching progress
}
func playVideo() {
let videoURL = URL(string: "https://example.com/video.mp4")!
let playerItem = CachingPlayerItem(url: videoURL, serviceDelegate: self)
let player = AVPlayer(playerItem: playerItem)
player.play()
}
}import AudioVisualService
let videoURL = URL(string: "https://example.com/video.mp4")!
let asset = CachingAVURLAsset(url: videoURL)
let playerItem = AVPlayerItem(asset: asset)
let player = AVPlayer(playerItem: playerItem)
player.play()import AudioVisualService
// Create a preloader with 5MB preload limit
let preloader = Preloader(preloadSize: 5 * 1024 * 1024)
// Preload a single video
await preloader.preload(videoURL)
// Preload multiple videos concurrently
await preloader.preload([videoURL1, videoURL2, videoURL3])
// Cancel preloading if needed
await preloader.cancelPreloading(for: videoURL)import AudioVisualService
// Get total cache size
let totalSize = CacheManager.totalCacheSize()
print("Total cache size: \(totalSize) bytes")
// Clean up old cache files
CacheManager.enforceCacheLimit()
// Invalidate cache for a specific video
try cacheManager.invalidateCache()
// Clear all cached data
try CacheManager.deleteCachedData()- iOS 16.0+ / macOS 13.0+ / tvOS 14.0+ / watchOS 7.0+
- Swift 5.8+
- Xcode 14.0+
A drop-in replacement for AVPlayerItem with automatic caching capabilities.
public class CachingPlayerItem: AVPlayerItem, Sendable {
public init(
url: URL,
automaticallyLoadedAssetKeys: [String]? = nil,
serviceDelegate: AudioVisualServiceDelegate? = nil
)
}An AVURLAsset subclass that provides caching through custom resource loading.
public final class CachingAVURLAsset: AVURLAsset, @unchecked Sendable {
public init(
url: URL,
options: [String: Any]? = nil,
serviceDelegate: AudioVisualServiceDelegate? = nil
)
}An actor that manages preloading of video content to improve playback performance.
public actor Preloader: Sendable {
public init(preloadSize: Int = 5 * 1024 * 1024)
public func preload(_ url: URL)
public func preload(_ urls: [URL])
public func cancelPreloading(for url: URL)
}Manages local caching of video data with comprehensive lifecycle management.
public final class CacheManager: Sendable {
public init(for url: URL)
// Cache operations
public func invalidateCache() throws
// Static methods
public static func totalCacheSize() -> Int
public static func deleteCachedData() throws
public static func enforceCacheLimit()
}A protocol for receiving callbacks about caching and loading events.
public protocol AudioVisualServiceDelegate: Sendable {
func didCacheData(url: URL, totalBytesCached: Int)
}AudioVisualService uses a layered architecture:
- CachingAVURLAsset: Intercepts AVFoundation's resource loading requests
- ResourceLoader: Manages the coordination between caching and network requests
- CacheManager: Handles disk storage, retrieval, and cache lifecycle
- Preloader: Provides proactive content downloading capabilities
- Range-Based Caching: Tracks downloaded byte ranges to detect missing segments
- Validation: Ensures cached content is contiguous and playable
- Automatic Cleanup: Removes old cache files based on access time
- Configurable Limits: Set maximum cache size and retention policies
- ✅ Progressive MP4, MOV, and other single-file video formats
- ❌ HTTP Live Streaming (HLS) - use native AVPlayer HLS support instead
- ❌ DASH streaming - not supported
- ❌ Encrypted/DRM-protected content
- Videos are cached in their entirety to disk
- Large videos will consume significant storage space
- Cache size management is automatic but configurable
- Default retention: 7 days since last access
- First playback may buffer while caching begins
- Subsequent playbacks use cached content for instant start
- Network usage is optimized through range requests
- Concurrent preloading is supported for multiple videos
Contributions are welcome! Please ensure that:
- Documentation is updated for public APIs
- Code follows Swift concurrency best practices
This project is licensed under the MIT License - see the LICENSE file for details.