적당한 고통은 희열이다

- 댄 브라운 '다빈치 코드' 중에서

Swift iOS 앱 개발/실전 Swift

[실전] Firebase Storage 활용한 Test App 구현

hongssup_ 2020. 12. 29. 18:16
반응형

다운받은 MaterialComponent kit를 활용하여 테스트 어플리케이션을 구현해보고자 한다.

나는 MDCDragon 내 CollectionsSimpleSwiftDemo 파일을 활용하여 프로젝트의 코어 시스템을 구현해보겠다.

아래 CollectionView를 활용한 데모 코드를 복사해와 Firebase 항목들을 추가해나가겠다.

import UIKit
import MaterialComponents.MaterialCollections

class CollectionsSimpleSwiftDemo: MDCCollectionViewController {

  let reusableIdentifierItem = "itemCellIdentifier"
  let colors = [ "red", "blue", "green", "black", "yellow", "purple" ]

  override func viewDidLoad() {
    super.viewDidLoad()
    // Register cell class.
    self.collectionView?.register(MDCCollectionViewTextCell.self,
                                       forCellWithReuseIdentifier: reusableIdentifierItem)
    // Customize collection view settings.
    self.styler.cellStyle = .card
  }

  // MARK: UICollectionViewDataSource
  override func collectionView(_ collectionView: UICollectionView,
                               numberOfItemsInSection section: Int) -> Int {
    return colors.count
  }
  override func collectionView(_ collectionView: UICollectionView,
                               cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCell(withReuseIdentifier: reusableIdentifierItem,
                                                                     for: indexPath)
    if let cell = cell as? MDCCollectionViewTextCell {
      cell.textLabel?.text = colors[indexPath.item]
    }
    return cell
  }
}

// MARK: Catalog by convention
extension CollectionsSimpleSwiftDemo {
  @objc class func catalogMetadata() -> [String: Any] {
    return [
      "breadcrumbs": [ "Collections", "Simple Swift Demo"],
      "primaryDemo": false,
      "presentable": false,
    ]
  }
}

 

Firebase Storage 연동하기

이번 프로젝트에서는 master님이 기존에 만들어 놓으신 firebase 프로젝트에서 스토리지를 받아와야 한다. 

 Firebase 구성 파일 다운로드 하는 법 : 

Firebase 스토리지와 연동해주기 위해서는 Firebase 해당 프로젝트의 설정 - 프로젝트 설정 - 하단의 해당 iOS앱의 GoogleService-Info.plist 파일을 다운받아 xcode 폴더 안에 넣어준다. 

그리고 프로젝트 General - Bundle Identifier 번들 ID 넣어준다.

(Firebase 연동은 Tutorial 5 참고)

 

Firebase Storage 사용법

podfile 열어 pod 'Firebase/Storage' 추가해주고 % pod install

import Firebase 해주고

application:didFinishLaunchingWithOptions: 메서드 내에 FirebaseApp.configure() 선언해준다. 

뷰컨트롤러에 let ref = Storage.storage().reference().child("storage/폴더이름")

으로 폴더 경로를 설정해준다.

 

Storage에서 리스트 받아오기

파일 경로를 설정해 주었다면, 해당 폴더로부터 listAll(completion:) 메서드를 통해 내부 리스트를 읽어들일 수 있다.

먼저, 클라스 내에 var files: [String] = [] 로 빈 Array를 생성해주고, //1

viewDidLoad() 안에서 listAll() 메서드를 통해 스토리지의 파일들을 prefixes로 받아와 폴더 이름들을 files 리스트에 추가해준다. //2

이 메서드는 *completion handler 이기 때문에, 클라스 내 다른 네트워크들이 끝나면 비동기로 실행이 된다. 

따라서 collectionView에 다시 reload를 해 주어야 결과값이 잘 출력된다. //3

 

import UIKit
import MaterialComponents.MaterialCollections
import Firebase

class ListController: MDCCollectionViewController {
    
    let reusableIdentifierItem = "itemCellIdentifier"
    let ref = Storage.storage().reference().child("storage/v1/asset")
	
