import UIKit
// 영화 제목 데이터 배열
let name = ["범죄도시4","쿵푸팬더4", "스턴트맨", "포켓몬스터", "남은 인생 10년", "파묘", "극장판 실바니안 패밀리", "꼬마참새 리차드", "챌린져스", "고스트버스터즈"]
// 영화 데이터 구조체
struct MovieData : Codable {
let boxOfficeResult : BoxOfficeResult
}
// 박스오피스 결과 데이터 구조체
struct BoxOfficeResult : Codable {
let dailyBoxOfficeList : [DailyBoxOfficeList]
}
// 일일 박스오피스 데이터 구조체
struct DailyBoxOfficeList : Codable {
let movieNm : String // 영화 제목
let audiCnt : String // 당일 관객수
let audiAcc : String // 누적 관객수
let rank : String // 순위
}
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
@IBOutlet weak var table: UITableView!
var movieData : MovieData? // 영화 데이터
// 영화 API URL
var movieURL = "https://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json?key=키&targetDt="
override func viewDidLoad() {
super.viewDidLoad()
table.dataSource = self
table.delegate = self
movieURL += makeYesterdayString() // 어제 날짜 문자열 생성하여 URL에 추가
getData() // 데이터 가져오기
}
// 어제 날짜 문자열 생성
func makeYesterdayString() -> String {
guard let yesterday = Calendar.current.date(byAdding: .day, value: -1, to: Date()) else {
return ""
}
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyyMMdd"
return dateFormatter.string(from: yesterday)
}
// 영화 데이터 가져오기
func getData() {
guard let url = URL(string: movieURL) else { return }
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { data, response, error in
if error != nil { print(error!); return }
guard let jsonData = data else { return }
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode(MovieData.self, from: jsonData)
self.movieData = decodedData
DispatchQueue.main.async {
self.table.reloadData()
}
} catch {
print(error)
}
}
task.resume()
}
// 테이블 뷰 데이터 소스 메서드
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 10 // 고정된 10개의 행 반환
}
// 테이블 뷰 셀 구성
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! MyTableViewCell
guard let movie = movieData?.boxOfficeResult.dailyBoxOfficeList[indexPath.row] else { return UITableViewCell() }
guard let rank = movieData?.boxOfficeResult.dailyBoxOfficeList[indexPath.row].rank else { return UITableViewCell() }
// 셀에 영화 정보 표시
cell.movieName.text = "[\(rank)위] \(movie.movieNm)"
// 당일 관객수 표시
if let aCnt = movieData?.boxOfficeResult.dailyBoxOfficeList[indexPath.row].audiCnt {
let numF = NumberFormatter()
numF.numberStyle = .decimal
let aCount = Int(aCnt)!
let result = numF.string(for: aCount)! + "명"
cell.audiCount.text = "어제 : \(result)"
}
// 누적 관객수 표시
if let aAcc = movieData?.boxOfficeResult.dailyBoxOfficeList[indexPath.row].audiAcc {
let numF = NumberFormatter()
numF.numberStyle = .decimal
let aAccu = Int(aAcc)!
let result = numF.string(for: aAccu)! + "명"
cell.audiAccumulate.text = "누적 : \(result)"
}
return cell
}
// 테이블 뷰 섹션 헤더 제목 설정
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "🍿박스오피스(영화진흥위원회제공:" + makeYesterdayString() + ")🍿"
}
// 테이블 뷰 섹션 푸터 제목 설정
func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
return "🙏 by MegaMaker 🙏 문의는 DM으로"
}
// 테이블 뷰 섹션 수 설정
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
// 테이블 뷰 셀 선택 시 동작
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// 선택된 셀 인덱스 로그 출력
}
// 화면 전환 시 데이터 전달
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let dest = segue.destination as! DetailViewController
let myIndexPath = table.indexPathForSelectedRow!
let row = myIndexPath.row
dest.movieName = (movieData?.boxOfficeResult.dailyBoxOfficeList[row].movieNm)!
}
}
ㄴ 작성했던 코드 분석
import UIKit
// 영화 정보를 담는 모델
struct Movie {
let title: String
let rank: String
let audiCnt: String
let audiAcc: String
}
// 프레젠터 프로토콜
protocol MoviePresenterProtocol {
func fetchMovies()
func numberOfMovies() -> Int
func movie(at index: Int) -> Movie?
}
// 뷰 컨트롤러
class ViewController: UIViewController {
@IBOutlet weak var tableView: UITableView!
var presenter: MoviePresenterProtocol!
override func viewDidLoad() {
super.viewDidLoad()
presenter = MoviePresenter(view: self)
presenter.fetchMovies()
tableView.dataSource = self
tableView.delegate = self
}
}
// 프레젠터
class MoviePresenter: MoviePresenterProtocol {
weak var view: ViewController?
var movies: [Movie] = []
init(view: ViewController) {
self.view = view
}
func fetchMovies() {
let movieService = MovieService()
movieService.fetchMovies { [weak self] result in
switch result {
case .success(let movies):
self?.movies = movies
self?.view?.reloadData()
case .failure(let error):
print("Error fetching movies: \(error)")
}
}
}
func numberOfMovies() -> Int {
return movies.count
}
func movie(at index: Int) -> Movie? {
guard index >= 0 && index < movies.count else { return nil }
return movies[index]
}
}
// 뷰 컨트롤러 확장 - 뷰와 관련된 메서드 구현
extension ViewController: UITableViewDataSource, UITableViewDelegate {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return presenter.numberOfMovies()
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! MyTableViewCell
if let movie = presenter.movie(at: indexPath.row) {
cell.movieName.text = "[\(movie.rank)위] \(movie.title)"
cell.audiCount.text = "어제: \(movie.audiCnt)명"
cell.audiAccumulate.text = "누적: \(movie.audiAcc)명"
}
return cell
}
}
// 영화 서비스 - 네트워크 통신을 담당
class MovieService {
func fetchMovies(completion: @escaping (Result<[Movie], Error>) -> Void) {
let movieURL = "https://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json?key=키&targetDt="
guard let url = URL(string: movieURL + makeYesterdayString()) else {
completion(.failure(NSError(domain: "Invalid URL", code: -1, userInfo: nil)))
return
}
URLSession.shared.dataTask(with: url) { data, _, error in
if let error = error {
completion(.failure(error))
return
}
guard let data = data else {
completion(.failure(NSError(domain: "No data", code: -1, userInfo: nil)))
return
}
do {
let decoder = JSONDecoder()
let movieData = try decoder.decode(MovieData.self, from: data)
let movies = movieData.boxOfficeResult.dailyBoxOfficeList.map {
Movie(title: $0.movieNm, rank: $0.rank, audiCnt: $0.audiCnt, audiAcc: $0.audiAcc)
}
completion(.success(movies))
} catch {
completion(.failure(error))
}
}.resume()
}
private func makeYesterdayString() -> String {
let yesterday = Calendar.current.date(byAdding: .day, value: -1, to: Date()) ?? Date()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyyMMdd"
return dateFormatter.string(from: yesterday)
}
}
ㄴ MVP로 변경한 코드
import UIKit
// 영화 정보 구조체들
struct MovieData: Codable {
let boxOfficeResult: BoxOfficeResult
}
struct BoxOfficeResult: Codable {
let dailyBoxOfficeList: [DailyBoxOfficeList]
}
struct DailyBoxOfficeList: Codable {
let movieNm: String
let audiCnt: String
let audiAcc: String
let rank: String
}
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
@IBOutlet weak var table: UITableView!
var movieData: MovieData?
// API 요청 URL
var movieURL = "https://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json?key=키&targetDt="
override func viewDidLoad() {
super.viewDidLoad()
table.dataSource = self
table.delegate = self
// 어제 날짜의 문자열 생성 및 URL 완성
movieURL += makeYesterdayString()
getData() // 데이터 가져오기
}
// 어제 날짜의 문자열 생성
func makeYesterdayString() -> String {
guard let yesterday = Calendar.current.date(byAdding: .day, value: -1, to: Date()) else {
return ""
}
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyyMMdd"
return dateFormatter.string(from: yesterday)
}
// 영화 데이터 가져오기
func getData() {
guard let url = URL(string: movieURL) else { return }
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { data, response, error in
if error != nil {
print(error!)
return
}
guard let jsonData = data else { return }
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode(MovieData.self, from: jsonData)
self.movieData = decodedData
DispatchQueue.main.async {
self.table.reloadData()
}
} catch {
print(error)
}
}
task.resume()
}
// 테이블 뷰 데이터 소스 메서드
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return movieData?.boxOfficeResult.dailyBoxOfficeList.count ?? 0
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as! MyTableViewCell
guard let movie = movieData?.boxOfficeResult.dailyBoxOfficeList[indexPath.row] else { return UITableViewCell() }
// 영화 제목과 순위 표시
cell.movieName.text = "[\(movie.rank)위] \(movie.movieNm)"
// 누적 관객수 표시
if let audiAcc = Int(movie.audiAcc) {
let formattedAcc = NumberFormatter.localizedString(from: NSNumber(value: audiAcc), number: .decimal)
cell.audiAccumulate.text = "누적 : \(formattedAcc)명"
}
// 어제의 관객수 표시
if let audiCnt = Int(movie.audiCnt) {
let formattedCnt = NumberFormatter.localizedString(from: NSNumber(value: audiCnt), number: .decimal)
cell.audiCount.text = "어제 : \(formattedCnt)명"
}
return cell
}
// 테이블 뷰 섹션 헤더와 푸터
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "🍿박스오피스(영화진흥위원회제공:" + makeYesterdayString() + ")🍿"
}
func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
return "🙏 by MegaMaker 🙏 문의는 DM으로"
}
// 섹션 수 반환
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
// 테이블 뷰 셀 선택 시 동작
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// 선택된 셀에 대한 작업 수행
}
// 세그웨이를 이용한 화면 전환 시 데이터 전달
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let dest = segue.destination as? DetailViewController,
let indexPath = table.indexPathForSelectedRow,
let movieName = movieData?.boxOfficeResult.dailyBoxOfficeList[indexPath.row].movieNm {
dest.movieName = movieName
}
}
}
ㄴ 기존 코드 리팩토링
'공부 > Swift' 카테고리의 다른 글
| [Swift][14, 15주차] 열거형, 구조체 (0) | 2024.06.13 |
|---|---|
| [iOS][12주차] 영화 상세 화면 만들기 (0) | 2024.05.23 |
| [iOS][11주차] 앱 만들기 진행 (0) | 2024.05.16 |
| [iOS][10주차] 박스오피스 앱 만들기 (0) | 2024.05.09 |
| [iOS][8, 9주차] 영화진흥위원회 오픈API 사용하기 (0) | 2024.05.02 |