[UIKit] NetflixClone: Networking

Junyoung Park·2022년 10월 30일
0

UIKit

목록 보기
68/142
post-thumbnail

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

NetflixClone: Networking

구현 목표

  • API를 통해 데이터를 받아와 UI 패치

구현 태스크

  • 컴플리션 핸들러를 통한 비동기 데이터 패치를 컴바인 형식으로 전환
  • JSON 파싱을 위한 Codable 프로토콜 구조체 구현

핵심 코드

func getTrendingMovies() -> AnyPublisher<[MovieModel], Error> {
        guard let url = URL(string: "\(TMDBConstants.baseURL.rawValue)/3/trending/all/day?api_key=\(TMDBConstants.API_KEY.rawValue)") else { return Fail(error: URLError(.badURL)).eraseToAnyPublisher() }
        return URLSession.shared.dataTaskPublisher(for: url)
            .receive(on: DispatchQueue.global(qos: .background))
            .compactMap({$0.data})
            .decode(type: TrendingMoviesResponse.self, decoder: JSONDecoder())
            .map{$0.results}
            .eraseToAnyPublisher()
    }
  • TMDB API를 통해 트렌드 영화 데이터를 리턴하는 APICaller 함수
struct TrendingMoviesResponse: Codable {
    let results: [MovieModel]
}

import Foundation

struct MovieModel: Codable {
    let adult: Bool
    let overview: String
    let release_date: String?
    let id: Int
    let genre_ids: [Int]
    let original_title: String?
    let original_language: String?
    let title: String?
    let vote_count: Int
    let vote_average: Double
    let popularity: Double
    let poster_path: String?
}
  • JSON 데이터를 디코딩해서 표현한 Codable 프로토콜 양식의 구조체
private func addSubcription() {
        APICaller
            .shared
            .getTrendingMovies()
            .sink { completion in
                switch completion {
                case.finished: break
                case .failure(let error): print(error.localizedDescription)
                }
            } receiveValue: { [weak self] movieModels in
                self?.movieModels.send(movieModels)
            }
            .store(in: &cancellabels)
    }
  • 홈 뷰에서 사용하는 뷰 모델이 이니셜라이즈될 때 선언되는 함수
  • API를 통해 트렌드 영화 데이터를 리턴
func configure(with model: MovieModel) {
        if let title = model.title {
            nameLabel.text = title
        } else if let anotherTitle = model.original_title {
            nameLabel.text = anotherTitle
        } else {
            nameLabel.text = "Default Title"
        }
        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?.imageView.image = image
            })
    }
  • 홈 뷰 컨트롤러 → 테이블 뷰의 단일 셀 → 컬렉션 뷰의 셀 순서대로 데이터 바인딩
  • MovieModel 데이터의 타이틀과 이미지를 통해 셀 UI 표현

구현 화면

기존의 Asset에 추가된 이미지를 사용하던 바와 달리 헤더 타이블 뷰에 사용되는 이미지 또한 API를 통해 리턴받은 영화 데이터 중 랜덤으로 택함

profile
JUST DO IT

0개의 댓글