SPHTTPCache is a lightweight HTTP caching framework for iOS, inspired by KTVHTTPCache. It provides a local HTTP proxy server to enable seamless video/audio caching and smooth playback with AVPlayer.
A lightweight, KTVHTTPCache-style HTTP proxy + cache for AVPlayer and general HTTP resources. Proof-of-concept: API complete, production-hardening left to you.
- Download while playing, slice-based caching, and Range awareness.
- Preloading via
SPHCDataLoader. - HLS (m3u8) playlist rewrite to proxied segment URLs.
- AirPlay friendly (via AVPlayer HTTP URLs).
- URL mapping & encoding hook.
- Thread-safe internals.
- Powerful logging with file sink.
- Cache management: size cap, LRU trimming, file enumeration & deletion.
- Low coupling: simple static API façade.
import SPHTTPCache
import AVKit
// Start local HTTP proxy
var nsErr: NSError?
let ok = SPHTTPCache.proxyStart(error: &nsErr)
guard ok else { fatalError("Proxy failed: \(String(describing: nsErr))") }
let remote = URL(string: "https://example.com/video.mp4")!
let playURL = SPHTTPCache.proxyURL(withOriginalURL: remote)
let item = AVPlayerItem(url: playURL)
let player = AVPlayer(playerItem: item)let req = SPHCDataRequest(url: remote)
let loader = SPHTTPCache.cacheLoader(with: req)
loader?.start() // prefetch into cacheif let file = SPHTTPCache.cacheCompleteFileURL(with: remote) {
print("Fully cached at", file)
}
let items = SPHTTPCache.cacheAllCacheItems()- This is a single-module SPM with minimal dependencies.
- The internal HTTP server handles
GET,HEAD, Range requests andm3u8rewrite. - See inline
TODO:for areas to extend (TLS tunneling, advanced playlist variations, resume robust handling, etc.).
import AVKit
var err: NSError?
SPHTTPCache.logConsoleLogEnable = true
// 1) Fire-and-forget
SPHTTPCache.proxyStart()
// 2) Capture NSError
var nsErr: NSError?
let ok = SPHTTPCache.proxyStart(error: &nsErr)
if !ok { print(nsErr?.localizedDescription ?? "unknown") }
let remote = URL(string: "https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4")!
let playURL = SPHTTPCache.proxyURL(withOriginalURL: remote)
let player = AVPlayer(url: playURL) // AirPlay OK
// Optional: preload ahead
let request = SPHCDataRequest(url: remote)
SPHTTPCache.cacheLoader(with: request)?.start()
- SPHTTPCache.downloadAcceptableContentTypes = ["video/mp4", "video/*", "application/octet-stream"]
- Paste this in your app startup (before playback):
SPHTTPCache.downloadAcceptableContentTypes = [
"video/mp4", "video/*", "application/octet-stream"
]
// Forward only these incoming headers to origin (safe defaults)
SPHTTPCache.downloadWhitelistHeaderKeys = [
"user-agent", "accept", "range", "if-none-match", "if-modified-since",
"referer", "cookie", "authorization"
]
// Add any CDN-required headers/tokens here
SPHTTPCache.downloadAdditionalHeaders = [
"User-Agent": "YourApp/1.0 (iOS)",
// "Authorization": "Bearer <token>",
// "X-Custom": "value"
]
SPHTTPCache.cacheMaxCacheLength = 2 * 1024 * 1024 * 1024
// e.g. 750 MB cap
SPHTTPCache.setCacheMaxLengthMB(750)
// (optional) your own URL mapper (for signed URLs / query cleanup)
SPHTTPCache.encodeSetURLConverter { url in
// Example: strip volatile query keys
var comps = URLComponents(url: url, resolvingAgainstBaseURL: false)!
comps.queryItems = (comps.queryItems ?? []).filter { $0.name != "ts" && $0.name != "nonce" }
return comps.url ?? url
}
- To read the current size in MB:
let currentMB = Double(SPHTTPCache.cacheTotalCacheLength) / (1024 * 1024)
- Xcode → File ▸ Add Packages… and use:
- .package(url: "https://github.com/ShardaPrasad/SPHTTPCache.git", branch: "1.0.0")
- .product(name: "SPHTTPCache", package: "SPHTTPCache")
- import SPHTTPCache