Skip to content

Commit 089498c

Browse files
authored
Merge pull request #71 from lee-yeonwoo/#67CourseDiscoveryAPI
[Feat] #67 - Course Discovery API 연결
2 parents 81572b3 + 0aeb849 commit 089498c

File tree

7 files changed

+170
-20
lines changed

7 files changed

+170
-20
lines changed

Runnect-iOS/Runnect-iOS.xcodeproj/project.pbxproj

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,8 @@
131131
DAD5A3D8296C6D9600C8166B /* AdImageCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD5A3D7296C6D9600C8166B /* AdImageCollectionViewCell.swift */; };
132132
DAD5A3DA296C6DA500C8166B /* TitleCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD5A3D9296C6DA500C8166B /* TitleCollectionViewCell.swift */; };
133133
DAD5A3DC296C6DB800C8166B /* MapCollectionViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD5A3DB296C6DB800C8166B /* MapCollectionViewCell.swift */; };
134+
DAD5A3E2296D4C6500C8166B /* PickedMapListResponseDto.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD5A3E1296D4C6500C8166B /* PickedMapListResponseDto.swift */; };
135+
DAD5A3E4296D526D00C8166B /* CourseDiscoveryRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DAD5A3E3296D526D00C8166B /* CourseDiscoveryRouter.swift */; };
134136
/* End PBXBuildFile section */
135137

136138
/* Begin PBXFileReference section */
@@ -152,9 +154,12 @@
152154
CE0C23782966D6AF00B45063 /* ViewPager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewPager.swift; sourceTree = "<group>"; };
153155
CE0D9FD229648DA300CEB5CD /* CustomAlertVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomAlertVC.swift; sourceTree = "<group>"; };
154156
CE10063929680C5700FD31FB /* .gitkeep */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitkeep; sourceTree = "<group>"; };
157+
CE10063B29680C6800FD31FB /* .gitkeep */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitkeep; sourceTree = "<group>"; };
155158
CE10063C29680C7000FD31FB /* .gitkeep */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitkeep; sourceTree = "<group>"; };
156159
CE10063D29680C8100FD31FB /* .gitkeep */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitkeep; sourceTree = "<group>"; };
157160
CE10063E29680C8800FD31FB /* .gitkeep */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitkeep; sourceTree = "<group>"; };
161+
CE10063F29680C9800FD31FB /* .gitkeep */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitkeep; sourceTree = "<group>"; };
162+
CE10064129680CA700FD31FB /* .gitkeep */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitkeep; sourceTree = "<group>"; };
158163
CE10064229680CAD00FD31FB /* .gitkeep */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitkeep; sourceTree = "<group>"; };
159164
CE10064329680CB400FD31FB /* .gitkeep */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitkeep; sourceTree = "<group>"; };
160165
CE10064429680CBC00FD31FB /* .gitkeep */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitkeep; sourceTree = "<group>"; };
@@ -280,6 +285,8 @@
280285
DAD5A3D7296C6D9600C8166B /* AdImageCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AdImageCollectionViewCell.swift; sourceTree = "<group>"; };
281286
DAD5A3D9296C6DA500C8166B /* TitleCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TitleCollectionViewCell.swift; sourceTree = "<group>"; };
282287
DAD5A3DB296C6DB800C8166B /* MapCollectionViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MapCollectionViewCell.swift; sourceTree = "<group>"; };
288+
DAD5A3E1296D4C6500C8166B /* PickedMapListResponseDto.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PickedMapListResponseDto.swift; sourceTree = "<group>"; };
289+
DAD5A3E3296D526D00C8166B /* CourseDiscoveryRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseDiscoveryRouter.swift; sourceTree = "<group>"; };
283290
E837271A78E1C0A0C30789BF /* Pods-Runnect-iOS.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runnect-iOS.release.xcconfig"; path = "Target Support Files/Pods-Runnect-iOS/Pods-Runnect-iOS.release.xcconfig"; sourceTree = "<group>"; };
284291
/* End PBXFileReference section */
285292

