ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • AsyncImage 구현하기(2)
    SwiftUI 2023. 6. 9. 23:25

    여기에서는 URLCache 를 이용해 CachedAsyncImage에 캐시기능을 추가하겠습니다. 우선 CachedAsyncImage에서만 사용하기 위해 해당 파일을 CachedAsyncImageViewModel에 fileprivate로 ImageCache 클래스를 구현합니다. 

    fileprivate final class ImageCache {
        static let shared = ImageCache()
        let cache: URLCache
        private init() {
            self.cache = URLCache(memoryCapacity: 32*1000*1000, diskCapacity: 100*1000*1000)
        }
        //캐시에 UIImage가 있는 경우 이미지를 반환하고 없는 경우 nil을 반환 
        func load(_ request: URLRequest) -> UIImage? {
            guard let response = cache.cachedResponse(for: request) else { return nil }
            guard let uiImage = UIImage(data: response.data) else {
                cache.removeCachedResponse(for: request)
                return nil
            }
            //print("cache hit: \(request.url!.absoluteString)")
            return uiImage
        }
        //캐시에 request에 해당 하는 response 값과 data를 이용해 CachedResponse를 생성해 저장 
        func store(_ request: URLRequest, response: URLResponse, data: Data) {
            cache.removeCachedResponse(for: request)
            let cachedResponse = CachedURLResponse(response: response, data: data)
            cache.storeCachedResponse(cachedResponse, for: request)
        }
        
    }

    위의 캐시를 이용해 이미지를 패치 시 우선 load 함수를 호출해 해당 request 에 대한 이미지가 있는 지 확인 후 있으면 해당 이미지를 사용해 Image 뷰를 생성하고 현재 CachedAsynImagePhase 상태를 success 로 변경합니다. request 대한 값이 없는 경우 이전과 동일하게  request를 요청하고 성공적으로 수행하면 ImageCache에 store 함수를 호출해 request에 대한 CachedResponse를 저장합니다. 

    func fetch(url: URL?) async {
    		...
            let request = URLRequest(url: url)
            if let uiImage = ImageCache.shared.load(request) {
                phase = .success(Image(uiImage: uiImage))
            }
            
            let configuration = URLSessionConfiguration.default
            configuration.urlCache = ImageCache.shared.cache
            configuration.httpMaximumConnectionsPerHost = 10
            let session = URLSession(configuration: configuration)
           	
            do {
                let (data, response) = try await session.data(for: request)
                let httpResponse = response as! HTTPURLResponse
                if httpResponse.statusCode < 200 || httpResponse.statusCode > 300 {
                    phase = .failure(CachedAsyncImageError.httpResponseError(httpResponse.statusCode))
                    return
                }
                
                guard let uiImage = UIImage(data: data) else {
                    phase = .failure(CachedAsyncImageError.imageCreatedFail)
                    return
                }
                phase = .success(Image(uiImage: uiImage))
                ImageCache.shared.store(request, response: response, data: data)
            } catch {
                phase = .failure(error)
            }

    위에서 보는 바와 같이 캐시 설정을 위해 URLSession.shared를 사용하는 대신 새로운 URLSession을 생성하고 이 세션을 사용하여 요청을 보내게 됩니다.

    해당 소스는 https://github.com/hjpark0724/CacheAsyncImage 에서 확인할 수 있습니다. 

    댓글

Designed by Tistory.