diff --git a/WordPress/Classes/Services/MediaImageService.swift b/WordPress/Classes/Services/MediaImageService.swift index 3c254159623c..61681fd8bd97 100644 --- a/WordPress/Classes/Services/MediaImageService.swift +++ b/WordPress/Classes/Services/MediaImageService.swift @@ -272,8 +272,8 @@ final class MediaImageService { // MARK: - Networking private func data(for info: RemoteImageInfo, isCached: Bool) async throws -> Data { - let options = ImageRequestOptions(isDiskCacheEnabled: isCached) - return try await downloader.data(from: info.imageURL, host: info.host, options: options) + let request = ImageRequest(url: info.imageURL, host: info.host, options: ImageRequestOptions(isDiskCacheEnabled: isCached)) + return try await downloader.data(for: request) } private struct RemoteImageInfo { diff --git a/WordPress/Classes/Utility/Media/ImageDownloader.swift b/WordPress/Classes/Utility/Media/ImageDownloader.swift index 6fda98a7aeb5..eb2c1818bc2b 100644 --- a/WordPress/Classes/Utility/Media/ImageDownloader.swift +++ b/WordPress/Classes/Utility/Media/ImageDownloader.swift @@ -1,19 +1,5 @@ import UIKit -struct ImageRequestOptions { - /// Resize the thumbnail to the given size. By default, `nil`. - /// - /// - warning: The size is in pixels. - var size: CGSize? - - /// If enabled, uses ``MemoryCache`` for caching decompressed images. - var isMemoryCacheEnabled = true - - /// If enabled, uses `URLSession` preconfigured with a custom `URLCache` - /// with a relatively high disk capacity. By default, `true`. - var isDiskCacheEnabled = true -} - /// The system that downloads and caches images, and prepares them for display. actor ImageDownloader { static let shared = ImageDownloader() @@ -38,22 +24,17 @@ actor ImageDownloader { self.cache = cache } - // MARK: - Images (URL) - - /// Downloads image for the given `URL`. - func image(from url: URL, options: ImageRequestOptions = .init()) async throws -> UIImage { - var request = URLRequest(url: url) - request.addValue("image/*", forHTTPHeaderField: "Accept") - return try await image(from: request, options: options) + func image(from url: URL, host: MediaHost? = nil, options: ImageRequestOptions = .init()) async throws -> UIImage { + try await image(for: ImageRequest(url: url, host: host, options: options)) } - /// Downloads image for the given `URLRequest`. - func image(from request: URLRequest, options: ImageRequestOptions = .init()) async throws -> UIImage { - let key = makeKey(for: request.url, size: options.size) + func image(for request: ImageRequest) async throws -> UIImage { + let options = request.options + let key = makeKey(for: request.source.url, size: options.size) if options.isMemoryCacheEnabled, let image = cache[key] { return image } - let data = try await data(for: request, options: options) + let data = try await data(for: request) let image = try await ImageDecoder.makeImage(from: data, size: options.size) if options.isMemoryCacheEnabled { cache[key] = image @@ -61,25 +42,26 @@ actor ImageDownloader { return image } - // MARK: - Images (Blog) - - /// Returns image for the given URL authenticated for the given host. - func image(from imageURL: URL, host: MediaHost, options: ImageRequestOptions = .init()) async throws -> UIImage { - let request = try await authenticatedRequest(for: imageURL, host: host) - return try await image(from: request, options: options) - } - - /// Returns data for the given URL authenticated for the given host. - func data(from imageURL: URL, host: MediaHost, options: ImageRequestOptions = .init()) async throws -> Data { - let request = try await authenticatedRequest(for: imageURL, host: host) - return try await data(for: request, options: options) + func data(for request: ImageRequest) async throws -> Data { + let urlRequest = try await makeURLRequest(for: request) + return try await _data(for: urlRequest, options: request.options) } - private func authenticatedRequest(for imageURL: URL, host: MediaHost) async throws -> URLRequest { - var request = try await MediaRequestAuthenticator() - .authenticatedRequest(for: imageURL, host: host) - request.setValue("image/*", forHTTPHeaderField: "Accept") - return request + private func makeURLRequest(for request: ImageRequest) async throws -> URLRequest { + switch request.source { + case .url(let url, let host): + var request: URLRequest + if let host { + request = try await MediaRequestAuthenticator() + .authenticatedRequest(for: url, host: host) + } else { + request = URLRequest(url: url) + } + request.addValue("image/*", forHTTPHeaderField: "Accept") + return request + case .urlRequest(let urlRequest): + return urlRequest + } } // MARK: - Caching @@ -115,8 +97,8 @@ actor ImageDownloader { // MARK: - Networking - private func data(for request: URLRequest, options: ImageRequestOptions) async throws -> Data { - let requestKey = request.urlRequest?.url?.absoluteString ?? "" + private func _data(for request: URLRequest, options: ImageRequestOptions) async throws -> Data { + let requestKey = request.url?.absoluteString ?? "" let task = tasks[requestKey] ?? ImageDataTask(key: requestKey, Task { try await self._data(for: request, options: options, key: requestKey) }) @@ -198,7 +180,7 @@ extension ImageDownloader { nonisolated func downloadImage(for request: URLRequest, completion: @escaping (UIImage?, Error?) -> Void) -> ImageDownloaderTask { let task = Task { do { - let image = try await self.image(from: request, options: .init()) + let image = try await self.image(for: ImageRequest(urlRequest: request)) completion(image, nil) } catch { completion(nil, error) diff --git a/WordPress/Classes/Utility/Media/ImageRequest.swift b/WordPress/Classes/Utility/Media/ImageRequest.swift new file mode 100644 index 000000000000..66fcf0b8affd --- /dev/null +++ b/WordPress/Classes/Utility/Media/ImageRequest.swift @@ -0,0 +1,40 @@ +import UIKit + +final class ImageRequest { + enum Source { + case url(URL, MediaHost?) + case urlRequest(URLRequest) + + var url: URL? { + switch self { + case .url(let url, _): url + case .urlRequest(let request): request.url + } + } + } + + let source: Source + let options: ImageRequestOptions + + init(url: URL, host: MediaHost? = nil, options: ImageRequestOptions = .init()) { + self.source = .url(url, host) + self.options = options + } + + init(urlRequest: URLRequest, options: ImageRequestOptions = .init()) { + self.source = .urlRequest(urlRequest) + self.options = options + } +} + +struct ImageRequestOptions { + /// Resize the thumbnail to the given size. By default, `nil`. + var size: CGSize? + + /// If enabled, uses ``MemoryCache`` for caching decompressed images. + var isMemoryCacheEnabled = true + + /// If enabled, uses `URLSession` preconfigured with a custom `URLCache` + /// with a relatively high disk capacity. By default, `true`. + var isDiskCacheEnabled = true +}