fileprivate actor ImageCacheManager {
static let shared = ImageCacheManager()
private var cacheManager = NSCache<NSString, UIImage>()
private var tasks: [String: Task<UIImage?, Error>] = [:]
private init() {}
}
private func requestImage(url: URL) async throws -> UIImage? {
guard !Task.isCancelled else { return nil }
let task: (data: Data, response: URLResponse) = try await URLSession.shared.data(from: url)
guard let httpResponse = task.response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
throw ImageCacheError.invalidResponse
}
guard let image = UIImage(data: task.data) else {
throw ImageCacheError.invalidImage
}
return image
}
data(from url: URL, delegate: URLSessionTaskDelegate? = nil)
를 사용해서 튜플타입으로 데이터와 응답을 요청받는다.func data(from url: URL, delegate: URLSessionTaskDelegate? = nil) async throws -> (Data, URLResponse)
guard !Task.isCancelled else { return nil }
현재 함수가 실행되는 Task가 isCancelled 상태이면 request를 보내지 않고, 빠져나간다.
라고 생각을 했는데 확실히 모르겠다...func image(key: String) async throws -> UIImage? {
// 1.
if let cachedImage = cacheManager.object(forKey: NSString(string: key)) {
return cachedImage
}
guard let url = URL(string: key) else { throw ImageCacheError.invalidURL }
// 2.
if tasks[key] != nil {
return try await tasks[key]?.value
}
//3.
let task = Task {
try await requestImage(url: url)
}
//4.
tasks[key] = task
//7
defer { tasks[key] = nil }
guard let image = try await task.value else { return nil }
//5.
if let oldImage = cacheManager.object(forKey: NSString(string: key)) {
return oldImage
}
//6.
cacheManager.setObject(image, forKey: NSString(string: key))
return image
}
func cancel(url: String) {
tasks[url]?.cancel()
tasks[url] = nil
}
https://developer.apple.com/videos/play/wwdc2021/10133
https://developer.apple.com/documentation/uikit/views_and_controls/table_views/asynchronously_loading_images_into_table_and_collection_views