iOS) 회원 사진 불러오기 Firebase Storage

2021. 7. 28. 22:11iOS

 

이전글 https://codecrafting.tistory.com/46 에서 이어지는 글이다 사진 라이브러리 이용하는 법을 참고하고 싶다면 읽고오기 바람!

 

iOS) 포토 라이브러리에서 사진 가져오기

보통 앱에서 회원가입을 할때 프로필 사진용으로 사진업로드 기능을 지원한다. 이 포스팅에서는 포토 라이브러리에서 사진을 가져오거나, 카메라를 사용하여 직접 사진을 올리는 방법에 대해

codecrafting.tistory.com

 

 

 

오늘 해볼것은 사진 라이브러리에서 사진을 선택했다면 이것을 프로필 사진에 띄우는 것이다.

이것을 응용하면 앞으로 구현할 채팅어플에서 대화할 대상을 테이블로 표시할 때 이름 옆에 회원의 프로필 사진을 띄울 수 있다.

 

 

로직을 구성해보자

 

1. 회원가입할 때 사진데이터를 Firebase Storage에 업로드한다. 이때 회원이메일+사진제목의 형식으로 포맷한다.

2. 프로필 창에 이미지 뷰를 띄울 레이아웃을 추가한다.

3. 현재 로그인한 유저의 이메일로 사진을 다운로드하여 이미지뷰에 띄운다.

 

 

회원가입시 사진을 Firebaes Storage에 추가하는 것은 이전 포스팅 https://codecrafting.tistory.com/47 에서 다룬 내용이다. 본 내용이 이해가 가지않으면 보고오길 강력추천함

 

남은것은 프로필 창에 레이아웃을 추가하고 프로필 탭으로 이동했을 때 유저 사진을 가져오기만 하면된다.

 

인터페이스 빌더 상황

 

Profile 관련 VC는 ProfileViewController에 구현되어 있다.

 

먼저 인터페이스 빌더에 있는 테이블뷰를 코드에 추가한다.

class ProfileViewController: UIViewController {
    
    @IBOutlet var tableView: UITableView!

}

 

그리고 createTableHeader() 라는 메소드를 만들어 여기에서 레이아웃을 만들어줄거다.

override func viewDidLoad() {
    super.viewDidLoad()
    tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
    tableView.delegate = self
    tableView.dataSource = self
    tableView.tableHeaderView = createTableHeader()
}

 

func createTableHeader() -> UIView? {
    guard let email = UserDefaults.standard.value(forKey: "email") as? String else {
        return nil
    }
    let safeEmail = DatabaseManager.safeEmail(emailAddress: email)
    let filename = safeEmail + "_profile_picture.png"
    print(filename)
    let path = "images/" + filename
    
    let headerView = UIView(frame: CGRect(x: 0,
                                    y: 0,
                                    width: self.view.width,
                                    height: 300))
    headerView.backgroundColor = .link
    
    let imageView = UIImageView(frame: CGRect(x: (headerView.width-150) / 2,
                                              y: 75,
                                              width: 150,
                                              height: 150))
    
    imageView.contentMode = .scaleAspectFill
    imageView.backgroundColor = .white
    imageView.layer.borderColor = UIColor.white.cgColor
    imageView.layer.borderWidth = 3
    imageView.layer.cornerRadius = imageView.width/2
    imageView.layer.masksToBounds = true
    headerView.addSubview(imageView)
    
    StorageManager.shared.downloadURL(for: path) { [weak self] result in
        switch result {
        case .success(let url):
            self?.downloadImage(imageView: imageView, url: url)
        case .failure(let error):
            print("Failed to get download url:\(error)")
        }
    }
    return headerView
}

제일 먼저 로그인 할 때 UserDefaults에 저장했던 이메일을 꺼내온다. 

그리고 safeEmail 로 이메일 형식을 바꾸어 주는데, Firebase Storage나 Firebase Database 에서는 "@"이나 "." 같은 문자를 허용하지 않기때문에 전부 "-"로 replace하고 거기에 profile_picture.png 문자열을 추가하여 이미지를 업로드했다. 

 

images 폴더 밑에
이렇게 추가했다

그래서 Firebase Storage에서 자원을 다운로드 할 때 path를 "images/"+"safeEmail"+"profile_picture"로 한다.

 

그리고 이미지 뷰가 들어갈 레이아웃을 설정해주는데, 코드를 보면 알겠지만 먼저 headerView라는 이름으로 CGRect 범위를 잡아주고 그 위에 image 뷰를 add 해주는 형식으로 구현했다.

 

그리고 나머지는 이제 확보한 path를 바탕으로 URL을 다운로드 해주면 되는데, 예전에 만들었던 Storage 모듈에 관련된 작업을 하는 함수를 추가해 두었다. StorageManager에 만들어둔 downloadURL 함수 구현부를 보면..

public enum StorageErrors: Error {
    case failedToUpload
    case failedToGetDownloadUrl
}

public func downloadURL(for path: String, completion: @escaping (Result<URL, Error>) -> Void) {
    let reference = storage.child(path)
    reference.downloadURL { url, error in
        guard let url = url, error == nil else {
            completion(.failure(StorageErrors.failedToGetDownloadUrl))
            return
        }
        completion(.success(url))
    }
}

path를 인자로 받아서 Result<URL, Error> 의 completion handler을 콜해준다.

Firebase Storage에서 URL을 다운로드 하는 api는 콜백으로 성공시 url 혹은 error을 전달해주는데 url을 성공적으로 전달받았으면 completion에 url을 담고, 아니면 Error을 담으면 된다.

 

 

다시 코드로 와서 해당 부분을 보면

StorageManager.shared.downloadURL(for: path) { [weak self] result in
    switch result {
    case .success(let url):
        self?.downloadImage(imageView: imageView, url: url)
    case .failure(let error):
        print("Failed to get download url:\(error)")
    }
}

url을 성공적으로 받아왔을 시 downloadImage(imageView:url:)을 호출한다. 여기에서는 만들어둔 imageView 자리에 이미지를 삽입하는 함수부다.

 

func downloadImage(imageView: UIImageView, url: URL) {
    URLSession.shared.dataTask(with: url) { data, _, error in
        guard let data = data, error == nil else {
            return
        }
        DispatchQueue.main.async {
            let image = UIImage(data: data)
            imageView.image = image
        }
    }.resume()
}

여기에서는 url과 관련된 작업을 하니 URLSession dataTask를 통해 받아와주고, UI 관련 작업을 해야 하니 DispatchQueue 비동기로 해당 이미지뷰에 다운로드한 이미지를 삽입하면된다.

 

모든 작업이 끝났으면 설정한 imageView를 리턴하여 viewDidLoad() 에서 프로필 탭 진입시 사진 다운로드와 이미지 띄우기를 자동으로 하게끔 하면 된다.