[UIKit] NetflixClone: New And Hot Controller

Junyoung Park·2022년 11월 1일
0

UIKit

목록 보기
71/142
post-thumbnail

Building Netflix App in Swift 5 and UIKit - (Xcode 13, 2021) - Episode 8 - Upcoming Controller

NetflixClone: New And Hot Controller

구현 목표

  • API 데이터 패치를 통한 테이블 뷰 UI 구현

구현 태스크

  • 뷰 모델을 통한 데이터 패치
  • 커스텀 테이블 뷰 셀을 통한 UI 배치

핵심 코드

import Foundation
import Combine

class NewAndHotViewModel {
    let upcomingMovieModels: CurrentValueSubject<[ContentModel], Never> = .init([])
    private var cancellables = Set<AnyCancellable>()
    
    init() {
        addSubscription()
    }
    
    private func addSubscription() {
        APICaller
            .shared
            .getUpcomingContents(with: .movie)
            .sink { completion in
                switch completion {
                case .failure(let error): print(error.localizedDescription)
                case .finished: break
                }
            } receiveValue: { [weak self] movieModels in
                self?.upcomingMovieModels.send(movieModels)
            }
            .store(in: &cancellables)
    }
}
  • API 함수를 통한 upcomingMovieModels를 패치 및 뷰 컨트롤러가 구독 가능한 데이터 퍼블리셔를 발행
 func configure(with model: ContentModel) {
        if let title = model.title {
            nameLabel.text = title
        } else if let anotherTitle = model.original_title {
            nameLabel.text = anotherTitle
        } else {
            nameLabel.text = "Default Title"
        }
        overviewLabel.text = model.overview
        
        guard
            let imageURLString = model.poster_path,
            let imageURL = URL(string: "\(TMDBConstants.imagePath.rawValue)\(imageURLString)") else { return }
        let publisher: AnyPublisher<UIImage?, Never> = URLSession
            .shared
            .dataTaskPublisher(for: imageURL)
            .compactMap({$0.data})
            .compactMap({UIImage(data: $0)})
            .replaceError(with: UIImage(systemName: "video"))
            .receive(on: DispatchQueue.main)
            .eraseToAnyPublisher()
        var cancellables: AnyCancellable?
        cancellables = publisher
            .sink(receiveCompletion: { _ in
                cancellables?.cancel()
            }, receiveValue: { [weak self] image in
                self?.contentImageView.image = image
            })
    }
  • 컨텐츠 모델을 파라미터로 받고 UI를 그리는 커스텀 테이블 뷰 셀의 configure 함수 구현
  • 텍스트 정보 입력 및 이미지 비동기 패치를 위한 컴바인 사용한 one-shot 퍼블리셔 사용
 private func bind() {
        viewModel
            .upcomingMovieModels
            .receive(on: DispatchQueue.main)
            .sink { [weak self] movieModels in
                self?.tableView.reloadData()
            }
            .store(in: &cancellabels)
    }
  • 뷰 컨트롤러의 viewDidLoaded() 메소드 내부에서 해당 바인드 함수 선언
  • 뷰 모델의 데이터 퍼블리셔를 구독, 테이블 뷰가 데이터 소스로 사용할 수 있도록 로드 함수 사용
extension NewAndHotViewController: UITableViewDataSource {
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: NewAndHotTableViewCell.identifier, for: indexPath) as? NewAndHotTableViewCell else {
            return UITableViewCell()
        }
        let model = viewModel.upcomingMovieModels.value[indexPath.row]
        cell.configure(with: model)
        return cell
    }
    
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return viewModel.upcomingMovieModels.value.count
    }
}
  • 커스텀 셀을 사용할 수 있도록 guard let 바인딩
  • 뷰 모델의 데이터 퍼블리셔 카운트를 통한 셀 그리기

구현 화면

profile
JUST DO IT

0개의 댓글