[SwiftUI] CryptoApp: FileManager

Junyoung Park·2022년 11월 3일
0

SwiftUI

목록 보기
78/136
post-thumbnail

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

1개의 댓글

comment-user-thumbnail
2025년 8월 21일

May be this app is great, by when it comes to file manager, Cx file explorer comes to my mind at the earliest due to its simplicity and convenientness. go and try https://cxfileexplorerr.com

답글 달기