    // 1. Storage에서 리스트를 받아올 빈 array 생성
    var files: [String] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()
        self.collectionView?.register(MDCCollectionViewTextCell.self,
                                      forCellWithReuseIdentifier: reusableIdentifierItem)
        self.styler.cellStyle = .card
        
        // 2. listAll()메서드를 통해 폴더 이름을 받아와 array에 추가해주기
        ref.listAll (completion: { (result, error) in
            for item in result.prefixes {
                print(item.name)
                self.files.append(item.name)
            }
            // 3. 리스트에 잘 담겼는지 확인 후 collectionView에 reload 해주기
            print(self.files)
            self.collectionView.reloadData()
        })
    }
// ...

 

UICollectionView Cell Touch 컬렉션뷰 셀 터치 이벤트 추가

폴더 리스트를 CollectionView cell로 보여주는 것에 성공했다. 

그렇다면 해당 cell을 터치했을 때, 해당 폴더 안의 파일들을 리스트로 출력해보자. 

collectionView에서 셀 터치 이벤트를 받아오는 메서드는 

func collectionView(collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath

셀을 터치 했을 때 실행하고자 하는 코드를 이 메서드 안에 작성해주면 된다. 

해당 셀에 적힌 폴더 이름은 files[indexPath.item] 로 받아올 수 있기 때문에, 이 폴더 속의 파일 목록들을 가져오기 위해서 우선 새로운 path를 설정해준다. let listRef = ref.child(files[indexPath.item])

그런 다음 앞서 사용했던 listAll() 메서드를 활용하여, 파일 이름들을 받아온다.  

*listAll( ) 메서드로 폴더들의 이름을 받아올 땐 'prefixes', 파일들의 이름을 받아올 땐 'items'를 사용해야 한다. 

override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    print(files[indexPath.item]) 	//터치한 셀의 폴더 이름
    let listRef = ref.child(files[indexPath.item])
    listRef.listAll (completion: { (result, error) in
        for i in result.items {
            print(i.name) 	//폴더 내 파일들의 이름                
        }
}    

 

Storage로부터 파일 다운로드

스토리지로부터 파일을 다운로드하는 방식에는 세 가지가 있다.

1) 메모리로 다운로드

2) 로컬 파일로 직접 다운로드 

3) 다운로드 URL 생성 (URL에 기초한 다운로드 인프라를 갖추고 있는 경우)

파일을 계속 저장을 해두기 보다는 그냥 빠르게 다운로드해서 보고 말 거면 메모리에 다운로드를 하는 방법이 맞겠지만,

나는 로컬 기기로 직접 다운로드하여 저장해두는 방식으로 구현할거라 로컬 파일로 다운로드 하는 방식으로 만들어보겠다. 

*이들의 차이에 대해 더 자세히 알고 싶다면 기억장치 및 메모리 계층구조에 대해 더 알아보는 것이 좋다. 

위에서 가져온 파일 이름들 중에서 "DATA"가 들어가는 파일을 골라 다운로드를 진행하는 코드를 작성해보겠다. 

파일 이름들을 읽어오고 for 구문이 종료되어도 해당 파일의 경로를 저장해두기 위해서

listAll() 메서드 내에 StorageReference 형식의 변수를 하나 설정해준다. 

 

override func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
    print(files[indexPath.item])
    let listRef = ref.child(files[indexPath.item])
    listRef.listAll (completion: { (result, error) in
        var myItemRef:StorageReference?
        for i in result.items {
        if i.name.contains("DATA") == true {
                //print(i.name)
                myItemRef = i
            }
            //print(myItem?.name)
            //print(i.name)            
        }
        print(myItemRef)
        // myItemRef null인지 아닌지 확인
        guard let myItemref = myItemRef else { return }
        // 로컬 파일로 다운로드
        let localURL = FileManager.default.temporaryDirectory.appendingPathComponent("storage")
        let downloadData = myItemref.write(toFile: localURL) { url, error in
            if let error = error {
                print("Download Fail \(error)")
            } else {
                print("Download Clear")
            }
        }
}
728x90
반응형