sendbird-uikit-ios/Sources/Manager/CacheManager/SBUCacheManager.swift

289 lines
9.6 KiB
Swift

//
// SBUCacheManager.swift
// SendbirdUIKit
//
// Created by Harry Kim on 2020/03/06.
// Copyright © 2020 Sendbird, Inc. All rights reserved.
//
import UIKit
import AVFoundation
import SendbirdChatSDK
typealias SBUCacheCompletionHandler = (URL?, NSData?) -> Void
class SBUCacheManager {
static internal let fileCacheQueue = DispatchQueue(label: "com.sendbird.cache.file", qos: .background)
// MARK: - Common
static func createHashName(urlString: String) -> String {
return "\(urlString.persistantHash)"
}
static func fileExtension(urlString: String) -> String {
let pathExtension = URL(fileURLWithPath: URLComponents(string: urlString)?.path ?? "").pathExtension
return pathExtension
}
// MARK: - DiskCache
struct DiskCache {
// MARK: - Properties
private let fileManager = FileManager.default
private let cacheType: String
private let diskQueue = DispatchQueue(label: "\(SBUConstant.bundleIdentifier).queue.diskcache", qos: .background)
// lazy var urlCache: URLCache = {
// if #available(iOS 13.0, *) {
// return URLCache(memoryCapacity: 10000000, diskCapacity: 1000000000, directory: self.cachePathURL())
// } else {
// return URLCache(memoryCapacity: 10000000, diskCapacity: 1000000000, diskPath: self.cachePathURL().path)
// }
// }()
//
// lazy var session: URLSession = {
// let config = URLSessionConfiguration.default
// config.urlCache = self.urlCache
// return URLSession(configuration: config)
// }()
//
// MARK: - Initializers
init(cacheType: String) {
self.cacheType = cacheType
let cachePath = self.cachePathURL().path
if self.fileManager.fileExists(atPath: cachePath) {
return
}
do {
try self.fileManager.createDirectory(
atPath: cachePath,
withIntermediateDirectories: true,
attributes: nil
)
} catch {
SBULog.error(error.localizedDescription)
}
}
func cacheExists(key: String) -> Bool {
return fileManager.fileExists(atPath: self.pathForKey(key))
}
func get(fullPath: URL) -> Data? {
do {
let data = try Data(contentsOf: fullPath)
return data
} catch {
SBULog.info(error.localizedDescription)
}
return nil
}
func get(key: String) -> NSData? {
guard cacheExists(key: key) else { return nil }
let filePath = URL(fileURLWithPath: self.pathForKey(key))
do {
let data = try Data(contentsOf: filePath)
return data as NSData
} catch {
SBULog.info(error.localizedDescription)
}
return nil
}
func set(key: String, data: NSData, completionHandler: SBUCacheCompletionHandler? = nil) {
diskQueue.async {
let filePath = URL(fileURLWithPath: self.pathForKey(key))
do {
let subPath = filePath.deletingLastPathComponent()
try self.fileManager.createDirectory(
atPath: subPath.path,
withIntermediateDirectories: true,
attributes: nil
)
} catch {
SBULog.error(error.localizedDescription)
DispatchQueue.main.async {
completionHandler?(nil, nil)
}
return
}
data.write(to: filePath, atomically: true)
DispatchQueue.main.async {
completionHandler?(filePath, data)
}
}
}
func rename(key: String, newKey: String) {
diskQueue.async {
let fileManager = self.fileManager
let atPath = self.pathForKey(key)
let toPath = self.pathForKey(newKey)
try? fileManager.moveItem(atPath: atPath, toPath: toPath)
}
}
func remove(key: String) {
diskQueue.async {
let path = self.pathForKey(key)
let fileManager = self.fileManager
do {
try fileManager.removeItem(atPath: path)
} catch {
SBULog.error("Could not remove file: \(error)")
}
}
}
func removeAll() {
diskQueue.async {
let fileManager = self.fileManager
let cachePath = self.cachePathURL().path
do {
try fileManager.removeItem(atPath: cachePath)
} catch {
SBULog.error("Could not remove cache path: \(error)")
}
}
}
func cachePathURL() -> URL {
guard let cacheDirectoryURL = try? FileManager.default.url(
for: .cachesDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: true) else { return URL(fileURLWithPath: "") }
let cachePathURL = cacheDirectoryURL.appendingPathComponent("\(self.cacheType)/")
return cachePathURL
}
func pathForKey(_ key: String) -> String {
let cachePathURL = cachePathURL()
let fullPath = cachePathURL.appendingPathComponent(key)
return fullPath.path
}
@available(*, deprecated, renamed: "cacheExists(key:)")
public func hasImage(key: String) -> Bool {
self.cacheExists(key: key)
}
// Voice
func voiceTempPath(fileName: String) -> URL? {
let documentPath = URL(fileURLWithPath: self.pathForKey("voice_temp"))
if !FileManager.default.fileExists(atPath: documentPath.path) {
do {
try FileManager.default.createDirectory(at: documentPath, withIntermediateDirectories: true)
return documentPath.appendingPathComponent(fileName)
} catch {
SBULog.error("[Failed] Create directory : \(error.localizedDescription)")
return nil
}
}
return documentPath.appendingPathComponent(fileName)
}
func removeVoiceTemp(fileName: String?) {
guard let fileName = fileName,
let path = self.voiceTempPath(fileName: fileName)?.path else { return }
diskQueue.async {
let fileManager = self.fileManager
do {
try fileManager.removeItem(atPath: path)
} catch {
SBULog.error("Could not remove file: \(error)")
}
}
}
}
// MARK: - MemoryCache (for Image)
struct MemoryCache {
private let memoryQueue = DispatchQueue(label: "\(SBUConstant.bundleIdentifier).queue.memorycache", qos: .background)
// MARK: - Memory Cache
private var memoryCache: NSCache<NSString, UIImage> = {
let cache = NSCache<NSString, UIImage>()
cache.totalCostLimit = 10 * 1024 * 1024 // Here the size in bytes of data is used as the cost, here 10 MB limit
cache.countLimit = 30 // 30 url limit
return cache
}()
func set(key: String, image: UIImage) {
memoryQueue.async {
self.memoryCache.setObject(image, forKey: key as NSString)
}
}
func set(key: String, data: NSData) {
memoryQueue.async {
guard let image = UIImage.createImage(from: data as Data) else { return }
self.set(key: key, image: image)
}
}
func get(key: String) -> UIImage? {
return self.memoryCache.object(forKey: key as NSString)
}
func remove(key: String) {
self.memoryCache.removeObject(forKey: key as NSString)
}
func cacheExists(key: String) -> Bool {
return self.memoryCache.object(forKey: key as NSString) != nil
}
}
}
@available(*, deprecated, message: "We can't guarantee the problems that occur with direct access to DiskCache and handling data.")
public struct DiskCache {
// MARK: - Properties
private let imageDiskCache: SBUCacheManager.DiskCache
// MARK: - Initializers
public init() {
self.imageDiskCache = SBUCacheManager.DiskCache(cacheType: "image")
}
public func hasImage(key: String) -> Bool {
return self.imageDiskCache.cacheExists(key: key)
}
public func get(key: String) -> NSData? {
return self.imageDiskCache.get(key: key)
}
public func set(key: String, data: NSData) {
self.imageDiskCache.set(key: key, data: data)
}
public func rename(key: String, newKey: String) {
self.imageDiskCache.rename(key: key, newKey: newKey)
}
public func remove(key: String) {
self.imageDiskCache.remove(key: key)
}
public func removeAll() {
self.imageDiskCache.removeAll()
}
}