[SwiftUI] NSCache

Junyoung Park·2022년 8월 20일
0

SwiftUI

목록 보기
27/136
post-thumbnail

Save and cache images in a SwiftUI app | Continued Learning #27

NSCache

구현 목표

  • NSCache의 용량, 오브젝트 개수를 커스텀한다.
  • NSCache에 데이터를 저장한다.
  • NSCache의 데이터를 삭제한다.
  • NSCache의 데이터를 조회한다.

구현 태스크

  1. 캐시 담당 매니저 싱글턴 패턴 구현
  2. 캐시 뷰 모델: 현재 뷰에서 그리고 있는 이미지 로드. 이미지 종류 두 개 → 이미지 이름에 따라 캐시 저장 및 로드, 삭제 가능
  3. 뷰: 현재 캐시 정보, 이미지 이름 및 이미지 정보 표시

핵심 코드

import SwiftUI

class CacheManager {
    static let instance = CacheManager()
    private init() {}
    
    var imageCache: NSCache<NSString, UIImage> = {
        let cache = NSCache<NSString, UIImage>()
        cache.countLimit = 3
        cache.totalCostLimit = 1024 * 1024 // 1MB
        return cache
    }()
    
    func add(image: UIImage, name: String) -> String {
        imageCache.setObject(image, forKey: name as NSString)
        return "ADDED TO CACHE"
    }
    
    func get(name: String) -> UIImage? {
        guard let image = imageCache.object(forKey: name as NSString) else {
            print("NO IMAGE AT ALL")
            return nil
        }
        return image
    }
    
    func remove(name: String) -> String {
        imageCache.removeObject(forKey: name as NSString)
        return "REMOVE FROM CACHE"
    }
}
  • 캐시 매니저는 NSCache 접근을 제어하는 클래스로 싱글턴 패턴으로 구현
  • 키는 이미지 이름, 값은 UIImage 데이터로 커스텀 캐시 설정
  • 커스텀 캐시의 countLimit, totalCostLimit를 통해 저장 가능한 데이터의 개수 및 용량 한도 설정
  • 데이터 추가, 삭제, 조회 함수 구현

소스 코드

import SwiftUI

class CacheManager {
    static let instance = CacheManager()
    private init() {}
    
    var imageCache: NSCache<NSString, UIImage> = {
        let cache = NSCache<NSString, UIImage>()
        cache.countLimit = 3
        cache.totalCostLimit = 1024 * 1024 // 1MB
        return cache
    }()
    
    func add(image: UIImage, name: String) -> String {
        imageCache.setObject(image, forKey: name as NSString)
        return "ADDED TO CACHE"
    }
    
    func get(name: String) -> UIImage? {
        guard let image = imageCache.object(forKey: name as NSString) else {
            print("NO IMAGE AT ALL")
            return nil
        }
        return image
    }
    
    func remove(name: String) -> String {
        imageCache.removeObject(forKey: name as NSString)
        return "REMOVE FROM CACHE"
    }
    
}

class CacheViewModel: ObservableObject {
    @Published var startingImage: UIImage? = nil
    @Published var cachedImage: UIImage? = nil
    @Published var infoMessage = ""
    let manager = CacheManager.instance
    var imageName = "Polar_Bear1"
    
    
    init() {
        getImageFromAssetsFolder()
    }
    
    func getImageFromAssetsFolder() {
        startingImage = UIImage(named: imageName)
    }
    
    func changeImage() {
        if imageName == "Polar_Bear1" {
            imageName = "Polar_Bear2"
            getImageFromAssetsFolder()
        } else {
            imageName = "Polar_Bear1"
            getImageFromAssetsFolder()
        }
    }
    
    func saveToCache() {
        guard let image = startingImage else { return }
        infoMessage = manager.add(image: image, name: imageName)
    }
    
    func removeFromCache() {
        infoMessage = manager.remove(name: imageName)
    }
    
    func getFromCache() {
        if let cachedImaged = manager.get(name: imageName) {
            self.cachedImage = cachedImaged
            infoMessage = "CACHE IMAGE"
        } else {
            self.cachedImage = nil
            infoMessage = "NO CACHE IMAGE"
        }
    }
}

struct NSCacheBootCamp: View {
    @StateObject private var viewModel = CacheViewModel()
    var body: some View {
        NavigationView {
            VStack {
                Text("CURRENT IMAGENAME: \(viewModel.imageName)")
                    .font(.title)
                    .fontWeight(.bold)
                    .foregroundColor(.purple)
                Text(viewModel.infoMessage)
                    .font(.headline)
                    .fontWeight(.bold)
                
                if let image = viewModel.startingImage {
                    Image(uiImage: image)
                        .resizable()
                        .scaledToFit()
                        .frame(width: 200, height: 200)
                        .clipped()
                        .cornerRadius(10)
                }
                
                HStack {
                    Button(action: {
                        viewModel.saveToCache()
                    }, label: {
                        Text("SAVE TO CACHE")
                            .font(.headline)
                            .foregroundColor(.white)
                            .padding()
                            .background(.blue)
                            .cornerRadius(10)
                    })
                    Button(action: {
                        viewModel.removeFromCache()
                    }, label: {
                        Text("DELETE FROM CACHE")
                            .font(.headline)
                            .foregroundColor(.white)
                            .padding()
                            .background(.red)
                            .cornerRadius(10)
                    })
                }
                
                if let image = viewModel.cachedImage {
                    Image(uiImage: image)
                        .resizable()
                        .scaledToFit()
                        .frame(width: 200, height: 200)
                        .clipped()
                        .cornerRadius(10)
                }
                
                HStack {
                    Button(action: {
                        viewModel.changeImage()
                    }, label: {
                        Text("CHANGE IMAGE")
                            .font(.headline)
                            .foregroundColor(.white)
                            .padding()
                            .background(.orange)
                            .cornerRadius(10)
                    })
                    
                    Button(action: {
                        viewModel.getFromCache()
                    }, label: {
                        Text("GET FROM CACHE")
                            .font(.headline)
                            .foregroundColor(.white)
                            .padding()
                            .background(.green)
                            .cornerRadius(10)
                    })
                }
                Spacer()
            }
            .navigationTitle("CACHE BOOTCAMP")
        }
    }
}

구현 화면

profile
JUST DO IT

0개의 댓글