Skip to content
Open
Show file tree
Hide file tree
Changes from 34 commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
9ac6ae6
add: MovieEntity 추가
jane1choi Mar 13, 2024
9d9a6af
feat: MovieInfoTableViewCell 기본 UI 구성
jane1choi Mar 13, 2024
81e2537
feat: 영화 상세 뷰 기본 UI 구성
jane1choi Mar 13, 2024
3dd3e49
chore: cell identifier 설정 코드 수정
jane1choi Mar 13, 2024
94ab493
chore: 빈 줄 삭제
jane1choi Mar 13, 2024
d35aae1
chore: 파일 경로 수정
jane1choi Mar 13, 2024
8863c93
chore: MovieAPIService 인스턴스 생성 방식 수정
angryeon7 Mar 15, 2024
c1e9745
feat: moviedto -> entity 변환 메서드 구현
angryeon7 Mar 15, 2024
aa4f585
chore: movieCode 추가
angryeon7 Mar 17, 2024
a921b87
feat: MovieRepository 구현
angryeon7 Mar 17, 2024
03078cc
feat: MovieUseCase 구현
angryeon7 Mar 17, 2024
f843f8d
feat: 배열 변환 메서드 구현
angryeon7 Mar 17, 2024
8f191c6
feat: MovieDetailViewModel 구현
angryeon7 Mar 17, 2024
7145e1f
feat: movieDetailVC 화면전환 메소드 구현
angryeon7 Mar 17, 2024
785a5a1
feat: viewmodel 주입
angryeon7 Mar 17, 2024
9f48613
chore: cell 별 데이터 삽입
angryeon7 Mar 17, 2024
e174da9
feat: MoviePosterDTO 생성
angryeon7 Mar 19, 2024
a4df8ab
feat: MoviePosterEntity 생성
angryeon7 Mar 19, 2024
60feb91
refactor: TargetType URL 삭제
angryeon7 Mar 19, 2024
f07e7fe
feat: MoviePosterAPI 생성
angryeon7 Mar 19, 2024
a3dec89
feat: MoviePosterAPIService 생성
angryeon7 Mar 19, 2024
6177f97
feat: PosterRepository 생성
angryeon7 Mar 19, 2024
4e4a95a
feat: MoviePosterUseCase 생성
angryeon7 Mar 19, 2024
bfea546
feat: DetailViewModel에 PosterUseCase 추가
angryeon7 Mar 19, 2024
93cc3ef
feat: ImageCell 생성
angryeon7 Mar 19, 2024
66abaed
feat: ImageCell 등록
angryeon7 Mar 19, 2024
48e2ab3
feat: DI 코드 추가
angryeon7 Mar 19, 2024
59e955d
chore: 셀 설정 코드 강제 옵셔널 추출 코드 수정
jane1choi Mar 19, 2024
7af04a8
chore: cell selectionStyle 설정 및 자잘한 코드 수정
jane1choi Mar 19, 2024
e1a0597
fix: 스택뷰 관련 레이아웃이 깨지는 문제 해결
jane1choi Mar 19, 2024
db3b894
chore: 자잘한 코드 수정
jane1choi Mar 19, 2024
51585f2
chore: 이미지 contentMode 수정 및 UI 자잘한 코드 수정
jane1choi Mar 20, 2024
290348f
chore: 프린트문 삭제
jane1choi Mar 20, 2024
b49421f
fix: 모든 요청에서 networkFail이 발생하는 문제
jane1choi Mar 20, 2024
7a6b0c6
chore: NetworkResult 케이스 추가 및 parsingError 부분 수정
jane1choi Mar 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 81 additions & 2 deletions BoxOffice.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

