202 lines
8.3 KiB
Swift
202 lines
8.3 KiB
Swift
//
|
|
// WebDAV+DiskCache.swift
|
|
// WebDAV-Swift
|
|
//
|
|
// Created by Isaac Lyons on 4/7/21.
|
|
//
|
|
|
|
import UIKit
|
|
|
|
//MARK: Public
|
|
|
|
public extension WebDAV {
|
|
|
|
//MARK: Data
|
|
|
|
func cachedDataURL<A: WebDAVAccount>(forItemAtPath path: String, account: A) -> URL? {
|
|
guard let encodedDescription = UnwrappedAccount(account: account)?.encodedDescription,
|
|
let caches = cacheFolder else { return nil }
|
|
return caches
|
|
.appendingPathComponent(encodedDescription)
|
|
.appendingPathComponent(path.trimmingCharacters(in: AccountPath.slash))
|
|
}
|
|
|
|
func cachedDataURLIfExists<A: WebDAVAccount>(forItemAtPath path: String, account: A) -> URL? {
|
|
guard let url = cachedDataURL(forItemAtPath: path, account: account) else { return nil }
|
|
return FileManager.default.fileExists(atPath: url.path) ? url : nil
|
|
}
|
|
|
|
func deleteCachedDataFromDisk<A: WebDAVAccount>(forItemAtPath path: String, account: A) throws {
|
|
guard let url = cachedDataURLIfExists(forItemAtPath: path, account: account) else { return }
|
|
try FileManager.default.removeItem(at: url)
|
|
}
|
|
|
|
func deleteAllDiskCachedData() throws {
|
|
guard let url = cacheFolder else { return }
|
|
let fm = FileManager.default
|
|
let filesCachePath = filesCacheURL?.path
|
|
for item in try fm.contentsOfDirectory(atPath: url.path) where item != filesCachePath {
|
|
try fm.removeItem(at: url.appendingPathComponent(item))
|
|
}
|
|
}
|
|
|
|
//MARK: Thumbnails
|
|
|
|
func cachedThumbnailURL<A: WebDAVAccount>(forItemAtPath path: String, account: A, with properties: ThumbnailProperties) -> URL? {
|
|
guard let imageURL = cachedDataURL(forItemAtPath: path, account: account) else { return nil }
|
|
|
|
// If the query is stored in the URL as an actualy query, it won't be included when
|
|
// saving to a file, so we have to manually add the query to the filename here.
|
|
let directory = imageURL.deletingLastPathComponent()
|
|
var filename = imageURL.lastPathComponent
|
|
if let query = nextcloudPreviewQuery(at: path, properties: properties)?.dropFirst() {
|
|
filename = query.reduce(filename + "?") { $0 + ($0.last == "?" ? "" : "&") + $1.description}
|
|
}
|
|
return directory.appendingPathComponent(filename)
|
|
}
|
|
|
|
func cachedThumbnailURLIfExists<A: WebDAVAccount>(forItemAtPath path: String, account: A, with properties: ThumbnailProperties) -> URL? {
|
|
guard let url = cachedThumbnailURL(forItemAtPath: path, account: account, with: properties) else { return nil }
|
|
return FileManager.default.fileExists(atPath: url.path) ? url : nil
|
|
}
|
|
|
|
func deleteCachedThumbnailFromDisk<A: WebDAVAccount>(forItemAtPath path: String, account: A, with properties: ThumbnailProperties) throws {
|
|
guard let url = cachedThumbnailURLIfExists(forItemAtPath: path, account: account, with: properties) else { return }
|
|
try FileManager.default.removeItem(at: url)
|
|
}
|
|
|
|
func deleteAllCachedThumbnailsFromDisk<A: WebDAVAccount>(forItemAtPath path: String, account: A) throws {
|
|
let fm = FileManager.default
|
|
guard let url = cachedDataURL(forItemAtPath: path, account: account) else { return }
|
|
|
|
let filename = url.lastPathComponent
|
|
let directory = url.deletingLastPathComponent()
|
|
guard fm.fileExists(atPath: directory.path) else { return }
|
|
|
|
for item in try fm.contentsOfDirectory(atPath: directory.path) where item != filename && item.starts(with: filename) {
|
|
try fm.removeItem(at: directory.appendingPathComponent(item))
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//MARK: Internal
|
|
|
|
extension WebDAV {
|
|
|
|
var cacheFolder: URL? {
|
|
let fm = FileManager.default
|
|
guard let caches = fm.urls(for: .cachesDirectory, in: .userDomainMask).first else { return nil }
|
|
let directory = caches.appendingPathComponent(WebDAV.domain, isDirectory: true)
|
|
|
|
if !fm.fileExists(atPath: directory.path) {
|
|
do {
|
|
try fm.createDirectory(at: directory, withIntermediateDirectories: true)
|
|
} catch {
|
|
NSLog("\(error)")
|
|
return nil
|
|
}
|
|
}
|
|
return directory
|
|
}
|
|
|
|
//MARK: Data Cache
|
|
|
|
func loadCachedValueFromDisk<A: WebDAVAccount, Value: Equatable>(cache: Cache<AccountPath, Value>, forItemAtPath path: String, account: A, valueFromData: @escaping (_ data: Data) -> Value?) -> Value? {
|
|
guard let url = cachedDataURL(forItemAtPath: path, account: account),
|
|
FileManager.default.fileExists(atPath: url.path),
|
|
let data = try? Data(contentsOf: url),
|
|
let value = valueFromData(data) else { return nil }
|
|
cache[AccountPath(account: account, path: path)] = value
|
|
return value
|
|
}
|
|
|
|
func saveDataToDiskCache(_ data: Data, url: URL) throws {
|
|
let directory = url.deletingLastPathComponent()
|
|
|
|
if !FileManager.default.fileExists(atPath: directory.path) {
|
|
try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
|
|
}
|
|
try data.write(to: url)
|
|
}
|
|
|
|
func saveDataToDiskCache<A: WebDAVAccount>(_ data: Data, forItemAtPath path: String, account: A) throws {
|
|
guard let url = cachedDataURL(forItemAtPath: path, account: account) else { return }
|
|
try saveDataToDiskCache(data, url: url)
|
|
}
|
|
|
|
func cleanupDiskCache<A: WebDAVAccount>(at path: String, account: A, files: [WebDAVFile]) throws {
|
|
let fm = FileManager.default
|
|
guard let url = cachedDataURL(forItemAtPath: path, account: account),fm.fileExists(atPath: url.path) else { return }
|
|
|
|
let goodFilePaths = Set(files.compactMap { cachedDataURL(forItemAtPath: $0.path, account: account)?.path })
|
|
|
|
let infoPlist = filesCacheURL?.path
|
|
for path in try fm.contentsOfDirectory(atPath: url.path).map({ url.appendingPathComponent($0).path })
|
|
where !goodFilePaths.contains(path)
|
|
&& path != infoPlist {
|
|
try fm.removeItem(atPath: path)
|
|
}
|
|
}
|
|
|
|
//MARK: Thumbnail Cache
|
|
|
|
func loadCachedThumbnailFromDisk<A: WebDAVAccount>(forItemAtPath path: String, account: A, with properties: ThumbnailProperties) -> UIImage? {
|
|
guard let url = cachedThumbnailURL(forItemAtPath: path, account: account, with: properties),
|
|
FileManager.default.fileExists(atPath: url.path),
|
|
let data = try? Data(contentsOf: url),
|
|
let thumbnail = UIImage(data: data) else { return nil }
|
|
saveToMemoryCache(thumbnail: thumbnail, forItemAtPath: path, account: account, with: properties)
|
|
return thumbnail
|
|
}
|
|
|
|
func saveThumbnailToDiskCache<A: WebDAVAccount>(data: Data, forItemAtPath path: String, account: A, with properties: ThumbnailProperties) throws {
|
|
guard let url = cachedThumbnailURL(forItemAtPath: path, account: account, with: properties) else { return }
|
|
try saveDataToDiskCache(data, url: url)
|
|
}
|
|
|
|
//MARK: Files Cache
|
|
|
|
var filesCacheURL: URL? {
|
|
cacheFolder?.appendingPathComponent("files.plist")
|
|
}
|
|
|
|
func saveFilesCacheToDisk() {
|
|
guard let fileURL = filesCacheURL else { return }
|
|
do {
|
|
let data = try PropertyListEncoder().encode(filesCache)
|
|
try data.write(to: fileURL)
|
|
} catch {
|
|
NSLog("Error saving files cache: \(error)")
|
|
}
|
|
}
|
|
|
|
func loadFilesCacheFromDisk() {
|
|
guard let fileURL = filesCacheURL,
|
|
FileManager.default.fileExists(atPath: fileURL.path) else { return }
|
|
do {
|
|
let data = try Data(contentsOf: fileURL)
|
|
let files = try PropertyListDecoder().decode([AccountPath: [WebDAVFile]].self, from: data)
|
|
filesCache.merge(files) { current, _ in current}
|
|
} catch {
|
|
NSLog("Error loading files cache: \(error)")
|
|
}
|
|
}
|
|
|
|
public func clearFilesDiskCache() {
|
|
guard let fileURL = filesCacheURL,
|
|
FileManager.default.fileExists(atPath: fileURL.path) else { return }
|
|
do {
|
|
try FileManager.default.removeItem(at: fileURL)
|
|
} catch {
|
|
NSLog("Error removing files disk cache: \(error)")
|
|
}
|
|
}
|
|
|
|
public func clearFilesCache() {
|
|
clearFilesMemoryCache()
|
|
clearFilesDiskCache()
|
|
}
|
|
|
|
}
|