@@ -406,7 +413,7 @@
406413
CE10063529680C0F00FD31FB /* CourseDiscoveryDto */ = {
407414
isa = PBXGroup;
408415
children = (
409-
CE10063C29680C7000FD31FB /* .gitkeep */,
416+
DAD5A3E0296D4C2A00C8166B /* ResponseDto */,
410417
);
411418
path = CourseDiscoveryDto;
412419
sourceTree = "<group>";
@@ -721,7 +728,7 @@
721728
CE40BB28296808300030ABCA /* CourseDiscoveryRouter */ = {
722729
isa = PBXGroup;
723730
children = (
724-
CE10064229680CAD00FD31FB /* .gitkeep */,
731+
DAD5A3E3296D526D00C8166B /* CourseDiscoveryRouter.swift */,
725732
);
726733
path = CourseDiscoveryRouter;
727734
sourceTree = "<group>";
@@ -1120,6 +1127,14 @@
11201127
path = VC;
11211128
sourceTree = "<group>";
11221129
};
1130+
DAD5A3E0296D4C2A00C8166B /* ResponseDto */ = {
1131+
isa = PBXGroup;
1132+
children = (
1133+
DAD5A3E1296D4C6500C8166B /* PickedMapListResponseDto.swift */,
1134+
);
1135+
path = ResponseDto;
1136+
sourceTree = "<group>";
1137+
};
11231138
/* End PBXGroup section */
11241139

11251140
/* Begin PBXNativeTarget section */
@@ -1292,6 +1307,7 @@
12921307
CEEC6B3C2961C51A00D00E1E /* CourseStorageVC.swift in Sources */,
12931308
CE4545C9295D7AF4003201E1 /* AppDelegate.swift in Sources */,
12941309
CE6655C8295D849F00C64E12 /* StringLiterals.swift in Sources */,
1310+
DAD5A3E4296D526D00C8166B /* CourseDiscoveryRouter.swift in Sources */,
12951311
CEEC6B3E2961C53700D00E1E /* CourseDiscoveryVC.swift in Sources */,
12961312
CE6655E0295D87D200C64E12 /* UIDevice+.swift in Sources */,
12971313
CE40BB242968068E0030ABCA /* Config.swift in Sources */,
@@ -1312,6 +1328,7 @@
13121328
CE40BB1C2967E4910030ABCA /* RunningWaitingVC.swift in Sources */,
13131329
CE6B63D6296731F9003F900F /* ScrapCourseListView.swift in Sources */,
13141330
CE6655F8295D90CF00C64E12 /* adjusted+.swift in Sources */,
1331+
DAD5A3E2296D4C6500C8166B /* PickedMapListResponseDto.swift in Sources */,
13151332
CE4545CB295D7AF4003201E1 /* SceneDelegate.swift in Sources */,
13161333
CE591EA1296D5EB5000FCBB3 /* PrivateCourseResponseDto.swift in Sources */,
13171334
A3BC2F3F2964706100198261 /* UploadedCourseInfoCVC.swift in Sources */,

Runnect-iOS/Runnect-iOS/Network/Dto/CourseDiscoveryDto/.gitkeep

Whitespace-only changes.
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//
2+
// CourseDiscoveryResponsDto.swift
3+
// Runnect-iOS
4+
//
5+
// Created by YEONOO on 2023/01/10.
6+
//
7+
8+
import Foundation
9+
10+
// MARK: - PickedMapListResponseDto
11+
12+
struct PickedMapListResponseDto: Codable {
13+
let publicCourses: [PublicCourse]
14+
}
15+
16+
// MARK: - PublicCourse
17+
18+
struct PublicCourse: Codable {
19+
let id, courseId: Int
20+
let title: String
21+
let image: String
22+
let scarp: Bool
23+
let departure: CourseDiscoveryDeparture
24+
}
25+
26+
// MARK: - CourseDiscoveryDeparture
27+
28+
struct CourseDiscoveryDeparture: Codable {
29+
let region: String
30+
let city: String
31+
}

Runnect-iOS/Runnect-iOS/Network/Router/CourseDiscoveryRouter/.gitkeep

Whitespace-only changes.
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//
2+
// pickedMapListRouter.swift
3+
// Runnect-iOS
4+
//
5+
// Created by YEONOO on 2023/01/10.
6+
//
7+
8+
import Foundation
9+
10+
import Moya
11+
12+
enum pickedMapListRouter {
13+
case getCourseData
14+
}
15+
16+
extension pickedMapListRouter: TargetType {
17+
var baseURL: URL {
18+
guard let url = URL(string: Config.baseURL) else {
19+
fatalError("baseURL could not be configured")
20+
}
21+
22+
return url
23+
}
24+
25+
var path: String {
26+
switch self {
27+
case .getCourseData:
28+
return "/public-course"
29+
}
30+
}
31+
32+
var method: Moya.Method {
33+
switch self {
34+
case .getCourseData:
35+
return .get
36+
}
37+
}
38+
39+
var task: Moya.Task {
40+
switch self {
41+
case .getCourseData:
42+
return .requestPlain
43+
}
44+
}
45+
46+
var headers: [String: String]? {
47+
switch self {
48+
case .getCourseData:
49+
return Config.headerWithDeviceId
50+
}
51+
}
52+
}

Runnect-iOS/Runnect-iOS/Presentation/CourseDiscovery/Views/VC/CourseDiscoveryVC.swift

Lines changed: 67 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,17 @@ import UIKit
1010
import Then
1111
import SnapKit
1212
import Combine
13+
import Moya
1314

1415
final class CourseDiscoveryVC: UIViewController {
15-
16+
// MARK: - Properties
17+
18+
let pickedMapListProvider = MoyaProvider<pickedMapListRouter>(
19+
plugins: [NetworkLoggerPlugin(verbose: true)]
20+
)
21+
22+
private var courseList = [PublicCourse]()
23+
1624
// MARK: - UIComponents
1725

1826
private lazy var navibar = CustomNavigationBar(self, type: .title).setTitle("코스 발견")
@@ -23,13 +31,13 @@ final class CourseDiscoveryVC: UIViewController {
2331
private let plusButton = UIButton(type: .system).then {
2432
$0.setImage(ImageLiterals.icPlus, for: .normal)
2533
}
26-
34+
2735
// MARK: - collectionview
2836

2937
private lazy var mapCollectionView: UICollectionView = {
3038
let layout = UICollectionViewFlowLayout()
3139
layout.scrollDirection = .vertical
32-
40+
3341
let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
3442
collectionView.backgroundColor = .clear
3543
collectionView.isScrollEnabled = true
@@ -51,13 +59,19 @@ final class CourseDiscoveryVC: UIViewController {
5159
override func viewWillAppear(_ animated: Bool) {
5260
super.viewWillAppear(animated)
5361
self.hideTabBar(wantsToHide: false)
62+
self.getCourseData()
5463
}
5564
}
5665

5766
// MARK: - Methods
5867

5968
extension CourseDiscoveryVC {
6069

70+
private func setData(courseList: [PublicCourse]) {
71+
self.courseList = courseList
72+
mapCollectionView.reloadData()
73+
}
74+
6175
private func setDelegate() {
6276
mapCollectionView.delegate = self
6377
mapCollectionView.dataSource = self
@@ -76,21 +90,22 @@ extension CourseDiscoveryVC {
7690

7791
}
7892
}
79-
80-
// MARK: - @objc Function
8193

82-
extension CourseDiscoveryVC {
83-
@objc private func pushToSearchVC() {
84-
let nextVC = CourseSearchVC()
85-
self.navigationController?.pushViewController(nextVC, animated: true)
86-
}
87-
@objc private func pushToDiscoveryVC() {
88-
let nextVC = MyCourseSelectVC()
89-
self.navigationController?.pushViewController(nextVC, animated: true)
90-
}
94+
// MARK: - @objc Function
95+
96+
extension CourseDiscoveryVC {
97+
@objc private func pushToSearchVC() {
98+
let nextVC = CourseSearchVC()
99+
self.navigationController?.pushViewController(nextVC, animated: true)
100+
}
101+
@objc private func pushToDiscoveryVC() {
102+
let nextVC = MyCourseSelectVC()
103+
self.navigationController?.pushViewController(nextVC, animated: true)
91104
}
105+
}
106+
107+
// MARK: - UI & Layout
92108

93-
// MARK: - UI & Layout
94109
extension CourseDiscoveryVC {
95110
private func setNavigationBar() {
96111
view.addSubview(navibar)
@@ -111,7 +126,7 @@ extension CourseDiscoveryVC {
111126
mapCollectionView.backgroundColor = .w1
112127
view.addSubviews(plusButton, mapCollectionView)
113128
view.bringSubviewToFront(plusButton)
114-
129+
115130
mapCollectionView.snp.makeConstraints {
116131
$0.top.equalTo(self.navibar.snp.bottom)
117132
$0.leading.bottom.trailing.equalTo(view.safeAreaLayoutGuide)
@@ -128,15 +143,15 @@ extension CourseDiscoveryVC {
128143

129144
extension CourseDiscoveryVC: UICollectionViewDelegate, UICollectionViewDataSource {
130145
func numberOfSections(in collectionView: UICollectionView) -> Int {
131-
3
146+
return 3
132147
}
133148

134149
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
135150
switch section {
136151
case 0, 1:
137152
return 1
138153
case 2:
139-
return 15
154+
return self.courseList.count
140155
default:
141156
return 0
142157
}
@@ -152,6 +167,9 @@ extension CourseDiscoveryVC: UICollectionViewDelegate, UICollectionViewDataSourc
152167
} else {
153168
guard let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CourseListCVC.className, for: indexPath) as? CourseListCVC else { return UICollectionViewCell() }
154169
cell.setCellType(type: .all)
170+
let model = self.courseList[indexPath.item]
171+
let location = "\(model.departure.region) \(model.departure.city)"
172+
cell.setData(imageURL: model.image, title: model.title, location: location, didLike: model.scarp)
155173
return cell
156174
}
157175
}
@@ -208,3 +226,34 @@ extension CourseDiscoveryVC: UICollectionViewDelegateFlowLayout {
208226
}
209227
}
210228
}
229+
230+
// MARK: - Network
231+
232+
extension CourseDiscoveryVC {
233+
private func getCourseData() {
234+
LoadingIndicator.showLoading()
235+
pickedMapListProvider.request(.getCourseData) { response in
236+
LoadingIndicator.hideLoading()
237+
switch response {
238+
case .success(let result):
239+
let status = result.statusCode
240+
if 200..<300 ~= status {
241+
do {
242+
let responseDto = try result.map(BaseResponse<PickedMapListResponseDto>.self)
243+
guard let data = responseDto.data else { return }
244+
self.setData(courseList: data.publicCourses)
245+
} catch {
246+
print(error.localizedDescription)
247+
}
248+
}
249+
if status >= 400 {
250+
print("400 error")
251+
self.showNetworkFailureToast()
252+
}
253+
case .failure(let error):
254+
print(error.localizedDescription)
255+
self.showNetworkFailureToast()
256+
}
257+
}
258+
}
259+
}

Runnect-iOS/Runnect-iOS/Presentation/CourseStorage/Views/CourseListView/PrivateCourseListView.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ extension PrivateCourseListView {
5858
func setData(courseList: [PrivateCourse]) {
5959
self.courseList = courseList
6060
self.courseListCollectionView.reloadData()
61+
self.emptyView.isHidden = !courseList.isEmpty
6162
}
6263

6364
private func setDelegate() {

0 commit comments

Comments
 (0)