Moya, Kingfisher, Codable을 이용하여 Movie API를 띄워보자
영화 API를 이용하여 네트워킹을 거쳐 iOS에 띄워보도록 하자. API는 아래의 사이트에서 회원가입을 하여 key를 발급 받으면 무료로 이용이 가능하다. https://developers.themoviedb.org/4/getting-started/authorization
프로젝트에 사용될 기술은 Moya, Kingfisher, Codable, tableView 내용을 담고 있다.
First (ViewController.swift)
받아올 영화 정보들을 띄워줄 tableView를 생성하자.
import UIKit
import Kingfisher
import FloatRatingView
class ViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
var movies = [Movie]()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
self.tableView.dataSource = self
self.tableView.delegate = self
getData()
}
func getData() {
MovieNetworkManager.getMovieData(target: .nowPlaying) { movies in
self.movies = movies
OperationQueue.main.addOperation {
self.tableView.reloadData()
}
}
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let detailVC = segue.destination as? DetailViewController {
let index = tableView.indexPathForSelectedRow
detailVC.moviesItem = movies[index!.row]
}
}
}
extension ViewController: UITableViewDataSource, UITableViewDelegate {
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return movies.count
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let basicCell = tableView.dequeueReusableCell(withIdentifier: "basicCell", for: indexPath) as! MovieTableViewCell
let movieList = movies[indexPath.row]
basicCell.titleLabel.text = movieList.title
basicCell.overviewLabel.text = movieList.overview
// poster 이미지 넣기
basicCell.posterImg.kf.setImage(with: URL(string: "https://image.tmdb.org/t/p/w500\(movieList.poster_path!)"))
basicCell.dateLabel.text = "개봉일 : " + movieList.release_date!
// rating library
basicCell.voteView.rating = Double(movieList.vote_average! / 2)
return basicCell
}
}
처음 부터 차근차근 보도록 하자. Kingfisher, FloatingRatingView는 Cocoapods를 통해 가져왔다. Kingfisher는 url을 받아오면 자동으로 이미지로 해석하여 띄워주는 오픈소스 라이브러리이다. FloatingRatingView는 영화 평점을 나타내어주기 위하여 가져왔다.
UIViewController 부분은 우선 Moya가 들어가기에 넘기고 TableView 정의 부분을 살펴보자.
TableView는 이렇게 extension을 통해 작성하는데, UITableViewDatasource와 UITableViewDelegate가 필요하다. 그런 다음 func으로 numberOfSections (Section의 수), numberOfRowsInSection(한 섹션에 들어갈 row의 수, 객체의 수 만큼), cellForRowAt(cell을 재사용을 하기 위해서 정의하고, 하나의 셀에 어떠한 정보가 들어가는지 정의를 해준다). 이렇게 TableView 생성을 위해서 총 3가지가 필수로 들어가야 한다.
가장 아래 cellForRowAt을 살펴보자면 여기서 kingfisher 라이브러리를 사용하였다. (영화의 이미지를 나타내기 위해)
basicCell.posterImg.kf.setImage(with: URL(string: "https://image.tmdb.org/t/p/w500\(movieList.poster_path!)"))
사용 법은 kf.setImage를 작성하고 안에는 URL을 적어주면 된다.
Second (Movie.swift)
import Foundation
import simd
struct Movie: Codable {
var id: Int?
var overview: String?
var poster_path: String?
var release_date: String?
var title: String?
var vote_average: Float?
enum CodingKeys: String, CodingKey {
case id
case overview
case poster_path
case release_date
case title
case vote_average
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
id = try values.decode(Int.self, forKey: .id)
title = try values.decode(String.self, forKey: .title)
overview = try values.decode(String.self, forKey: .overview)
poster_path = try values.decode(String.self, forKey: .poster_path)
release_date = try values.decode(String.self, forKey: .release_date)
vote_average = try values.decode(Float.self, forKey: .vote_average)
}
}
struct MovieDataStore: Codable {
var results: [Movie]
}
Codable을 이용하여 JSON 형식의 데이터를 파싱하기 위한 준비를 한다. 우선 내가 사용할 id, overview, …. 등을 정의하고 enum 타입으로 CodingKeys를 열겨해준다. 그런 다음 Decoder를 정의 해주는데, 어떠한 키에서 받아서 변수에 할당을 해줄 것인지를 적으면 된다. 마지막으로 MovieDataStore에 결과를 저장한다.
Codable 프로토콜은 간편하게 JSON 데이터 파싱을 할 수 있어서 매우 유용하다. 설명을 조금 더 하자면, 첫번째 파라미터는 Decoding할 타입을 정의한다. 여기서 Codable인지 Decodable인지 정의를 해줘야한다. 그 다음 파라미터에는 JSON 파일에 저장되어 있는 데이터를 전달하는 과정이다.
Third (MovieAPI.swift)
네트워킹을 위해 Moya를 이용할 것이다. 전체 코드는 아래와 같다.
import Foundation
import Moya
enum MovieAPI {
case nowPlaying
case detail(id: Int)
}
extension MovieAPI: TargetType {
var baseURL: URL {
guard let url = URL(string: "https://api.themoviedb.org/3/movie") else {
fatalError("url error!")
}
return url
}
var path: String {
switch self {
case .nowPlaying:
return "/now_playing"
case .detail(let id):
return "/\(id)"
}
}
var method: Moya.Method {
switch self {
case .nowPlaying, .detail(_):
return .get
}
}
var task: Task {
switch self {
case .nowPlaying:
return .requestParameters(parameters: ["api_key": "자신의 api key"], encoding: URLEncoding.queryString)
case .detail(let id):
return .requestParameters(parameters: ["api_key": "자신의 api key", "movie_id" : id], encoding: URLEncoding.queryString)
}
}
var headers: [String : String]? {
return ["Content-Type": "application/json"]
}
}
이전 포스팅에서 Moya를 정리한 적이 있기에 코드를 해석하는 식으로 정리해 보겠다. baseURL에 endpoint를 작성하여 주고 path에 nowPlaying일 시의 경로와 detail일 시의 경로를 작성한다. method는 데이터를 받아오는 것이므로 get이라고 적어주었고 task에는 nowPlaying시에는 파라미터가 없기에 api key만 작성을 하였고, detail에는 객체의 id가 필요하기에 함께 적어준다. 마지막으로 header는 기본적으로 위의 property들을 정의한다.
이렇게 Moya를 통해 네트워킹의 준비를 마쳤으면 다음 과정으로 이동하자.
Fourth (MovieNetworkManager)
import Foundation
import Moya
class MovieNetworkManager {
static let provider = MoyaProvider<MovieAPI>()
static func getMovieData(target: MovieAPI, completion: @escaping([Movie]) -> ()) {
provider.request(target) { result in
switch result {
case .failure(let err):
print(err.localizedDescription)
break
case .success(let res):
// data parsing
do {
let movieData = try JSONDecoder().decode(MovieDataStore.self, from: res.data)
completion(movieData.results)
} catch let err {
print(err.localizedDescription)
return
}
}
}
}
}
MoyaProvider를 통해 이전에서 정의한 MovieAPI를 가져온다. 그런 다음 getMovieData라는 함수를 정의하여 request를 보내어 성공하면 JSONDeoder를 통해 movieData를 받아온다.
마지막으로 처음 ViewController.swift에 Movie struct를 불러와서 각각의 요소에 연결을 시켜 화면에 띄워주면 된다.
댓글남기기