From b6b4dc67dd00ac43a404749625e08ed7db5cd022 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthew=28=EB=A7=A4=ED=8A=9C=29?= Date: Fri, 8 Mar 2024 20:09:08 +0900 Subject: [PATCH 01/39] =?UTF-8?q?feat:=20UIScrollView=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BoxOfficeDetailViewController.swift | 8 ++ BoxOffice/View/BoxOfficeDetailView.swift | 97 +++++++++++++++++++ BoxOffice/View/ReusedDetailStackView.swift | 8 ++ 3 files changed, 113 insertions(+) create mode 100644 BoxOffice/Controller/BoxOfficeDetailViewController.swift create mode 100644 BoxOffice/View/BoxOfficeDetailView.swift create mode 100644 BoxOffice/View/ReusedDetailStackView.swift diff --git a/BoxOffice/Controller/BoxOfficeDetailViewController.swift b/BoxOffice/Controller/BoxOfficeDetailViewController.swift new file mode 100644 index 00000000..3af14ac8 --- /dev/null +++ b/BoxOffice/Controller/BoxOfficeDetailViewController.swift @@ -0,0 +1,8 @@ +// +// BoxOfficeDetailViewController.swift +// BoxOffice +// +// Created by Matthew on 3/8/24. +// + +import Foundation diff --git a/BoxOffice/View/BoxOfficeDetailView.swift b/BoxOffice/View/BoxOfficeDetailView.swift new file mode 100644 index 00000000..7c4f0972 --- /dev/null +++ b/BoxOffice/View/BoxOfficeDetailView.swift @@ -0,0 +1,97 @@ +// +// BoxOfficeDetailView.swift +// BoxOffice +// +// Created by Matthew on 3/8/24. +// + +import UIKit + +class BoxOfficeDetailView: UIScrollView { + + let directorView = ReusedDetailStackView() + let productYearView = ReusedDetailStackView() + let openDateView = ReusedDetailStackView() + let showTimeView = ReusedDetailStackView() + let watchGradeView = ReusedDetailStackView() + let nationsView = ReusedDetailStackView() + let genresView = ReusedDetailStackView() + let actorsView = ReusedDetailStackView() + + lazy var movieInfoStackView: UIStackView = { + let stack = UIStackView(arrangedSubviews: [ + directorView, + productYearView, + openDateView, + showTimeView, + watchGradeView, + nationsView, + genresView, + actorsView + ]) + stack.axis = .vertical + stack.alignment = .leading + stack.distribution = .equalSpacing + stack.spacing = 5 + return stack + }() + + lazy var innerView: UIView = { + let view = UIView() + view.backgroundColor = .white + return view + }() + + let movieImageView: UIImageView = { + let view = UIImageView() + view.image = UIImage(systemName: "photo.fill") + + return view + }() + + override init(frame: CGRect) { + super.init(frame: frame) + setupView() + setupConstraints() + directorView.setupStackView(title: "", contents: [""]) + productYearView.setupStackView(title: "", contents: [""]) + openDateView.setupStackView(title: "", contents: [""]) + showTimeView.setupStackView(title: "", contents: [""]) + watchGradeView.setupStackView(title: "", contents: [""]) + nationsView.setupStackView(title: "", contents: [""]) + genresView.setupStackView(title: "", contents: [""]) + actorsView.setupStackView(title: "", contents: [""]) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func setupView() { + self.addSubview(innerView) + innerView.addSubview(movieImageView) + innerView.addSubview(movieInfoStackView) + } + + func setupConstraints() { + innerView.translatesAutoresizingMaskIntoConstraints = false + innerView.topAnchor.constraint(equalTo: self.contentLayoutGuide.topAnchor).isActive = true + innerView.leadingAnchor.constraint(equalTo: self.contentLayoutGuide.leadingAnchor).isActive = true + innerView.trailingAnchor.constraint(equalTo: self.contentLayoutGuide.trailingAnchor).isActive = true + innerView.bottomAnchor.constraint(equalTo: self.contentLayoutGuide.bottomAnchor).isActive = true + + innerView.widthAnchor.constraint(equalTo: self.frameLayoutGuide.widthAnchor).isActive = true + innerView.heightAnchor.constraint(equalToConstant: 1000).isActive = true + + movieImageView.translatesAutoresizingMaskIntoConstraints = false + movieImageView.topAnchor.constraint(equalTo: innerView.topAnchor, constant: 10).isActive = true + movieImageView.centerXAnchor.constraint(equalTo: innerView.centerXAnchor).isActive = true + movieImageView.widthAnchor.constraint(equalTo: innerView.widthAnchor).isActive = true + movieImageView.heightAnchor.constraint(equalTo: innerView.widthAnchor).isActive = true + + movieInfoStackView.translatesAutoresizingMaskIntoConstraints = false + movieInfoStackView.topAnchor.constraint(equalTo: movieImageView.bottomAnchor, constant: 10).isActive = true + movieInfoStackView.leadingAnchor.constraint(equalTo: innerView.leadingAnchor).isActive = true + movieInfoStackView.trailingAnchor.constraint(equalTo: innerView.trailingAnchor).isActive = true + } +} diff --git a/BoxOffice/View/ReusedDetailStackView.swift b/BoxOffice/View/ReusedDetailStackView.swift new file mode 100644 index 00000000..8bf41e11 --- /dev/null +++ b/BoxOffice/View/ReusedDetailStackView.swift @@ -0,0 +1,8 @@ +// +// ReusedDetailStackView.swift +// BoxOffice +// +// Created by Matthew on 3/8/24. +// + +import Foundation From dfbeca102e6a118cebc908b95787094ee6ef7732 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthew=28=EB=A7=A4=ED=8A=9C=29?= Date: Fri, 8 Mar 2024 20:10:04 +0900 Subject: [PATCH 02/39] =?UTF-8?q?feat:=20UIScrollView=EC=97=90=EC=84=9C=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=ED=95=A0=20StackView=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice/View/ReusedDetailStackView.swift | 61 +++++++++++++++++++++- 1 file changed, 60 insertions(+), 1 deletion(-) diff --git a/BoxOffice/View/ReusedDetailStackView.swift b/BoxOffice/View/ReusedDetailStackView.swift index 8bf41e11..9e7b11d3 100644 --- a/BoxOffice/View/ReusedDetailStackView.swift +++ b/BoxOffice/View/ReusedDetailStackView.swift @@ -5,4 +5,63 @@ // Created by Matthew on 3/8/24. // -import Foundation +import UIKit + +class ReusedDetailStackView: UIStackView { + lazy var titleLabel: UILabel = { + let label = UILabel() + label.font = UIFont.preferredFont(for: .body, weight: .bold) + label.textAlignment = .center + return label + }() + + lazy var contentLabel: UILabel = { + let label = UILabel() + label.font = UIFont.preferredFont(forTextStyle: .body) + label.numberOfLines = 10 + label.textAlignment = .left + return label + }() + + override init(frame: CGRect) { + super.init(frame: frame) + configure() + setupConstraint() + } + + required init(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func configure() { + self.addArrangedSubview(titleLabel) + self.addArrangedSubview(contentLabel) + self.axis = .horizontal + self.alignment = .leading + self.distribution = .fill + self.spacing = 5 + self.backgroundColor = .red + } + + func setupStackView(title: String, contents: [String]) { + titleLabel.text = title + var textList: String = "" + for (index, text) in contents.enumerated() { + textList.append(text) + if index != contents.endIndex - 1 { + textList.append(", ") + } + } + contentLabel.text = textList + } + + func setupStackView(title: String, content: String) { + titleLabel.text = title + contentLabel.text = content + } + + func setupConstraint() { + titleLabel.translatesAutoresizingMaskIntoConstraints = false + titleLabel.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width / 6).isActive = true + } +} From cf250e9b9dd5c1f4677df250b44ef06802322fe1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthew=28=EB=A7=A4=ED=8A=9C=29?= Date: Fri, 8 Mar 2024 20:10:44 +0900 Subject: [PATCH 03/39] =?UTF-8?q?refactor:=20UIScrollView=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=EB=A5=BC=20=EC=9C=84=ED=95=9C=20=EA=B8=B0=EC=A1=B4=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice.xcodeproj/project.pbxproj | 14 ++++++++++++++ .../Controller/BoxOfficeDetailViewController.swift | 13 ++++++++++++- BoxOffice/Controller/BoxOfficeViewController.swift | 3 ++- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/BoxOffice.xcodeproj/project.pbxproj b/BoxOffice.xcodeproj/project.pbxproj index 7d70a1a1..2a57029d 100644 --- a/BoxOffice.xcodeproj/project.pbxproj +++ b/BoxOffice.xcodeproj/project.pbxproj @@ -49,6 +49,9 @@ C14DF4B52B7DAE49009E3258 /* Bundle+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C14DF4B42B7DAE49009E3258 /* Bundle+Extension.swift */; }; C14DF4B72B7DAF3A009E3258 /* Private.plist in Resources */ = {isa = PBXBuildFile; fileRef = C14DF4B62B7DAF3A009E3258 /* Private.plist */; }; C14DF4B92B7DAF97009E3258 /* MovieURL.swift in Sources */ = {isa = PBXBuildFile; fileRef = C14DF4B82B7DAF97009E3258 /* MovieURL.swift */; }; + C16A4D6E2B9ADF2100498BB3 /* BoxOfficeDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C16A4D6D2B9ADF2100498BB3 /* BoxOfficeDetailView.swift */; }; + C16A4D702B9AE4E600498BB3 /* BoxOfficeDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C16A4D6F2B9AE4E600498BB3 /* BoxOfficeDetailViewController.swift */; }; + C16A4D722B9AE92C00498BB3 /* ReusedDetailStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C16A4D712B9AE92C00498BB3 /* ReusedDetailStackView.swift */; }; C1FC46B02B84962000C6E49E /* JSONFileName.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1FC46AF2B84962000C6E49E /* JSONFileName.swift */; }; C1FC46B22B8496B300C6E49E /* URLSessionProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1FC46B12B8496B300C6E49E /* URLSessionProtocol.swift */; }; C1FC46B42B84AFFC00C6E49E /* URLSessionDataTaskProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1FC46B32B84AFFC00C6E49E /* URLSessionDataTaskProtocol.swift */; }; @@ -113,6 +116,9 @@ C14DF4B42B7DAE49009E3258 /* Bundle+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+Extension.swift"; sourceTree = ""; }; C14DF4B62B7DAF3A009E3258 /* Private.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Private.plist; sourceTree = ""; }; C14DF4B82B7DAF97009E3258 /* MovieURL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MovieURL.swift; sourceTree = ""; }; + C16A4D6D2B9ADF2100498BB3 /* BoxOfficeDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoxOfficeDetailView.swift; sourceTree = ""; }; + C16A4D6F2B9AE4E600498BB3 /* BoxOfficeDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoxOfficeDetailViewController.swift; sourceTree = ""; }; + C16A4D712B9AE92C00498BB3 /* ReusedDetailStackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReusedDetailStackView.swift; sourceTree = ""; }; C1FC46AF2B84962000C6E49E /* JSONFileName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONFileName.swift; sourceTree = ""; }; C1FC46B12B8496B300C6E49E /* URLSessionProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLSessionProtocol.swift; sourceTree = ""; }; C1FC46B32B84AFFC00C6E49E /* URLSessionDataTaskProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLSessionDataTaskProtocol.swift; sourceTree = ""; }; @@ -212,6 +218,7 @@ 276ED7742B7B016100D37EBF /* Controller */ = { isa = PBXGroup; children = ( + C16A4D6F2B9AE4E600498BB3 /* BoxOfficeDetailViewController.swift */, 63DF20F22970E1A0005DF7D1 /* BoxOfficeViewController.swift */, 278FA99F2B8B0BF500FFDB3F /* Protocol */, ); @@ -336,6 +343,8 @@ 27AE2EDB2B86E950009B43E2 /* RankStackView.swift */, 27AE2ED92B86E930009B43E2 /* RankStateView.swift */, 27AE2EDD2B86E965009B43E2 /* MovieStackView.swift */, + C16A4D6D2B9ADF2100498BB3 /* BoxOfficeDetailView.swift */, + C16A4D712B9AE92C00498BB3 /* ReusedDetailStackView.swift */, ); path = View; sourceTree = ""; @@ -469,9 +478,12 @@ 2757937B2B7F1E2400C08906 /* Genre.swift in Sources */, 275793792B7F1E1400C08906 /* Director.swift in Sources */, 276ED76A2B7B006400D37EBF /* BoxOffice.swift in Sources */, + C16A4D702B9AE4E600498BB3 /* BoxOfficeDetailViewController.swift in Sources */, 278FA99E2B88BEE100FFDB3F /* BoxOfficeListDataSource.swift in Sources */, 276ED7982B7B638C00D37EBF /* APIService.swift in Sources */, + C16A4D6E2B9ADF2100498BB3 /* BoxOfficeDetailView.swift in Sources */, 275793732B7F1DE200C08906 /* Actor.swift in Sources */, + C16A4D722B9AE92C00498BB3 /* ReusedDetailStackView.swift in Sources */, C1FC46C22B8772BF00C6E49E /* Int+Extension.swift in Sources */, 27C0258B2B8B30BD009C3F1F /* String+Extension.swift in Sources */, C1FC46BE2B85D1A000C6E49E /* BoxOfficeListView.swift in Sources */, @@ -684,6 +696,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = T92LV6HV88; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = BoxOffice/Resource/Info.plist; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; @@ -711,6 +724,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = T92LV6HV88; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = BoxOffice/Resource/Info.plist; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; diff --git a/BoxOffice/Controller/BoxOfficeDetailViewController.swift b/BoxOffice/Controller/BoxOfficeDetailViewController.swift index 3af14ac8..da5be35f 100644 --- a/BoxOffice/Controller/BoxOfficeDetailViewController.swift +++ b/BoxOffice/Controller/BoxOfficeDetailViewController.swift @@ -5,4 +5,15 @@ // Created by Matthew on 3/8/24. // -import Foundation +import UIKit + +final class BoxOfficeDetailViewController: UIViewController { + + let scrollView = BoxOfficeDetailView() + + override func viewDidLoad() { + super.viewDidLoad() + view = scrollView + view.backgroundColor = .white + } +} diff --git a/BoxOffice/Controller/BoxOfficeViewController.swift b/BoxOffice/Controller/BoxOfficeViewController.swift index 8d355c84..46bb28ad 100644 --- a/BoxOffice/Controller/BoxOfficeViewController.swift +++ b/BoxOffice/Controller/BoxOfficeViewController.swift @@ -91,7 +91,8 @@ private extension BoxOfficeViewController { extension BoxOfficeViewController: UICollectionViewDelegate { func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { - self.navigationController?.popViewController(animated: true) + let detailVC = BoxOfficeDetailViewController() + self.navigationController?.pushViewController(detailVC, animated: true) } } From 7fa5b7484b34b9c1deb99d4593f748cf5f8bd17c Mon Sep 17 00:00:00 2001 From: Changhyun Kang Date: Fri, 8 Mar 2024 20:22:33 +0900 Subject: [PATCH 04/39] =?UTF-8?q?feat:=20=EA=B2=80=EC=83=89=20REST=20API?= =?UTF-8?q?=20=EB=AA=A8=EB=8D=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice.xcodeproj/project.pbxproj | 20 +++++++++++++++ BoxOffice/Extension/Bundle+Extension.swift | 13 +++++++++- BoxOffice/Model/Movie/Image/Document.swift | 27 ++++++++++++++++++++ BoxOffice/Model/Movie/Image/Meta.swift | 12 +++++++++ BoxOffice/Model/Movie/Image/MovieImage.swift | 12 +++++++++ BoxOffice/Model/MovieURL.swift | 4 +-- BoxOffice/Resource/Private.plist | 2 ++ 7 files changed, 87 insertions(+), 3 deletions(-) create mode 100644 BoxOffice/Model/Movie/Image/Document.swift create mode 100644 BoxOffice/Model/Movie/Image/Meta.swift create mode 100644 BoxOffice/Model/Movie/Image/MovieImage.swift diff --git a/BoxOffice.xcodeproj/project.pbxproj b/BoxOffice.xcodeproj/project.pbxproj index 2a57029d..16b41f99 100644 --- a/BoxOffice.xcodeproj/project.pbxproj +++ b/BoxOffice.xcodeproj/project.pbxproj @@ -24,6 +24,9 @@ 2757938C2B7F52B500C08906 /* JSONLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2757938B2B7F52B500C08906 /* JSONLoader.swift */; }; 2757938D2B7F546300C08906 /* box_office.json in Resources */ = {isa = PBXBuildFile; fileRef = 275793822B7F402300C08906 /* box_office.json */; }; 2757D9D82B9A9EDB00B13BD7 /* DateFormatterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2757D9D72B9A9EDB00B13BD7 /* DateFormatterTests.swift */; }; + 2757D9DB2B9B2BCB00B13BD7 /* MovieImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2757D9DA2B9B2BCB00B13BD7 /* MovieImage.swift */; }; + 2757D9DD2B9B2C4200B13BD7 /* Document.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2757D9DC2B9B2C4200B13BD7 /* Document.swift */; }; + 2757D9DF2B9B2C5400B13BD7 /* Meta.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2757D9DE2B9B2C5400B13BD7 /* Meta.swift */; }; 276ED7632B7B002600D37EBF /* BoxOfficeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 276ED7622B7B002600D37EBF /* BoxOfficeTests.swift */; }; 276ED76A2B7B006400D37EBF /* BoxOffice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 276ED7692B7B006400D37EBF /* BoxOffice.swift */; }; 276ED76C2B7B006D00D37EBF /* MovieManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 276ED76B2B7B006D00D37EBF /* MovieManager.swift */; }; @@ -88,6 +91,9 @@ 275793892B7F51BF00C08906 /* URLSession+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLSession+Extension.swift"; sourceTree = ""; }; 2757938B2B7F52B500C08906 /* JSONLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONLoader.swift; sourceTree = ""; }; 2757D9D72B9A9EDB00B13BD7 /* DateFormatterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateFormatterTests.swift; sourceTree = ""; }; + 2757D9DA2B9B2BCB00B13BD7 /* MovieImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MovieImage.swift; sourceTree = ""; }; + 2757D9DC2B9B2C4200B13BD7 /* Document.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Document.swift; sourceTree = ""; }; + 2757D9DE2B9B2C5400B13BD7 /* Meta.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Meta.swift; sourceTree = ""; }; 276ED7602B7B002600D37EBF /* BoxOfficeTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BoxOfficeTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 276ED7622B7B002600D37EBF /* BoxOfficeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoxOfficeTests.swift; sourceTree = ""; }; 276ED7692B7B006400D37EBF /* BoxOffice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoxOffice.swift; sourceTree = ""; }; @@ -150,6 +156,7 @@ children = ( 276ED7692B7B006400D37EBF /* BoxOffice.swift */, 27157BF62B8344F2000BAC45 /* MovieInfomationDetail.swift */, + 2757D9D92B9B2B6F00B13BD7 /* Image */, 2757936D2B7F1D9C00C08906 /* DetailMovie */, 2757936C2B7F1D8900C08906 /* DailyBoxOffice */, ); @@ -193,6 +200,16 @@ path = Mock; sourceTree = ""; }; + 2757D9D92B9B2B6F00B13BD7 /* Image */ = { + isa = PBXGroup; + children = ( + 2757D9DA2B9B2BCB00B13BD7 /* MovieImage.swift */, + 2757D9DC2B9B2C4200B13BD7 /* Document.swift */, + 2757D9DE2B9B2C5400B13BD7 /* Meta.swift */, + ); + path = Image; + sourceTree = ""; + }; 276ED7612B7B002600D37EBF /* BoxOfficeTests */ = { isa = PBXGroup; children = ( @@ -473,6 +490,7 @@ 276ED76E2B7B010D00D37EBF /* BoxOfficeResult.swift in Sources */, 2757936F2B7F1DB900C08906 /* MovieInfoResult.swift in Sources */, 2757937F2B7F1E3F00C08906 /* ShowType.swift in Sources */, + 2757D9DD2B9B2C4200B13BD7 /* Document.swift in Sources */, 27AE2EE02B86E9AD009B43E2 /* UIFont+Extension.swift in Sources */, 276ED76C2B7B006D00D37EBF /* MovieManager.swift in Sources */, 2757937B2B7F1E2400C08906 /* Genre.swift in Sources */, @@ -481,8 +499,10 @@ C16A4D702B9AE4E600498BB3 /* BoxOfficeDetailViewController.swift in Sources */, 278FA99E2B88BEE100FFDB3F /* BoxOfficeListDataSource.swift in Sources */, 276ED7982B7B638C00D37EBF /* APIService.swift in Sources */, + 2757D9DF2B9B2C5400B13BD7 /* Meta.swift in Sources */, C16A4D6E2B9ADF2100498BB3 /* BoxOfficeDetailView.swift in Sources */, 275793732B7F1DE200C08906 /* Actor.swift in Sources */, + 2757D9DB2B9B2BCB00B13BD7 /* MovieImage.swift in Sources */, C16A4D722B9AE92C00498BB3 /* ReusedDetailStackView.swift in Sources */, C1FC46C22B8772BF00C6E49E /* Int+Extension.swift in Sources */, 27C0258B2B8B30BD009C3F1F /* String+Extension.swift in Sources */, diff --git a/BoxOffice/Extension/Bundle+Extension.swift b/BoxOffice/Extension/Bundle+Extension.swift index d53beb37..39ba2dc5 100644 --- a/BoxOffice/Extension/Bundle+Extension.swift +++ b/BoxOffice/Extension/Bundle+Extension.swift @@ -8,7 +8,7 @@ import Foundation extension Bundle { - var apiKey: String { + var movieApiKey: String { guard let file = self.path(forResource: "Private", ofType: "plist"), let resource = NSDictionary(contentsOfFile: file), @@ -18,4 +18,15 @@ extension Bundle { } return key } + + var kakaoApiKey: String { + guard + let file = self.path(forResource: "Private", ofType: "plist"), + let resource = NSDictionary(contentsOfFile: file), + let key = resource["Kakao_API_KEY"] as? String + else { + return "" + } + return key + } } diff --git a/BoxOffice/Model/Movie/Image/Document.swift b/BoxOffice/Model/Movie/Image/Document.swift new file mode 100644 index 00000000..d0194439 --- /dev/null +++ b/BoxOffice/Model/Movie/Image/Document.swift @@ -0,0 +1,27 @@ +// +// Document.swift +// BoxOffice +// +// Created by 강창현 on 3/8/24. +// + +struct Document: Codable { + let collection: String + let thumbnailURL: String + let imageURL: String + let width, height: Int + let displaySitename: String + let docURL: String + let datetime: String + + enum CodingKeys: String, CodingKey { + case collection + case thumbnailURL + case imageURL + case width + case height + case displaySitename = "displaySiteName" + case docURL + case datetime + } +} diff --git a/BoxOffice/Model/Movie/Image/Meta.swift b/BoxOffice/Model/Movie/Image/Meta.swift new file mode 100644 index 00000000..dec383ad --- /dev/null +++ b/BoxOffice/Model/Movie/Image/Meta.swift @@ -0,0 +1,12 @@ +// +// Meta.swift +// BoxOffice +// +// Created by 강창현 on 3/8/24. +// + +struct Meta: Codable { + let totalCount: Int + let pageableCount: Int + let isEnd: Bool +} diff --git a/BoxOffice/Model/Movie/Image/MovieImage.swift b/BoxOffice/Model/Movie/Image/MovieImage.swift new file mode 100644 index 00000000..007b9ae3 --- /dev/null +++ b/BoxOffice/Model/Movie/Image/MovieImage.swift @@ -0,0 +1,12 @@ +// +// MovieImage.swift +// BoxOffice +// +// Created by 강창현 on 3/8/24. +// + +struct MovieImage: Codable { + let meta: Meta + let documents: [Document] +} + diff --git a/BoxOffice/Model/MovieURL.swift b/BoxOffice/Model/MovieURL.swift index 4669bf62..03f258aa 100644 --- a/BoxOffice/Model/MovieURL.swift +++ b/BoxOffice/Model/MovieURL.swift @@ -9,7 +9,7 @@ import Foundation struct MovieURL { static func makeDailyBoxOfficeURL(date: String) -> String { - let key = Bundle.main.apiKey + let key = Bundle.main.movieApiKey var url: String { return "https://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json?key=\(String(describing: key))&targetDt=\(date)" } @@ -17,7 +17,7 @@ struct MovieURL { } static func makeMovieInfomationDetailURL(code: String) -> String { - let key = Bundle.main.apiKey + let key = Bundle.main.movieApiKey var url: String { return "https://kobis.or.kr/kobisopenapi/webservice/rest/movie/searchMovieInfo.json?key=\(String(describing: key))&movieCd=\(code)" } diff --git a/BoxOffice/Resource/Private.plist b/BoxOffice/Resource/Private.plist index caf75b1c..45950a08 100644 --- a/BoxOffice/Resource/Private.plist +++ b/BoxOffice/Resource/Private.plist @@ -4,5 +4,7 @@ API_KEY f5eef3421c602c6cb7ea224104795888 + Kakao_API_KEY + 3ac5fdb6d1defd44daa87c6b277fc587 From 553324d02adf3b4f8bce478556530670ba678257 Mon Sep 17 00:00:00 2001 From: Changhyun Kang Date: Fri, 8 Mar 2024 20:23:14 +0900 Subject: [PATCH 05/39] =?UTF-8?q?chore:=20=EB=B6=88=ED=95=84=EC=9A=94=20?= =?UTF-8?q?=EC=9D=B8=EB=8D=B4=ED=8A=B8=20=EC=A0=9C=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice/Model/Movie/Image/MovieImage.swift | 1 - 1 file changed, 1 deletion(-) diff --git a/BoxOffice/Model/Movie/Image/MovieImage.swift b/BoxOffice/Model/Movie/Image/MovieImage.swift index 007b9ae3..894a8167 100644 --- a/BoxOffice/Model/Movie/Image/MovieImage.swift +++ b/BoxOffice/Model/Movie/Image/MovieImage.swift @@ -9,4 +9,3 @@ struct MovieImage: Codable { let meta: Meta let documents: [Document] } - From 4cdee9897316508bb45c3a2955995e2c5584fc0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthew=28=EB=A7=A4=ED=8A=9C=29?= Date: Sun, 10 Mar 2024 21:30:28 +0900 Subject: [PATCH 06/39] =?UTF-8?q?refactor:=20NetworkURL=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A7=81=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice/Model/{MovieURL.swift => NetworkURL.swift} | 10 +++++++++- BoxOfficeTests/BoxOfficeMockTests.swift | 4 ++-- BoxOfficeTests/BoxOfficeTests.swift | 8 ++++---- 3 files changed, 15 insertions(+), 7 deletions(-) rename BoxOffice/Model/{MovieURL.swift => NetworkURL.swift} (73%) diff --git a/BoxOffice/Model/MovieURL.swift b/BoxOffice/Model/NetworkURL.swift similarity index 73% rename from BoxOffice/Model/MovieURL.swift rename to BoxOffice/Model/NetworkURL.swift index 03f258aa..00cbfcb2 100644 --- a/BoxOffice/Model/MovieURL.swift +++ b/BoxOffice/Model/NetworkURL.swift @@ -7,7 +7,7 @@ import Foundation -struct MovieURL { +struct NetworkURL { static func makeDailyBoxOfficeURL(date: String) -> String { let key = Bundle.main.movieApiKey var url: String { @@ -23,4 +23,12 @@ struct MovieURL { } return url } + + static func makeMovieImageURL(code: String) -> String { + let key = Bundle.main.kakaoApiKey + var url: String { + return "https://dapi.kakao.com/v2/search/image/&query=\(code)" + } + return url + } } diff --git a/BoxOfficeTests/BoxOfficeMockTests.swift b/BoxOfficeTests/BoxOfficeMockTests.swift index 9aae747e..b4fdb0d9 100644 --- a/BoxOfficeTests/BoxOfficeMockTests.swift +++ b/BoxOfficeTests/BoxOfficeMockTests.swift @@ -11,7 +11,7 @@ import XCTest final class BoxOfficeMockTests: XCTestCase { func test_MockURLSession의_응답코드가_400이면_clientError가_발생한다() { // given - let urlString = MovieURL.makeDailyBoxOfficeURL(date: Date.movieDateToString) + let urlString = NetworkURL.makeDailyBoxOfficeURL(date: Date.movieDateToString) let mockURLSession = makeMockURLSession(fileName: JSONFileName.boxOffice, url: urlString, statusCode: 400) let sut = setSUT(session: mockURLSession) @@ -35,7 +35,7 @@ final class BoxOfficeMockTests: XCTestCase { func test_MockURLSession의_응답코드가_200이면_boxOfficeResult는_nil이_아니다() { // given let date = "20240210" - let urlString = MovieURL.makeDailyBoxOfficeURL(date: date) + let urlString = NetworkURL.makeDailyBoxOfficeURL(date: date) let mockURLSession = makeMockURLSession(fileName: JSONFileName.boxOffice, url: urlString, statusCode: 200) let sut = setSUT(session: mockURLSession) diff --git a/BoxOfficeTests/BoxOfficeTests.swift b/BoxOfficeTests/BoxOfficeTests.swift index 404c8f69..3ccfd838 100644 --- a/BoxOfficeTests/BoxOfficeTests.swift +++ b/BoxOfficeTests/BoxOfficeTests.swift @@ -25,7 +25,7 @@ final class BoxOfficeTests: XCTestCase { func test_date가_20230101이고_데이터_파싱이_올바르게_됐을_때_fetchData는_nil이_아니다() { // given let date = "20170319" - let urlString = MovieURL.makeDailyBoxOfficeURL(date: date) + let urlString = NetworkURL.makeDailyBoxOfficeURL(date: date) // when let expectation = XCTestExpectation(description: "데이터 패치 중...") @@ -47,7 +47,7 @@ final class BoxOfficeTests: XCTestCase { func test_date가_잘못된_타입으로_데이터_파싱_됐을_때_fetchMovie에서_decodingError발생() { // given let wrongDate = "iWantToGoHome" - let urlString = MovieURL.makeDailyBoxOfficeURL(date: wrongDate) + let urlString = NetworkURL.makeDailyBoxOfficeURL(date: wrongDate) // when let expectation = XCTestExpectation(description: "데이터 패치 중...") @@ -89,7 +89,7 @@ final class BoxOfficeTests: XCTestCase { func test_어제날짜_영화_데이터_파싱이_올바르게_됐을_때_fetchData에_boxOfficeResult는_nil이_아니다() { // given - let urlString = MovieURL.makeDailyBoxOfficeURL(date: Date.movieDateToString) + let urlString = NetworkURL.makeDailyBoxOfficeURL(date: Date.movieDateToString) // when let expectation = XCTestExpectation(description: "데이터 패치 중...") @@ -110,7 +110,7 @@ final class BoxOfficeTests: XCTestCase { func test_특정_영화_코드로_데이터_파싱이_올바르게_됐을_때_fetchData에_movieInfoResult는_nil이_아니다() { // given - let urlString = MovieURL.makeMovieInfomationDetailURL(code: "20124079") + let urlString = NetworkURL.makeMovieInfomationDetailURL(code: "20124079") // when let expectation = XCTestExpectation(description: "데이터 패치 중...") From 2e5a676ebac1d9d3601eb8e036752da63d22a3c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthew=28=EB=A7=A4=ED=8A=9C=29?= Date: Sun, 10 Mar 2024 21:32:08 +0900 Subject: [PATCH 07/39] =?UTF-8?q?refactor:=20DataTask=20URLRequest?= =?UTF-8?q?=EB=A1=9C=20=EA=B5=AC=ED=98=84=EB=A1=9C=EC=A7=81=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice/Network/APIService.swift | 18 ++++++++++++++++-- BoxOffice/Network/Mock/MockURLSession.swift | 13 +++++++++++++ .../Network/Protocol/URLSessionProtocol.swift | 5 +++++ 3 files changed, 34 insertions(+), 2 deletions(-) diff --git a/BoxOffice/Network/APIService.swift b/BoxOffice/Network/APIService.swift index be24a0df..91a8e237 100644 --- a/BoxOffice/Network/APIService.swift +++ b/BoxOffice/Network/APIService.swift @@ -14,6 +14,19 @@ struct APIService { self.session = session } + func fetchImageData(urlRequest: URLRequest, completion: @escaping (Result) -> Void) { + self.session.dataTask(with: urlRequest) { data, response, error in + DispatchQueue.global().async { + self.handleDataTaskCompletion( + data: data, + response: response, + error: error, + completion: completion + ) + } + }.resume() + } + func fetchData(urlString: String, completion: @escaping (Result) -> Void) { guard let url = URL(string: urlString) @@ -82,15 +95,16 @@ struct APIService { data: Data?, completion: @escaping (Result) -> Void ) { + var receivedData = Data() guard let data = data else { completion(.failure(.noDataError)) return } - + receivedData.append(data) do { - let decodedData = try JSONDecoder().decode(T.self, from: data) + let decodedData = try JSONDecoder().decode(T.self, from: receivedData) completion(.success(decodedData)) } catch { completion(.failure(.decodingError)) diff --git a/BoxOffice/Network/Mock/MockURLSession.swift b/BoxOffice/Network/Mock/MockURLSession.swift index 524e11ac..5e97ea18 100644 --- a/BoxOffice/Network/Mock/MockURLSession.swift +++ b/BoxOffice/Network/Mock/MockURLSession.swift @@ -28,4 +28,17 @@ final class MockURLSession: URLSessionProtocol { )} ) } + + func dataTask( + with request: URLRequest, + completionHandler: @escaping @Sendable (Data?, URLResponse?, Error?) -> Void + ) -> URLSessionDataTaskProtocol { + return MockURLSessionDataTask(completionHandler: { + completionHandler( + self.response.data, + self.response.urlResponse, + self.response.error + )} + ) + } } diff --git a/BoxOffice/Network/Protocol/URLSessionProtocol.swift b/BoxOffice/Network/Protocol/URLSessionProtocol.swift index 8d08c8ae..105c82f2 100644 --- a/BoxOffice/Network/Protocol/URLSessionProtocol.swift +++ b/BoxOffice/Network/Protocol/URLSessionProtocol.swift @@ -12,4 +12,9 @@ protocol URLSessionProtocol { with url: URL, completionHandler: @escaping @Sendable (Data?, URLResponse?, Error?) -> Void ) -> URLSessionDataTaskProtocol + + func dataTask( + with request: URLRequest, + completionHandler: @escaping @Sendable (Data?, URLResponse?, Error?) -> Void + ) -> URLSessionDataTaskProtocol } From 471108a6e295921d7d6f2e4a54cc392307e33e5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthew=28=EB=A7=A4=ED=8A=9C=29?= Date: Sun, 10 Mar 2024 21:32:58 +0900 Subject: [PATCH 08/39] =?UTF-8?q?refactor:=20Kakao=20=EA=B2=80=EC=83=89=20?= =?UTF-8?q?API=EB=A5=BC=20=EC=9C=84=ED=95=9C=20URLRequest=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice/Model/NetworkURL.swift | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/BoxOffice/Model/NetworkURL.swift b/BoxOffice/Model/NetworkURL.swift index 00cbfcb2..b869c9b8 100644 --- a/BoxOffice/Model/NetworkURL.swift +++ b/BoxOffice/Model/NetworkURL.swift @@ -24,11 +24,23 @@ struct NetworkURL { return url } - static func makeMovieImageURL(code: String) -> String { + static func makeMovieImageURL(movieName: String) -> URLRequest? { let key = Bundle.main.kakaoApiKey - var url: String { - return "https://dapi.kakao.com/v2/search/image/&query=\(code)" + var urlComponents = URLComponents() + urlComponents.scheme = "https" + urlComponents.host = "dapi.kakao.com" + urlComponents.path = "/v2/search/image" + urlComponents.queryItems = [ + URLQueryItem(name: "query", value: "\(movieName) 영화포스터"), + URLQueryItem(name: "sort", value: "accuracy") + ] + guard let url = urlComponents.url else { + return nil } - return url + + var request = URLRequest(url: url) + request.httpMethod = "GET" + request.allHTTPHeaderFields = ["Authorization": "KakaoAK \(key)"] + return request } } From 0f1d566c253c8d4e1d771e579fda54387ead1765 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthew=28=EB=A7=A4=ED=8A=9C=29?= Date: Sun, 10 Mar 2024 21:33:52 +0900 Subject: [PATCH 09/39] =?UTF-8?q?refactor:=20=EC=98=81=ED=99=94=20?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=20API=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice/Model/MovieManager.swift | 59 ++++++++++++++++++++++++++++-- 1 file changed, 56 insertions(+), 3 deletions(-) diff --git a/BoxOffice/Model/MovieManager.swift b/BoxOffice/Model/MovieManager.swift index 0a6b73c8..b72194ae 100644 --- a/BoxOffice/Model/MovieManager.swift +++ b/BoxOffice/Model/MovieManager.swift @@ -5,11 +5,12 @@ // Created by 강창현 on 2/13/24. // -import Foundation +import UIKit final class MovieManager { var movieDetailData: MovieInfomationDetail? var dailyBoxOfficeData: BoxOfficeResult? + var movieImageData: Document? } extension MovieManager { @@ -18,7 +19,7 @@ extension MovieManager { completion: @escaping (Result) -> Void ) { let apiService = APIService() - let urlString = MovieURL.makeDailyBoxOfficeURL(date: date) + let urlString = NetworkURL.makeDailyBoxOfficeURL(date: date) apiService.fetchData(urlString: urlString) { [weak self] (result: Result) in switch result { @@ -37,7 +38,7 @@ extension MovieManager { completion: @escaping (Result) -> Void ) { let apiService = APIService() - let urlString = MovieURL.makeMovieInfomationDetailURL(code: code) + let urlString = NetworkURL.makeMovieInfomationDetailURL(code: code) apiService.fetchData(urlString: urlString) { (result: Result) in switch result { @@ -49,4 +50,56 @@ extension MovieManager { } } } + + func fetchMoiveImageData( + movieName: String, + completion: @escaping (Result) -> Void + ) { + let apiService = APIService() + guard + let urlString = NetworkURL.makeMovieImageURL(movieName: movieName) + else { + print("fetchMoiveImageData\(NetworkError.invalidURLError)") + return + } + + apiService.fetchImageData(urlRequest: urlString) { [weak self] (result: Result) in + switch result { + case .success(let image): + self?.movieImageData = image.documents[0] + guard + let data = self?.movieImageData + else { + return + } + completion(.success(data)) + case .failure(let error): + completion(.failure(error)) + } + } + } + + private func makeHttps(data: Document) -> String { + let originUrl = data.imageURL + var editingUrl = originUrl.components(separatedBy: "://") + editingUrl[0] = "https" + let resultUrl = "\(editingUrl[0])://\(editingUrl[1])" + return resultUrl + } + + func loadImage(data: Document, + completion: @escaping (Result) -> Void) -> UIImage? { + let url = makeHttps(data: data) + APIService().fetchData(urlString: url) { (result: Result) in + <#code#> + } +// guard +// let url = URL(string: url), +// let data = try? Data(contentsOf: url), +// let image = UIImage(data: data) +// else { +// return nil +// } +// return image + } } From cac53ecfad4bed9d4ba785a2aedddfa4f63b2cf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthew=28=EB=A7=A4=ED=8A=9C=29?= Date: Sun, 10 Mar 2024 21:34:48 +0900 Subject: [PATCH 10/39] =?UTF-8?q?fix:=20Json=20=EB=94=94=EC=BD=94=EB=94=A9?= =?UTF-8?q?=20=ED=83=80=EC=9E=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice/Model/Movie/Image/Document.swift | 13 +++++++------ BoxOffice/Model/Movie/Image/Meta.swift | 6 ++++++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/BoxOffice/Model/Movie/Image/Document.swift b/BoxOffice/Model/Movie/Image/Document.swift index d0194439..59d3c117 100644 --- a/BoxOffice/Model/Movie/Image/Document.swift +++ b/BoxOffice/Model/Movie/Image/Document.swift @@ -9,19 +9,20 @@ struct Document: Codable { let collection: String let thumbnailURL: String let imageURL: String - let width, height: Int - let displaySitename: String + let width: Int + let height: Int + let displaySiteName: String let docURL: String let datetime: String enum CodingKeys: String, CodingKey { case collection - case thumbnailURL - case imageURL + case thumbnailURL = "thumbnail_url" + case imageURL = "image_url" case width case height - case displaySitename = "displaySiteName" - case docURL + case displaySiteName = "display_sitename" + case docURL = "doc_url" case datetime } } diff --git a/BoxOffice/Model/Movie/Image/Meta.swift b/BoxOffice/Model/Movie/Image/Meta.swift index dec383ad..ff4219b7 100644 --- a/BoxOffice/Model/Movie/Image/Meta.swift +++ b/BoxOffice/Model/Movie/Image/Meta.swift @@ -9,4 +9,10 @@ struct Meta: Codable { let totalCount: Int let pageableCount: Int let isEnd: Bool + + enum CodingKeys: String, CodingKey { + case isEnd = "is_end" + case pageableCount = "pageable_count" + case totalCount = "total_count" + } } From d8d52e6fd5a515ec1a52d4d38281225db9319b5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthew=28=EB=A7=A4=ED=8A=9C=29?= Date: Sun, 10 Mar 2024 21:35:29 +0900 Subject: [PATCH 11/39] =?UTF-8?q?refactor:=20=EC=98=81=ED=99=94=ED=8F=AC?= =?UTF-8?q?=EC=8A=A4=ED=84=B0=20=EB=B7=B0=EC=97=90=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../BoxOfficeDetailViewController.swift | 29 ++++++++++++++++++- .../Controller/BoxOfficeViewController.swift | 7 ++++- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/BoxOffice/Controller/BoxOfficeDetailViewController.swift b/BoxOffice/Controller/BoxOfficeDetailViewController.swift index da5be35f..437bb20d 100644 --- a/BoxOffice/Controller/BoxOfficeDetailViewController.swift +++ b/BoxOffice/Controller/BoxOfficeDetailViewController.swift @@ -9,11 +9,38 @@ import UIKit final class BoxOfficeDetailViewController: UIViewController { - let scrollView = BoxOfficeDetailView() + private let movieName: String + private let movieManger: MovieManager + private let scrollView = BoxOfficeDetailView() + private var receivedData: Data? + + init(movieName: String, movieManger: MovieManager) { + self.movieName = movieName + self.movieManger = movieManger + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } override func viewDidLoad() { super.viewDidLoad() view = scrollView view.backgroundColor = .white + setupMovieImage() + } + + func setupMovieImage() { + movieManger.fetchMoiveImageData(movieName: movieName) { result in + switch result { + case .success(let data): + DispatchQueue.main.async { + self.scrollView.movieImageView.image = self.movieManger.loadImage(data: data) + } + case .failure(let failure): + print("fetchMoiveImageData 실패: \(failure)") + } + } } } diff --git a/BoxOffice/Controller/BoxOfficeViewController.swift b/BoxOffice/Controller/BoxOfficeViewController.swift index 46bb28ad..60960eaa 100644 --- a/BoxOffice/Controller/BoxOfficeViewController.swift +++ b/BoxOffice/Controller/BoxOfficeViewController.swift @@ -91,7 +91,12 @@ private extension BoxOfficeViewController { extension BoxOfficeViewController: UICollectionViewDelegate { func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { - let detailVC = BoxOfficeDetailViewController() + guard + let movieName = movieManager.dailyBoxOfficeData?.dailyBoxOfficeList[indexPath.row].name + else { + return + } + let detailVC = BoxOfficeDetailViewController(movieName: movieName, movieManger: movieManager) self.navigationController?.pushViewController(detailVC, animated: true) } } From 73a4e2154ee4052c662a433b78acce3273307a85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthew=28=EB=A7=A4=ED=8A=9C=29?= Date: Sun, 10 Mar 2024 21:35:49 +0900 Subject: [PATCH 12/39] =?UTF-8?q?refactor:=20Step4=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice.xcodeproj/project.pbxproj | 10 ++++++---- BoxOffice/Extension/URLSession+Extension.swift | 10 ++++++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/BoxOffice.xcodeproj/project.pbxproj b/BoxOffice.xcodeproj/project.pbxproj index 16b41f99..e02916ab 100644 --- a/BoxOffice.xcodeproj/project.pbxproj +++ b/BoxOffice.xcodeproj/project.pbxproj @@ -51,7 +51,7 @@ 63DF20FB2970E1A1005DF7D1 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 63DF20F92970E1A1005DF7D1 /* LaunchScreen.storyboard */; }; C14DF4B52B7DAE49009E3258 /* Bundle+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C14DF4B42B7DAE49009E3258 /* Bundle+Extension.swift */; }; C14DF4B72B7DAF3A009E3258 /* Private.plist in Resources */ = {isa = PBXBuildFile; fileRef = C14DF4B62B7DAF3A009E3258 /* Private.plist */; }; - C14DF4B92B7DAF97009E3258 /* MovieURL.swift in Sources */ = {isa = PBXBuildFile; fileRef = C14DF4B82B7DAF97009E3258 /* MovieURL.swift */; }; + C14DF4B92B7DAF97009E3258 /* NetworkURL.swift in Sources */ = {isa = PBXBuildFile; fileRef = C14DF4B82B7DAF97009E3258 /* NetworkURL.swift */; }; C16A4D6E2B9ADF2100498BB3 /* BoxOfficeDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C16A4D6D2B9ADF2100498BB3 /* BoxOfficeDetailView.swift */; }; C16A4D702B9AE4E600498BB3 /* BoxOfficeDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C16A4D6F2B9AE4E600498BB3 /* BoxOfficeDetailViewController.swift */; }; C16A4D722B9AE92C00498BB3 /* ReusedDetailStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C16A4D712B9AE92C00498BB3 /* ReusedDetailStackView.swift */; }; @@ -121,7 +121,7 @@ 63DF20FC2970E1A1005DF7D1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; C14DF4B42B7DAE49009E3258 /* Bundle+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Bundle+Extension.swift"; sourceTree = ""; }; C14DF4B62B7DAF3A009E3258 /* Private.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Private.plist; sourceTree = ""; }; - C14DF4B82B7DAF97009E3258 /* MovieURL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MovieURL.swift; sourceTree = ""; }; + C14DF4B82B7DAF97009E3258 /* NetworkURL.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkURL.swift; sourceTree = ""; }; C16A4D6D2B9ADF2100498BB3 /* BoxOfficeDetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoxOfficeDetailView.swift; sourceTree = ""; }; C16A4D6F2B9AE4E600498BB3 /* BoxOfficeDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoxOfficeDetailViewController.swift; sourceTree = ""; }; C16A4D712B9AE92C00498BB3 /* ReusedDetailStackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReusedDetailStackView.swift; sourceTree = ""; }; @@ -226,7 +226,7 @@ 276ED76B2B7B006D00D37EBF /* MovieManager.swift */, 2757936B2B7F1D8100C08906 /* Movie */, 276ED7752B7B08E100D37EBF /* NetworkError.swift */, - C14DF4B82B7DAF97009E3258 /* MovieURL.swift */, + C14DF4B82B7DAF97009E3258 /* NetworkURL.swift */, C1FC46AE2B84961300C6E49E /* JSON */, ); path = Model; @@ -524,7 +524,7 @@ 275793882B7F518900C08906 /* MockURLSessionDataTask.swift in Sources */, 63DF20F12970E1A0005DF7D1 /* SceneDelegate.swift in Sources */, 27AE2EDC2B86E950009B43E2 /* RankStackView.swift in Sources */, - C14DF4B92B7DAF97009E3258 /* MovieURL.swift in Sources */, + C14DF4B92B7DAF97009E3258 /* NetworkURL.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -557,6 +557,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = T92LV6HV88; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; @@ -580,6 +581,7 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = T92LV6HV88; ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu17; GENERATE_INFOPLIST_FILE = YES; diff --git a/BoxOffice/Extension/URLSession+Extension.swift b/BoxOffice/Extension/URLSession+Extension.swift index 594c4ff7..a9189306 100644 --- a/BoxOffice/Extension/URLSession+Extension.swift +++ b/BoxOffice/Extension/URLSession+Extension.swift @@ -8,6 +8,16 @@ import Foundation extension URLSession: URLSessionProtocol { + func dataTask( + with request: URLRequest, + completionHandler: @escaping @Sendable (Data?, URLResponse?, Error?) -> Void + ) -> URLSessionDataTaskProtocol { + return dataTask( + with: request, + completionHandler: completionHandler + ) as URLSessionDataTask + } + func dataTask( with url: URL, completionHandler: @escaping @Sendable (Data?, URLResponse?, Error?) -> Void From bb09d0692b723863ba3b87b09d69e5537094a639 Mon Sep 17 00:00:00 2001 From: Changhyun Kang Date: Fri, 15 Mar 2024 17:35:19 +0900 Subject: [PATCH 13/39] =?UTF-8?q?chore:=20=EB=B6=88=ED=95=84=EC=9A=94=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EC=82=AD=EC=A0=9C=20=EB=B0=8F=20=EC=97=90?= =?UTF-8?q?=EC=85=8B=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice/Model/MovieRepository.swift | 101 ++++++++++++++++++ .../Network/Mock/MockURLSessionDataTask.swift | 20 ---- .../Protocol/URLSessionDataTaskProtocol.swift | 12 --- .../prepare.imageset/Contents.json | 21 ++++ .../prepare.imageset/prepare.jpeg | Bin 0 -> 5282 bytes BoxOffice/View/LoadingIndicatorView.swift | 45 ++++++++ 6 files changed, 167 insertions(+), 32 deletions(-) create mode 100644 BoxOffice/Model/MovieRepository.swift delete mode 100644 BoxOffice/Network/Mock/MockURLSessionDataTask.swift delete mode 100644 BoxOffice/Network/Protocol/URLSessionDataTaskProtocol.swift create mode 100644 BoxOffice/Resource/Assets.xcassets/prepare.imageset/Contents.json create mode 100644 BoxOffice/Resource/Assets.xcassets/prepare.imageset/prepare.jpeg create mode 100644 BoxOffice/View/LoadingIndicatorView.swift diff --git a/BoxOffice/Model/MovieRepository.swift b/BoxOffice/Model/MovieRepository.swift new file mode 100644 index 00000000..a9d78476 --- /dev/null +++ b/BoxOffice/Model/MovieRepository.swift @@ -0,0 +1,101 @@ +// +// MovieRepository.swift +// BoxOffice +// +// Created by 강창현 on 3/13/24. +// + +import Foundation + +struct MovieRepository { + private let apiService = APIService.init() + + func fetchBoxOfficeResultData(date: String) async throws -> (Result) { + guard + let urlRequest = NetworkURL.makeDailyBoxOfficeRequest(date: date) + else { + return .failure(.invalidURLRequestError) + } + + let result = try await apiService.fetchData(with: urlRequest) + switch result { + case .success(let success): + return handleDecodedData(data: success) + case .failure(let failure): + return .failure(failure) + } + } + + func fetchMovieInfoResultData(code: String) async throws -> (Result) { + guard + let urlRequest = NetworkURL.makeMovieInfomationDetailRequest(code: code) + else { + return .failure(.invalidURLRequestError) + } + + let result = try await apiService.fetchData(with: urlRequest) + switch result { + case .success(let success): + return handleDecodedData(data: success) + case .failure(let failure): + return .failure(failure) + } + } + + func fetchMoiveImageURL(movieName: String) async throws -> (Result) { + guard + let urlRequest = NetworkURL.makeMovieImageRequest(movieName: movieName) + else { + return .failure(.invalidURLRequestError) + } + + let result = try await apiService.fetchData(with: urlRequest) + switch result { + case .success(let success): + return handleDecodedData(data: success) + case .failure(let failure): + return .failure(failure) + } + } + + func fetchMovieImage(urlString: String) async throws -> (Result) { + guard + let url = makeHttps(urlString: urlString) + else { + return .failure(.invalidURLError) + } + let urlRequest = URLRequest(url: url) + + let result = try await apiService.fetchData(with: urlRequest) + return result + } +} + +private extension MovieRepository { + func handleDecodedData(data: Data?) -> (Result) { + guard + let data = data + else { + return .failure(.noDataError) + } + + do { + let decodedData = try JSONDecoder().decode(T.self, from: data) + return .success(decodedData) + } catch { + return .failure(.decodingError) + } + } + + func makeHttps(urlString: String) -> URL? { + let originUrl = urlString + var editingUrl = originUrl.components(separatedBy: "://") + editingUrl[0] = "https" + guard + let resultUrl = URL(string: "\(editingUrl[0])://\(editingUrl[1])") + else { + return nil + } + return resultUrl + } +} diff --git a/BoxOffice/Network/Mock/MockURLSessionDataTask.swift b/BoxOffice/Network/Mock/MockURLSessionDataTask.swift deleted file mode 100644 index 57bc4a5c..00000000 --- a/BoxOffice/Network/Mock/MockURLSessionDataTask.swift +++ /dev/null @@ -1,20 +0,0 @@ -// -// MockURLSessionDataTask.swift -// BoxOffice -// -// Created by 강창현 on 2/16/24. -// - -import Foundation - -final class MockURLSessionDataTask: URLSessionDataTaskProtocol { - private let completionHandler: () -> Void - - init(completionHandler: @escaping () -> Void) { - self.completionHandler = completionHandler - } - - func resume() { - completionHandler() - } -} diff --git a/BoxOffice/Network/Protocol/URLSessionDataTaskProtocol.swift b/BoxOffice/Network/Protocol/URLSessionDataTaskProtocol.swift deleted file mode 100644 index ec14af79..00000000 --- a/BoxOffice/Network/Protocol/URLSessionDataTaskProtocol.swift +++ /dev/null @@ -1,12 +0,0 @@ -// -// URLSessionDataTaskProtocol.swift -// BoxOffice -// -// Created by Matthew on 2/20/24. -// - -import Foundation - -protocol URLSessionDataTaskProtocol { - func resume() -} diff --git a/BoxOffice/Resource/Assets.xcassets/prepare.imageset/Contents.json b/BoxOffice/Resource/Assets.xcassets/prepare.imageset/Contents.json new file mode 100644 index 00000000..bb97684a --- /dev/null +++ b/BoxOffice/Resource/Assets.xcassets/prepare.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "prepare.jpeg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/BoxOffice/Resource/Assets.xcassets/prepare.imageset/prepare.jpeg b/BoxOffice/Resource/Assets.xcassets/prepare.imageset/prepare.jpeg new file mode 100644 index 0000000000000000000000000000000000000000..37c4daa319fe219121a22f22cf9468a4470c72d3 GIT binary patch literal 5282 zcmeHJXH-+^y50~31VvE?6{HAQkdguk0Rt!{bWo8FVn{+sNCHWK1S#r>3<84+C{6T; zAXU%+Q9x;;2mwZVktW4LlMYgHqE}~Ttr_Q@ANT${@7jCqZ$IB#p0|8^ZEhEL2-su9 z@b&-zQ_~}WFz|c{y*Kj=VkG5@P9VfcNq?38La{f~-zsgNRry zoV=c{9EzdMAo-H;bc`H>$x(b9%M<-UnLFB%o+p?%5_{Vf*n zO-KG)Qh|YiYJuu%6sji}s->j`hQPoum?}?0mBu8~F$`5QP2sx-Lp%*j_4cKEQ^;~( zJYw7_{&XFA9@GCxf#hpx`Mcx4trm&&C9kjAH2Nw0A2j|}n|7M%iwB>=(qHDBlIJONxK@4)bJdx1*;-+wOre+cMz`DV_~$H&{{-M?@ArweW+ATG=&!6yOY69@Rk z`9R`)+~>e8-l^sX@d3Q+yYLHb5)>BT18o-J`vd&8xgFpW06-uCA#oAGonJAKz$QWd zT|#_2f7+p-s3E>z-G*i;@d38jVTs(Y8 z|5SR-rF%tfMtJ}5gJ_SELxJLd3C8UKw(xlIi-W`gJz%5bJHh`x|34~VeT}`E8dlf4 zeC+%`x!+ftlRP7}?aHK4Y}7*qRCh`pVo`8fp_@fi94&J#QSqpXUM={NC%{$^^{l0pln6gKZJcuQ%!>4yKL^ zpfgO&YK^O!4W9fGi#{F-Ez!GW)DT^8Ww}dY@CWG_%RcP=7_?^#}{Gn9s!=bL%q&FxK_hu>&rx!!~~V4-X?}e2Nyyjs?n7b zzx^QhU-hu;wuME$m7943Pl-z{_)u$tV1z`9e}?=uW?M(%Wy^ONaWuJ3_1)qhU>1o1%p@2|ot}8#tX@vtTvu%;#7f zVm~20LU;GU^B=q$V4?^5<3N`luBtw}pI<0;>vPW9#FOOWutDM3w~U{yjw9!N!r``ew=bfPCtdEBt+^ieUPp z4J)*?L(ixy43I@W%R%@!Fp&@ZT^Oh-stmUA{RN#0TW9^q2}|>0iX`%AGQqe@!nkP( zvVB#_kqfxyaDntgmCLJ2-4(7x*}D^Z=S9yM)5$g#8TT_3@88X2RfNzP}qRL+?!oE+wuRG;zUOxk`h-cCRo?aCOEDyH{TZBF_?a?p84Bue>9X%YEyVge<>c_ zU1$F`)!^=Uq@A?22dpVgZvw}>11-8~pFOGEnteD$KzB2HVbS`~0X1>z&T%sj+L_Tr zOv_?fcVFeA&ASIy#r!p=3UzZhko>C3a>i_h(Ee=+X^+6&F%h)dgR*1Kj`-Yd3O;f+ zNF(!7}GUFfHUF4*VJ~pK3aE!axUbU88@7L-N#^AqMw8_ zgRO?VtYnPzqVEnZpL2MOp0zBKmT;Uy_bv%Dp3QH)j*T{YQ`G-kQ?|z=BS@>(p1?Fq z7J0$r!N}DM4IVV>J z;Q}!wAm@8MY4-CGX6a`K&p47;?HV=(HJN?Ykkzs7JWlv^N_K5$;@Bz2m(j?@t1y3u zqXEfXNkjDX)e)VIlPOV`o}WXknF*apUC54RW7#Ibtw~(KlN<+9PS=BbhJCiX7dA3= zzT|J~#JK0Cc_o%9KQ5X|N>q0ozSPa~Q=Fsr7Qaq+T9nZaToC{YT!@=gbCk8|w;EX< zug3PF{O$){_UTD~kED*a>00ISVs2e7eX5RPM+K26!#mnUAzB8QOiot!K8vKCu`@Oro^7 z;M4b3)K7dG#9|lB;=*f#o`##-3C%hp8Hm|kM-AOuq7dZv0(7H6;Syq{y3e&xg8IvJ zoZ~Z@{uOx2T<8j=IBQoUdi_5A^V(MU(`3^%+jIPIJ(Yd-W8%5m*L{YSZ_;pt=2s;T z=7O688#Ww7wc4-iA{t%u9#_xd^25t(>Yiq^2h`tmJbHj*sRi5TAG!(Jd^|clG9sQR zq;;{nSGsy|a`<^G`Bx`*Gc5eTHZA}PEwZ;;#sdAKv;j$c z{tR{@B6Q|~QTpwftA|ZdeRQr; zw}B1nCbSt&I@xFcHa!ua^yIStyoM^Zoj~Soni_B|;u$A13bXOoQgtSrJr0ovodj zdBmtoow2?dsRny0l--UlnJ2_aN`-O)Onra66*?Zch_l zYwd;`G!YaWBC;ZF{JK^ub99XqR1gq#HGbOMmm&O^(_HhY(g59R^HV^3ln7yX;$!Ch zKn*T1wQZO`=uUEEz1P^B7)o~aqL3%vdI|MOh9DX+?gD4;Xmret6}*d{YV&O@?G5#; zUCnI*eQ4)cB@VW<-+lW@kMV2Xv20nfl*^yqSGYx3(}SCBh(nb{m^Y@V7>|$cht9uJ z{W;TMMsRNLCYH2YB;q}}^pM`lZ?M074?w4%>L$gc_sQDZQKRPwZ)$!C_GukJnVGUe zV{VY*LXrbh_;a0BoUbRh_M$HKL|rtyy&KY3qt_i$g~Q7@Do>^>mReaCcFIpa%B4x4 z6$MLO*~mFPCf{LITZxMwoZVb!+|Z6oXu!MDJzApYQbG!De>(4et!4T2$o`RA4#gta zeOqd@>RtqhaxOh5oPs`ee7Tj**`^_>jOnm6ao(YP_Bi28N@hy&c<{B>0sJ>mjRm?@>t(96YY5Rn{WO2R!&9cQQtvlLf2~oRQi|5W| zPFl|D-u4mmeuVN9%~`sc;bs?ps>-4>dNSsrJOsX4OxM!k|Q+T*8|7w z`b!hruSiWGL`SptWF37=mT}>AyKWwH<9aOd2D!VzAz(|NPsokMjrmPcQN|yTsVLtx z<#Xf%^0Oz08LOjr;mHMxsUG)IeLD(ZBwEuVU!J291n#(1xyCt?+NqV7z>X8!#e_m; zI-Dv{?X1W{uvpphdMIa2_7;fbmr0UHOiRX4+m6h3msG#wDjq2Lb986$xl`T_lwn__%V?H5mLv=!v{MYR|@2&Pd-d*^YVc`Q~ zyJ{mS#OcKl)jQTf>`=EA;)Sh`OWyX@*G9?c2^YNKmosNY#l0HEjxe7d+vDe6!%m2h zRgev~?&N3OaF&oRgQ}FEq$iDZmokOg1PZe<_n}fnuvLR?Yf^ywNekYO*u(r-AquPf ugzyD5I~CoR5_&IJ`_GH>JACmq Date: Fri, 15 Mar 2024 17:35:50 +0900 Subject: [PATCH 14/39] =?UTF-8?q?test:=20=EC=9C=A0=EB=8B=9B=20=ED=85=8C?= =?UTF-8?q?=EC=8A=A4=ED=8A=B8=20=EB=A1=9C=EC=A7=81=20=EC=9E=91=EC=84=B1=20?= =?UTF-8?q?=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOfficeTests/BoxOfficeMockTests.swift | 150 +++++++------- BoxOfficeTests/BoxOfficeTests.swift | 248 ++++++++++++------------ 2 files changed, 199 insertions(+), 199 deletions(-) diff --git a/BoxOfficeTests/BoxOfficeMockTests.swift b/BoxOfficeTests/BoxOfficeMockTests.swift index b4fdb0d9..36341f82 100644 --- a/BoxOfficeTests/BoxOfficeMockTests.swift +++ b/BoxOfficeTests/BoxOfficeMockTests.swift @@ -1,78 +1,78 @@ +//// +//// BoxOfficeMockTests.swift +//// BoxOfficeTests +//// +//// Created by 강창현 on 2/22/24. +//// // -// BoxOfficeMockTests.swift -// BoxOfficeTests +//import XCTest +//@testable import BoxOffice // -// Created by 강창현 on 2/22/24. +//final class BoxOfficeMockTests: XCTestCase { +// func test_MockURLSession의_응답코드가_400이면_clientError가_발생한다() { +// // given +// let urlString = NetworkURL.makeDailyBoxOfficeURL(date: Date.movieDateToString) +// let mockURLSession = makeMockURLSession(fileName: JSONFileName.boxOffice, url: urlString, statusCode: 400) +// let sut = setSUT(session: mockURLSession) +// +// // when +// let expectation = XCTestExpectation(description: "데이터 패치 중...") +// +// sut.fetchData(urlString: urlString) { (result: Result) in +// switch result { +// case .success(_): +// // then +// XCTFail() +// case .failure(let error): +// // then +// XCTAssertEqual(error, NetworkError.clientError) +// } +// expectation.fulfill() +// } +// wait(for: [expectation], timeout: 5.0) +// } +// +// func test_MockURLSession의_응답코드가_200이면_boxOfficeResult는_nil이_아니다() { +// // given +// let date = "20240210" +// let urlString = NetworkURL.makeDailyBoxOfficeURL(date: date) +// let mockURLSession = makeMockURLSession(fileName: JSONFileName.boxOffice, url: urlString, statusCode: 200) +// let sut = setSUT(session: mockURLSession) +// +// // when +// let expectation = XCTestExpectation(description: "데이터 패치 중...") +// +// sut.fetchData(urlString: urlString) { (result: Result) in +// switch result { +// case .success(let movies): +// // then +// XCTAssertNotNil(movies) +// case .failure(let error): +// // then +// XCTFail("데이터 파싱 에러 발생: \(error))") +// } +// expectation.fulfill() +// } +// wait(for: [expectation], timeout: 5.0) +// } +//} // - -import XCTest -@testable import BoxOffice - -final class BoxOfficeMockTests: XCTestCase { - func test_MockURLSession의_응답코드가_400이면_clientError가_발생한다() { - // given - let urlString = NetworkURL.makeDailyBoxOfficeURL(date: Date.movieDateToString) - let mockURLSession = makeMockURLSession(fileName: JSONFileName.boxOffice, url: urlString, statusCode: 400) - let sut = setSUT(session: mockURLSession) - - // when - let expectation = XCTestExpectation(description: "데이터 패치 중...") - - sut.fetchData(urlString: urlString) { (result: Result) in - switch result { - case .success(_): - // then - XCTFail() - case .failure(let error): - // then - XCTAssertEqual(error, NetworkError.clientError) - } - expectation.fulfill() - } - wait(for: [expectation], timeout: 5.0) - } - - func test_MockURLSession의_응답코드가_200이면_boxOfficeResult는_nil이_아니다() { - // given - let date = "20240210" - let urlString = NetworkURL.makeDailyBoxOfficeURL(date: date) - let mockURLSession = makeMockURLSession(fileName: JSONFileName.boxOffice, url: urlString, statusCode: 200) - let sut = setSUT(session: mockURLSession) - - // when - let expectation = XCTestExpectation(description: "데이터 패치 중...") - - sut.fetchData(urlString: urlString) { (result: Result) in - switch result { - case .success(let movies): - // then - XCTAssertNotNil(movies) - case .failure(let error): - // then - XCTFail("데이터 파싱 에러 발생: \(error))") - } - expectation.fulfill() - } - wait(for: [expectation], timeout: 5.0) - } -} - -private extension BoxOfficeMockTests { - func setSUT(session: URLSessionProtocol) -> APIService { - return APIService(session: session) - } - - func makeMockURLSession(fileName: String, url: String, statusCode: Int) -> MockURLSession { - var response: MockURLSession.Response { - let data: Data? = JSONLoader.load(fileName: fileName) - let response = HTTPURLResponse( - url: URL(string: url)!, - statusCode: statusCode, - httpVersion: nil, - headerFields: nil - ) - return (data, response, nil) - } - return MockURLSession(response: response) - } -} +//private extension BoxOfficeMockTests { +// func setSUT(session: URLSessionProtocol) -> APIService { +// return APIService(session: session) +// } +// +// func makeMockURLSession(fileName: String, url: String, statusCode: Int) -> MockURLSession { +// var response: MockURLSession.Response { +// let data: Data? = JSONLoader.load(fileName: fileName) +// let response = HTTPURLResponse( +// url: URL(string: url)!, +// statusCode: statusCode, +// httpVersion: nil, +// headerFields: nil +// ) +// return (data, response, nil) +// } +// return MockURLSession(response: response) +// } +//} diff --git a/BoxOfficeTests/BoxOfficeTests.swift b/BoxOfficeTests/BoxOfficeTests.swift index 3ccfd838..8591602b 100644 --- a/BoxOfficeTests/BoxOfficeTests.swift +++ b/BoxOfficeTests/BoxOfficeTests.swift @@ -5,127 +5,127 @@ // Created by 강창현 on 2/13/24. // -import XCTest -@testable import BoxOffice - -final class BoxOfficeTests: XCTestCase { - - private var sut: APIService! - - override func setUp() { - super.setUp() - self.sut = APIService() - } - - override func tearDown() { - super.tearDown() - self.sut = nil - } - - func test_date가_20230101이고_데이터_파싱이_올바르게_됐을_때_fetchData는_nil이_아니다() { - // given - let date = "20170319" - let urlString = NetworkURL.makeDailyBoxOfficeURL(date: date) - - // when - let expectation = XCTestExpectation(description: "데이터 패치 중...") - - sut.fetchData(urlString: urlString) { (result: Result) in - switch result { - case .success(let movies): - // then - XCTAssertNotNil(movies) - case .failure(let error): - // then - XCTFail("데이터 파싱 에러 발생: \(error))") - } - expectation.fulfill() - } - wait(for: [expectation], timeout: 5.0) - } - - func test_date가_잘못된_타입으로_데이터_파싱_됐을_때_fetchMovie에서_decodingError발생() { - // given - let wrongDate = "iWantToGoHome" - let urlString = NetworkURL.makeDailyBoxOfficeURL(date: wrongDate) - - // when - let expectation = XCTestExpectation(description: "데이터 패치 중...") - - sut.fetchData(urlString: urlString) { (result: Result) in - switch result { - case .success(let movies): - // then - XCTFail("데이터 파싱 성공: \(movies))") - case .failure(let error): - // then - XCTAssertEqual(error, NetworkError.decodingError) - } - expectation.fulfill() - } - wait(for: [expectation], timeout: 5.0) - } - - func test_url이_잘못된_주소로_데이터_파싱_됐을_때_fetchMovie에서_invalidURLError_감지발생() { - // given - let url = "qqqq://안녕하세요.닷컴 " - - // when - let expectation = XCTestExpectation(description: "데이터 패치 중...") - - sut.fetchData(urlString: url) { (result: Result) in - switch result { - case .success(let movies): - // then - XCTFail("데이터 파싱 성공: \(movies))") - case .failure(let error): - // then - XCTAssertEqual(error, NetworkError.invalidURLError) - } - expectation.fulfill() - } - wait(for: [expectation], timeout: 5.0) - } - - func test_어제날짜_영화_데이터_파싱이_올바르게_됐을_때_fetchData에_boxOfficeResult는_nil이_아니다() { - // given - let urlString = NetworkURL.makeDailyBoxOfficeURL(date: Date.movieDateToString) - - // when - let expectation = XCTestExpectation(description: "데이터 패치 중...") - - sut.fetchData(urlString: urlString) { (result: Result) in - switch result { - case .success(let movies): - // then - XCTAssertNotNil(movies) - case .failure(let error): - // then - XCTFail("데이터 파싱 에러 발생: \(error))") - } - expectation.fulfill() - } - wait(for: [expectation], timeout: 5.0) - } - - func test_특정_영화_코드로_데이터_파싱이_올바르게_됐을_때_fetchData에_movieInfoResult는_nil이_아니다() { - // given - let urlString = NetworkURL.makeMovieInfomationDetailURL(code: "20124079") - - // when - let expectation = XCTestExpectation(description: "데이터 패치 중...") - - sut.fetchData(urlString: urlString) { (result: Result) in - switch result { - case .success(let movies): - // then - XCTAssertNotNil(movies) - case .failure(let error): - // then - XCTFail("데이터 파싱 에러 발생: \(error))") - } - expectation.fulfill() - } - wait(for: [expectation], timeout: 5.0) - } -} +//import XCTest +//@testable import BoxOffice +// +//final class BoxOfficeTests: XCTestCase { +// +// private var sut: APIService! +// +// override func setUp() { +// super.setUp() +// self.sut = APIService() +// } +// +// override func tearDown() { +// super.tearDown() +// self.sut = nil +// } +// +// func test_date가_20230101이고_데이터_파싱이_올바르게_됐을_때_fetchData는_nil이_아니다() { +// // given +// let date = "20170319" +// let urlString = NetworkURL.makeDailyBoxOfficeURL(date: date) +// +// // when +// let expectation = XCTestExpectation(description: "데이터 패치 중...") +// +// sut.fetchData(urlString: urlString) { (result: Result) in +// switch result { +// case .success(let movies): +// // then +// XCTAssertNotNil(movies) +// case .failure(let error): +// // then +// XCTFail("데이터 파싱 에러 발생: \(error))") +// } +// expectation.fulfill() +// } +// wait(for: [expectation], timeout: 5.0) +// } +// +// func test_date가_잘못된_타입으로_데이터_파싱_됐을_때_fetchMovie에서_decodingError발생() { +// // given +// let wrongDate = "iWantToGoHome" +// let urlString = NetworkURL.makeDailyBoxOfficeURL(date: wrongDate) +// +// // when +// let expectation = XCTestExpectation(description: "데이터 패치 중...") +// +// sut.fetchData(urlString: urlString) { (result: Result) in +// switch result { +// case .success(let movies): +// // then +// XCTFail("데이터 파싱 성공: \(movies))") +// case .failure(let error): +// // then +// XCTAssertEqual(error, NetworkError.decodingError) +// } +// expectation.fulfill() +// } +// wait(for: [expectation], timeout: 5.0) +// } +// +// func test_url이_잘못된_주소로_데이터_파싱_됐을_때_fetchMovie에서_invalidURLError_감지발생() { +// // given +// let url = "qqqq://안녕하세요.닷컴 " +// +// // when +// let expectation = XCTestExpectation(description: "데이터 패치 중...") +// +// sut.fetchData(urlString: url) { (result: Result) in +// switch result { +// case .success(let movies): +// // then +// XCTFail("데이터 파싱 성공: \(movies))") +// case .failure(let error): +// // then +// XCTAssertEqual(error, NetworkError.invalidURLError) +// } +// expectation.fulfill() +// } +// wait(for: [expectation], timeout: 5.0) +// } +// +// func test_어제날짜_영화_데이터_파싱이_올바르게_됐을_때_fetchData에_boxOfficeResult는_nil이_아니다() { +// // given +// let urlString = NetworkURL.makeDailyBoxOfficeURL(date: Date.movieDateToString) +// +// // when +// let expectation = XCTestExpectation(description: "데이터 패치 중...") +// +// sut.fetchData(urlString: urlString) { (result: Result) in +// switch result { +// case .success(let movies): +// // then +// XCTAssertNotNil(movies) +// case .failure(let error): +// // then +// XCTFail("데이터 파싱 에러 발생: \(error))") +// } +// expectation.fulfill() +// } +// wait(for: [expectation], timeout: 5.0) +// } +// +// func test_특정_영화_코드로_데이터_파싱이_올바르게_됐을_때_fetchData에_movieInfoResult는_nil이_아니다() { +// // given +// let urlString = NetworkURL.makeMovieInfomationDetailURL(code: "20124079") +// +// // when +// let expectation = XCTestExpectation(description: "데이터 패치 중...") +// +// sut.fetchData(urlString: urlString) { (result: Result) in +// switch result { +// case .success(let movies): +// // then +// XCTAssertNotNil(movies) +// case .failure(let error): +// // then +// XCTFail("데이터 파싱 에러 발생: \(error))") +// } +// expectation.fulfill() +// } +// wait(for: [expectation], timeout: 5.0) +// } +//} From 8f239b1bebcc4aaf45ee96e3b0552ae3499e86da Mon Sep 17 00:00:00 2001 From: Changhyun Kang Date: Fri, 15 Mar 2024 17:37:59 +0900 Subject: [PATCH 15/39] =?UTF-8?q?fix:=20numberOfLines=20->=200=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice/View/ReusedDetailStackView.swift | 42 +++++++++++----------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/BoxOffice/View/ReusedDetailStackView.swift b/BoxOffice/View/ReusedDetailStackView.swift index 9e7b11d3..43e60777 100644 --- a/BoxOffice/View/ReusedDetailStackView.swift +++ b/BoxOffice/View/ReusedDetailStackView.swift @@ -7,18 +7,18 @@ import UIKit -class ReusedDetailStackView: UIStackView { - lazy var titleLabel: UILabel = { +final class ReusedDetailStackView: UIStackView { + private lazy var titleLabel: UILabel = { let label = UILabel() label.font = UIFont.preferredFont(for: .body, weight: .bold) label.textAlignment = .center return label }() - lazy var contentLabel: UILabel = { + private lazy var contentLabel: UILabel = { let label = UILabel() label.font = UIFont.preferredFont(forTextStyle: .body) - label.numberOfLines = 10 + label.numberOfLines = 0 label.textAlignment = .left return label }() @@ -32,19 +32,15 @@ class ReusedDetailStackView: UIStackView { required init(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } - - func configure() { - self.addArrangedSubview(titleLabel) - self.addArrangedSubview(contentLabel) - self.axis = .horizontal - self.alignment = .leading - self.distribution = .fill - self.spacing = 5 - self.backgroundColor = .red - } - + func setupStackView(title: String, contents: [String]) { titleLabel.text = title + guard + !contents.isEmpty + else { + contentLabel.text = "-" + return + } var textList: String = "" for (index, text) in contents.enumerated() { textList.append(text) @@ -54,13 +50,19 @@ class ReusedDetailStackView: UIStackView { } contentLabel.text = textList } - - func setupStackView(title: String, content: String) { - titleLabel.text = title - contentLabel.text = content +} + +private extension ReusedDetailStackView { + private func configure() { + self.addArrangedSubview(titleLabel) + self.addArrangedSubview(contentLabel) + self.axis = .horizontal + self.alignment = .leading + self.distribution = .fill + self.spacing = 5 } - func setupConstraint() { + private func setupConstraint() { titleLabel.translatesAutoresizingMaskIntoConstraints = false titleLabel.widthAnchor.constraint(equalToConstant: UIScreen.main.bounds.width / 6).isActive = true } From 58db67de49da69dfab001babafedaf28fac2db22 Mon Sep 17 00:00:00 2001 From: Changhyun Kang Date: Fri, 15 Mar 2024 17:38:40 +0900 Subject: [PATCH 16/39] =?UTF-8?q?chore:=20Step3=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice/View/MovieStackView.swift | 4 +- BoxOffice/View/RankStackView.swift | 23 ++----- BoxOffice/View/RankStateView.swift | 95 +++++++++++++++++------------ 3 files changed, 61 insertions(+), 61 deletions(-) diff --git a/BoxOffice/View/MovieStackView.swift b/BoxOffice/View/MovieStackView.swift index 82416120..ec443cfc 100644 --- a/BoxOffice/View/MovieStackView.swift +++ b/BoxOffice/View/MovieStackView.swift @@ -37,6 +37,8 @@ private extension MovieStackView { self.spacing = 0 self.distribution = .fillEqually self.alignment = .leading + self.addArrangedSubview(movieNameLabel) + self.addArrangedSubview(movieAudienceLabel) } } @@ -50,7 +52,5 @@ extension MovieStackView { return } movieAudienceLabel.text = "오늘 \(todayAudienceCount.numberFormatter) / 총 \(totalAudienceCount.numberFormatter)" - self.addArrangedSubview(movieNameLabel) - self.addArrangedSubview(movieAudienceLabel) } } diff --git a/BoxOffice/View/RankStackView.swift b/BoxOffice/View/RankStackView.swift index c8b04301..1727ea9a 100644 --- a/BoxOffice/View/RankStackView.swift +++ b/BoxOffice/View/RankStackView.swift @@ -15,15 +15,6 @@ final class RankStackView: UIStackView { return label }() - private let newMovieLabel: UILabel = { - let label = UILabel() - label.font = UIFont.preferredFont(forTextStyle: .caption1) - label.textAlignment = .center - label.textColor = .red - label.text = "신작" - return label - }() - private let rankStateView = RankStateView() override init(frame: CGRect) { @@ -42,15 +33,6 @@ private extension RankStackView { self.spacing = 4 self.distribution = .fill self.alignment = .center - } - - func configureNewMovieLabel() { - self.addArrangedSubview(rankLabel) - self.addArrangedSubview(newMovieLabel) - } - - func configureRankChangedMovieLabel(rankChanged: String) { - rankStateView.configureRankStateView(rankState: rankChanged) self.addArrangedSubview(rankLabel) self.addArrangedSubview(rankStateView) } @@ -59,6 +41,9 @@ private extension RankStackView { extension RankStackView { func configure(rank: String, rankState: String, rankChanged: String) { rankLabel.text = rank - rankState == "NEW" ? configureNewMovieLabel() : configureRankChangedMovieLabel(rankChanged: rankChanged) + rankStateView.configureRankState( + rankState: rankState, + rankChanged: rankChanged + ) } } diff --git a/BoxOffice/View/RankStateView.swift b/BoxOffice/View/RankStateView.swift index 950af9d2..6c62de3f 100644 --- a/BoxOffice/View/RankStateView.swift +++ b/BoxOffice/View/RankStateView.swift @@ -15,25 +15,32 @@ final class RankStateView: UIStackView { return label }() - private let rankUpImage: UIImageView = { - let imageView = UIImageView() - imageView.image = UIImage(systemName: "arrowtriangle.up.fill") - imageView.tintColor = .systemRed - imageView.adjustsImageSizeForAccessibilityContentSizeCategory = true - return imageView + private var rankStateImage = UIImageView() + + private let rankUpImage: UIImage? = { + let image = UIImage( + systemName: "arrowtriangle.up.fill" + )?.withTintColor( + .systemRed, + renderingMode: .alwaysOriginal + ) + return image }() - private let rankDownImage: UIImageView = { - let imageView = UIImageView() - imageView.image = UIImage(systemName: "arrowtriangle.down.fill") - imageView.tintColor = .systemBlue - imageView.adjustsImageSizeForAccessibilityContentSizeCategory = true - return imageView + private let rankDownImage: UIImage? = { + let image = UIImage( + systemName: "arrowtriangle.down.fill" + )?.withTintColor( + .systemBlue, + renderingMode: .alwaysOriginal + ) + return image }() override init(frame: CGRect) { super.init(frame: frame) setupStackView() + setupConstraints() } required init(coder: NSCoder) { @@ -47,45 +54,53 @@ private extension RankStateView { self.spacing = 0 self.distribution = .fill self.alignment = .center - } - - func configureRankUp(rankState: String) { - self.rankStateLabel.text = rankState - self.addArrangedSubview(rankUpImage) + self.addArrangedSubview(rankStateImage) self.addArrangedSubview(rankStateLabel) - rankUpImage.translatesAutoresizingMaskIntoConstraints = false - rankUpImage.heightAnchor.constraint(equalTo: rankStateLabel.heightAnchor).isActive = true } - func configureRankDown(rankState: String) { - self.rankStateLabel.text = rankState - self.addArrangedSubview(rankDownImage) - self.addArrangedSubview(rankStateLabel) - rankDownImage.translatesAutoresizingMaskIntoConstraints = false - rankDownImage.heightAnchor.constraint(equalTo: rankStateLabel.heightAnchor).isActive = true + func setupConstraints() { + rankStateImage.translatesAutoresizingMaskIntoConstraints = false + rankStateImage.heightAnchor.constraint(equalTo: rankStateLabel.heightAnchor).isActive = true } - - func configureRankNone() { - self.rankStateLabel.text = "-" - self.addArrangedSubview(rankStateLabel) - } -} -extension RankStateView { - func configureRankStateView(rankState: String) { - guard + func setRankStateView(rankState: String) -> [String? : UIImage?] { + guard let intRankState = Int(rankState) else { - return + return [nil: nil] } - self.subviews.forEach { $0.removeFromSuperview() } + self.rankStateLabel.textColor = .black switch intRankState { case let state where 0 < state : - return configureRankUp(rankState: rankState) + return [String(state): rankUpImage] case let state where state < 0 : - return configureRankDown(rankState: String(abs(intRankState))) - default: - return configureRankNone() + return [String(abs(state)): rankDownImage] + default : + return ["-": nil] + } + } +} + +extension RankStateView { + func configureRankState(rankState: String, rankChanged: String) { + guard + rankState != "NEW" + else { + self.rankStateLabel.textColor = .systemRed + self.rankStateLabel.text = "신작" + return + } + + _ = setRankStateView(rankState: rankChanged).map { (key: String?, value: UIImage?) in + self.rankStateLabel.text = key + guard + let rankImageView = value + else { + rankStateImage.isHidden = true + return + } + rankStateImage.isHidden = false + self.rankStateImage.image = rankImageView } } } From 60eafb377f6ca372f85553591d85aa458b3ad569 Mon Sep 17 00:00:00 2001 From: Changhyun Kang Date: Fri, 15 Mar 2024 17:39:44 +0900 Subject: [PATCH 17/39] =?UTF-8?q?feat:=20BoxOfficeDetailView=20=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=ED=8C=A8=EC=B9=98=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice/View/BoxOfficeDetailView.swift | 67 ++++++++++++++---------- 1 file changed, 38 insertions(+), 29 deletions(-) diff --git a/BoxOffice/View/BoxOfficeDetailView.swift b/BoxOffice/View/BoxOfficeDetailView.swift index 7c4f0972..7d3559c9 100644 --- a/BoxOffice/View/BoxOfficeDetailView.swift +++ b/BoxOffice/View/BoxOfficeDetailView.swift @@ -7,18 +7,18 @@ import UIKit -class BoxOfficeDetailView: UIScrollView { +final class BoxOfficeDetailView: UIScrollView { - let directorView = ReusedDetailStackView() - let productYearView = ReusedDetailStackView() - let openDateView = ReusedDetailStackView() - let showTimeView = ReusedDetailStackView() - let watchGradeView = ReusedDetailStackView() - let nationsView = ReusedDetailStackView() - let genresView = ReusedDetailStackView() - let actorsView = ReusedDetailStackView() + private let directorView = ReusedDetailStackView() + private let productYearView = ReusedDetailStackView() + private let openDateView = ReusedDetailStackView() + private let showTimeView = ReusedDetailStackView() + private let watchGradeView = ReusedDetailStackView() + private let nationsView = ReusedDetailStackView() + private let genresView = ReusedDetailStackView() + private let actorsView = ReusedDetailStackView() - lazy var movieInfoStackView: UIStackView = { + private lazy var movieInfoStackView: UIStackView = { let stack = UIStackView(arrangedSubviews: [ directorView, productYearView, @@ -36,16 +36,15 @@ class BoxOfficeDetailView: UIScrollView { return stack }() - lazy var innerView: UIView = { + private lazy var innerView: UIView = { let view = UIView() view.backgroundColor = .white return view }() - let movieImageView: UIImageView = { + private let movieImageView: UIImageView = { let view = UIImageView() - view.image = UIImage(systemName: "photo.fill") - + view.image = UIImage(named: "prepare") return view }() @@ -53,20 +52,29 @@ class BoxOfficeDetailView: UIScrollView { super.init(frame: frame) setupView() setupConstraints() - directorView.setupStackView(title: "", contents: [""]) - productYearView.setupStackView(title: "", contents: [""]) - openDateView.setupStackView(title: "", contents: [""]) - showTimeView.setupStackView(title: "", contents: [""]) - watchGradeView.setupStackView(title: "", contents: [""]) - nationsView.setupStackView(title: "", contents: [""]) - genresView.setupStackView(title: "", contents: [""]) - actorsView.setupStackView(title: "", contents: [""]) } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } + + func setupDetailView(data: MovieInfo) { + directorView.setupStackView(title: "감독", contents: data.directors.map { $0.peopleName }) + productYearView.setupStackView(title: "제작년도", contents: ["\(data.productYear)년"]) + openDateView.setupStackView(title: "개봉일", contents: [data.openDate.makeDateFormat]) + showTimeView.setupStackView(title: "상영시간", contents: ["\(data.showTime)분"]) + watchGradeView.setupStackView(title: "관람등급", contents: data.audits.map { $0.watchGradeName }) + nationsView.setupStackView(title: "제작국가", contents: data.nations.map { $0.nationName }) + genresView.setupStackView(title: "장르", contents: data.genres.map { $0.genreName }) + actorsView.setupStackView(title: "배우", contents: data.actors.map { $0.peopleName }) + } + func setupImage(image: UIImage?) { + self.movieImageView.image = image + } +} + +private extension BoxOfficeDetailView { func setupView() { self.addSubview(innerView) innerView.addSubview(movieImageView) @@ -75,14 +83,14 @@ class BoxOfficeDetailView: UIScrollView { func setupConstraints() { innerView.translatesAutoresizingMaskIntoConstraints = false - innerView.topAnchor.constraint(equalTo: self.contentLayoutGuide.topAnchor).isActive = true - innerView.leadingAnchor.constraint(equalTo: self.contentLayoutGuide.leadingAnchor).isActive = true - innerView.trailingAnchor.constraint(equalTo: self.contentLayoutGuide.trailingAnchor).isActive = true + innerView.topAnchor.constraint(equalTo: self.topAnchor).isActive = true + innerView.leadingAnchor.constraint(equalTo: self.leadingAnchor).isActive = true + innerView.trailingAnchor.constraint(equalTo: self.trailingAnchor).isActive = true innerView.bottomAnchor.constraint(equalTo: self.contentLayoutGuide.bottomAnchor).isActive = true innerView.widthAnchor.constraint(equalTo: self.frameLayoutGuide.widthAnchor).isActive = true - innerView.heightAnchor.constraint(equalToConstant: 1000).isActive = true - + innerView.heightAnchor.constraint(equalTo: self.contentLayoutGuide.heightAnchor).isActive = true + movieImageView.translatesAutoresizingMaskIntoConstraints = false movieImageView.topAnchor.constraint(equalTo: innerView.topAnchor, constant: 10).isActive = true movieImageView.centerXAnchor.constraint(equalTo: innerView.centerXAnchor).isActive = true @@ -91,7 +99,8 @@ class BoxOfficeDetailView: UIScrollView { movieInfoStackView.translatesAutoresizingMaskIntoConstraints = false movieInfoStackView.topAnchor.constraint(equalTo: movieImageView.bottomAnchor, constant: 10).isActive = true - movieInfoStackView.leadingAnchor.constraint(equalTo: innerView.leadingAnchor).isActive = true - movieInfoStackView.trailingAnchor.constraint(equalTo: innerView.trailingAnchor).isActive = true + movieInfoStackView.leadingAnchor.constraint(equalTo: innerView.leadingAnchor, constant: 10).isActive = true + movieInfoStackView.trailingAnchor.constraint(equalTo: innerView.trailingAnchor, constant: -10).isActive = true + movieInfoStackView.bottomAnchor.constraint(equalTo: innerView.bottomAnchor, constant: -10).isActive = true } } From 186da18c7412c310db6841d25d03b78d969804c9 Mon Sep 17 00:00:00 2001 From: Changhyun Kang Date: Fri, 15 Mar 2024 17:40:34 +0900 Subject: [PATCH 18/39] =?UTF-8?q?refactor:=20URLSession=20=ED=8C=A8?= =?UTF-8?q?=EC=B9=98=20=EB=A1=9C=EC=A7=81=20=EB=B3=80=EA=B2=BD=EC=97=90=20?= =?UTF-8?q?=EB=94=B0=EB=A5=B8=20=ED=94=84=EB=A1=9C=ED=86=A0=EC=BD=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Extension/URLSession+Extension.swift | 22 ++----------- BoxOffice/Network/Mock/MockURLSession.swift | 31 +++++-------------- .../Network/Protocol/URLSessionProtocol.swift | 12 ++----- 3 files changed, 13 insertions(+), 52 deletions(-) diff --git a/BoxOffice/Extension/URLSession+Extension.swift b/BoxOffice/Extension/URLSession+Extension.swift index a9189306..f6400783 100644 --- a/BoxOffice/Extension/URLSession+Extension.swift +++ b/BoxOffice/Extension/URLSession+Extension.swift @@ -8,25 +8,7 @@ import Foundation extension URLSession: URLSessionProtocol { - func dataTask( - with request: URLRequest, - completionHandler: @escaping @Sendable (Data?, URLResponse?, Error?) -> Void - ) -> URLSessionDataTaskProtocol { - return dataTask( - with: request, - completionHandler: completionHandler - ) as URLSessionDataTask - } - - func dataTask( - with url: URL, - completionHandler: @escaping @Sendable (Data?, URLResponse?, Error?) -> Void - ) -> URLSessionDataTaskProtocol { - return dataTask( - with: url, - completionHandler: completionHandler - ) as URLSessionDataTask + func requestData(with request: URLRequest) async throws -> (Data, URLResponse) { + return try await data(for: request) } } - -extension URLSessionDataTask: URLSessionDataTaskProtocol {} diff --git a/BoxOffice/Network/Mock/MockURLSession.swift b/BoxOffice/Network/Mock/MockURLSession.swift index 5e97ea18..19a91b15 100644 --- a/BoxOffice/Network/Mock/MockURLSession.swift +++ b/BoxOffice/Network/Mock/MockURLSession.swift @@ -8,7 +8,7 @@ import Foundation final class MockURLSession: URLSessionProtocol { - typealias Response = (data: Data?, urlResponse: URLResponse?, error: Error?) + typealias Response = (Data, URLResponse) private let response: Response @@ -16,29 +16,14 @@ final class MockURLSession: URLSessionProtocol { self.response = response } - func dataTask( - with url: URL, - completionHandler: @escaping @Sendable (Data?, URLResponse?, Error?) -> Void - ) -> URLSessionDataTaskProtocol { - return MockURLSessionDataTask(completionHandler: { - completionHandler( - self.response.data, - self.response.urlResponse, - self.response.error - )} - ) + func data( + for request: URLRequest, + delegate: (any URLSessionTaskDelegate)? = nil + ) async throws -> (Data, URLResponse) { + return response } - func dataTask( - with request: URLRequest, - completionHandler: @escaping @Sendable (Data?, URLResponse?, Error?) -> Void - ) -> URLSessionDataTaskProtocol { - return MockURLSessionDataTask(completionHandler: { - completionHandler( - self.response.data, - self.response.urlResponse, - self.response.error - )} - ) + func requestData(with request: URLRequest) async throws -> (Data, URLResponse) { + return try await data(for: request) } } diff --git a/BoxOffice/Network/Protocol/URLSessionProtocol.swift b/BoxOffice/Network/Protocol/URLSessionProtocol.swift index 105c82f2..145d423a 100644 --- a/BoxOffice/Network/Protocol/URLSessionProtocol.swift +++ b/BoxOffice/Network/Protocol/URLSessionProtocol.swift @@ -8,13 +8,7 @@ import Foundation protocol URLSessionProtocol { - func dataTask( - with url: URL, - completionHandler: @escaping @Sendable (Data?, URLResponse?, Error?) -> Void - ) -> URLSessionDataTaskProtocol - - func dataTask( - with request: URLRequest, - completionHandler: @escaping @Sendable (Data?, URLResponse?, Error?) -> Void - ) -> URLSessionDataTaskProtocol + func requestData( + with request: URLRequest + ) async throws -> (Data, URLResponse) } From 4129046e45b5b290a13f81e8af5e6126ab7b0dfe Mon Sep 17 00:00:00 2001 From: Changhyun Kang Date: Fri, 15 Mar 2024 17:41:11 +0900 Subject: [PATCH 19/39] =?UTF-8?q?chore:=20=ED=94=84=EB=A1=9C=ED=8D=BC?= =?UTF-8?q?=ED=8B=B0=20=EB=B0=8F=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=9D=80?= =?UTF-8?q?=EB=8B=89=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../View/BoxOfficeCollectionView/BoxOfficeListView.swift | 8 -------- 1 file changed, 8 deletions(-) diff --git a/BoxOffice/View/BoxOfficeCollectionView/BoxOfficeListView.swift b/BoxOffice/View/BoxOfficeCollectionView/BoxOfficeListView.swift index 3438edf9..3c1dd023 100644 --- a/BoxOffice/View/BoxOfficeCollectionView/BoxOfficeListView.swift +++ b/BoxOffice/View/BoxOfficeCollectionView/BoxOfficeListView.swift @@ -9,7 +9,6 @@ import UIKit final class BoxOfficeListView: UICollectionView { weak var boxOfficeListDelegate: BoxOfficeListViewDelegate? - let indicatorView = UIActivityIndicatorView() private lazy var refresher: UIRefreshControl = { let refreshControl = UIRefreshControl() let action = UIAction { _ in @@ -24,7 +23,6 @@ final class BoxOfficeListView: UICollectionView { collectionViewLayout layout: UICollectionViewLayout ) { super.init(frame: frame, collectionViewLayout: layout) - setupIndicatorView() collectionViewRegister() } @@ -42,12 +40,6 @@ private extension BoxOfficeListView { self.refreshControl = refresher } - func setupIndicatorView() { - self.backgroundView = indicatorView - self.isScrollEnabled = false - indicatorView.startAnimating() - } - func refreshCollectionView() { DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) { self.boxOfficeListDelegate?.applyBoxOfficeListView() From 0dfffa6a05ac67cd0fae075692c8b22d403dcbeb Mon Sep 17 00:00:00 2001 From: Changhyun Kang Date: Fri, 15 Mar 2024 17:42:08 +0900 Subject: [PATCH 20/39] =?UTF-8?q?refactor:=20=EA=B8=B0=EC=A1=B4=20completi?= =?UTF-8?q?on=20Handler=20->=20async=20await=20=EB=A1=9C=EC=A7=81=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice/Network/APIService.swift | 103 ++++++++--------------------- 1 file changed, 29 insertions(+), 74 deletions(-) diff --git a/BoxOffice/Network/APIService.swift b/BoxOffice/Network/APIService.swift index 91a8e237..9726fe89 100644 --- a/BoxOffice/Network/APIService.swift +++ b/BoxOffice/Network/APIService.swift @@ -8,106 +8,61 @@ import Foundation struct APIService { + typealias APIResult = (Result) private let session: URLSessionProtocol init(session: URLSessionProtocol = URLSession.shared) { self.session = session } - func fetchImageData(urlRequest: URLRequest, completion: @escaping (Result) -> Void) { - self.session.dataTask(with: urlRequest) { data, response, error in - DispatchQueue.global().async { - self.handleDataTaskCompletion( - data: data, - response: response, - error: error, - completion: completion - ) - } - }.resume() - } - - func fetchData(urlString: String, completion: @escaping (Result) -> Void) { + func fetchData(with urlRequest: URLRequest) async throws -> APIResult { guard - let url = URL(string: urlString) + let cachedResponse = URLCache.shared.cachedResponse(for: urlRequest) else { - completion(.failure(.invalidURLError)) - return + let (data, response) = try await self.session.requestData(with: urlRequest) + let cachedURLResponse = CachedURLResponse(response: response, data: data) + URLCache.shared.storeCachedResponse(cachedURLResponse, for: urlRequest) + return handleDataTaskCompletion(data: data, response: response) } - - self.session.dataTask(with: url) { data, response, error in - DispatchQueue.global().async { - self.handleDataTaskCompletion( - data: data, - response: response, - error: error, - completion: completion - ) - } - }.resume() + return handleDataTaskCompletion(data: cachedResponse.data, response: cachedResponse.response) } - - private func handleDataTaskCompletion( +} + +private extension APIService { + func handleDataTaskCompletion( data: Data?, - response: URLResponse?, - error: Error?, - completion: @escaping (Result) -> Void - ) { - guard - error == nil - else { - completion(.failure(.requestFailError)) - return - } - + response: URLResponse? + ) -> APIResult { guard let httpResponse = response as? HTTPURLResponse else { - completion(.failure(.invalidResponseError)) - return + return .failure(.invalidResponseError) } - self.handleHTTPResponse( + return self.handleHTTPResponse( data: data, - httpResponse: httpResponse, - completion: completion + httpResponse: httpResponse ) } - private func handleHTTPResponse( + func handleHTTPResponse( data: Data?, - httpResponse: HTTPURLResponse, - completion: @escaping (Result) -> Void - ) { + httpResponse: HTTPURLResponse + ) -> APIResult { + guard + let data = data + else { + return .failure(.noDataError) + } switch httpResponse.statusCode { case 300..<400: - completion(.failure(.redirectionError)) + return .failure(.redirectionError) case 400..<500: - completion(.failure(.clientError)) + return .failure(.clientError) case 500..<600: - completion(.failure(.serverError)) + return .failure(.serverError) default: - self.handleDecodedData(data: data, completion: completion) - } - } - - private func handleDecodedData( - data: Data?, - completion: @escaping (Result) -> Void - ) { - var receivedData = Data() - guard - let data = data - else { - completion(.failure(.noDataError)) - return - } - receivedData.append(data) - do { - let decodedData = try JSONDecoder().decode(T.self, from: receivedData) - completion(.success(decodedData)) - } catch { - completion(.failure(.decodingError)) + return .success(data) } } } From a7b94c1424fdd3be6cbb64ebecbb5e1aab7683f3 Mon Sep 17 00:00:00 2001 From: Changhyun Kang Date: Fri, 15 Mar 2024 17:43:05 +0900 Subject: [PATCH 21/39] =?UTF-8?q?refactor:=20=EA=B8=B0=EC=A1=B4=20url=20?= =?UTF-8?q?=ED=8C=A8=EC=B9=98=20=EB=A1=9C=EC=A7=81=20->=20urlRequest=20?= =?UTF-8?q?=ED=8C=A8=EC=B9=98=EB=A1=9C=20=EB=A6=AC=ED=8C=A9=ED=86=A0?= =?UTF-8?q?=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice/Model/NetworkError.swift | 3 ++ BoxOffice/Model/NetworkURL.swift | 65 +++++++++++++++++++++++++----- 2 files changed, 59 insertions(+), 9 deletions(-) diff --git a/BoxOffice/Model/NetworkError.swift b/BoxOffice/Model/NetworkError.swift index b878463b..c06c6d88 100644 --- a/BoxOffice/Model/NetworkError.swift +++ b/BoxOffice/Model/NetworkError.swift @@ -9,6 +9,7 @@ import Foundation enum NetworkError: LocalizedError { case invalidURLError + case invalidURLRequestError case noDataError case requestFailError case invalidResponseError @@ -21,6 +22,8 @@ enum NetworkError: LocalizedError { switch self { case .invalidURLError: return "잘못된 URL 주소입니다." + case .invalidURLRequestError: + return "잘못된 URL 요청입니다." case .noDataError: return "데이터가 존재하지 않습니다." case .requestFailError: diff --git a/BoxOffice/Model/NetworkURL.swift b/BoxOffice/Model/NetworkURL.swift index b869c9b8..94e2de63 100644 --- a/BoxOffice/Model/NetworkURL.swift +++ b/BoxOffice/Model/NetworkURL.swift @@ -7,24 +7,70 @@ import Foundation +enum APIType { + case boxOffice + case movieInfo + case kakao +} + struct NetworkURL { - static func makeDailyBoxOfficeURL(date: String) -> String { +// static func makeDailyBoxOfficeURL(date: String) -> String { +// let key = Bundle.main.movieApiKey +// var url: String { +// return "https://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json?key=\(String(describing: key))&targetDt=\(date)" +// } +// return url +// } +// +// static func makeMovieInfomationDetailURL(code: String) -> String { +// let key = Bundle.main.movieApiKey +// var url: String { +// return "https://kobis.or.kr/kobisopenapi/webservice/rest/movie/searchMovieInfo.json?key=\(String(describing: key))&movieCd=\(code)" +// } +// return url +// } + + static func makeDailyBoxOfficeRequest(date: String) -> URLRequest? { let key = Bundle.main.movieApiKey - var url: String { - return "https://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json?key=\(String(describing: key))&targetDt=\(date)" + var urlComponents = URLComponents() + urlComponents.scheme = "https" + urlComponents.host = "www.kobis.or.kr" + urlComponents.path = "/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json" + urlComponents.queryItems = [ + URLQueryItem(name: "key", value: key), + URLQueryItem(name: "targetDt", value: date) + ] + + guard let url = urlComponents.url else { + return nil } - return url + + var request = URLRequest(url: url) + request.httpMethod = "GET" + return request } - static func makeMovieInfomationDetailURL(code: String) -> String { + static func makeMovieInfomationDetailRequest(code: String) -> URLRequest? { let key = Bundle.main.movieApiKey - var url: String { - return "https://kobis.or.kr/kobisopenapi/webservice/rest/movie/searchMovieInfo.json?key=\(String(describing: key))&movieCd=\(code)" + var urlComponents = URLComponents() + urlComponents.scheme = "https" + urlComponents.host = "www.kobis.or.kr" + urlComponents.path = "/kobisopenapi/webservice/rest/movie/searchMovieInfo.json" + urlComponents.queryItems = [ + URLQueryItem(name: "key", value: key), + URLQueryItem(name: "movieCd", value: code) + ] + + guard let url = urlComponents.url else { + return nil } - return url + + var request = URLRequest(url: url) + request.httpMethod = "GET" + return request } - static func makeMovieImageURL(movieName: String) -> URLRequest? { + static func makeMovieImageRequest(movieName: String) -> URLRequest? { let key = Bundle.main.kakaoApiKey var urlComponents = URLComponents() urlComponents.scheme = "https" @@ -34,6 +80,7 @@ struct NetworkURL { URLQueryItem(name: "query", value: "\(movieName) 영화포스터"), URLQueryItem(name: "sort", value: "accuracy") ] + guard let url = urlComponents.url else { return nil } From 12ed88946e20dc994d46f87fa971a5a3ab3d38f9 Mon Sep 17 00:00:00 2001 From: Changhyun Kang Date: Fri, 15 Mar 2024 17:43:18 +0900 Subject: [PATCH 22/39] =?UTF-8?q?chore:=20Step3=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice/Extension/Date+Extension.swift | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/BoxOffice/Extension/Date+Extension.swift b/BoxOffice/Extension/Date+Extension.swift index eead3a1b..12685332 100644 --- a/BoxOffice/Extension/Date+Extension.swift +++ b/BoxOffice/Extension/Date+Extension.swift @@ -8,9 +8,17 @@ import Foundation extension Date { - static let yesterday: Date = { + static var yesterday: Date { return Calendar.current.date(byAdding: .day, value: -1, to: Date()) ?? Date() - 86400 - }() + } + + static var movieDateToString: String { + return movieDateFormatter.string(from: yesterday) + } + + static var titleDateToString: String { + return titleDateFormatter.string(from: yesterday) + } static let titleDateFormatter: DateFormatter = { let dateFormatter = DateFormatter() @@ -23,12 +31,4 @@ extension Date { dateFormatter.dateFormat = "yyyyMMdd" return dateFormatter }() - - static let movieDateToString: String = { - return movieDateFormatter.string(from: yesterday) - }() - - static let titleDateToString: String = { - return titleDateFormatter.string(from: yesterday) - }() } From 217aa9249c6a4c913d27027fed6f1e272b91e1e0 Mon Sep 17 00:00:00 2001 From: Changhyun Kang Date: Fri, 15 Mar 2024 17:43:58 +0900 Subject: [PATCH 23/39] =?UTF-8?q?feat:=20makeDateFormat=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice/Extension/String+Extension.swift | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/BoxOffice/Extension/String+Extension.swift b/BoxOffice/Extension/String+Extension.swift index f1be98e4..caa2e55c 100644 --- a/BoxOffice/Extension/String+Extension.swift +++ b/BoxOffice/Extension/String+Extension.swift @@ -9,9 +9,20 @@ import Foundation extension String { func toDateFromRange() -> Date? { - guard let result = self.split(separator: "~").last else { return nil } - let dateFormatter = DateFormatter() - dateFormatter.dateFormat = "yyyyMMdd" - return dateFormatter.date(from: Self(result)) + guard + let result = self.split(separator: "~").last + else { + return nil + } + return Date.movieDateFormatter.date(from: Self(result)) + } + + var makeDateFormat: String { + guard + let date = Date.movieDateFormatter.date(from: self) + else { + return "-" + } + return Date.titleDateFormatter.string(from: date) } } From 85d731afc4af0d1e5c5f173286a40c56eaae704d Mon Sep 17 00:00:00 2001 From: Changhyun Kang Date: Fri, 15 Mar 2024 17:44:13 +0900 Subject: [PATCH 24/39] =?UTF-8?q?refactor:=20=EA=B8=B0=EC=A1=B4=20completi?= =?UTF-8?q?on=20Handler=20->=20async=20await=20=EB=A1=9C=EC=A7=81=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice/Model/MovieManager.swift | 120 +++++++++++------------------ 1 file changed, 43 insertions(+), 77 deletions(-) diff --git a/BoxOffice/Model/MovieManager.swift b/BoxOffice/Model/MovieManager.swift index b72194ae..033baee5 100644 --- a/BoxOffice/Model/MovieManager.swift +++ b/BoxOffice/Model/MovieManager.swift @@ -8,98 +8,64 @@ import UIKit final class MovieManager { - var movieDetailData: MovieInfomationDetail? var dailyBoxOfficeData: BoxOfficeResult? - var movieImageData: Document? + private let movieRepository = MovieRepository() + private var imageURL: String? } extension MovieManager { - func fetchBoxOfficeResultData( - date: String, - completion: @escaping (Result) -> Void - ) { - let apiService = APIService() - let urlString = NetworkURL.makeDailyBoxOfficeURL(date: date) - - apiService.fetchData(urlString: urlString) { [weak self] (result: Result) in - switch result { - case .success(let movies): - self?.dailyBoxOfficeData = movies.boxOfficeResult - guard let result = self?.dailyBoxOfficeData else { return } - completion(.success(result)) - case .failure(let error): - completion(.failure(error)) - } + func setupBoxOfficeDetailData(for indexPath: Int) -> (movieName: String, movieCode: String)? { + guard + let movieName = dailyBoxOfficeData?.dailyBoxOfficeList[indexPath].name, + let movieCode = dailyBoxOfficeData?.dailyBoxOfficeList[indexPath].code + else { + return nil } + return (movieName, movieCode) } - func fetchMovieInfoResultData( - code: String, - completion: @escaping (Result) -> Void - ) { - let apiService = APIService() - let urlString = NetworkURL.makeMovieInfomationDetailURL(code: code) - - apiService.fetchData(urlString: urlString) { (result: Result) in - switch result { - case .success(let movies): - completion(.success(movies)) - self.movieDetailData = movies - case .failure(let error): - completion(.failure(error)) - } + func fetchBoxOfficeResultData(date: String) async throws -> BoxOfficeResult { + let result = try await movieRepository.fetchBoxOfficeResultData(date: date) + switch result { + case .success(let movies): + let result = movies.boxOfficeResult + self.dailyBoxOfficeData = result + return result + case .failure(let error): + throw error } } - func fetchMoiveImageData( - movieName: String, - completion: @escaping (Result) -> Void - ) { - let apiService = APIService() - guard - let urlString = NetworkURL.makeMovieImageURL(movieName: movieName) - else { - print("fetchMoiveImageData\(NetworkError.invalidURLError)") - return - } - - apiService.fetchImageData(urlRequest: urlString) { [weak self] (result: Result) in - switch result { - case .success(let image): - self?.movieImageData = image.documents[0] - guard - let data = self?.movieImageData - else { - return - } - completion(.success(data)) - case .failure(let error): - completion(.failure(error)) - } + func fetchMovieInfoResultData(code: String) async throws -> MovieInfo { + let result = try await movieRepository.fetchMovieInfoResultData(code: code) + switch result { + case .success(let movies): + let result = movies.movieInfoResult.movieInfo + return result + case .failure(let error): + throw error } } - private func makeHttps(data: Document) -> String { - let originUrl = data.imageURL - var editingUrl = originUrl.components(separatedBy: "://") - editingUrl[0] = "https" - let resultUrl = "\(editingUrl[0])://\(editingUrl[1])" - return resultUrl + func fetchMoiveImageURL(movieName: String) async throws { + let result = try await movieRepository.fetchMoiveImageURL(movieName: movieName) + switch result { + case .success(let image): + let movieImageData = image.documents[0] + self.imageURL = movieImageData.imageURL + case .failure(let error): + throw error + } } - func loadImage(data: Document, - completion: @escaping (Result) -> Void) -> UIImage? { - let url = makeHttps(data: data) - APIService().fetchData(urlString: url) { (result: Result) in - <#code#> + func fetchImageData() async throws -> Data? { + guard let imageURL else { return nil } + let result = try await movieRepository.fetchMovieImage(urlString: imageURL) + switch result { + case .success(let data): + return data + case .failure(let error): + throw error } -// guard -// let url = URL(string: url), -// let data = try? Data(contentsOf: url), -// let image = UIImage(data: data) -// else { -// return nil -// } -// return image } } From 4fa307a8b1a183e7358c397e70c3f897c96228a0 Mon Sep 17 00:00:00 2001 From: Changhyun Kang Date: Fri, 15 Mar 2024 17:44:35 +0900 Subject: [PATCH 25/39] =?UTF-8?q?refactor:=20=EA=B8=B0=EC=A1=B4=20completi?= =?UTF-8?q?on=20Handler=20->=20async=20await=20=EB=A1=9C=EC=A7=81=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice.xcodeproj/project.pbxproj | 22 +++---- .../BoxOfficeDetailViewController.swift | 56 +++++++++++----- .../Controller/BoxOfficeViewController.swift | 65 +++++++++++-------- 3 files changed, 89 insertions(+), 54 deletions(-) diff --git a/BoxOffice.xcodeproj/project.pbxproj b/BoxOffice.xcodeproj/project.pbxproj index e02916ab..d0965d92 100644 --- a/BoxOffice.xcodeproj/project.pbxproj +++ b/BoxOffice.xcodeproj/project.pbxproj @@ -19,7 +19,6 @@ 2757937F2B7F1E3F00C08906 /* ShowType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2757937E2B7F1E3F00C08906 /* ShowType.swift */; }; 275793812B7F1E4C00C08906 /* Staff.swift in Sources */ = {isa = PBXBuildFile; fileRef = 275793802B7F1E4C00C08906 /* Staff.swift */; }; 275793862B7F516800C08906 /* MockURLSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 275793852B7F516800C08906 /* MockURLSession.swift */; }; - 275793882B7F518900C08906 /* MockURLSessionDataTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 275793872B7F518900C08906 /* MockURLSessionDataTask.swift */; }; 2757938A2B7F51BF00C08906 /* URLSession+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 275793892B7F51BF00C08906 /* URLSession+Extension.swift */; }; 2757938C2B7F52B500C08906 /* JSONLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2757938B2B7F52B500C08906 /* JSONLoader.swift */; }; 2757938D2B7F546300C08906 /* box_office.json in Resources */ = {isa = PBXBuildFile; fileRef = 275793822B7F402300C08906 /* box_office.json */; }; @@ -35,6 +34,8 @@ 276ED7722B7B013E00D37EBF /* RankOldAndNew.swift in Sources */ = {isa = PBXBuildFile; fileRef = 276ED7712B7B013E00D37EBF /* RankOldAndNew.swift */; }; 276ED7762B7B08E100D37EBF /* NetworkError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 276ED7752B7B08E100D37EBF /* NetworkError.swift */; }; 276ED7982B7B638C00D37EBF /* APIService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 276ED7972B7B638C00D37EBF /* APIService.swift */; }; + 2782B6082BA1ACDD00D55005 /* MovieRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2782B6072BA1ACDD00D55005 /* MovieRepository.swift */; }; + 2782B60A2BA2B99E00D55005 /* LoadingIndicatorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2782B6092BA2B99E00D55005 /* LoadingIndicatorView.swift */; }; 278FA99E2B88BEE100FFDB3F /* BoxOfficeListDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 278FA99D2B88BEE100FFDB3F /* BoxOfficeListDataSource.swift */; }; 278FA9A32B8B0C7500FFDB3F /* BoxOfficeListViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 278FA9A22B8B0C7500FFDB3F /* BoxOfficeListViewDelegate.swift */; }; 27AE2ED62B86D99C009B43E2 /* Date+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27AE2ED52B86D99C009B43E2 /* Date+Extension.swift */; }; @@ -57,7 +58,6 @@ C16A4D722B9AE92C00498BB3 /* ReusedDetailStackView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C16A4D712B9AE92C00498BB3 /* ReusedDetailStackView.swift */; }; C1FC46B02B84962000C6E49E /* JSONFileName.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1FC46AF2B84962000C6E49E /* JSONFileName.swift */; }; C1FC46B22B8496B300C6E49E /* URLSessionProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1FC46B12B8496B300C6E49E /* URLSessionProtocol.swift */; }; - C1FC46B42B84AFFC00C6E49E /* URLSessionDataTaskProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1FC46B32B84AFFC00C6E49E /* URLSessionDataTaskProtocol.swift */; }; C1FC46BC2B85CD5A00C6E49E /* MovieCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1FC46BB2B85CD5A00C6E49E /* MovieCell.swift */; }; C1FC46BE2B85D1A000C6E49E /* BoxOfficeListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1FC46BD2B85D1A000C6E49E /* BoxOfficeListView.swift */; }; C1FC46C22B8772BF00C6E49E /* Int+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1FC46C12B8772BF00C6E49E /* Int+Extension.swift */; }; @@ -87,7 +87,6 @@ 275793802B7F1E4C00C08906 /* Staff.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Staff.swift; sourceTree = ""; }; 275793822B7F402300C08906 /* box_office.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = box_office.json; sourceTree = ""; }; 275793852B7F516800C08906 /* MockURLSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockURLSession.swift; sourceTree = ""; }; - 275793872B7F518900C08906 /* MockURLSessionDataTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockURLSessionDataTask.swift; sourceTree = ""; }; 275793892B7F51BF00C08906 /* URLSession+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLSession+Extension.swift"; sourceTree = ""; }; 2757938B2B7F52B500C08906 /* JSONLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONLoader.swift; sourceTree = ""; }; 2757D9D72B9A9EDB00B13BD7 /* DateFormatterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateFormatterTests.swift; sourceTree = ""; }; @@ -103,6 +102,8 @@ 276ED7712B7B013E00D37EBF /* RankOldAndNew.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RankOldAndNew.swift; sourceTree = ""; }; 276ED7752B7B08E100D37EBF /* NetworkError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkError.swift; sourceTree = ""; }; 276ED7972B7B638C00D37EBF /* APIService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIService.swift; sourceTree = ""; }; + 2782B6072BA1ACDD00D55005 /* MovieRepository.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MovieRepository.swift; sourceTree = ""; }; + 2782B6092BA2B99E00D55005 /* LoadingIndicatorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoadingIndicatorView.swift; sourceTree = ""; }; 278FA99D2B88BEE100FFDB3F /* BoxOfficeListDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoxOfficeListDataSource.swift; sourceTree = ""; }; 278FA9A22B8B0C7500FFDB3F /* BoxOfficeListViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoxOfficeListViewDelegate.swift; sourceTree = ""; }; 27AE2ED52B86D99C009B43E2 /* Date+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Extension.swift"; sourceTree = ""; }; @@ -127,7 +128,6 @@ C16A4D712B9AE92C00498BB3 /* ReusedDetailStackView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReusedDetailStackView.swift; sourceTree = ""; }; C1FC46AF2B84962000C6E49E /* JSONFileName.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONFileName.swift; sourceTree = ""; }; C1FC46B12B8496B300C6E49E /* URLSessionProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLSessionProtocol.swift; sourceTree = ""; }; - C1FC46B32B84AFFC00C6E49E /* URLSessionDataTaskProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLSessionDataTaskProtocol.swift; sourceTree = ""; }; C1FC46BB2B85CD5A00C6E49E /* MovieCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MovieCell.swift; sourceTree = ""; }; C1FC46BD2B85D1A000C6E49E /* BoxOfficeListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoxOfficeListView.swift; sourceTree = ""; }; C1FC46C12B8772BF00C6E49E /* Int+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Int+Extension.swift"; sourceTree = ""; }; @@ -195,7 +195,6 @@ children = ( 2757938B2B7F52B500C08906 /* JSONLoader.swift */, 275793852B7F516800C08906 /* MockURLSession.swift */, - 275793872B7F518900C08906 /* MockURLSessionDataTask.swift */, ); path = Mock; sourceTree = ""; @@ -224,6 +223,7 @@ isa = PBXGroup; children = ( 276ED76B2B7B006D00D37EBF /* MovieManager.swift */, + 2782B6072BA1ACDD00D55005 /* MovieRepository.swift */, 2757936B2B7F1D8100C08906 /* Movie */, 276ED7752B7B08E100D37EBF /* NetworkError.swift */, C14DF4B82B7DAF97009E3258 /* NetworkURL.swift */, @@ -235,8 +235,8 @@ 276ED7742B7B016100D37EBF /* Controller */ = { isa = PBXGroup; children = ( - C16A4D6F2B9AE4E600498BB3 /* BoxOfficeDetailViewController.swift */, 63DF20F22970E1A0005DF7D1 /* BoxOfficeViewController.swift */, + C16A4D6F2B9AE4E600498BB3 /* BoxOfficeDetailViewController.swift */, 278FA99F2B8B0BF500FFDB3F /* Protocol */, ); path = Controller; @@ -295,7 +295,6 @@ isa = PBXGroup; children = ( C1FC46B12B8496B300C6E49E /* URLSessionProtocol.swift */, - C1FC46B32B84AFFC00C6E49E /* URLSessionDataTaskProtocol.swift */, ); path = Protocol; sourceTree = ""; @@ -362,6 +361,7 @@ 27AE2EDD2B86E965009B43E2 /* MovieStackView.swift */, C16A4D6D2B9ADF2100498BB3 /* BoxOfficeDetailView.swift */, C16A4D712B9AE92C00498BB3 /* ReusedDetailStackView.swift */, + 2782B6092BA2B99E00D55005 /* LoadingIndicatorView.swift */, ); path = View; sourceTree = ""; @@ -503,6 +503,7 @@ C16A4D6E2B9ADF2100498BB3 /* BoxOfficeDetailView.swift in Sources */, 275793732B7F1DE200C08906 /* Actor.swift in Sources */, 2757D9DB2B9B2BCB00B13BD7 /* MovieImage.swift in Sources */, + 2782B60A2BA2B99E00D55005 /* LoadingIndicatorView.swift in Sources */, C16A4D722B9AE92C00498BB3 /* ReusedDetailStackView.swift in Sources */, C1FC46C22B8772BF00C6E49E /* Int+Extension.swift in Sources */, 27C0258B2B8B30BD009C3F1F /* String+Extension.swift in Sources */, @@ -510,8 +511,8 @@ 2757937D2B7F1E3200C08906 /* Nation.swift in Sources */, 276ED7762B7B08E100D37EBF /* NetworkError.swift in Sources */, 2757938C2B7F52B500C08906 /* JSONLoader.swift in Sources */, + 2782B6082BA1ACDD00D55005 /* MovieRepository.swift in Sources */, C1FC46B02B84962000C6E49E /* JSONFileName.swift in Sources */, - C1FC46B42B84AFFC00C6E49E /* URLSessionDataTaskProtocol.swift in Sources */, 27AE2ED62B86D99C009B43E2 /* Date+Extension.swift in Sources */, 278FA9A32B8B0C7500FFDB3F /* BoxOfficeListViewDelegate.swift in Sources */, 2757938A2B7F51BF00C08906 /* URLSession+Extension.swift in Sources */, @@ -521,7 +522,6 @@ 27AE2EDA2B86E930009B43E2 /* RankStateView.swift in Sources */, 275793862B7F516800C08906 /* MockURLSession.swift in Sources */, 275793752B7F1DF900C08906 /* Audit.swift in Sources */, - 275793882B7F518900C08906 /* MockURLSessionDataTask.swift in Sources */, 63DF20F12970E1A0005DF7D1 /* SceneDelegate.swift in Sources */, 27AE2EDC2B86E950009B43E2 /* RankStackView.swift in Sources */, C14DF4B92B7DAF97009E3258 /* NetworkURL.swift in Sources */, @@ -718,7 +718,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = T92LV6HV88; + DEVELOPMENT_TEAM = K9NKZR257N; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = BoxOffice/Resource/Info.plist; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; @@ -746,7 +746,7 @@ ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = T92LV6HV88; + DEVELOPMENT_TEAM = K9NKZR257N; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = BoxOffice/Resource/Info.plist; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; diff --git a/BoxOffice/Controller/BoxOfficeDetailViewController.swift b/BoxOffice/Controller/BoxOfficeDetailViewController.swift index 437bb20d..f5005723 100644 --- a/BoxOffice/Controller/BoxOfficeDetailViewController.swift +++ b/BoxOffice/Controller/BoxOfficeDetailViewController.swift @@ -8,15 +8,19 @@ import UIKit final class BoxOfficeDetailViewController: UIViewController { - private let movieName: String - private let movieManger: MovieManager + private let movieCode: String + private let movieManager: MovieManager private let scrollView = BoxOfficeDetailView() - private var receivedData: Data? - init(movieName: String, movieManger: MovieManager) { + init( + movieName: String, + movieCode: String, + movieManager: MovieManager + ) { self.movieName = movieName - self.movieManger = movieManger + self.movieCode = movieCode + self.movieManager = movieManager super.init(nibName: nil, bundle: nil) } @@ -26,21 +30,43 @@ final class BoxOfficeDetailViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() + setupView() + fetchBoxOfficeDetailData() + } +} + +private extension BoxOfficeDetailViewController { + func setupView() { + LoadingIndicatorView.showLoading() view = scrollView view.backgroundColor = .white - setupMovieImage() + self.title = movieName } - func setupMovieImage() { - movieManger.fetchMoiveImageData(movieName: movieName) { result in - switch result { - case .success(let data): - DispatchQueue.main.async { - self.scrollView.movieImageView.image = self.movieManger.loadImage(data: data) - } - case .failure(let failure): - print("fetchMoiveImageData 실패: \(failure)") + func fetchBoxOfficeDetailData() { + Task { + do { + try await fetchMoiveImageURL() + try await fetchMovieInfo() + try await setupMovieImage() + } catch { + print(error.localizedDescription) } + LoadingIndicatorView.hideLoading() } } + + func fetchMovieInfo() async throws { + let data = try await movieManager.fetchMovieInfoResultData(code: movieCode) + self.scrollView.setupDetailView(data: data) + } + + func fetchMoiveImageURL() async throws { + try await movieManager.fetchMoiveImageURL(movieName: movieName) + } + + func setupMovieImage() async throws { + guard let data = try await movieManager.fetchImageData() else { return } + self.scrollView.setupImage(image: UIImage(data: data)) + } } diff --git a/BoxOffice/Controller/BoxOfficeViewController.swift b/BoxOffice/Controller/BoxOfficeViewController.swift index 60960eaa..5810599b 100644 --- a/BoxOffice/Controller/BoxOfficeViewController.swift +++ b/BoxOffice/Controller/BoxOfficeViewController.swift @@ -28,10 +28,7 @@ final class BoxOfficeViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() - boxOfficeListView.boxOfficeListDelegate = self - boxOfficeListView.delegate = self - boxOfficeListView.dataSource = dataSource - view = boxOfficeListView + setupBoxOfficeListView() setupBoxOfficeData() } } @@ -41,13 +38,22 @@ private extension BoxOfficeViewController { self.title = Date.titleDateFormatter.string(from: date) } + func setupBoxOfficeListView() { + LoadingIndicatorView.showLoading() + boxOfficeListView.boxOfficeListDelegate = self + boxOfficeListView.delegate = self + boxOfficeListView.dataSource = dataSource + view = boxOfficeListView + } + func setupBoxOfficeData() { - movieManager.fetchBoxOfficeResultData(date: Date.movieDateToString) { result in - switch result { - case .success(let success): - self.reloadCollectionListData(result: success) - case .failure(let failure): - print("fetchBoxOfficeResultData 실패: \(failure)") + Task { + do { + let result = try await movieManager.fetchBoxOfficeResultData(date: Date.movieDateToString) + self.reloadCollectionListData(result: result) + LoadingIndicatorView.hideLoading() + } catch { + print(error.localizedDescription) } } } @@ -61,27 +67,26 @@ private extension BoxOfficeViewController { } func updateBoxOfficeList() { - movieManager.fetchBoxOfficeResultData(date: Date.movieDateToString) { [weak self] result in - switch result { - case .success(let success): - DispatchQueue.main.async { - self?.applyBoxOfficeList(result: success) - guard - let date = success.showRange.toDateFromRange() - else { - return - } - self?.configureNavigation(date: date) + Task { + do { + let result = try await self.movieManager.fetchBoxOfficeResultData( + date: Date.movieDateToString + ) + self.applyBoxOfficeList(result: result) + guard + let date = result.showRange.toDateFromRange() + else { + return } - case .failure(let failure): - print(failure.localizedDescription) + self.configureNavigation(date: date) + } catch { + print(error.localizedDescription) } } } func reloadCollectionListData(result: BoxOfficeResult) { DispatchQueue.main.async { - self.boxOfficeListView.indicatorView.stopAnimating() self.configureNavigation(date: Date.yesterday) self.applyBoxOfficeList(result: result) self.boxOfficeListView.isScrollEnabled = true @@ -91,13 +96,17 @@ private extension BoxOfficeViewController { extension BoxOfficeViewController: UICollectionViewDelegate { func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { - guard - let movieName = movieManager.dailyBoxOfficeData?.dailyBoxOfficeList[indexPath.row].name + guard + let data = movieManager.setupBoxOfficeDetailData(for: indexPath.row) else { return } - let detailVC = BoxOfficeDetailViewController(movieName: movieName, movieManger: movieManager) - self.navigationController?.pushViewController(detailVC, animated: true) + let detailViewController = BoxOfficeDetailViewController( + movieName: data.movieName, + movieCode: data.movieCode, + movieManager: movieManager + ) + self.navigationController?.pushViewController(detailViewController, animated: true) } } From a49e293726a1cda5361ec931e1303ec1eced6a9d Mon Sep 17 00:00:00 2001 From: Changhyun Kang Date: Tue, 19 Mar 2024 15:24:31 +0900 Subject: [PATCH 26/39] =?UTF-8?q?add:=20URLRequest=20=EC=83=9D=EC=84=B1=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice/Model/NetworkURL.swift | 93 --------------- BoxOffice/Network/NetworkURL.swift | 109 ++++++++++++++++++ BoxOffice/Network/URLRequest/APIPath.swift | 8 ++ BoxOffice/Network/URLRequest/APIQuery.swift | 8 ++ BoxOffice/Network/URLRequest/APIType.swift | 8 ++ .../Network/URLRequest/BoxOfficeType.swift | 8 ++ 6 files changed, 141 insertions(+), 93 deletions(-) delete mode 100644 BoxOffice/Model/NetworkURL.swift create mode 100644 BoxOffice/Network/NetworkURL.swift create mode 100644 BoxOffice/Network/URLRequest/APIPath.swift create mode 100644 BoxOffice/Network/URLRequest/APIQuery.swift create mode 100644 BoxOffice/Network/URLRequest/APIType.swift create mode 100644 BoxOffice/Network/URLRequest/BoxOfficeType.swift diff --git a/BoxOffice/Model/NetworkURL.swift b/BoxOffice/Model/NetworkURL.swift deleted file mode 100644 index 94e2de63..00000000 --- a/BoxOffice/Model/NetworkURL.swift +++ /dev/null @@ -1,93 +0,0 @@ -// -// URL.swift -// BoxOffice -// -// Created by Matthew on 2/15/24. -// - -import Foundation - -enum APIType { - case boxOffice - case movieInfo - case kakao -} - -struct NetworkURL { -// static func makeDailyBoxOfficeURL(date: String) -> String { -// let key = Bundle.main.movieApiKey -// var url: String { -// return "https://kobis.or.kr/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json?key=\(String(describing: key))&targetDt=\(date)" -// } -// return url -// } -// -// static func makeMovieInfomationDetailURL(code: String) -> String { -// let key = Bundle.main.movieApiKey -// var url: String { -// return "https://kobis.or.kr/kobisopenapi/webservice/rest/movie/searchMovieInfo.json?key=\(String(describing: key))&movieCd=\(code)" -// } -// return url -// } - - static func makeDailyBoxOfficeRequest(date: String) -> URLRequest? { - let key = Bundle.main.movieApiKey - var urlComponents = URLComponents() - urlComponents.scheme = "https" - urlComponents.host = "www.kobis.or.kr" - urlComponents.path = "/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json" - urlComponents.queryItems = [ - URLQueryItem(name: "key", value: key), - URLQueryItem(name: "targetDt", value: date) - ] - - guard let url = urlComponents.url else { - return nil - } - - var request = URLRequest(url: url) - request.httpMethod = "GET" - return request - } - - static func makeMovieInfomationDetailRequest(code: String) -> URLRequest? { - let key = Bundle.main.movieApiKey - var urlComponents = URLComponents() - urlComponents.scheme = "https" - urlComponents.host = "www.kobis.or.kr" - urlComponents.path = "/kobisopenapi/webservice/rest/movie/searchMovieInfo.json" - urlComponents.queryItems = [ - URLQueryItem(name: "key", value: key), - URLQueryItem(name: "movieCd", value: code) - ] - - guard let url = urlComponents.url else { - return nil - } - - var request = URLRequest(url: url) - request.httpMethod = "GET" - return request - } - - static func makeMovieImageRequest(movieName: String) -> URLRequest? { - let key = Bundle.main.kakaoApiKey - var urlComponents = URLComponents() - urlComponents.scheme = "https" - urlComponents.host = "dapi.kakao.com" - urlComponents.path = "/v2/search/image" - urlComponents.queryItems = [ - URLQueryItem(name: "query", value: "\(movieName) 영화포스터"), - URLQueryItem(name: "sort", value: "accuracy") - ] - - guard let url = urlComponents.url else { - return nil - } - - var request = URLRequest(url: url) - request.httpMethod = "GET" - request.allHTTPHeaderFields = ["Authorization": "KakaoAK \(key)"] - return request - } -} diff --git a/BoxOffice/Network/NetworkURL.swift b/BoxOffice/Network/NetworkURL.swift new file mode 100644 index 00000000..8aec4ef0 --- /dev/null +++ b/BoxOffice/Network/NetworkURL.swift @@ -0,0 +1,109 @@ +// +// URL.swift +// BoxOffice +// +// Created by Matthew on 2/15/24. +// + +import Foundation + +enum APIType { + case boxOffice + case kakao + + var host: String { + switch self { + case .boxOffice: + return "www.kobis.or.kr" + case .kakao: + return "dapi.kakao.com" + } + } +} + +enum APIPath { + case dailyBoxOffice + case movieDetail + case kakao + + var path: String { + switch self { + case .dailyBoxOffice: + "/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json" + case .movieDetail: + "/kobisopenapi/webservice/rest/movie/searchMovieInfo.json" + case .kakao: + "/v2/search/image" + } + } +} + +enum APIQuery { + case kakao(movieName: String) + case boxOffice(BoxOfficeType) + + var queries: [URLQueryItem] { + switch self { + case .kakao(let movieName): + [ + URLQueryItem(name: "query", value: "\(movieName) 영화포스터"), + URLQueryItem(name: "sort", value: "accuracy") + ] + case .boxOffice(let type): + type.queries + } + } + + var header: [String:String]? { + switch self { + case .kakao: + ["Authorization": "KakaoAK \(Bundle.main.kakaoApiKey)"] + case .boxOffice: + nil + } + } +} +enum BoxOfficeType { + case dailyBoxOffice(date: String) + case movieDetail(code: String) + + var queries: [URLQueryItem] { + switch self { + case .dailyBoxOffice(let date): + [ + URLQueryItem(name: "key", value: Bundle.main.movieApiKey), + URLQueryItem(name: "targetDt", value: date) + ] + case .movieDetail(let code): + [ + URLQueryItem(name: "key", value: Bundle.main.movieApiKey), + URLQueryItem(name: "movieCd", value: code) + ] + } + } +} +struct NetworkURL { + static func makeURLRequest(type: APIType, path: APIPath, queries: APIQuery) -> URLRequest? { + var urlComponents = URLComponents() + urlComponents.scheme = "https" + urlComponents.host = type.host + urlComponents.path = path.path + urlComponents.queryItems = queries.queries + + guard + let url = urlComponents.url + else { + return nil + } + + var request = URLRequest(url: url) + request.httpMethod = "GET" + guard + let header = queries.header + else { + return request + } + request.allHTTPHeaderFields = header + return request + } +} diff --git a/BoxOffice/Network/URLRequest/APIPath.swift b/BoxOffice/Network/URLRequest/APIPath.swift new file mode 100644 index 00000000..aef882f5 --- /dev/null +++ b/BoxOffice/Network/URLRequest/APIPath.swift @@ -0,0 +1,8 @@ +// +// APIPath.swift +// BoxOffice +// +// Created by 강창현 on 3/19/24. +// + +import Foundation diff --git a/BoxOffice/Network/URLRequest/APIQuery.swift b/BoxOffice/Network/URLRequest/APIQuery.swift new file mode 100644 index 00000000..d21947d5 --- /dev/null +++ b/BoxOffice/Network/URLRequest/APIQuery.swift @@ -0,0 +1,8 @@ +// +// APIQuery.swift +// BoxOffice +// +// Created by 강창현 on 3/19/24. +// + +import Foundation diff --git a/BoxOffice/Network/URLRequest/APIType.swift b/BoxOffice/Network/URLRequest/APIType.swift new file mode 100644 index 00000000..e2a3de61 --- /dev/null +++ b/BoxOffice/Network/URLRequest/APIType.swift @@ -0,0 +1,8 @@ +// +// APIType.swift +// BoxOffice +// +// Created by 강창현 on 3/19/24. +// + +import Foundation diff --git a/BoxOffice/Network/URLRequest/BoxOfficeType.swift b/BoxOffice/Network/URLRequest/BoxOfficeType.swift new file mode 100644 index 00000000..84de4a11 --- /dev/null +++ b/BoxOffice/Network/URLRequest/BoxOfficeType.swift @@ -0,0 +1,8 @@ +// +// BoxOfficeType.swift +// BoxOffice +// +// Created by 강창현 on 3/19/24. +// + +import Foundation From 8df64644843ec77a36f780cc59cae715a5a1c2cc Mon Sep 17 00:00:00 2001 From: Changhyun Kang Date: Tue, 19 Mar 2024 15:25:07 +0900 Subject: [PATCH 27/39] =?UTF-8?q?remove:=20=EB=B6=88=ED=95=84=EC=9A=94=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=ED=8C=8C=EC=9D=BC=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOfficeTests/DateFormatterTests.swift | 84 ------------------------- 1 file changed, 84 deletions(-) delete mode 100644 BoxOfficeTests/DateFormatterTests.swift diff --git a/BoxOfficeTests/DateFormatterTests.swift b/BoxOfficeTests/DateFormatterTests.swift deleted file mode 100644 index 093dca33..00000000 --- a/BoxOfficeTests/DateFormatterTests.swift +++ /dev/null @@ -1,84 +0,0 @@ -// -// DateFormatterTests.swift -// BoxOfficeTests -// -// Created by 강창현 on 3/8/24. -// - -import XCTest - -final class DateFormatterTests: XCTestCase { - let dateFormatter = DateFormatter() - let yesterday: Date = { - return Calendar.current.date(byAdding: .day, value: -1, to: Date()) ?? Date() - 86400 - }() - - lazy var yesterdayToString: String = { - return Date.toString(date: yesterday) - }() - - func test_기존_Date_Extension_활용_효율성_검사() throws { // 0.001 - self.measure { - for _ in (0..<500) { - _ = yesterdayToString - } - } - } - - func test_Date_Extension_static_키워드_활용_효율성_검사() throws { // 0.000 - self.measure { - for _ in (0..<500) { - _ = Date.yesterdayToString - } - } - } - - func test_싱글턴_활용_효율성_검사() throws { // 0.002 - self.measure { - for _ in (0..<500) { - _ = Test.shared.yesterdayToString - } - } - } -} - -extension Date { - static let df: DateFormatter = { - let dateFormatter = DateFormatter() - dateFormatter.dateFormat = "yyyyMMdd" - return dateFormatter - }() - - static let yesterday: Date = { - return Calendar.current.date(byAdding: .day, value: -1, to: Date()) ?? Date() - 86400 - }() - - static let yesterdayToString: String = { - return toString(date: yesterday) - }() - - static func toString(date: Date) -> String { - return df.string(from: date) - } -} - -final class Test { - static let shared = Test() - var yesterday: Date { - return Calendar.current.date(byAdding: .day, value: -1, to: Date()) ?? Date() - 86400 - } - - var yesterdayToString: String { - return toString(date: yesterday) - } - - private let df: DateFormatter = { - let dateFormatter = DateFormatter() - dateFormatter.dateFormat = "yyyyMMdd" - return dateFormatter - }() - - func toString(date: Date) -> String { - return df.string(from: date) - } -} From fa5fcf2406b516f364822476aaac570ee3aceb5a Mon Sep 17 00:00:00 2001 From: Changhyun Kang Date: Tue, 19 Mar 2024 15:26:19 +0900 Subject: [PATCH 28/39] =?UTF-8?q?feat:=20URLRequest=20=EC=83=9D=EC=84=B1?= =?UTF-8?q?=20=EB=A1=9C=EC=A7=81=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice/Network/NetworkURL.swift | 75 ------------------- BoxOffice/Network/URLRequest/APIPath.swift | 17 ++++- BoxOffice/Network/URLRequest/APIQuery.swift | 26 +++++++ BoxOffice/Network/URLRequest/APIType.swift | 14 +++- .../Network/URLRequest/BoxOfficeType.swift | 20 +++++ 5 files changed, 75 insertions(+), 77 deletions(-) diff --git a/BoxOffice/Network/NetworkURL.swift b/BoxOffice/Network/NetworkURL.swift index 8aec4ef0..a2073ad6 100644 --- a/BoxOffice/Network/NetworkURL.swift +++ b/BoxOffice/Network/NetworkURL.swift @@ -7,81 +7,6 @@ import Foundation -enum APIType { - case boxOffice - case kakao - - var host: String { - switch self { - case .boxOffice: - return "www.kobis.or.kr" - case .kakao: - return "dapi.kakao.com" - } - } -} - -enum APIPath { - case dailyBoxOffice - case movieDetail - case kakao - - var path: String { - switch self { - case .dailyBoxOffice: - "/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json" - case .movieDetail: - "/kobisopenapi/webservice/rest/movie/searchMovieInfo.json" - case .kakao: - "/v2/search/image" - } - } -} - -enum APIQuery { - case kakao(movieName: String) - case boxOffice(BoxOfficeType) - - var queries: [URLQueryItem] { - switch self { - case .kakao(let movieName): - [ - URLQueryItem(name: "query", value: "\(movieName) 영화포스터"), - URLQueryItem(name: "sort", value: "accuracy") - ] - case .boxOffice(let type): - type.queries - } - } - - var header: [String:String]? { - switch self { - case .kakao: - ["Authorization": "KakaoAK \(Bundle.main.kakaoApiKey)"] - case .boxOffice: - nil - } - } -} -enum BoxOfficeType { - case dailyBoxOffice(date: String) - case movieDetail(code: String) - - var queries: [URLQueryItem] { - switch self { - case .dailyBoxOffice(let date): - [ - URLQueryItem(name: "key", value: Bundle.main.movieApiKey), - URLQueryItem(name: "targetDt", value: date) - ] - case .movieDetail(let code): - [ - URLQueryItem(name: "key", value: Bundle.main.movieApiKey), - URLQueryItem(name: "movieCd", value: code) - ] - } - } -} struct NetworkURL { static func makeURLRequest(type: APIType, path: APIPath, queries: APIQuery) -> URLRequest? { var urlComponents = URLComponents() diff --git a/BoxOffice/Network/URLRequest/APIPath.swift b/BoxOffice/Network/URLRequest/APIPath.swift index aef882f5..384a7894 100644 --- a/BoxOffice/Network/URLRequest/APIPath.swift +++ b/BoxOffice/Network/URLRequest/APIPath.swift @@ -5,4 +5,19 @@ // Created by 강창현 on 3/19/24. // -import Foundation +enum APIPath { + case dailyBoxOffice + case movieDetail + case kakao + + var path: String { + switch self { + case .dailyBoxOffice: + "/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json" + case .movieDetail: + "/kobisopenapi/webservice/rest/movie/searchMovieInfo.json" + case .kakao: + "/v2/search/image" + } + } +} diff --git a/BoxOffice/Network/URLRequest/APIQuery.swift b/BoxOffice/Network/URLRequest/APIQuery.swift index d21947d5..5f5fef8a 100644 --- a/BoxOffice/Network/URLRequest/APIQuery.swift +++ b/BoxOffice/Network/URLRequest/APIQuery.swift @@ -6,3 +6,29 @@ // import Foundation + +enum APIQuery { + case kakao(movieName: String) + case boxOffice(BoxOfficeType) + + var queries: [URLQueryItem] { + switch self { + case .kakao(let movieName): + [ + URLQueryItem(name: "query", value: "\(movieName) 영화포스터"), + URLQueryItem(name: "sort", value: "accuracy") + ] + case .boxOffice(let type): + type.queries + } + } + + var header: [String:String]? { + switch self { + case .kakao: + ["Authorization": "KakaoAK \(Bundle.main.kakaoApiKey)"] + case .boxOffice: + nil + } + } +} diff --git a/BoxOffice/Network/URLRequest/APIType.swift b/BoxOffice/Network/URLRequest/APIType.swift index e2a3de61..df7795c6 100644 --- a/BoxOffice/Network/URLRequest/APIType.swift +++ b/BoxOffice/Network/URLRequest/APIType.swift @@ -5,4 +5,16 @@ // Created by 강창현 on 3/19/24. // -import Foundation +enum APIType { + case boxOffice + case kakao + + var host: String { + switch self { + case .boxOffice: + return "www.kobis.or.kr" + case .kakao: + return "dapi.kakao.com" + } + } +} diff --git a/BoxOffice/Network/URLRequest/BoxOfficeType.swift b/BoxOffice/Network/URLRequest/BoxOfficeType.swift index 84de4a11..39d41fda 100644 --- a/BoxOffice/Network/URLRequest/BoxOfficeType.swift +++ b/BoxOffice/Network/URLRequest/BoxOfficeType.swift @@ -6,3 +6,23 @@ // import Foundation + +enum BoxOfficeType { + case dailyBoxOffice(date: String) + case movieDetail(code: String) + + var queries: [URLQueryItem] { + switch self { + case .dailyBoxOffice(let date): + [ + URLQueryItem(name: "key", value: Bundle.main.movieApiKey), + URLQueryItem(name: "targetDt", value: date) + ] + case .movieDetail(let code): + [ + URLQueryItem(name: "key", value: Bundle.main.movieApiKey), + URLQueryItem(name: "movieCd", value: code) + ] + } + } +} From 85d47592f0fdce15ec39023b57e60e6220978140 Mon Sep 17 00:00:00 2001 From: Changhyun Kang Date: Tue, 19 Mar 2024 15:26:51 +0900 Subject: [PATCH 29/39] =?UTF-8?q?refactor:=20URLRequest=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EB=A1=9C=EC=A7=81=20=EB=A6=AC=ED=8C=A9=ED=86=A0?= =?UTF-8?q?=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice/Model/MovieRepository.swift | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/BoxOffice/Model/MovieRepository.swift b/BoxOffice/Model/MovieRepository.swift index a9d78476..46ed3de1 100644 --- a/BoxOffice/Model/MovieRepository.swift +++ b/BoxOffice/Model/MovieRepository.swift @@ -12,7 +12,11 @@ struct MovieRepository { func fetchBoxOfficeResultData(date: String) async throws -> (Result) { guard - let urlRequest = NetworkURL.makeDailyBoxOfficeRequest(date: date) + let urlRequest = NetworkURL.makeURLRequest( + type: .boxOffice, + path: .dailyBoxOffice, + queries: .boxOffice(.dailyBoxOffice(date: date)) + ) else { return .failure(.invalidURLRequestError) } @@ -28,7 +32,11 @@ struct MovieRepository { func fetchMovieInfoResultData(code: String) async throws -> (Result) { guard - let urlRequest = NetworkURL.makeMovieInfomationDetailRequest(code: code) + let urlRequest = NetworkURL.makeURLRequest( + type: .boxOffice, + path: .movieDetail, + queries: .boxOffice(.movieDetail(code: code)) + ) else { return .failure(.invalidURLRequestError) } @@ -44,7 +52,11 @@ struct MovieRepository { func fetchMoiveImageURL(movieName: String) async throws -> (Result) { guard - let urlRequest = NetworkURL.makeMovieImageRequest(movieName: movieName) + let urlRequest = NetworkURL.makeURLRequest( + type: .kakao, + path: .kakao, + queries: .kakao(movieName: movieName) + ) else { return .failure(.invalidURLRequestError) } From 5a36a524488591c2998bc952b466de5f27727cfb Mon Sep 17 00:00:00 2001 From: Changhyun Kang Date: Tue, 19 Mar 2024 15:27:27 +0900 Subject: [PATCH 30/39] =?UTF-8?q?test:=20=EB=A1=9C=EC=A7=81=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A7=81=EC=97=90=20=EB=94=B0=EB=A5=B8=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOfficeTests/BoxOfficeMockTests.swift | 141 ++++++++-------- BoxOfficeTests/BoxOfficeTests.swift | 210 ++++++++++-------------- 2 files changed, 152 insertions(+), 199 deletions(-) diff --git a/BoxOfficeTests/BoxOfficeMockTests.swift b/BoxOfficeTests/BoxOfficeMockTests.swift index 36341f82..941a512a 100644 --- a/BoxOfficeTests/BoxOfficeMockTests.swift +++ b/BoxOfficeTests/BoxOfficeMockTests.swift @@ -1,78 +1,69 @@ -//// -//// BoxOfficeMockTests.swift -//// BoxOfficeTests -//// -//// Created by 강창현 on 2/22/24. -//// // -//import XCTest -//@testable import BoxOffice +// BoxOfficeMockTests.swift +// BoxOfficeTests // -//final class BoxOfficeMockTests: XCTestCase { -// func test_MockURLSession의_응답코드가_400이면_clientError가_발생한다() { -// // given -// let urlString = NetworkURL.makeDailyBoxOfficeURL(date: Date.movieDateToString) -// let mockURLSession = makeMockURLSession(fileName: JSONFileName.boxOffice, url: urlString, statusCode: 400) -// let sut = setSUT(session: mockURLSession) -// -// // when -// let expectation = XCTestExpectation(description: "데이터 패치 중...") -// -// sut.fetchData(urlString: urlString) { (result: Result) in -// switch result { -// case .success(_): -// // then -// XCTFail() -// case .failure(let error): -// // then -// XCTAssertEqual(error, NetworkError.clientError) -// } -// expectation.fulfill() -// } -// wait(for: [expectation], timeout: 5.0) -// } -// -// func test_MockURLSession의_응답코드가_200이면_boxOfficeResult는_nil이_아니다() { -// // given -// let date = "20240210" -// let urlString = NetworkURL.makeDailyBoxOfficeURL(date: date) -// let mockURLSession = makeMockURLSession(fileName: JSONFileName.boxOffice, url: urlString, statusCode: 200) -// let sut = setSUT(session: mockURLSession) -// -// // when -// let expectation = XCTestExpectation(description: "데이터 패치 중...") -// -// sut.fetchData(urlString: urlString) { (result: Result) in -// switch result { -// case .success(let movies): -// // then -// XCTAssertNotNil(movies) -// case .failure(let error): -// // then -// XCTFail("데이터 파싱 에러 발생: \(error))") -// } -// expectation.fulfill() -// } -// wait(for: [expectation], timeout: 5.0) -// } -//} +// Created by 강창현 on 2/22/24. // -//private extension BoxOfficeMockTests { -// func setSUT(session: URLSessionProtocol) -> APIService { -// return APIService(session: session) -// } -// -// func makeMockURLSession(fileName: String, url: String, statusCode: Int) -> MockURLSession { -// var response: MockURLSession.Response { -// let data: Data? = JSONLoader.load(fileName: fileName) -// let response = HTTPURLResponse( -// url: URL(string: url)!, -// statusCode: statusCode, -// httpVersion: nil, -// headerFields: nil -// ) -// return (data, response, nil) -// } -// return MockURLSession(response: response) -// } -//} + +import XCTest +@testable import BoxOffice + +final class BoxOfficeMockTests: XCTestCase { + func test_MockURLSession의_응답코드가_400이면_clientError가_발생한다() async throws { + // given + let urlString = "www.naver.com" + let mockURLSession = makeMockURLSession(fileName: JSONFileName.boxOffice, url: urlString, statusCode: 400) + let sut = setSUT(session: mockURLSession) + let request = URLRequest(url: URL(string: urlString)!) + + // when + let result = try await sut.fetchData(with: request) + switch result { + case .success(_): + // then + XCTFail() + case .failure(let error): + // then + XCTAssertEqual(error, NetworkError.clientError) + } + } + + func test_MockURLSession의_응답코드가_200이면_boxOfficeResult는_nil이_아니다() async throws { + // given + let urlString = "www.naver.com" + let mockURLSession = makeMockURLSession(fileName: JSONFileName.boxOffice, url: urlString, statusCode: 200) + let sut = setSUT(session: mockURLSession) + let request = URLRequest(url: URL(string: urlString)!) + + // when + let result = try await sut.fetchData(with: request) + switch result { + case .success(let data): + // then + XCTAssertNotNil(data) + case .failure(let error): + // then + XCTFail("데이터 파싱 에러 발생: \(error))") + } + } +} + +private extension BoxOfficeMockTests { + func setSUT(session: URLSessionProtocol) -> APIService { + return APIService(session: session) + } + + func makeMockURLSession(fileName: String, url: String, statusCode: Int) -> MockURLSession { + var response: MockURLSession.Response { + let data: Data = JSONLoader.load(fileName: fileName)! + let response = HTTPURLResponse( + url: URL(string: url)!, + statusCode: statusCode, + httpVersion: nil, + headerFields: nil + )! + return (data, response) + } + return MockURLSession(response: response) + } +} diff --git a/BoxOfficeTests/BoxOfficeTests.swift b/BoxOfficeTests/BoxOfficeTests.swift index 8591602b..e5b2b65f 100644 --- a/BoxOfficeTests/BoxOfficeTests.swift +++ b/BoxOfficeTests/BoxOfficeTests.swift @@ -5,127 +5,89 @@ // Created by 강창현 on 2/13/24. // -//import XCTest -//@testable import BoxOffice -// -//final class BoxOfficeTests: XCTestCase { -// -// private var sut: APIService! -// -// override func setUp() { -// super.setUp() -// self.sut = APIService() -// } -// -// override func tearDown() { -// super.tearDown() -// self.sut = nil -// } -// -// func test_date가_20230101이고_데이터_파싱이_올바르게_됐을_때_fetchData는_nil이_아니다() { -// // given -// let date = "20170319" -// let urlString = NetworkURL.makeDailyBoxOfficeURL(date: date) -// -// // when -// let expectation = XCTestExpectation(description: "데이터 패치 중...") -// -// sut.fetchData(urlString: urlString) { (result: Result) in -// switch result { -// case .success(let movies): -// // then -// XCTAssertNotNil(movies) -// case .failure(let error): -// // then -// XCTFail("데이터 파싱 에러 발생: \(error))") -// } -// expectation.fulfill() -// } -// wait(for: [expectation], timeout: 5.0) -// } -// -// func test_date가_잘못된_타입으로_데이터_파싱_됐을_때_fetchMovie에서_decodingError발생() { -// // given -// let wrongDate = "iWantToGoHome" -// let urlString = NetworkURL.makeDailyBoxOfficeURL(date: wrongDate) -// -// // when -// let expectation = XCTestExpectation(description: "데이터 패치 중...") -// -// sut.fetchData(urlString: urlString) { (result: Result) in -// switch result { -// case .success(let movies): -// // then -// XCTFail("데이터 파싱 성공: \(movies))") -// case .failure(let error): -// // then -// XCTAssertEqual(error, NetworkError.decodingError) -// } -// expectation.fulfill() -// } -// wait(for: [expectation], timeout: 5.0) -// } -// -// func test_url이_잘못된_주소로_데이터_파싱_됐을_때_fetchMovie에서_invalidURLError_감지발생() { -// // given -// let url = "qqqq://안녕하세요.닷컴 " -// -// // when -// let expectation = XCTestExpectation(description: "데이터 패치 중...") -// -// sut.fetchData(urlString: url) { (result: Result) in -// switch result { -// case .success(let movies): -// // then -// XCTFail("데이터 파싱 성공: \(movies))") -// case .failure(let error): -// // then -// XCTAssertEqual(error, NetworkError.invalidURLError) -// } -// expectation.fulfill() -// } -// wait(for: [expectation], timeout: 5.0) -// } -// -// func test_어제날짜_영화_데이터_파싱이_올바르게_됐을_때_fetchData에_boxOfficeResult는_nil이_아니다() { -// // given -// let urlString = NetworkURL.makeDailyBoxOfficeURL(date: Date.movieDateToString) -// -// // when -// let expectation = XCTestExpectation(description: "데이터 패치 중...") -// -// sut.fetchData(urlString: urlString) { (result: Result) in -// switch result { -// case .success(let movies): -// // then -// XCTAssertNotNil(movies) -// case .failure(let error): -// // then -// XCTFail("데이터 파싱 에러 발생: \(error))") -// } -// expectation.fulfill() -// } -// wait(for: [expectation], timeout: 5.0) -// } -// -// func test_특정_영화_코드로_데이터_파싱이_올바르게_됐을_때_fetchData에_movieInfoResult는_nil이_아니다() { -// // given -// let urlString = NetworkURL.makeMovieInfomationDetailURL(code: "20124079") -// -// // when -// let expectation = XCTestExpectation(description: "데이터 패치 중...") -// -// sut.fetchData(urlString: urlString) { (result: Result) in -// switch result { -// case .success(let movies): -// // then -// XCTAssertNotNil(movies) -// case .failure(let error): -// // then -// XCTFail("데이터 파싱 에러 발생: \(error))") -// } -// expectation.fulfill() -// } -// wait(for: [expectation], timeout: 5.0) -// } -//} +import XCTest +@testable import BoxOffice + +final class BoxOfficeTests: XCTestCase { + + private var sut: APIService! + + override func setUp() { + super.setUp() + self.sut = APIService() + } + + override func tearDown() { + super.tearDown() + self.sut = nil + } + + func test_date가_20230101이고_데이터_파싱이_올바르게_됐을_때_fetchData는_nil이_아니다() async throws { + // given + let date = "20170319" + guard + let urlString = NetworkURL.makeURLRequest( + type: .boxOffice, + path: .dailyBoxOffice, + queries: .boxOffice(.dailyBoxOffice(date: date)) + ) + else { + return + } + + // when + let result = try await sut.fetchData(with: urlString) + switch result { + case .success(let movies): + // then + XCTAssertNotNil(movies) + case .failure(let error): + // then + XCTFail("데이터 파싱 에러 발생: \(error))") + } + } + + func test_어제날짜_영화_데이터_파싱이_올바르게_됐을_때_fetchData에_boxOfficeResult는_nil이_아니다() async throws { + // given + guard + let urlString = NetworkURL.makeURLRequest( + type: .boxOffice, + path: .dailyBoxOffice, + queries: .boxOffice(.dailyBoxOffice(date: Date.yesterdayToString)) + ) + else { + return + } + let result = try await sut.fetchData(with: urlString) + switch result { + case .success(let movies): + // then + XCTAssertNotNil(movies) + case .failure(let error): + // then + XCTFail("데이터 파싱 에러 발생: \(error))") + } + } + + func test_특정_영화_코드로_데이터_파싱이_올바르게_됐을_때_fetchData에_movieInfoResult는_nil이_아니다() async throws { + // given + guard + let urlString = NetworkURL.makeURLRequest( + type: .boxOffice, + path: .movieDetail, + queries: .boxOffice(.movieDetail(code: "20230102")) + ) + else { + return + } + let result = try await sut.fetchData(with: urlString) + switch result { + case .success(let movies): + // then + XCTAssertNotNil(movies) + case .failure(let error): + // then + XCTFail("데이터 파싱 에러 발생: \(error))") + } + } +} From e4e760d750a040761031e04c0274dabb5134651f Mon Sep 17 00:00:00 2001 From: Changhyun Kang Date: Tue, 19 Mar 2024 15:27:55 +0900 Subject: [PATCH 31/39] =?UTF-8?q?feat:=20=EC=98=81=ED=99=94=20=ED=8F=AC?= =?UTF-8?q?=EC=8A=A4=ED=84=B0=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=82=AC?= =?UTF-8?q?=EC=9D=B4=EC=A6=88=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice/View/BoxOfficeDetailView.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/BoxOffice/View/BoxOfficeDetailView.swift b/BoxOffice/View/BoxOfficeDetailView.swift index 7d3559c9..5ca5770b 100644 --- a/BoxOffice/View/BoxOfficeDetailView.swift +++ b/BoxOffice/View/BoxOfficeDetailView.swift @@ -44,6 +44,7 @@ final class BoxOfficeDetailView: UIScrollView { private let movieImageView: UIImageView = { let view = UIImageView() + view.contentMode = .scaleAspectFit view.image = UIImage(named: "prepare") return view }() From c7dcd5c42082767827e8816094e71f03f88f98b4 Mon Sep 17 00:00:00 2001 From: Changhyun Kang Date: Tue, 19 Mar 2024 15:28:21 +0900 Subject: [PATCH 32/39] =?UTF-8?q?chore:=20NetworkURL=20=ED=8F=B4=EB=8D=94?= =?UTF-8?q?=EB=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice.xcodeproj/project.pbxproj | 30 ++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/BoxOffice.xcodeproj/project.pbxproj b/BoxOffice.xcodeproj/project.pbxproj index d0965d92..1c456de0 100644 --- a/BoxOffice.xcodeproj/project.pbxproj +++ b/BoxOffice.xcodeproj/project.pbxproj @@ -22,10 +22,13 @@ 2757938A2B7F51BF00C08906 /* URLSession+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 275793892B7F51BF00C08906 /* URLSession+Extension.swift */; }; 2757938C2B7F52B500C08906 /* JSONLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2757938B2B7F52B500C08906 /* JSONLoader.swift */; }; 2757938D2B7F546300C08906 /* box_office.json in Resources */ = {isa = PBXBuildFile; fileRef = 275793822B7F402300C08906 /* box_office.json */; }; - 2757D9D82B9A9EDB00B13BD7 /* DateFormatterTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2757D9D72B9A9EDB00B13BD7 /* DateFormatterTests.swift */; }; 2757D9DB2B9B2BCB00B13BD7 /* MovieImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2757D9DA2B9B2BCB00B13BD7 /* MovieImage.swift */; }; 2757D9DD2B9B2C4200B13BD7 /* Document.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2757D9DC2B9B2C4200B13BD7 /* Document.swift */; }; 2757D9DF2B9B2C5400B13BD7 /* Meta.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2757D9DE2B9B2C5400B13BD7 /* Meta.swift */; }; + 27674EE82BA95ED400564726 /* APIType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27674EE72BA95ED400564726 /* APIType.swift */; }; + 27674EEA2BA95EF000564726 /* APIPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27674EE92BA95EF000564726 /* APIPath.swift */; }; + 27674EEC2BA95F0A00564726 /* APIQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27674EEB2BA95F0A00564726 /* APIQuery.swift */; }; + 27674EEE2BA95F3300564726 /* BoxOfficeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27674EED2BA95F3300564726 /* BoxOfficeType.swift */; }; 276ED7632B7B002600D37EBF /* BoxOfficeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 276ED7622B7B002600D37EBF /* BoxOfficeTests.swift */; }; 276ED76A2B7B006400D37EBF /* BoxOffice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 276ED7692B7B006400D37EBF /* BoxOffice.swift */; }; 276ED76C2B7B006D00D37EBF /* MovieManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 276ED76B2B7B006D00D37EBF /* MovieManager.swift */; }; @@ -89,10 +92,13 @@ 275793852B7F516800C08906 /* MockURLSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockURLSession.swift; sourceTree = ""; }; 275793892B7F51BF00C08906 /* URLSession+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "URLSession+Extension.swift"; sourceTree = ""; }; 2757938B2B7F52B500C08906 /* JSONLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSONLoader.swift; sourceTree = ""; }; - 2757D9D72B9A9EDB00B13BD7 /* DateFormatterTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateFormatterTests.swift; sourceTree = ""; }; 2757D9DA2B9B2BCB00B13BD7 /* MovieImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MovieImage.swift; sourceTree = ""; }; 2757D9DC2B9B2C4200B13BD7 /* Document.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Document.swift; sourceTree = ""; }; 2757D9DE2B9B2C5400B13BD7 /* Meta.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Meta.swift; sourceTree = ""; }; + 27674EE72BA95ED400564726 /* APIType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIType.swift; sourceTree = ""; }; + 27674EE92BA95EF000564726 /* APIPath.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIPath.swift; sourceTree = ""; }; + 27674EEB2BA95F0A00564726 /* APIQuery.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIQuery.swift; sourceTree = ""; }; + 27674EED2BA95F3300564726 /* BoxOfficeType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoxOfficeType.swift; sourceTree = ""; }; 276ED7602B7B002600D37EBF /* BoxOfficeTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BoxOfficeTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 276ED7622B7B002600D37EBF /* BoxOfficeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoxOfficeTests.swift; sourceTree = ""; }; 276ED7692B7B006400D37EBF /* BoxOffice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoxOffice.swift; sourceTree = ""; }; @@ -209,12 +215,22 @@ path = Image; sourceTree = ""; }; + 27674EE62BA95E9800564726 /* URLRequest */ = { + isa = PBXGroup; + children = ( + 27674EE72BA95ED400564726 /* APIType.swift */, + 27674EE92BA95EF000564726 /* APIPath.swift */, + 27674EEB2BA95F0A00564726 /* APIQuery.swift */, + 27674EED2BA95F3300564726 /* BoxOfficeType.swift */, + ); + path = URLRequest; + sourceTree = ""; + }; 276ED7612B7B002600D37EBF /* BoxOfficeTests */ = { isa = PBXGroup; children = ( 276ED7622B7B002600D37EBF /* BoxOfficeTests.swift */, 27AE2ED72B86DAFD009B43E2 /* BoxOfficeMockTests.swift */, - 2757D9D72B9A9EDB00B13BD7 /* DateFormatterTests.swift */, ); path = BoxOfficeTests; sourceTree = ""; @@ -226,7 +242,6 @@ 2782B6072BA1ACDD00D55005 /* MovieRepository.swift */, 2757936B2B7F1D8100C08906 /* Movie */, 276ED7752B7B08E100D37EBF /* NetworkError.swift */, - C14DF4B82B7DAF97009E3258 /* NetworkURL.swift */, C1FC46AE2B84961300C6E49E /* JSON */, ); path = Model; @@ -266,7 +281,9 @@ 276ED79B2B7C605500D37EBF /* Network */ = { isa = PBXGroup; children = ( + 27674EE62BA95E9800564726 /* URLRequest */, 276ED7972B7B638C00D37EBF /* APIService.swift */, + C14DF4B82B7DAF97009E3258 /* NetworkURL.swift */, 27AE2ED42B86D91E009B43E2 /* Protocol */, 275793842B7F515500C08906 /* Mock */, ); @@ -468,7 +485,6 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 2757D9D82B9A9EDB00B13BD7 /* DateFormatterTests.swift in Sources */, 276ED7632B7B002600D37EBF /* BoxOfficeTests.swift in Sources */, 27AE2ED82B86DAFD009B43E2 /* BoxOfficeMockTests.swift in Sources */, ); @@ -482,12 +498,14 @@ 275793712B7F1DCC00C08906 /* MovieInfo.swift in Sources */, 63DF20F32970E1A0005DF7D1 /* BoxOfficeViewController.swift in Sources */, C1FC46B22B8496B300C6E49E /* URLSessionProtocol.swift in Sources */, + 27674EE82BA95ED400564726 /* APIType.swift in Sources */, 275793812B7F1E4C00C08906 /* Staff.swift in Sources */, 27157BF72B8344F3000BAC45 /* MovieInfomationDetail.swift in Sources */, 276ED7702B7B012900D37EBF /* DailyBoxOfficeList.swift in Sources */, 63DF20EF2970E1A0005DF7D1 /* AppDelegate.swift in Sources */, 27AE2EDE2B86E965009B43E2 /* MovieStackView.swift in Sources */, 276ED76E2B7B010D00D37EBF /* BoxOfficeResult.swift in Sources */, + 27674EEA2BA95EF000564726 /* APIPath.swift in Sources */, 2757936F2B7F1DB900C08906 /* MovieInfoResult.swift in Sources */, 2757937F2B7F1E3F00C08906 /* ShowType.swift in Sources */, 2757D9DD2B9B2C4200B13BD7 /* Document.swift in Sources */, @@ -502,6 +520,7 @@ 2757D9DF2B9B2C5400B13BD7 /* Meta.swift in Sources */, C16A4D6E2B9ADF2100498BB3 /* BoxOfficeDetailView.swift in Sources */, 275793732B7F1DE200C08906 /* Actor.swift in Sources */, + 27674EEE2BA95F3300564726 /* BoxOfficeType.swift in Sources */, 2757D9DB2B9B2BCB00B13BD7 /* MovieImage.swift in Sources */, 2782B60A2BA2B99E00D55005 /* LoadingIndicatorView.swift in Sources */, C16A4D722B9AE92C00498BB3 /* ReusedDetailStackView.swift in Sources */, @@ -517,6 +536,7 @@ 278FA9A32B8B0C7500FFDB3F /* BoxOfficeListViewDelegate.swift in Sources */, 2757938A2B7F51BF00C08906 /* URLSession+Extension.swift in Sources */, C14DF4B52B7DAE49009E3258 /* Bundle+Extension.swift in Sources */, + 27674EEC2BA95F0A00564726 /* APIQuery.swift in Sources */, 275793772B7F1E0800C08906 /* Company.swift in Sources */, 276ED7722B7B013E00D37EBF /* RankOldAndNew.swift in Sources */, 27AE2EDA2B86E930009B43E2 /* RankStateView.swift in Sources */, From 8025c2a8cfd9a125d20b771c87ba35a45f54c2e4 Mon Sep 17 00:00:00 2001 From: Changhyun Kang Date: Tue, 19 Mar 2024 15:40:47 +0900 Subject: [PATCH 33/39] =?UTF-8?q?chore:=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=A1=9C=EC=A7=81=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOfficeTests/BoxOfficeTests.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/BoxOfficeTests/BoxOfficeTests.swift b/BoxOfficeTests/BoxOfficeTests.swift index e5b2b65f..602412c0 100644 --- a/BoxOfficeTests/BoxOfficeTests.swift +++ b/BoxOfficeTests/BoxOfficeTests.swift @@ -53,7 +53,7 @@ final class BoxOfficeTests: XCTestCase { let urlString = NetworkURL.makeURLRequest( type: .boxOffice, path: .dailyBoxOffice, - queries: .boxOffice(.dailyBoxOffice(date: Date.yesterdayToString)) + queries: .boxOffice(.dailyBoxOffice(date: Date.movieDateToString)) ) else { return From 296fc8141685b9821301e2f53e11d9078ee367ba Mon Sep 17 00:00:00 2001 From: Changhyun Kang Date: Tue, 19 Mar 2024 15:41:56 +0900 Subject: [PATCH 34/39] =?UTF-8?q?chore:=20BDD=20=EC=A3=BC=EC=84=9D=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOfficeTests/BoxOfficeTests.swift | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/BoxOfficeTests/BoxOfficeTests.swift b/BoxOfficeTests/BoxOfficeTests.swift index 602412c0..936d07d8 100644 --- a/BoxOfficeTests/BoxOfficeTests.swift +++ b/BoxOfficeTests/BoxOfficeTests.swift @@ -58,6 +58,8 @@ final class BoxOfficeTests: XCTestCase { else { return } + + // when let result = try await sut.fetchData(with: urlString) switch result { case .success(let movies): @@ -80,6 +82,8 @@ final class BoxOfficeTests: XCTestCase { else { return } + + // when let result = try await sut.fetchData(with: urlString) switch result { case .success(let movies): From a15fe3a363edfb2bb3078d79c182ee41a2cf5a90 Mon Sep 17 00:00:00 2001 From: Changhyun Kang Date: Mon, 25 Mar 2024 13:02:35 +0900 Subject: [PATCH 35/39] =?UTF-8?q?chore:=20=EB=84=A4=ED=8A=B8=EC=9B=8C?= =?UTF-8?q?=ED=81=AC=20=ED=86=B5=EC=8B=A0=20=ED=8C=8C=EC=9D=BC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EB=B0=8F=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice/Network/URLRequest/APIPath.swift | 23 ------------- BoxOffice/Network/URLRequest/APIQuery.swift | 34 ------------------- BoxOffice/Network/URLRequest/HttpMethod.swift | 31 +++++++++++++++++ 3 files changed, 31 insertions(+), 57 deletions(-) delete mode 100644 BoxOffice/Network/URLRequest/APIPath.swift delete mode 100644 BoxOffice/Network/URLRequest/APIQuery.swift create mode 100644 BoxOffice/Network/URLRequest/HttpMethod.swift diff --git a/BoxOffice/Network/URLRequest/APIPath.swift b/BoxOffice/Network/URLRequest/APIPath.swift deleted file mode 100644 index 384a7894..00000000 --- a/BoxOffice/Network/URLRequest/APIPath.swift +++ /dev/null @@ -1,23 +0,0 @@ -// -// APIPath.swift -// BoxOffice -// -// Created by 강창현 on 3/19/24. -// - -enum APIPath { - case dailyBoxOffice - case movieDetail - case kakao - - var path: String { - switch self { - case .dailyBoxOffice: - "/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json" - case .movieDetail: - "/kobisopenapi/webservice/rest/movie/searchMovieInfo.json" - case .kakao: - "/v2/search/image" - } - } -} diff --git a/BoxOffice/Network/URLRequest/APIQuery.swift b/BoxOffice/Network/URLRequest/APIQuery.swift deleted file mode 100644 index 5f5fef8a..00000000 --- a/BoxOffice/Network/URLRequest/APIQuery.swift +++ /dev/null @@ -1,34 +0,0 @@ -// -// APIQuery.swift -// BoxOffice -// -// Created by 강창현 on 3/19/24. -// - -import Foundation - -enum APIQuery { - case kakao(movieName: String) - case boxOffice(BoxOfficeType) - - var queries: [URLQueryItem] { - switch self { - case .kakao(let movieName): - [ - URLQueryItem(name: "query", value: "\(movieName) 영화포스터"), - URLQueryItem(name: "sort", value: "accuracy") - ] - case .boxOffice(let type): - type.queries - } - } - - var header: [String:String]? { - switch self { - case .kakao: - ["Authorization": "KakaoAK \(Bundle.main.kakaoApiKey)"] - case .boxOffice: - nil - } - } -} diff --git a/BoxOffice/Network/URLRequest/HttpMethod.swift b/BoxOffice/Network/URLRequest/HttpMethod.swift new file mode 100644 index 00000000..ace3fe71 --- /dev/null +++ b/BoxOffice/Network/URLRequest/HttpMethod.swift @@ -0,0 +1,31 @@ +// +// HttpMethodType.swift +// BoxOffice +// +// Created by 강창현 on 3/24/24. +// + +enum HttpMethod { + case get + case post + case put + case patch + case delete + + var + switch method { + case .get: + self.httpMethod = "GET" + case .post(let body): + self.httpMethod = "POST" + self.httpBody = try? JSONEncoder().encode(body) + case .put(let body): + self.httpMethod = "PUT" + self.httpBody = try? JSONEncoder().encode(body) + case .patch(let body): + self.httpMethod = "PATCH" + self.httpBody = try? JSONEncoder().encode(body) + case .delete(let body): + self.httpMethod = "DELETE" + self.httpBody = try? JSONEncoder().encode(body) +} From c5f2c2b8e2b74878a3815936db0d06cfb0fbb97f Mon Sep 17 00:00:00 2001 From: Changhyun Kang Date: Mon, 25 Mar 2024 13:03:31 +0900 Subject: [PATCH 36/39] =?UTF-8?q?feat:=20URLRequest=20=EC=83=9D=EC=84=B1?= =?UTF-8?q?=20=EB=A1=9C=EC=A7=81=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice.xcodeproj/project.pbxproj | 12 ++-- BoxOffice/Network/NetworkURL.swift | 26 +++++---- BoxOffice/Network/URLRequest/APIType.swift | 55 ++++++++++++++++++- .../Network/URLRequest/BoxOfficeType.swift | 9 +++ BoxOffice/Network/URLRequest/HttpMethod.swift | 30 +++++----- 5 files changed, 94 insertions(+), 38 deletions(-) diff --git a/BoxOffice.xcodeproj/project.pbxproj b/BoxOffice.xcodeproj/project.pbxproj index 1c456de0..d7dfb512 100644 --- a/BoxOffice.xcodeproj/project.pbxproj +++ b/BoxOffice.xcodeproj/project.pbxproj @@ -8,6 +8,7 @@ /* Begin PBXBuildFile section */ 27157BF72B8344F3000BAC45 /* MovieInfomationDetail.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27157BF62B8344F2000BAC45 /* MovieInfomationDetail.swift */; }; + 273615902BB04070004C3F15 /* HttpMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2736158F2BB04070004C3F15 /* HttpMethod.swift */; }; 2757936F2B7F1DB900C08906 /* MovieInfoResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2757936E2B7F1DB900C08906 /* MovieInfoResult.swift */; }; 275793712B7F1DCC00C08906 /* MovieInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = 275793702B7F1DCC00C08906 /* MovieInfo.swift */; }; 275793732B7F1DE200C08906 /* Actor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 275793722B7F1DE200C08906 /* Actor.swift */; }; @@ -26,8 +27,6 @@ 2757D9DD2B9B2C4200B13BD7 /* Document.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2757D9DC2B9B2C4200B13BD7 /* Document.swift */; }; 2757D9DF2B9B2C5400B13BD7 /* Meta.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2757D9DE2B9B2C5400B13BD7 /* Meta.swift */; }; 27674EE82BA95ED400564726 /* APIType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27674EE72BA95ED400564726 /* APIType.swift */; }; - 27674EEA2BA95EF000564726 /* APIPath.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27674EE92BA95EF000564726 /* APIPath.swift */; }; - 27674EEC2BA95F0A00564726 /* APIQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27674EEB2BA95F0A00564726 /* APIQuery.swift */; }; 27674EEE2BA95F3300564726 /* BoxOfficeType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27674EED2BA95F3300564726 /* BoxOfficeType.swift */; }; 276ED7632B7B002600D37EBF /* BoxOfficeTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 276ED7622B7B002600D37EBF /* BoxOfficeTests.swift */; }; 276ED76A2B7B006400D37EBF /* BoxOffice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 276ED7692B7B006400D37EBF /* BoxOffice.swift */; }; @@ -78,6 +77,7 @@ /* Begin PBXFileReference section */ 27157BF62B8344F2000BAC45 /* MovieInfomationDetail.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MovieInfomationDetail.swift; sourceTree = ""; }; + 2736158F2BB04070004C3F15 /* HttpMethod.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HttpMethod.swift; sourceTree = ""; }; 2757936E2B7F1DB900C08906 /* MovieInfoResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MovieInfoResult.swift; sourceTree = ""; }; 275793702B7F1DCC00C08906 /* MovieInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MovieInfo.swift; sourceTree = ""; }; 275793722B7F1DE200C08906 /* Actor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Actor.swift; sourceTree = ""; }; @@ -96,8 +96,6 @@ 2757D9DC2B9B2C4200B13BD7 /* Document.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Document.swift; sourceTree = ""; }; 2757D9DE2B9B2C5400B13BD7 /* Meta.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Meta.swift; sourceTree = ""; }; 27674EE72BA95ED400564726 /* APIType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIType.swift; sourceTree = ""; }; - 27674EE92BA95EF000564726 /* APIPath.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIPath.swift; sourceTree = ""; }; - 27674EEB2BA95F0A00564726 /* APIQuery.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIQuery.swift; sourceTree = ""; }; 27674EED2BA95F3300564726 /* BoxOfficeType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoxOfficeType.swift; sourceTree = ""; }; 276ED7602B7B002600D37EBF /* BoxOfficeTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BoxOfficeTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 276ED7622B7B002600D37EBF /* BoxOfficeTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BoxOfficeTests.swift; sourceTree = ""; }; @@ -219,9 +217,8 @@ isa = PBXGroup; children = ( 27674EE72BA95ED400564726 /* APIType.swift */, - 27674EE92BA95EF000564726 /* APIPath.swift */, - 27674EEB2BA95F0A00564726 /* APIQuery.swift */, 27674EED2BA95F3300564726 /* BoxOfficeType.swift */, + 2736158F2BB04070004C3F15 /* HttpMethod.swift */, ); path = URLRequest; sourceTree = ""; @@ -505,7 +502,6 @@ 63DF20EF2970E1A0005DF7D1 /* AppDelegate.swift in Sources */, 27AE2EDE2B86E965009B43E2 /* MovieStackView.swift in Sources */, 276ED76E2B7B010D00D37EBF /* BoxOfficeResult.swift in Sources */, - 27674EEA2BA95EF000564726 /* APIPath.swift in Sources */, 2757936F2B7F1DB900C08906 /* MovieInfoResult.swift in Sources */, 2757937F2B7F1E3F00C08906 /* ShowType.swift in Sources */, 2757D9DD2B9B2C4200B13BD7 /* Document.swift in Sources */, @@ -536,8 +532,8 @@ 278FA9A32B8B0C7500FFDB3F /* BoxOfficeListViewDelegate.swift in Sources */, 2757938A2B7F51BF00C08906 /* URLSession+Extension.swift in Sources */, C14DF4B52B7DAE49009E3258 /* Bundle+Extension.swift in Sources */, - 27674EEC2BA95F0A00564726 /* APIQuery.swift in Sources */, 275793772B7F1E0800C08906 /* Company.swift in Sources */, + 273615902BB04070004C3F15 /* HttpMethod.swift in Sources */, 276ED7722B7B013E00D37EBF /* RankOldAndNew.swift in Sources */, 27AE2EDA2B86E930009B43E2 /* RankStateView.swift in Sources */, 275793862B7F516800C08906 /* MockURLSession.swift in Sources */, diff --git a/BoxOffice/Network/NetworkURL.swift b/BoxOffice/Network/NetworkURL.swift index a2073ad6..1eff432e 100644 --- a/BoxOffice/Network/NetworkURL.swift +++ b/BoxOffice/Network/NetworkURL.swift @@ -7,28 +7,32 @@ import Foundation -struct NetworkURL { - static func makeURLRequest(type: APIType, path: APIPath, queries: APIQuery) -> URLRequest? { - var urlComponents = URLComponents() - urlComponents.scheme = "https" - urlComponents.host = type.host - urlComponents.path = path.path - urlComponents.queryItems = queries.queries - - guard +enum NetworkURL { + static func makeURLRequest(type: APIType, httpMethod: HttpMethod = .get) -> URLRequest? { + let urlComponents = makeURLComponents(type: type) + guard let url = urlComponents.url else { return nil } var request = URLRequest(url: url) - request.httpMethod = "GET" + request.httpMethod = httpMethod.type guard - let header = queries.header + let header = type.header else { return request } request.allHTTPHeaderFields = header return request } + + private static func makeURLComponents(type: APIType) -> URLComponents { + var urlComponents = URLComponents() + urlComponents.scheme = "https" + urlComponents.host = type.host + urlComponents.path = type.path + urlComponents.queryItems = type.queries + return urlComponents + } } diff --git a/BoxOffice/Network/URLRequest/APIType.swift b/BoxOffice/Network/URLRequest/APIType.swift index df7795c6..e76bc88f 100644 --- a/BoxOffice/Network/URLRequest/APIType.swift +++ b/BoxOffice/Network/URLRequest/APIType.swift @@ -5,16 +5,65 @@ // Created by 강창현 on 3/19/24. // +import Foundation + enum APIType { - case boxOffice - case kakao + case kakao(movieName: String) + case boxOffice(BoxOfficeType) + case image(url: URL) - var host: String { + var host: String? { switch self { case .boxOffice: return "www.kobis.or.kr" case .kakao: return "dapi.kakao.com" + case .image(let url): + return url.host + } + } + + var header: [String:String]? { + switch self { + case .kakao: + return ["Authorization": "KakaoAK \(Bundle.main.kakaoApiKey)"] + case .boxOffice: + return nil + case .image: + return nil + } + } + + var queries: [URLQueryItem]? { + switch self { + case .kakao(let movieName): + return [ + URLQueryItem(name: "query", value: "\(movieName) 영화포스터"), + URLQueryItem(name: "sort", value: "accuracy") + ] + case .boxOffice(let type): + return type.queries + case .image(let url): + guard + let urlComponents = URLComponents( + url: url, + resolvingAgainstBaseURL: false + ) + else { + return nil + } + return urlComponents.queryItems + } + } + + var path: String { + switch self { + case .kakao: + return "/v2/search/image" + case .boxOffice(let type): + return type.path + case .image(url: let url): + return url.path } } } diff --git a/BoxOffice/Network/URLRequest/BoxOfficeType.swift b/BoxOffice/Network/URLRequest/BoxOfficeType.swift index 39d41fda..f04b7ba8 100644 --- a/BoxOffice/Network/URLRequest/BoxOfficeType.swift +++ b/BoxOffice/Network/URLRequest/BoxOfficeType.swift @@ -25,4 +25,13 @@ enum BoxOfficeType { ] } } + + var path: String { + switch self { + case .dailyBoxOffice: + "/kobisopenapi/webservice/rest/boxoffice/searchDailyBoxOfficeList.json" + case .movieDetail: + "/kobisopenapi/webservice/rest/movie/searchMovieInfo.json" + } + } } diff --git a/BoxOffice/Network/URLRequest/HttpMethod.swift b/BoxOffice/Network/URLRequest/HttpMethod.swift index ace3fe71..b49227c8 100644 --- a/BoxOffice/Network/URLRequest/HttpMethod.swift +++ b/BoxOffice/Network/URLRequest/HttpMethod.swift @@ -12,20 +12,18 @@ enum HttpMethod { case patch case delete - var - switch method { - case .get: - self.httpMethod = "GET" - case .post(let body): - self.httpMethod = "POST" - self.httpBody = try? JSONEncoder().encode(body) - case .put(let body): - self.httpMethod = "PUT" - self.httpBody = try? JSONEncoder().encode(body) - case .patch(let body): - self.httpMethod = "PATCH" - self.httpBody = try? JSONEncoder().encode(body) - case .delete(let body): - self.httpMethod = "DELETE" - self.httpBody = try? JSONEncoder().encode(body) + var type: String { + switch self { + case .get: + return "GET" + case .post: + return "POST" + case .put: + return "PUT" + case .patch: + return "PATCH" + case .delete: + return "DELETE" + } + } } From 7afcf0c1cba5fb8497e5be6229bf0ea62accf52b Mon Sep 17 00:00:00 2001 From: Changhyun Kang Date: Mon, 25 Mar 2024 13:04:17 +0900 Subject: [PATCH 37/39] =?UTF-8?q?refactor:=20enum=20=ED=83=80=EC=9E=85?= =?UTF-8?q?=EC=9C=BC=EB=A1=9C=20=EC=88=98=EC=A0=95=20=ED=9B=84=20View?= =?UTF-8?q?=EB=A7=88=EB=8B=A4=20=EB=8C=80=EC=9D=91=ED=95=A0=20=EC=88=98=20?= =?UTF-8?q?=EC=9E=88=EA=B2=8C=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice/View/LoadingIndicatorView.swift | 30 +++++++++-------------- 1 file changed, 11 insertions(+), 19 deletions(-) diff --git a/BoxOffice/View/LoadingIndicatorView.swift b/BoxOffice/View/LoadingIndicatorView.swift index 809a76d6..ed440f62 100644 --- a/BoxOffice/View/LoadingIndicatorView.swift +++ b/BoxOffice/View/LoadingIndicatorView.swift @@ -7,22 +7,20 @@ import UIKit -struct LoadingIndicatorView { - static func showLoading() { +enum LoadingIndicatorView { + static func showLoading(in view: UIView) { DispatchQueue.main.async { - guard - let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, - let window = windowScene.windows.last - else { - return - } let loadingIndicatorView: UIActivityIndicatorView guard - let existedView = window.subviews.first(where: { $0 is UIActivityIndicatorView } ) as? UIActivityIndicatorView + let existedView = view.subviews.first(where: { $0 is UIActivityIndicatorView } ) as? UIActivityIndicatorView else { loadingIndicatorView = UIActivityIndicatorView() - loadingIndicatorView.frame = window.frame - window.addSubview(loadingIndicatorView) + loadingIndicatorView.frame = view.frame + loadingIndicatorView.center = CGPoint( + x: view.frame.width / 2, + y: view.frame.height / 2 + view.bounds.origin.y + ) + view.addSubview(loadingIndicatorView) loadingIndicatorView.startAnimating() return } @@ -31,15 +29,9 @@ struct LoadingIndicatorView { } } - static func hideLoading() { + static func hideLoading(in view: UIView) { DispatchQueue.main.async { - guard - let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene, - let window = windowScene.windows.last - else { - return - } - window.subviews.filter({ $0 is UIActivityIndicatorView }).forEach { $0.removeFromSuperview() } + view.subviews.filter({ $0 is UIActivityIndicatorView }).forEach { $0.removeFromSuperview() } } } } From 5356d9cca824a786d37a1a4c452e341801a6a434 Mon Sep 17 00:00:00 2001 From: Changhyun Kang Date: Mon, 25 Mar 2024 13:05:12 +0900 Subject: [PATCH 38/39] =?UTF-8?q?refactor:=20=EC=88=98=EC=A0=95=EB=90=9C?= =?UTF-8?q?=20=EB=A1=9C=EC=A7=81=EC=97=90=20=EB=94=B0=EB=9D=BC=20=EB=A6=AC?= =?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- BoxOffice/Model/MovieRepository.swift | 39 +++++++-------------------- 1 file changed, 10 insertions(+), 29 deletions(-) diff --git a/BoxOffice/Model/MovieRepository.swift b/BoxOffice/Model/MovieRepository.swift index 46ed3de1..d1e8e4b3 100644 --- a/BoxOffice/Model/MovieRepository.swift +++ b/BoxOffice/Model/MovieRepository.swift @@ -12,11 +12,7 @@ struct MovieRepository { func fetchBoxOfficeResultData(date: String) async throws -> (Result) { guard - let urlRequest = NetworkURL.makeURLRequest( - type: .boxOffice, - path: .dailyBoxOffice, - queries: .boxOffice(.dailyBoxOffice(date: date)) - ) + let urlRequest = NetworkURL.makeURLRequest(type: .boxOffice(.dailyBoxOffice(date: date))) else { return .failure(.invalidURLRequestError) } @@ -32,11 +28,7 @@ struct MovieRepository { func fetchMovieInfoResultData(code: String) async throws -> (Result) { guard - let urlRequest = NetworkURL.makeURLRequest( - type: .boxOffice, - path: .movieDetail, - queries: .boxOffice(.movieDetail(code: code)) - ) + let urlRequest = NetworkURL.makeURLRequest(type: .boxOffice(.movieDetail(code: code))) else { return .failure(.invalidURLRequestError) } @@ -52,11 +44,7 @@ struct MovieRepository { func fetchMoiveImageURL(movieName: String) async throws -> (Result) { guard - let urlRequest = NetworkURL.makeURLRequest( - type: .kakao, - path: .kakao, - queries: .kakao(movieName: movieName) - ) + let urlRequest = NetworkURL.makeURLRequest(type: .kakao(movieName: movieName)) else { return .failure(.invalidURLRequestError) } @@ -72,11 +60,16 @@ struct MovieRepository { func fetchMovieImage(urlString: String) async throws -> (Result) { guard - let url = makeHttps(urlString: urlString) + let url = URL(string: urlString) else { return .failure(.invalidURLError) } - let urlRequest = URLRequest(url: url) + + guard + let urlRequest = NetworkURL.makeURLRequest(type: .image(url: url)) + else { + return .failure(.invalidURLRequestError) + } let result = try await apiService.fetchData(with: urlRequest) return result @@ -98,16 +91,4 @@ private extension MovieRepository { return .failure(.decodingError) } } - - func makeHttps(urlString: String) -> URL? { - let originUrl = urlString - var editingUrl = originUrl.components(separatedBy: "://") - editingUrl[0] = "https" - guard - let resultUrl = URL(string: "\(editingUrl[0])://\(editingUrl[1])") - else { - return nil - } - return resultUrl - } } From 9b840bed1b58fba701a2670920573a6b19a7bb3f Mon Sep 17 00:00:00 2001 From: Changhyun Kang Date: Mon, 25 Mar 2024 13:05:40 +0900 Subject: [PATCH 39/39] =?UTF-8?q?refactor:=20=EC=88=98=EC=A0=95=EB=90=9C?= =?UTF-8?q?=20indicator=20=EB=A1=9C=EC=A7=81=EC=97=90=20=EB=94=B0=EB=9D=BC?= =?UTF-8?q?=20=EB=A6=AC=ED=8C=A9=ED=86=A0=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controller/BoxOfficeDetailViewController.swift | 14 +++++++------- BoxOffice/Controller/BoxOfficeViewController.swift | 14 +++----------- 2 files changed, 10 insertions(+), 18 deletions(-) diff --git a/BoxOffice/Controller/BoxOfficeDetailViewController.swift b/BoxOffice/Controller/BoxOfficeDetailViewController.swift index f5005723..47cd3a39 100644 --- a/BoxOffice/Controller/BoxOfficeDetailViewController.swift +++ b/BoxOffice/Controller/BoxOfficeDetailViewController.swift @@ -11,7 +11,7 @@ final class BoxOfficeDetailViewController: UIViewController { private let movieName: String private let movieCode: String private let movieManager: MovieManager - private let scrollView = BoxOfficeDetailView() + private let boxOfficeDetailView = BoxOfficeDetailView() init( movieName: String, @@ -37,8 +37,8 @@ final class BoxOfficeDetailViewController: UIViewController { private extension BoxOfficeDetailViewController { func setupView() { - LoadingIndicatorView.showLoading() - view = scrollView + LoadingIndicatorView.showLoading(in: self.boxOfficeDetailView) + view = boxOfficeDetailView view.backgroundColor = .white self.title = movieName } @@ -52,21 +52,21 @@ private extension BoxOfficeDetailViewController { } catch { print(error.localizedDescription) } - LoadingIndicatorView.hideLoading() + LoadingIndicatorView.hideLoading(in: self.boxOfficeDetailView) } } func fetchMovieInfo() async throws { let data = try await movieManager.fetchMovieInfoResultData(code: movieCode) - self.scrollView.setupDetailView(data: data) + self.boxOfficeDetailView.setupDetailView(data: data) } func fetchMoiveImageURL() async throws { try await movieManager.fetchMoiveImageURL(movieName: movieName) } - + func setupMovieImage() async throws { guard let data = try await movieManager.fetchImageData() else { return } - self.scrollView.setupImage(image: UIImage(data: data)) + self.boxOfficeDetailView.setupImage(image: UIImage(data: data)) } } diff --git a/BoxOffice/Controller/BoxOfficeViewController.swift b/BoxOffice/Controller/BoxOfficeViewController.swift index a2caf478..a962208b 100644 --- a/BoxOffice/Controller/BoxOfficeViewController.swift +++ b/BoxOffice/Controller/BoxOfficeViewController.swift @@ -17,14 +17,6 @@ final class BoxOfficeViewController: UIViewController { }() private lazy var dataSource = BoxOfficeListDataSource(self.boxOfficeListView) -// init() { -// super.init(nibName: nil, bundle: nil) -// } -// -// required init?(coder: NSCoder) { -// fatalError("init(coder:) has not been implemented") -// } - override func viewDidLoad() { super.viewDidLoad() setupBoxOfficeListView() @@ -38,7 +30,7 @@ private extension BoxOfficeViewController { } func setupBoxOfficeListView() { - LoadingIndicatorView.showLoading() + LoadingIndicatorView.showLoading(in: self.boxOfficeListView) boxOfficeListView.boxOfficeListDelegate = self boxOfficeListView.delegate = self boxOfficeListView.dataSource = dataSource @@ -49,8 +41,8 @@ private extension BoxOfficeViewController { Task { do { let result = try await movieManager.fetchBoxOfficeResultData(date: Date.movieDateToString) - self.reloadCollectionListData(result: result) - LoadingIndicatorView.hideLoading() + self.reloadCollectionListData(result: result) + LoadingIndicatorView.hideLoading(in: self.view) } catch { print(error.localizedDescription) }