diff --git a/KkuMulKum/Resource/Assets.xcassets/Image/img_empty.imageset/Contents.json b/KkuMulKum/Resource/Assets.xcassets/Image/img_empty.imageset/Contents.json new file mode 100644 index 00000000..319c98e2 --- /dev/null +++ b/KkuMulKum/Resource/Assets.xcassets/Image/img_empty.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "empty.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "empty@2x.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "empty@3x.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/KkuMulKum/Resource/Assets.xcassets/Image/img_empty.imageset/empty.png b/KkuMulKum/Resource/Assets.xcassets/Image/img_empty.imageset/empty.png new file mode 100644 index 00000000..ebc5618f Binary files /dev/null and b/KkuMulKum/Resource/Assets.xcassets/Image/img_empty.imageset/empty.png differ diff --git a/KkuMulKum/Resource/Assets.xcassets/Image/img_empty.imageset/empty@2x.png b/KkuMulKum/Resource/Assets.xcassets/Image/img_empty.imageset/empty@2x.png new file mode 100644 index 00000000..c6981472 Binary files /dev/null and b/KkuMulKum/Resource/Assets.xcassets/Image/img_empty.imageset/empty@2x.png differ diff --git a/KkuMulKum/Resource/Assets.xcassets/Image/img_empty.imageset/empty@3x.png b/KkuMulKum/Resource/Assets.xcassets/Image/img_empty.imageset/empty@3x.png new file mode 100644 index 00000000..6719945e Binary files /dev/null and b/KkuMulKum/Resource/Assets.xcassets/Image/img_empty.imageset/empty@3x.png differ diff --git a/KkuMulKum/Source/Core/MainTabBarController.swift b/KkuMulKum/Source/Core/MainTabBarController.swift index 2f6876d9..f35f541e 100644 --- a/KkuMulKum/Source/Core/MainTabBarController.swift +++ b/KkuMulKum/Source/Core/MainTabBarController.swift @@ -25,7 +25,7 @@ final class MainTabBarController: UITabBarController { private func setTabBar() { let homeViewController: HomeViewController = HomeViewController( viewModel: HomeViewModel( - service: MockHomeService() + service: HomeService() )).then { $0.tabBarItem.title = "홈" $0.tabBarItem.image = .iconHome @@ -33,7 +33,7 @@ final class MainTabBarController: UITabBarController { let meetingListViewController = MeetingListViewController( viewModel: MeetingListViewModel( - service: MockMeetingListService() + service: MeetingService() )).then { $0.tabBarItem.title = "내 모임" $0.tabBarItem.image = .iconGroup diff --git a/KkuMulKum/Source/Home/ServiceType/HomeServiceType.swift b/KkuMulKum/Source/Home/ServiceType/HomeServiceType.swift index 7b6df25b..d7a4f77a 100644 --- a/KkuMulKum/Source/Home/ServiceType/HomeServiceType.swift +++ b/KkuMulKum/Source/Home/ServiceType/HomeServiceType.swift @@ -10,13 +10,27 @@ import Foundation import Moya protocol HomeServiceType { - func fetchLoginUser() -> ResponseBodyDTO - func fetchNearestPromise() -> ResponseBodyDTO - func fetchUpcomingPromise() -> ResponseBodyDTO + func fetchLoginUser() async throws -> ResponseBodyDTO? + func fetchNearestPromise() async throws -> ResponseBodyDTO? + func fetchUpcomingPromise() async throws -> ResponseBodyDTO? +} + +extension HomeService: HomeServiceType { + func fetchLoginUser() async throws -> ResponseBodyDTO? { + return try await request(with: .fetchLoginUser) + } + + func fetchNearestPromise() async throws -> ResponseBodyDTO? { + return try await request(with: .fetchNearestPromise) + } + + func fetchUpcomingPromise() async throws -> ResponseBodyDTO? { + return try await request(with: .fetchUpcomingPromise) + } } final class MockHomeService: HomeServiceType { - func fetchLoginUser() -> ResponseBodyDTO { + func fetchLoginUser() async throws -> ResponseBodyDTO? { let mockData = ResponseBodyDTO( success: true, data: LoginUserModel( @@ -33,7 +47,7 @@ final class MockHomeService: HomeServiceType { return mockData } - func fetchNearestPromise() -> ResponseBodyDTO { + func fetchNearestPromise() async throws -> ResponseBodyDTO? { let mockData = ResponseBodyDTO( success: true, data: NearestPromiseModel( @@ -51,7 +65,7 @@ final class MockHomeService: HomeServiceType { return mockData } - func fetchUpcomingPromise() -> ResponseBodyDTO { + func fetchUpcomingPromise() async throws -> ResponseBodyDTO? { let mockData = ResponseBodyDTO( success: true, data: UpcomingPromiseListModel( diff --git a/KkuMulKum/Source/Home/View/HomeView.swift b/KkuMulKum/Source/Home/View/HomeView.swift index 38db8bfa..3430fd19 100644 --- a/KkuMulKum/Source/Home/View/HomeView.swift +++ b/KkuMulKum/Source/Home/View/HomeView.swift @@ -186,7 +186,7 @@ final class HomeView: BaseView { promiseView.snp.makeConstraints { $0.leading.trailing.equalToSuperview() - $0.height.equalTo(646) + $0.height.equalTo(Screen.height(630)) $0.top.equalToSuperview().offset(396) $0.bottom.equalTo(contentView) } diff --git a/KkuMulKum/Source/Home/ViewController/HomeViewController.swift b/KkuMulKum/Source/Home/ViewController/HomeViewController.swift index b2168d42..5d314d51 100644 --- a/KkuMulKum/Source/Home/ViewController/HomeViewController.swift +++ b/KkuMulKum/Source/Home/ViewController/HomeViewController.swift @@ -45,7 +45,7 @@ class HomeViewController: BaseViewController { override func viewDidLoad() { super.viewDidLoad() - view.backgroundColor = .white + view.backgroundColor = .maincolor register() updateUI() @@ -170,10 +170,6 @@ extension HomeViewController: UICollectionViewDataSource { extension HomeViewController: UIScrollViewDelegate { func scrollViewDidScroll(_ scrollView: UIScrollView) { - if rootView.scrollView.contentOffset.y < 0 { - rootView.scrollView.contentOffset.y = 0 - } - let maxOffsetY = rootView.scrollView.contentSize.height - rootView.scrollView.bounds.height if rootView.scrollView.contentOffset.y > maxOffsetY { rootView.scrollView.contentOffset.y = maxOffsetY @@ -257,27 +253,34 @@ private extension HomeViewController { func updateNearestPromise() { viewModel.nearestPromise.bind { [weak self] _ in DispatchQueue.main.async { - let data = self?.viewModel.nearestPromise.value - self?.rootView.todayPromiseView.meetingNameLabel.setText( - data?.data?.meetingName ?? "", - style: .caption02, - color: .green3 - ) - self?.rootView.todayPromiseView.nameLabel.setText( - data?.data?.name ?? "", - style: .body03, - color: .gray8 - ) - self?.rootView.todayPromiseView.placeNameLabel.setText( - data?.data?.placeName ?? "", - style: .body06, - color: .gray7 - ) - self?.rootView.todayPromiseView.timeLabel.setText( - data?.data?.time ?? "", - style: .body06, - color: .gray7 - ) + guard let self = self else { return } + let data = self.viewModel.nearestPromise.value + + if data?.data == nil { + self.rootView.todayPromiseView.isHidden = true + self.rootView.todayEmptyView.isHidden = false + } else { + self.rootView.todayPromiseView.meetingNameLabel.setText( + data?.data?.meetingName ?? "", + style: .caption02, + color: .green3 + ) + self.rootView.todayPromiseView.nameLabel.setText( + data?.data?.name ?? "", + style: .body03, + color: .gray8 + ) + self.rootView.todayPromiseView.placeNameLabel.setText( + data?.data?.placeName ?? "", + style: .body06, + color: .gray7 + ) + self.rootView.todayPromiseView.timeLabel.setText( + data?.data?.time ?? "", + style: .body06, + color: .gray7 + ) + } } } } @@ -285,7 +288,15 @@ private extension HomeViewController { func updateUpcomingPromise() { viewModel.upcomingPromiseList.bind { [weak self] _ in DispatchQueue.main.async { - self?.rootView.upcomingPromiseView.reloadData() + guard let self = self else { return } + let data = self.viewModel.nearestPromise.value + + if data?.data == nil { + self.rootView.upcomingPromiseView.isHidden = true + self.rootView.upcomingEmptyView.isHidden = false + } else { + self.rootView.upcomingPromiseView.reloadData() + } } } } diff --git a/KkuMulKum/Source/Home/ViewModel/HomeViewModel.swift b/KkuMulKum/Source/Home/ViewModel/HomeViewModel.swift index de6a9329..aa791aba 100644 --- a/KkuMulKum/Source/Home/ViewModel/HomeViewModel.swift +++ b/KkuMulKum/Source/Home/ViewModel/HomeViewModel.swift @@ -85,16 +85,34 @@ final class HomeViewModel { } func requestLoginUser() { - loginUser.value = service.fetchLoginUser() - levelName.value = getLevelName(level: loginUser.value?.data?.level ?? 1) - levelCaption.value = getLevelCaption(level: loginUser.value?.data?.level ?? 1) + Task { + do { + loginUser.value = try await service.fetchLoginUser() + levelName.value = getLevelName(level: loginUser.value?.data?.level ?? 1) + levelCaption.value = getLevelCaption(level: loginUser.value?.data?.level ?? 1) + } catch { + print(">>> \(error.localizedDescription) : \(#function)") + } + } } func requestNearestPromise() { - nearestPromise.value = service.fetchNearestPromise() + Task { + do { + nearestPromise.value = try await service.fetchNearestPromise() + } catch { + print(">>> \(error.localizedDescription) : \(#function)") + } + } } func requestUpcomingPromise() { - upcomingPromiseList.value = service.fetchUpcomingPromise() + Task { + do { + upcomingPromiseList.value = try await service.fetchUpcomingPromise() + } catch { + print(">>> \(error.localizedDescription) : \(#function)") + } + } } } diff --git a/KkuMulKum/Source/MeetingList/ServiceType/MeetingListServiceType.swift b/KkuMulKum/Source/MeetingList/ServiceType/MeetingListServiceType.swift index d60cfa1c..d104bf69 100644 --- a/KkuMulKum/Source/MeetingList/ServiceType/MeetingListServiceType.swift +++ b/KkuMulKum/Source/MeetingList/ServiceType/MeetingListServiceType.swift @@ -10,17 +10,17 @@ import Foundation import Moya protocol MeetingListServiceType { - func fetchMeetingList() -> ResponseBodyDTO + func fetchMeetingList() async throws -> ResponseBodyDTO? } -//extension MeetingService: MeetingListServiceType { -// func fetchMeetingList() -> ResponseBodyDTO { -// <#code#> -// } -//} +extension MeetingService: MeetingListServiceType { + func fetchMeetingList() async throws -> ResponseBodyDTO? { + return try await request(with: .fetchMeetingList) + } +} final class MockMeetingListService: MeetingListServiceType { - func fetchMeetingList() -> ResponseBodyDTO { + func fetchMeetingList() async throws -> ResponseBodyDTO? { let mockData = ResponseBodyDTO( success: true, data: MeetingListModel( diff --git a/KkuMulKum/Source/MeetingList/View/MeetingListView.swift b/KkuMulKum/Source/MeetingList/View/MeetingListView.swift index 0f1f3486..0ec7190e 100644 --- a/KkuMulKum/Source/MeetingList/View/MeetingListView.swift +++ b/KkuMulKum/Source/MeetingList/View/MeetingListView.swift @@ -46,12 +46,23 @@ final class MeetingListView: BaseView { $0.tableHeaderView = header } + let emptyCharacter = UIImageView().then { + $0.image = .imgEmpty + $0.isHidden = true + } + + let emptyLabel = UILabel().then { + $0.setText("아직 모임이 없네요!\n모임을 추가해 보세요.", style: .body05, color: .gray4) + $0.textAlignment = .center + $0.isHidden = true + } + // MARK: - UI Setting override func setupView() { self.backgroundColor = .gray0 - addSubview(tableView) + addSubviews(tableView, emptyCharacter, emptyLabel) header.addSubviews(infoLabel, addButton, addInfoView) addInfoView.addArrangedSubviews(addIconImageView, addInfoLabel) } @@ -68,6 +79,18 @@ final class MeetingListView: BaseView { $0.height.equalTo(Screen.height(48)) } + emptyCharacter.snp.makeConstraints { + $0.top.equalTo(addButton.snp.bottom).offset(94) + $0.centerX.equalToSuperview() + $0.height.equalTo(Screen.height(126)) + $0.width.equalTo(Screen.width(73)) + } + + emptyLabel.snp.makeConstraints { + $0.top.equalTo(emptyCharacter.snp.bottom).offset(16) + $0.centerX.equalToSuperview() + } + addInfoView.snp.makeConstraints { $0.centerY.equalTo(addButton.snp.centerY) $0.centerX.equalTo(addButton.snp.centerX) diff --git a/KkuMulKum/Source/MeetingList/ViewController/MeetingListViewController.swift b/KkuMulKum/Source/MeetingList/ViewController/MeetingListViewController.swift index 5af21742..56d1accb 100644 --- a/KkuMulKum/Source/MeetingList/ViewController/MeetingListViewController.swift +++ b/KkuMulKum/Source/MeetingList/ViewController/MeetingListViewController.swift @@ -69,12 +69,21 @@ class MeetingListViewController: BaseViewController { private func updateMeetingList() { viewModel.meetingList.bind { [weak self] _ in DispatchQueue.main.async { - self?.rootView.tableView.reloadData() - self?.rootView.infoLabel.setText( - "꾸물리안이 가입한 모임은\n총 \(self?.viewModel.meetingList.value?.data?.count ?? 0)개예요!", + guard let self = self else { return } + let data = self.viewModel.meetingList.value + + self.rootView.infoLabel.setText( + "꾸물리안이 가입한 모임은\n총 \(self.viewModel.meetingList.value?.data?.count ?? 0)개예요!", style: .head01, color: .gray8 ) + + if data?.data?.count == 0 { + self.rootView.emptyLabel.isHidden = false + self.rootView.emptyCharacter.isHidden = false + } else { + self.rootView.tableView.reloadData() + } } } } diff --git a/KkuMulKum/Source/MeetingList/ViewModel/MeetingListViewModel.swift b/KkuMulKum/Source/MeetingList/ViewModel/MeetingListViewModel.swift index 17a92ba9..23bbd282 100644 --- a/KkuMulKum/Source/MeetingList/ViewModel/MeetingListViewModel.swift +++ b/KkuMulKum/Source/MeetingList/ViewModel/MeetingListViewModel.swift @@ -19,6 +19,13 @@ final class MeetingListViewModel { } func requestMeetingList() { - meetingList.value = service.fetchMeetingList() + Task { + do { + meetingList.value = try await service.fetchMeetingList() + } catch { + print(">>> \(error.localizedDescription) : \(#function)") + } + } +// meetingList.value = service.fetchMeetingList() } }