[SwiftUI] CryptoApp: FileManager

Junyoung Park·2022년 11월 3일
0

SwiftUI

목록 보기
78/136
post-thumbnail
post-custom-banner

Saving images to the File Manager | SwiftUI Crypto App #9

CryptoApp: FileManager

구현 목표

  • 반복 다운로드 방지를 위한 파일 매니저 내 데이터 저장

구현 태스크

  • 파일 매니저 서비스 클래스 싱글턴 구현
  • 저장된 이미지일 때 파일 매니저에서 이미지 리턴 또는 네트워크 다운로드

핵심 코드

func saveImage(image: UIImage, imageName: String, folderName: String) {
        // create folder
        createFolderIfNeeded(folderName: folderName)
        // get path for image
        guard
            let url = getURLForImage(imageName: imageName, folderName: folderName),
            let data = image.pngData() else { return }
        // save image to path
        do {
            try data.write(to: url)
        } catch {
            print("Error saving image")
            print(error.localizedDescription)
        }
    }
  • 파일 매니저 내 폴더 및 파일 URL 주소를 통해 해당 데이터를 저장
func getImage(imageName: String, folderName: String) -> UIImage? {
        guard
            let url = getURLForImage(imageName: imageName, folderName: folderName),
            FileManager.default.fileExists(atPath: url.path) else { return nil }
        return UIImage(contentsOfFile: url.path)
    }
  • 해당 데이터의 URL 주소를 통해 데이터 리턴
private func getCoinImage() {
        if let image = fileManager.getImage(imageName: coin.id, folderName: folderName) {
            self.image = image
            print("Retrieved image from File Manager")
        } else {
            downloadCoinImage()
            print("Downloading image now")
        }
    }
  • 뷰 모델 이니셜라이즈될 때 사용되는 함수
  • 다운로드된 이미지가 있다면 파일 매니저의 함수를, 그렇지 않다면 네트워크를 통해 비동기 데이터 다운로드

소스 코드

import Foundation
import SwiftUI

class LocalFileManager {
    static let shared = LocalFileManager()
    private init() {}
    
    private func getURLForFolder(folderName: String) -> URL? {
        guard let url = FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first else { return nil }
        return url.appendingPathComponent(folderName)
    }
    
    private func createFolderIfNeeded(folderName: String) {
        guard let url = getURLForFolder(folderName: folderName) else { return }
        if !FileManager.default.fileExists(atPath: url.path) {
            do {
                try FileManager.default.createDirectory(at: url, withIntermediateDirectories: true, attributes: nil)
            } catch {
                print("Error creating folder")
                print(error.localizedDescription)
            }
        }
    }
    
    private func getURLForImage(imageName: String, folderName: String) -> URL? {
        guard let folderURL = getURLForFolder(folderName: folderName) else { return nil }
        return folderURL.appendingPathComponent(imageName + ".png")
    }
    
    func saveImage(image: UIImage, imageName: String, folderName: String) {
        // create folder
        createFolderIfNeeded(folderName: folderName)
        // get path for image
        guard
            let url = getURLForImage(imageName: imageName, folderName: folderName),
            let data = image.pngData() else { return }
        // save image to path
        do {
            try data.write(to: url)
        } catch {
            print("Error saving image")
            print(error.localizedDescription)
        }
    }
    
    func getImage(imageName: String, folderName: String) -> UIImage? {
        guard
            let url = getURLForImage(imageName: imageName, folderName: folderName),
            FileManager.default.fileExists(atPath: url.path) else { return nil }
        return UIImage(contentsOfFile: url.path)
    }
}
  • 파일 매니저 서비스 클래스 싱글턴 패턴 구현
  • 모든 종류의 데이터를 파일 내에 지속적인 바이너리로 가지고 있어야 할 때 해당 싱글턴 사용 가능
  • 특정 id에 따른 폴더, 파일이 존재하는지에 따라 URL을 리턴 가능
import Foundation
import SwiftUI
import Combine

class CoinImageService {
    @Published var image: UIImage? = nil
    private var imageSubscription: AnyCancellable?
    private let coin: CoinModel
    private let fileManager = LocalFileManager.shared
    private let folderName = "coin_images"
    init(coin: CoinModel) {
        self.coin = coin
        getCoinImage()
    }
    
    private func getCoinImage() {
        if let image = fileManager.getImage(imageName: coin.id, folderName: folderName) {
            self.image = image
            print("Retrieved image from File Manager")
        } else {
            downloadCoinImage()
            print("Downloading image now")
        }
    }
    
    private func downloadCoinImage() {
        guard let url = URL(string: coin.image) else { return }
        imageSubscription = NetworkingManager
            .download(with: url)
            .tryMap({ data -> UIImage? in
                return UIImage(data: data)
            })
            .sink(receiveCompletion: NetworkingManager.handleCompletion,
                  receiveValue: { [weak self] image in
                if
                    let folderName = self?.folderName,
                    let coin = self?.coin,
                    let image = image {
                    self?.image = image
                    self?.fileManager.saveImage(image: image, imageName: coin.id, folderName: folderName)
                    self?.imageSubscription?.cancel()
                }
            })
    }
}
  • 최초 다운로드 파일이라면 다운로드받은 뒤 sink 단에서 파일 매니저 서비스를 통해 해당 데이터를 저장

구현 화면

반복적으로 사용할 데이터가 아니라면 파일 매니저보다는 NSCache를 사용하는 게 보다 이롭다!

profile
JUST DO IT
post-custom-banner

0개의 댓글