2 changes: 0 additions & 2 deletions BoxOffice/App/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {



func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
Expand Down
4 changes: 4 additions & 0 deletions BoxOffice/Data/Network/APIs/BoxOfficeAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ enum BoxOfficeAPI {

extension BoxOfficeAPI: TargetType {

var baseURL: String {
return NetworkEnvironment.baseURL
}

var method: HTTPMethod {
switch self {
case .requestDailyBoxOfficeInfo(_, _):
Expand Down
4 changes: 4 additions & 0 deletions BoxOffice/Data/Network/APIs/MovieAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,10 @@ enum MovieAPI {

extension MovieAPI: TargetType {

var baseURL: String {
return NetworkEnvironment.baseURL
}

var method: HTTPMethod {
switch self {
case .requestMovieDetailInfo(_, _):
Expand Down
46 changes: 46 additions & 0 deletions BoxOffice/Data/Network/APIs/MoviePosterAPI.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
//
// MoviePosterAPI.swift
// BoxOffice
//
// Created by nayeon on 3/19/24.
//

import Foundation

enum MoviePosterAPI {
case requestMoviePosterImage(userkey: String, query: String)
}

extension MoviePosterAPI: TargetType {

var baseURL: String {
return "https://dapi.kakao.com/v2"
}

var method: HTTPMethod {
return .get
}

var path: String {
switch self {
case .requestMoviePosterImage:
return "/search/image"
}
}

var parameters: RequestParameters {
switch self {
case let .requestMoviePosterImage(_, movieName):
return .query(["query": "\(movieName) 영화 포스터",
"size": "1",
"sort": "accuracy"])
}
}

var header: HeaderType {
switch self {
case let .requestMoviePosterImage(apiKey, _):
return .custom(["Authorization": "KakaoAK \(apiKey)"])
}
}
}
3 changes: 2 additions & 1 deletion BoxOffice/Data/Network/DTOs/BoxOfficeDTO.swift
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ extension DailyBoxOfficeList {
movieName: movieName,
salesAmount: salesAmount,
audienceCount: audienceCount,
rankChangeValue: rankChangeValue,
rankChangeValue: rankChangeValue,
movieCode: movieCode,
isNewMovie: rankOldAndNew == "New" ? true : false)
}
}
24 changes: 24 additions & 0 deletions BoxOffice/Data/Network/DTOs/MovieDTO.swift
Original file line number Diff line number Diff line change
Expand Up @@ -164,3 +164,27 @@ extension Staff {
case roleName = "staffRoleNm"
}
}

extension DetailMovieInformation {
func toEntity() -> MovieEntity {
let movieName = self.movieName
let director = directors.map { $0.name }.joined(separator: ", ")
let productYear = self.productYear
let openDate = self.openDate
let showTime = self.showTime
let watchGrade = audits.map { $0.watchGrade }.joined(separator: ", ")
let nation = nations.map { $0.name }.joined(separator: ", ")
let genres = self.genres.map { $0.name }.joined(separator: ", ")
let actors = self.actors.map { $0.name }.joined(separator: ", ")

return MovieEntity(movieName: movieName,
director: director,
productYear: productYear,
openDate: openDate,
showTime: showTime,
watchGrade: watchGrade,
nation: nation,
genres: genres,
actors: actors)
}
}
47 changes: 47 additions & 0 deletions BoxOffice/Data/Network/DTOs/MoviePosterDTO.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
//
// MoviePosterDTO.swift
// BoxOffice
//
// Created by nayeon on 3/19/24.
//

import Foundation

struct MoviePosterDTO: Codable {
let meta: Meta
let documents: [Document]
}

struct Meta: Codable {
let totalCount: Int
let pageableCount: Int
let isEnd: Bool

enum CodingKeys: String, CodingKey {
case totalCount = "total_count"
case pageableCount = "pageable_count"
case isEnd = "is_end"
}
}

struct Document: Codable {
let collection: String
let thumbnailURL: String
let imageURL: String
let width: Int
let height: Int
let displaySiteName: String
let docURL: String
let datetime: String

enum CodingKeys: String, CodingKey {
case collection
case thumbnailURL = "thumbnail_url"
case imageURL = "image_url"
case width
case height
case displaySiteName = "display_sitename"
case docURL = "doc_url"
case datetime
}
}
8 changes: 3 additions & 5 deletions BoxOffice/Data/Network/Services/MovieAPIService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,12 @@ import Foundation

final class MovieAPIService: BaseAPIService {

static let shared = MovieAPIService(provider: NetworkProvider())

private override init(provider: Requestable) {
super.init(provider: provider)
override init(provider: Requestable) {
super.init(provider: NetworkProvider())
}

func requestMovieDetailAPI(userKey: String, movieCode: String,
completion: @escaping ((NetworkResult<Any>) -> Void)) {
completion: @escaping ((NetworkResult<Any>) -> Void)) {
guard let request = try? MovieAPI
.requestMovieDetailInfo(userKey: userKey, movieCode: movieCode)
.creatURLRequest()
Expand Down
37 changes: 37 additions & 0 deletions BoxOffice/Data/Network/Services/MoviePosterAPIService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//
// MoviePosterAPIService.swift
// BoxOffice
//
// Created by nayeon on 3/19/24.
//

import Foundation

final class MoviePosterAPIService: BaseAPIService {

override init(provider: Requestable) {
super.init(provider: NetworkProvider())
}

func requestMoviePosterAPI(userKey: String, query: String, completion: @escaping ((NetworkResult<Any>) -> Void)) {
guard let request = try? MoviePosterAPI
.requestMoviePosterImage(userkey: userKey, query: query)
.creatURLRequest()
else {
completion(.networkFail)
return
}

provider.request(request) { result in
switch result {
case .success(let result):
let networkResult = self.judgeStatus(by: result.response.statusCode,
result.data,
MoviePosterDTO.self)
completion(networkResult)
case .failure(_):
completion(.networkFail)
}
}
}
}
71 changes: 71 additions & 0 deletions BoxOffice/Data/Repositories/DefaultMoviePosterRepository.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//
// DefaultMoviePosterRepository.swift
// BoxOffice
//
// Created by nayeon on 3/19/24.
//

import UIKit

final class DefaultMoviePosterRepository: MoviePosterRepository {

private let apiService: MoviePosterAPIService

init(apiService: MoviePosterAPIService) {
self.apiService = apiService
}

func fetchMoviePoster(query: String, completion: @escaping (NetworkResult<MoviePosterEntity>) -> Void) {
guard let apiKey = Bundle.main.object(forInfoDictionaryKey: "KAKAO_KEY") as? String else { return }

apiService.requestMoviePosterAPI(userKey: apiKey, query: query) { result in
switch result {
case .success(let data):
if let dto = data as? MoviePosterDTO,
let firstImageUrl = dto.documents.first?.imageURL {
self.loadImage(from: firstImageUrl) { result in
switch result {
case .success(let image):
let posterEntity = MoviePosterEntity(image: image)
DispatchQueue.main.async {
completion(.success(posterEntity))
}
case .failure:
completion(.networkFail)
}
}
} else {
completion(.pathError)
}
Comment on lines 36 to 39

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분은 imagery 이 없을 때, dto 가 MoviePosterDTO 타입이 아닐때,
에러가 발생하는 부분 같은데, pathError 라는 이름은 맞지 않는 것 같아요

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

확인 감사합니다!
NetworkResult.parsingError 케이스 추가 후 해당 부분 수정해주었습니다!
반영 커밋: 7a6b0c6

case .pathError:
completion(.pathError)
case .requestError:
completion(.requestError)
case .serverError:
completion(.serverError)
case .networkFail:
completion(.networkFail)
}
}
}

private func loadImage(from imageUrl: String, completion: @escaping (Result<UIImage, Error>) -> Void) {
guard let url = URL(string: imageUrl) else {
completion(.failure(NetworkError.invalidURL))
return
}

URLSession.shared.dataTask(with: url) { data, response, error in
if let error = error {
completion(.failure(error))
return
}

guard let data = data, let image = UIImage(data: data) else {
completion(.failure(NetworkError.invalidURL))
return
}
completion(.success(image))
}.resume()
}
}
41 changes: 41 additions & 0 deletions BoxOffice/Data/Repositories/DefaultMovieRepository.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
//
// DefaultMovieRepository.swift
// BoxOffice
//
// Created by nayeon on 3/13/24.
//

import Foundation

final class DefaultMovieRepository: MovieRepository {

private let apiService: MovieAPIService

init(apiService: MovieAPIService) {
self.apiService = apiService
}

func fetchMovieDetail(movieCode: String, completion: @escaping (NetworkResult<MovieEntity>) -> Void) {
guard let apiKey = Bundle.main.object(forInfoDictionaryKey: "API_KEY") as? String else { return }

apiService.requestMovieDetailAPI(userKey: apiKey, movieCode: movieCode) { result in
switch result {
case .success(let data):
if let dto = data as? MovieDTO {
DispatchQueue.main.async {
let movieEntity = dto.movieInformationResult.detailMovieInformation.toEntity()
completion(.success(movieEntity))
}
}
case .pathError:
completion(.pathError)
case .requestError:
completion(.requestError)
case .serverError:
completion(.serverError)
case .networkFail:
completion(.networkFail)
}
}
}
}
1 change: 1 addition & 0 deletions BoxOffice/Domain/Entities/BoxOfficeEntity.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ struct BoxOfficeEntity {
let salesAmount: String
let audienceCount: String
let rankChangeValue: String
let movieCode: String
let isNewMovie: Bool
}
31 changes: 31 additions & 0 deletions BoxOffice/Domain/Entities/MovieEntity.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//
// MovieEntity.swift
// BoxOffice
//
// Created by EUNJU on 3/13/24.
//

struct MovieEntity {
let movieName: String
let director: String
let productYear: String
let openDate: String
let showTime: String
let watchGrade: String
let nation: String
let genres: String
let actors: String

func getInfoArray() -> [(title: String, info: String)] {
return [
("감독:", director),
("제작년도:", productYear),
("개봉일:", openDate),
("상영시간:", showTime),
("관람등급:", watchGrade),
("제작국가:", nation),
("장르:", genres),
("배우:", actors)
]
}
Comment on lines +19 to +30

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

이 부분을 튜플로 처리하신 이유가 있을까요?
key value 가 있는 형태라면, dictionary도 괜찮았을 것 같은데,
이런 타입을 채택하신 이유가 궁금합니다🤔

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dictionary를 사용할 경우 순서를 지킬수 없기 때문에 순서를 지키기위해 튜플을 채택하였습니다.

}
12 changes: 12 additions & 0 deletions BoxOffice/Domain/Entities/MoviePosterEntity.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//
// MoviePosterEntity.swift
// BoxOffice
//
// Created by nayeon on 3/19/24.
//

import UIKit

struct MoviePosterEntity {
let image: UIImage
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//
// MoviePosterRepository.swift
// BoxOffice
//
// Created by nayeon on 3/19/24.
//

import Foundation

protocol MoviePosterRepository {
func fetchMoviePoster(query: String, completion: @escaping
(NetworkResult<MoviePosterEntity>) -> Void)
}
Loading