Reputation: 1
Im trying to make a cache system so that cache some videos in splash screen to have better user experience specially for first time opening the app
here's what I did in my cache manager
class CacheManager {static let shared = LocalJackpotsCacheManager()
private let fileManager = FileManager.default
private let cacheDirectory: URL
private let databaseURL: URL
private var db: OpaquePointer?
private init() {
// Set cacheDirectory to the Documents directory
cacheDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask)[0].appendingPathComponent("VideoCache")
databaseURL = cacheDirectory.appendingPathComponent("videoCacheMetadata.sqlite")
createCacheDirectoryIfNeeded()
setupDatabase()
}
private func createCacheDirectoryIfNeeded() {
if !fileManager.fileExists(atPath: cacheDirectory.path) {
do {
try fileManager.createDirectory(at: cacheDirectory, withIntermediateDirectories: true, attributes: nil)
} catch {
print("Failed to create cache directory: \(error)")
}
}
}
private func setupDatabase() {
if sqlite3_open(databaseURL.path, &db) == SQLITE_OK {
let createTableQuery = """
CREATE TABLE IF NOT EXISTS VideoCache (
id TEXT PRIMARY KEY,
path TEXT,
lastAccessed INTEGER
);
"""
if sqlite3_exec(db, createTableQuery, nil, nil, nil) != SQLITE_OK {
print("Failed to create VideoCache table")
}
} else {
print("Unable to open database")
}
}
func cacheVideo(from url: URL, forKey key: String, completion: @escaping () -> Void) {
let hashedKey = key.sha256()
let destinationURL = cacheDirectory.appendingPathComponent(hashedKey)
if fileManager.fileExists(atPath: destinationURL.path) {
completion() // File already cached
return
}
let downloadTask = URLSession.shared.downloadTask(with: url) { tempURL, _, error in
guard let tempURL = tempURL, error == nil else {
print("Download error: \(error?.localizedDescription ?? "Unknown error")")
completion()
return
}
do {
try self.fileManager.moveItem(at: tempURL, to: destinationURL)
self.updateDatabase(with: hashedKey, filePath: destinationURL.path)
completion() // Call completion on success
} catch {
print("Error saving video to cache: \(error)")
completion()
}
}
downloadTask.resume()
}
func getCachedURL(forKey key: String) -> URL? {
let hashedKey = key.sha256()
let fileURL = cacheDirectory.appendingPathComponent(hashedKey)
return fileManager.fileExists(atPath: fileURL.path) ? fileURL : nil
}
private func updateDatabase(with key: String, filePath: String) {
let updateQuery = """
INSERT OR REPLACE INTO VideoCache (id, path, lastAccessed) VALUES (?, ?, ?);
"""
var statement: OpaquePointer?
if sqlite3_prepare_v2(db, updateQuery, -1, &statement, nil) == SQLITE_OK {
let timestamp = Int(Date().timeIntervalSince1970)
sqlite3_bind_text(statement, 1, key, -1, nil)
sqlite3_bind_text(statement, 2, filePath, -1, nil)
sqlite3_bind_int(statement, 3, Int32(timestamp))
sqlite3_step(statement)
} else {
print("Error preparing update statement")
}
sqlite3_finalize(statement)
}
func cleanOldCache(limit: Int) {
let selectOldItemsQuery = """
SELECT id, path FROM VideoCache ORDER BY lastAccessed ASC LIMIT ?;
"""
var selectStatement: OpaquePointer?
if sqlite3_prepare_v2(db, selectOldItemsQuery, -1, &selectStatement, nil) == SQLITE_OK {
sqlite3_bind_int(selectStatement, 1, Int32(limit))
while sqlite3_step(selectStatement) == SQLITE_ROW {
let id = String(cString: sqlite3_column_text(selectStatement, 0))
let path = String(cString: sqlite3_column_text(selectStatement, 1))
// Delete the file from disk
do {
try fileManager.removeItem(atPath: path)
} catch {
print("Error deleting file from disk: \(error)")
}
// Remove from database
deleteCacheEntry(withId: id)
}
}
sqlite3_finalize(selectStatement)
}
private func deleteCacheEntry(withId id: String) {
let deleteQuery = "DELETE FROM VideoCache WHERE id = ?;"
var deleteStatement: OpaquePointer?
if sqlite3_prepare_v2(db, deleteQuery, -1, &deleteStatement, nil) == SQLITE_OK {
sqlite3_bind_text(deleteStatement, 1, id, -1, nil)
sqlite3_step(deleteStatement)
} else {
print("Error preparing delete statement")
}
sqlite3_finalize(deleteStatement)
}
}
and how Im trying to use it
if let cachedFileURL = LocalJackpotsCacheManager.shared.getCachedURL(forKey: cacheKey) {
// Log cached file details for confirmation
do {
let data = try Data(contentsOf: cachedFileURL)
print("Cached file successfully loaded into Data object, size: \(data.count) bytes")
} catch {
print("Failed to load cached file into Data object: \(error.localizedDescription)")
}
// Prepare the video for playback
let asset = AVURLAsset(url: cachedFileURL, options: [AVURLAssetPreferPreciseDurationAndTimingKey: true])
let playerItem = AVPlayerItem(asset: asset)
let player = AVPlayer(playerItem: playerItem)
// Configure the cell with the player
cell.configure(with: player)
player.play() // Start playback immediately if desired
} else {
// If file is not cached, download and cache it
LocalJackpotsCacheManager.shared.cacheVideo(from: videoURL, forKey: cacheKey) {
if let cachedFileURL = LocalJackpotsCacheManager.shared.getCachedURL(forKey: cacheKey) {
DispatchQueue.main.async {
// Prepare the downloaded video for playback
let asset = AVURLAsset(url: cachedFileURL, options: [AVURLAssetPreferPreciseDurationAndTimingKey: true])
let playerItem = AVPlayerItem(asset: asset)
let player = AVPlayer(playerItem: playerItem)
cell.configure(with: player)
player.play()
}
}
}
}
dk what exactly wrong with this but file loaded and printed it's size ! keep says Cannot open
try to cache and play video from local and keep fails
Upvotes: 0
Views: 29