BoxOffice App
MVC
Model
import Foundation
// Codable을 준수하는 구조체 정의
// 영화 데이터를 담는 구조체
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 MovieDataManager {
var movieData: MovieData?
let movieURLBase = "https://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json?key=d9dfbf5b0a8b138015b48bd3b9ecfe2b&targetDt="
func fetchMovieData(completion: @escaping (MovieData?) -> Void) {
let movieURL = movieURLBase + makeYesterdayString()
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!)
completion(nil)
return
}
guard let JSONdata = data else {
completion(nil)
return
}
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode(MovieData.self, from: JSONdata)
completion(decodedData)
} catch {
print(error)
completion(nil)
}
}
task.resume()
}
private func makeYesterdayString() -> String {
let today = Date()
let calendar = Calendar.current
let yesterday = calendar.date(byAdding: .day, value: -1, to: today)!
let formatter = DateFormatter()
formatter.dateFormat = "yyyyMMdd"
return formatter.string(from: yesterday)
}
}
View
import UIKit
class MyTableViewCell: UITableViewCell {
@IBOutlet weak var movieName: UILabel!
@IBOutlet weak var audiAccumulate: UILabel!
@IBOutlet weak var audiCount: UILabel!
}
Controller
import UIKit
class ViewController: UIViewController, UITableViewDelegate, UITableViewDataSource {
@IBOutlet weak var table: UITableView!
var movieDataManager = MovieDataManager()
override func viewDidLoad() {
super.viewDidLoad()
// 테이블 뷰의 데이터 소스와 델리게이트 설정
table.dataSource = self
table.delegate = self
// 데이터 가져오기 메소드 호출
movieDataManager.fetchMovieData { [weak self] data in
guard let self = self else { return }
self.movieDataManager.movieData = data
// 메인 스레드에서 테이블 뷰 리로드
DispatchQueue.main.async {
self.table.reloadData()
}
}
}
// 테이블 뷰의 섹션당 행의 수를 반환하는 메소드
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return movieDataManager.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 = movieDataManager.movieData?.boxOfficeResult.dailyBoxOfficeList[indexPath.row] else { return UITableViewCell() }
cell.movieName.text = "[\(movie.rank)위] \(movie.movieNm)"
if let aAcc = Int(movie.audiAcc) {
let numF = NumberFormatter()
numF.numberStyle = .decimal
cell.audiAccumulate.text = "누적 : \(numF.string(from: NSNumber(value: aAcc))!)명"
}
if let aCnt = Int(movie.audiCnt) {
let numF = NumberFormatter()
numF.numberStyle = .decimal
cell.audiCount.text = "어제 : \(numF.string(from: NSNumber(value: aCnt))!)명"
}
return cell
}
// 테이블 뷰의 섹션 수를 반환하는 메소드
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
// 테이블 뷰의 행이 선택되었을 때 호출되는 메소드
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// 선택된 행에 대한 처리
}
// 테이블 뷰의 섹션 헤더 제목을 반환하는 메소드
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "🍿박스오피스(영화진흥위원회제공:" + movieDataManager.makeYesterdayString() + ")🍿"
}
// 테이블 뷰의 섹션 푸터 제목을 반환하는 메소드
func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
return "by choimu"
}
// 세그웨이를 준비하는 메소드
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let dest = segue.destination as! DetailViewController
// 선택된 행의 인덱스 경로를 가져와서 영화 이름 전달
let myIndexPath = table.indexPathForSelectedRow!
let row = myIndexPath.row
dest.movieName = (movieDataManager.movieData?.boxOfficeResult.dailyBoxOfficeList[row].movieNm)!
}
}
Refactoring
``` Swift // // ViewController.swift // MovieCMU // // Created by Induk-cs on 2024/05/02. //
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? let movieURL = “https://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json?key=d9dfbf5b0a8b138015b48bd3b9ecfe2b&targetDt=”
override func viewDidLoad() {
super.viewDidLoad()
table.dataSource = self
table.delegate = self
let yesterdayString = makeYesterdayString()
getData(from: movieURL + yesterdayString)
}
func makeYesterdayString() -> String {
let calendar = Calendar.current
guard let yesterday = calendar.date(byAdding: .day, value: -1, to: Date()) else { return "" }
let formatter = DateFormatter()
formatter.dateFormat = "yyyyMMdd"
return formatter.string(from: yesterday)
}
func getData(from urlString: String) {
guard let url = URL(string: urlString) else { return }
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { data, response, error in
if let error = error {
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 {
guard let cell = tableView.dequeueReusableCell(withIdentifier: "myCell", for: indexPath) as? MyTableViewCell else {
return UITableViewCell()
}
let movie = movieData?.boxOfficeResult.dailyBoxOfficeList[indexPath.row]
cell.movieName.text = "[\(movie?.rank ?? "")위] \(movie?.movieNm ?? "")"
cell.audiAccumulate.text = "누적: \(formatNumber(movie?.audiAcc ?? ""))명"
cell.audiCount.text = "어제: \(formatNumber(movie?.audiCnt ?? ""))명"
return cell
}
func formatNumber(_ numberString: String) -> String {
guard let number = Int(numberString) else { return numberString }
let formatter = NumberFormatter()
formatter.numberStyle = .decimal
return formatter.string(from: NSNumber(value: number)) ?? numberString
}
func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
// 선택된 셀에 대한 작업을 여기에 추가할 수 있습니다.
}
func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? {
return "🍿박스오피스(영화진흥위원회제공: \(makeYesterdayString()))🍿"
}
func tableView(_ tableView: UITableView, titleForFooterInSection section: Int) -> String? {
return "by choimu"
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard let dest = segue.destination as? DetailViewController,
let indexPath = table.indexPathForSelectedRow else { return }
dest.movieName = movieData?.boxOfficeResult.dailyBoxOfficeList[indexPath.row].movieNm ?? ""
} }
class MyTableViewCell: UITableViewCell { @IBOutlet weak var movieName: UILabel! @IBOutlet weak var audiAccumulate: UILabel! @IBOutlet weak var audiCount: UILabel! }
class DetailViewController: UIViewController { var movieName: String?
override func viewDidLoad() {
super.viewDidLoad()
// movieName을 사용하는 추가 설정
} }
``’
출처
- Smile Han의 iOS 프로그래밍 실무, 한성현(출판 예정), PPT로 제공
- Do it! 스위프트로 아이폰 앱 만들기 입문(개정 7판)(이지스퍼블리싱,송호정, 이범근, 2023.1)
- 핵심만 골라 배우는 SwiftUI 기반의 iOS 프로그래밍(제이펍, 닐 스미스, 2023.12)
- https://www.techotopia.com/index.php/IOS_iPhone_iPad_eBooks
- 스위프트 프로그래밍(Swift 5) 3판(한빛미디어, 야곰, 2019.10)
- 꼼꼼한 재은 씨의 스위프트 기본편 (루비페이퍼, 이재은, 2018.05)
- 꼼꼼한 재은 씨의 스위프트 실전편 (Swift) (루비페이퍼, 이재은, 2018.08)
- 꼼꼼한 재은 씨의 Swift 문법편 (루비페이퍼, 이재은, 2017.12)