From af884ae90f064fb2654a0849260242e00139f2bc Mon Sep 17 00:00:00 2001 From: hooni Date: Thu, 22 Aug 2024 21:17:28 +0900 Subject: [PATCH 1/6] =?UTF-8?q?feat/#324=20=ED=86=B5=EC=8B=A0=20=EB=AA=A8?= =?UTF-8?q?=EB=93=88=20=EC=84=A4=EC=A0=95=EB=B0=8F=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=ED=86=A0=EC=BD=9C=20=EC=9D=B4=EB=A6=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- KkuMulKum.xcodeproj/project.pbxproj | 20 +++++++ .../DTO/Model/MyPage/MyPageIngoModel.swift | 19 +++++++ KkuMulKum/Network/Service/AuthService.swift | 2 +- KkuMulKum/Network/Service/UserService.swift | 44 +++++++++++++++ .../Network/TargetType/MypageTargetType.swift | 56 +++++++++++++++++++ .../Source/Core/Auth/AuthInterceptor.swift | 4 +- .../ViewController/MyPageViewController.swift | 1 - .../Login/VIewModel/LoginViewModel.swift | 4 +- .../ViewModel/NicknameViewModel.swift | 4 +- .../ViewModel/ProfileSetupViewModel.swift | 4 +- .../ServiceProtocol/AuthServiceProtocol.swift | 2 +- 11 files changed, 149 insertions(+), 11 deletions(-) create mode 100644 KkuMulKum/Network/DTO/Model/MyPage/MyPageIngoModel.swift create mode 100644 KkuMulKum/Network/Service/UserService.swift create mode 100644 KkuMulKum/Network/TargetType/MypageTargetType.swift diff --git a/KkuMulKum.xcodeproj/project.pbxproj b/KkuMulKum.xcodeproj/project.pbxproj index db606bd0..fddad230 100644 --- a/KkuMulKum.xcodeproj/project.pbxproj +++ b/KkuMulKum.xcodeproj/project.pbxproj @@ -27,6 +27,9 @@ 784824F72C6E1C9900FE07A0 /* AuthServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784824F62C6E1C9900FE07A0 /* AuthServiceProtocol.swift */; }; 784824FC2C75BF7900FE07A0 /* MyPageViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784824FB2C75BF7900FE07A0 /* MyPageViewModel.swift */; }; 784824FE2C75F25900FE07A0 /* MyPageEditViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784824FD2C75F25900FE07A0 /* MyPageEditViewController.swift */; }; + 784825092C77623C00FE07A0 /* MypageTargetType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784825082C77623C00FE07A0 /* MypageTargetType.swift */; }; + 7848250B2C77627200FE07A0 /* UserService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7848250A2C77627200FE07A0 /* UserService.swift */; }; + 7848250E2C7762EF00FE07A0 /* MyPageIngoModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7848250D2C7762EF00FE07A0 /* MyPageIngoModel.swift */; }; 784E4D942C3B1C7F00BC943C /* KakaoSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 784E4D932C3B1C7F00BC943C /* KakaoSDK */; }; 784E4D962C3B1C7F00BC943C /* KakaoSDKAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 784E4D952C3B1C7F00BC943C /* KakaoSDKAuth */; }; 784E4D992C3B95A900BC943C /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 784E4D982C3B95A900BC943C /* KeychainAccess */; }; @@ -258,6 +261,9 @@ 784824F62C6E1C9900FE07A0 /* AuthServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthServiceProtocol.swift; sourceTree = ""; }; 784824FB2C75BF7900FE07A0 /* MyPageViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageViewModel.swift; sourceTree = ""; }; 784824FD2C75F25900FE07A0 /* MyPageEditViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageEditViewController.swift; sourceTree = ""; }; + 784825082C77623C00FE07A0 /* MypageTargetType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MypageTargetType.swift; sourceTree = ""; }; + 7848250A2C77627200FE07A0 /* UserService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserService.swift; sourceTree = ""; }; + 7848250D2C7762EF00FE07A0 /* MyPageIngoModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageIngoModel.swift; sourceTree = ""; }; 789196332C486F6B00FF8CDF /* KeychainAccessible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainAccessible.swift; sourceTree = ""; }; 789196352C492F8600FF8CDF /* AuthTargetType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthTargetType.swift; sourceTree = ""; }; 789196372C49697B00FF8CDF /* AuthError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthError.swift; sourceTree = ""; }; @@ -564,6 +570,14 @@ path = ViewModel; sourceTree = ""; }; + 7848250C2C7762A800FE07A0 /* MyPage */ = { + isa = PBXGroup; + children = ( + 7848250D2C7762EF00FE07A0 /* MyPageIngoModel.swift */, + ); + path = MyPage; + sourceTree = ""; + }; 789196392C49697F00FF8CDF /* Auth */ = { isa = PBXGroup; children = ( @@ -728,6 +742,7 @@ A3DD9C672C45C78300E58A13 /* HomeService.swift */, DD3F9DC52C484DEB008E1FF7 /* PromiseService.swift */, DD3F9DD12C485753008E1FF7 /* UtilService.swift */, + 7848250A2C77627200FE07A0 /* UserService.swift */, ); path = Service; sourceTree = ""; @@ -1165,6 +1180,7 @@ DDE7D2C92C47EE81005A921F /* PromiseTargetType.swift */, A39F2B1C2C47F3D0008DA5F5 /* HomeTargetType.swift */, DD3F9DD32C4858A3008E1FF7 /* UtilTargetType.swift */, + 784825082C77623C00FE07A0 /* MypageTargetType.swift */, ); path = TargetType; sourceTree = ""; @@ -1436,6 +1452,7 @@ DE9E188E2C3BCC6B00DB76B4 /* Meetings */, DE9E188F2C3BCC7600DB76B4 /* Promises */, DE9E18902C3BCC8000DB76B4 /* Utils */, + 7848250C2C7762A800FE07A0 /* MyPage */, ); path = Model; sourceTree = ""; @@ -1853,6 +1870,7 @@ 78B9286C2C29402C006D9942 /* AppDelegate.swift in Sources */, DD3F9DD42C4858A3008E1FF7 /* UtilTargetType.swift in Sources */, DECB84562C43FB990022A003 /* AddPromiseViewModel.swift in Sources */, + 7848250E2C7762EF00FE07A0 /* MyPageIngoModel.swift in Sources */, DECB84602C4446930022A003 /* FindPlaceService.swift in Sources */, DEFBEBD52C46C86600437188 /* SelectPenaltyService.swift in Sources */, DEBA032F2C3C24F2002ED8F2 /* ModelType.swift in Sources */, @@ -1861,6 +1879,7 @@ DE6D4D0F2C3F14D80005584B /* MeetingInfoService.swift in Sources */, DD3F9DD62C4988E2008E1FF7 /* RegisterMeetingsResponseModel.swift in Sources */, DD3F9DCC2C485614008E1FF7 /* HomeServiceType.swift in Sources */, + 7848250B2C77627200FE07A0 /* UserService.swift in Sources */, DD39768A2C41C2AD00E2A4C4 /* HomeViewController.swift in Sources */, DED5DBF42C34539A006ECE7E /* BaseTableViewCell.swift in Sources */, DDE7D2CA2C47EE81005A921F /* PromiseTargetType.swift in Sources */, @@ -1882,6 +1901,7 @@ DE32D1D22C3BF703006848DF /* LoginUserResponseModel.swift in Sources */, DD8626692C4606A300E4F980 /* ReadyStatusProgressView.swift in Sources */, DE9E18892C3BC91000DB76B4 /* ResponseBodyDTO.swift in Sources */, + 784825092C77623C00FE07A0 /* MypageTargetType.swift in Sources */, DD8626642C4606A300E4F980 /* ReadyPlanInfoView.swift in Sources */, DD3976682C41769400E2A4C4 /* CreateMeetingViewModel.swift in Sources */, DECB84582C43FBEB0022A003 /* AddPromiseViewController.swift in Sources */, diff --git a/KkuMulKum/Network/DTO/Model/MyPage/MyPageIngoModel.swift b/KkuMulKum/Network/DTO/Model/MyPage/MyPageIngoModel.swift new file mode 100644 index 00000000..59f54111 --- /dev/null +++ b/KkuMulKum/Network/DTO/Model/MyPage/MyPageIngoModel.swift @@ -0,0 +1,19 @@ +// +// MyPageIngoModel.swift +// KkuMulKum +// +// Created by 이지훈 on 8/22/24. +// + +import Foundation + + +struct MyPageUserInfo: ResponseModelType { + let userId: Int + let name: String? + let level: Int + let promiseCount: Int + let tardyCount: Int + let tardySum: Int + let profileImg: String? +} diff --git a/KkuMulKum/Network/Service/AuthService.swift b/KkuMulKum/Network/Service/AuthService.swift index a22d2218..15dd0ddd 100644 --- a/KkuMulKum/Network/Service/AuthService.swift +++ b/KkuMulKum/Network/Service/AuthService.swift @@ -23,7 +23,7 @@ enum NetworkErrorMapper { } } -class AuthService: AuthServiceType { +class AuthService: AuthServiceProtocol { private var keychainService: KeychainService private var provider = MoyaProvider() diff --git a/KkuMulKum/Network/Service/UserService.swift b/KkuMulKum/Network/Service/UserService.swift new file mode 100644 index 00000000..579a6f23 --- /dev/null +++ b/KkuMulKum/Network/Service/UserService.swift @@ -0,0 +1,44 @@ +// +// UserService.swift +// KkuMulKum +// +// Created by 이지훈 on 8/22/24. +// + +import Foundation + +import Moya + +protocol UserServiceType { + func getUserInfo() async throws -> MyPageUserInfo +} + +class UserService: UserServiceType { + private var provider = MoyaProvider() + + init(provider: MoyaProvider = MoyaProvider(plugins: [MoyaLoggingPlugin()])) { + self.provider = provider + } + + func getUserInfo() async throws -> MyPageUserInfo { + return try await withCheckedThrowingContinuation { continuation in + provider.request(.getUserInfo) { result in + switch result { + case .success(let response): + do { + let decodedResponse = try JSONDecoder().decode(ResponseBodyDTO.self, from: response.data) + guard decodedResponse.success, let userInfo = decodedResponse.data else { + throw decodedResponse.error.map(NetworkErrorMapper.mapErrorResponse) ?? + NetworkError.unknownError("Unknown error occurred") + } + continuation.resume(returning: userInfo) + } catch { + continuation.resume(throwing: error is NetworkError ? error : NetworkError.decodingError) + } + case .failure(let error): + continuation.resume(throwing: NetworkError.networkError(error)) + } + } + } + } +} diff --git a/KkuMulKum/Network/TargetType/MypageTargetType.swift b/KkuMulKum/Network/TargetType/MypageTargetType.swift new file mode 100644 index 00000000..ce63f138 --- /dev/null +++ b/KkuMulKum/Network/TargetType/MypageTargetType.swift @@ -0,0 +1,56 @@ +// +// MypageTargetType.swift +// KkuMulKum +// +// Created by 이지훈 on 8/22/24. +// + +import Foundation + +import Moya + +enum UserTargetType { + case getUserInfo +} + +extension UserTargetType: TargetType { + var baseURL: URL { + guard let privacyInfo = Bundle.main.privacyInfo, + let urlString = privacyInfo["BASE_URL"] as? String, + let url = URL(string: urlString) else { + fatalError("Invalid BASE_URL in PrivacyInfo.plist") + } + return url + } + + var path: String { + switch self { + case .getUserInfo: + return "/api/v1/users/me" + } + } + + var method: Moya.Method { + switch self { + case .getUserInfo: + return .get + } + } + + var task: Task { + switch self { + case .getUserInfo: + return .requestPlain + } + } + + var headers: [String : String]? { + guard let token = DefaultKeychainService.shared.accessToken else { + fatalError("No access token available") + } + return [ + "Content-Type": "application/json", + "Authorization": "Bearer \(token)" + ] + } +} diff --git a/KkuMulKum/Source/Core/Auth/AuthInterceptor.swift b/KkuMulKum/Source/Core/Auth/AuthInterceptor.swift index 7f9422e5..0b6f205e 100644 --- a/KkuMulKum/Source/Core/Auth/AuthInterceptor.swift +++ b/KkuMulKum/Source/Core/Auth/AuthInterceptor.swift @@ -15,10 +15,10 @@ enum AuthError: Error { } class AuthInterceptor: RequestInterceptor { - let authService: AuthServiceType + let authService: AuthServiceProtocol let provider: MoyaProvider - init(authService: AuthServiceType, provider: MoyaProvider) { + init(authService: AuthServiceProtocol, provider: MoyaProvider) { self.authService = authService self.provider = provider } diff --git a/KkuMulKum/Source/MyPage/ViewController/MyPageViewController.swift b/KkuMulKum/Source/MyPage/ViewController/MyPageViewController.swift index 04683a49..08201f8c 100644 --- a/KkuMulKum/Source/MyPage/ViewController/MyPageViewController.swift +++ b/KkuMulKum/Source/MyPage/ViewController/MyPageViewController.swift @@ -10,7 +10,6 @@ import UIKit import RxSwift import RxCocoa - class MyPageViewController: BaseViewController, CustomActionSheetDelegate { private let rootView = MyPageView() private let viewModel = MyPageViewModel() diff --git a/KkuMulKum/Source/Onboarding/Login/VIewModel/LoginViewModel.swift b/KkuMulKum/Source/Onboarding/Login/VIewModel/LoginViewModel.swift index 2080af3c..daff4861 100644 --- a/KkuMulKum/Source/Onboarding/Login/VIewModel/LoginViewModel.swift +++ b/KkuMulKum/Source/Onboarding/Login/VIewModel/LoginViewModel.swift @@ -25,7 +25,7 @@ class LoginViewModel: NSObject { var userName: ObservablePattern = ObservablePattern(nil) private let provider: MoyaProvider - private var authService: AuthServiceType + private var authService: AuthServiceProtocol private let authInterceptor: AuthInterceptor private let keychainAccessible: KeychainAccessible @@ -33,7 +33,7 @@ class LoginViewModel: NSObject { provider: MoyaProvider = MoyaProvider( plugins: [NetworkLoggerPlugin(configuration: .init(logOptions: .verbose))] ), - authService: AuthServiceType = AuthService(), + authService: AuthServiceProtocol = AuthService(), keychainAccessible: KeychainAccessible = DefaultKeychainAccessible() ) { self.provider = provider diff --git a/KkuMulKum/Source/Onboarding/Nickname/ViewModel/NicknameViewModel.swift b/KkuMulKum/Source/Onboarding/Nickname/ViewModel/NicknameViewModel.swift index ce646700..acab9749 100644 --- a/KkuMulKum/Source/Onboarding/Nickname/ViewModel/NicknameViewModel.swift +++ b/KkuMulKum/Source/Onboarding/Nickname/ViewModel/NicknameViewModel.swift @@ -32,10 +32,10 @@ class NicknameViewModel { private let disposeBag = DisposeBag() private let nicknameRegex = "^[가-힣a-zA-Z]{1,5}$" private let provider: MoyaProvider - private let authService: AuthServiceType + private let authService: AuthServiceProtocol init(provider: MoyaProvider = MoyaProvider(), - authService: AuthServiceType = AuthService()) { + authService: AuthServiceProtocol = AuthService()) { self.provider = provider self.authService = authService diff --git a/KkuMulKum/Source/Onboarding/Profile/ViewModel/ProfileSetupViewModel.swift b/KkuMulKum/Source/Onboarding/Profile/ViewModel/ProfileSetupViewModel.swift index c3ea45bc..0ab93193 100644 --- a/KkuMulKum/Source/Onboarding/Profile/ViewModel/ProfileSetupViewModel.swift +++ b/KkuMulKum/Source/Onboarding/Profile/ViewModel/ProfileSetupViewModel.swift @@ -14,11 +14,11 @@ class ProfileSetupViewModel { let nickname: String let serverResponse = ObservablePattern(nil) - private let authService: AuthServiceType + private let authService: AuthServiceProtocol private var imageData: Data? private let maxImageSizeBytes = 4 * 1024 * 1024 - init(nickname: String, authService: AuthServiceType = AuthService()) { + init(nickname: String, authService: AuthServiceProtocol = AuthService()) { self.nickname = nickname self.authService = authService } diff --git a/KkuMulKum/Source/Onboarding/ServiceProtocol/AuthServiceProtocol.swift b/KkuMulKum/Source/Onboarding/ServiceProtocol/AuthServiceProtocol.swift index 5bb64773..1cbde734 100644 --- a/KkuMulKum/Source/Onboarding/ServiceProtocol/AuthServiceProtocol.swift +++ b/KkuMulKum/Source/Onboarding/ServiceProtocol/AuthServiceProtocol.swift @@ -7,7 +7,7 @@ import Foundation -protocol AuthServiceType { +protocol AuthServiceProtocol { func saveAccessToken(_ token: String) -> Bool func saveRefreshToken(_ token: String) -> Bool func getAccessToken() -> String? From 36782d8e8d712a9789d61cb4714b849018ae88b9 Mon Sep 17 00:00:00 2001 From: hooni Date: Thu, 22 Aug 2024 22:02:38 +0900 Subject: [PATCH 2/6] =?UTF-8?q?feat/#324=20=EB=82=B4=EC=A0=95=EB=B3=B4=20a?= =?UTF-8?q?pi=20=EC=97=B0=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- KkuMulKum.xcodeproj/project.pbxproj | 12 ++ .../DTO/Model/MyPage/MyPageIngoModel.swift | 1 - KkuMulKum/Network/Service/UserService.swift | 18 +- .../ServiceProtocol/ServiceProtocol.swift | 13 ++ .../ViewController/MyPageViewController.swift | 154 ++++++++++-------- .../MyPage/ViewModel/MyPageViewModel.swift | 21 ++- 6 files changed, 142 insertions(+), 77 deletions(-) create mode 100644 KkuMulKum/Source/MyPage/ServiceProtocol/ServiceProtocol.swift diff --git a/KkuMulKum.xcodeproj/project.pbxproj b/KkuMulKum.xcodeproj/project.pbxproj index fddad230..9b420a3d 100644 --- a/KkuMulKum.xcodeproj/project.pbxproj +++ b/KkuMulKum.xcodeproj/project.pbxproj @@ -30,6 +30,7 @@ 784825092C77623C00FE07A0 /* MypageTargetType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784825082C77623C00FE07A0 /* MypageTargetType.swift */; }; 7848250B2C77627200FE07A0 /* UserService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7848250A2C77627200FE07A0 /* UserService.swift */; }; 7848250E2C7762EF00FE07A0 /* MyPageIngoModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7848250D2C7762EF00FE07A0 /* MyPageIngoModel.swift */; }; + 784825112C77666500FE07A0 /* ServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784825102C77666500FE07A0 /* ServiceProtocol.swift */; }; 784E4D942C3B1C7F00BC943C /* KakaoSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 784E4D932C3B1C7F00BC943C /* KakaoSDK */; }; 784E4D962C3B1C7F00BC943C /* KakaoSDKAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 784E4D952C3B1C7F00BC943C /* KakaoSDKAuth */; }; 784E4D992C3B95A900BC943C /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 784E4D982C3B95A900BC943C /* KeychainAccess */; }; @@ -264,6 +265,7 @@ 784825082C77623C00FE07A0 /* MypageTargetType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MypageTargetType.swift; sourceTree = ""; }; 7848250A2C77627200FE07A0 /* UserService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserService.swift; sourceTree = ""; }; 7848250D2C7762EF00FE07A0 /* MyPageIngoModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageIngoModel.swift; sourceTree = ""; }; + 784825102C77666500FE07A0 /* ServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceProtocol.swift; sourceTree = ""; }; 789196332C486F6B00FF8CDF /* KeychainAccessible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainAccessible.swift; sourceTree = ""; }; 789196352C492F8600FF8CDF /* AuthTargetType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthTargetType.swift; sourceTree = ""; }; 789196372C49697B00FF8CDF /* AuthError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthError.swift; sourceTree = ""; }; @@ -578,6 +580,14 @@ path = MyPage; sourceTree = ""; }; + 7848250F2C77664400FE07A0 /* ServiceProtocol */ = { + isa = PBXGroup; + children = ( + 784825102C77666500FE07A0 /* ServiceProtocol.swift */, + ); + path = ServiceProtocol; + sourceTree = ""; + }; 789196392C49697F00FF8CDF /* Auth */ = { isa = PBXGroup; children = ( @@ -1250,6 +1260,7 @@ DE159D312C406E1600425101 /* MyPage */ = { isa = PBXGroup; children = ( + 7848250F2C77664400FE07A0 /* ServiceProtocol */, 784824FA2C75BF6800FE07A0 /* ViewModel */, DE159D2E2C406E1600425101 /* View */, DE159D302C406E1600425101 /* ViewController */, @@ -1903,6 +1914,7 @@ DE9E18892C3BC91000DB76B4 /* ResponseBodyDTO.swift in Sources */, 784825092C77623C00FE07A0 /* MypageTargetType.swift in Sources */, DD8626642C4606A300E4F980 /* ReadyPlanInfoView.swift in Sources */, + 784825112C77666500FE07A0 /* ServiceProtocol.swift in Sources */, DD3976682C41769400E2A4C4 /* CreateMeetingViewModel.swift in Sources */, DECB84582C43FBEB0022A003 /* AddPromiseViewController.swift in Sources */, DDE7D2C62C47D2BB005A921F /* MeetingTargetType.swift in Sources */, diff --git a/KkuMulKum/Network/DTO/Model/MyPage/MyPageIngoModel.swift b/KkuMulKum/Network/DTO/Model/MyPage/MyPageIngoModel.swift index 59f54111..fa011293 100644 --- a/KkuMulKum/Network/DTO/Model/MyPage/MyPageIngoModel.swift +++ b/KkuMulKum/Network/DTO/Model/MyPage/MyPageIngoModel.swift @@ -7,7 +7,6 @@ import Foundation - struct MyPageUserInfo: ResponseModelType { let userId: Int let name: String? diff --git a/KkuMulKum/Network/Service/UserService.swift b/KkuMulKum/Network/Service/UserService.swift index 579a6f23..cf6b0e75 100644 --- a/KkuMulKum/Network/Service/UserService.swift +++ b/KkuMulKum/Network/Service/UserService.swift @@ -9,11 +9,7 @@ import Foundation import Moya -protocol UserServiceType { - func getUserInfo() async throws -> MyPageUserInfo -} - -class UserService: UserServiceType { +final class MyPageUserService: MyPageUserServiceType { private var provider = MoyaProvider() init(provider: MoyaProvider = MoyaProvider(plugins: [MoyaLoggingPlugin()])) { @@ -21,17 +17,21 @@ class UserService: UserServiceType { } func getUserInfo() async throws -> MyPageUserInfo { + return try await performRequest(.getUserInfo) + } + + func performRequest(_ target: UserTargetType) async throws -> T { return try await withCheckedThrowingContinuation { continuation in - provider.request(.getUserInfo) { result in + provider.request(target) { result in switch result { case .success(let response): do { - let decodedResponse = try JSONDecoder().decode(ResponseBodyDTO.self, from: response.data) - guard decodedResponse.success, let userInfo = decodedResponse.data else { + let decodedResponse = try JSONDecoder().decode(ResponseBodyDTO.self, from: response.data) + guard decodedResponse.success, let data = decodedResponse.data else { throw decodedResponse.error.map(NetworkErrorMapper.mapErrorResponse) ?? NetworkError.unknownError("Unknown error occurred") } - continuation.resume(returning: userInfo) + continuation.resume(returning: data) } catch { continuation.resume(throwing: error is NetworkError ? error : NetworkError.decodingError) } diff --git a/KkuMulKum/Source/MyPage/ServiceProtocol/ServiceProtocol.swift b/KkuMulKum/Source/MyPage/ServiceProtocol/ServiceProtocol.swift new file mode 100644 index 00000000..36d39953 --- /dev/null +++ b/KkuMulKum/Source/MyPage/ServiceProtocol/ServiceProtocol.swift @@ -0,0 +1,13 @@ +// +// ServiceProtocol.swift +// KkuMulKum +// +// Created by 이지훈 on 8/22/24. +// + +import Foundation + +protocol MyPageUserServiceType { + func getUserInfo() async throws -> MyPageUserInfo + func performRequest(_ target: UserTargetType) async throws -> T +} diff --git a/KkuMulKum/Source/MyPage/ViewController/MyPageViewController.swift b/KkuMulKum/Source/MyPage/ViewController/MyPageViewController.swift index 08201f8c..d4c42b07 100644 --- a/KkuMulKum/Source/MyPage/ViewController/MyPageViewController.swift +++ b/KkuMulKum/Source/MyPage/ViewController/MyPageViewController.swift @@ -24,6 +24,7 @@ class MyPageViewController: BaseViewController, CustomActionSheetDelegate { view.backgroundColor = .green1 bindViewModel() + viewModel.fetchUserInfo() } override func setupView() { @@ -32,65 +33,91 @@ class MyPageViewController: BaseViewController, CustomActionSheetDelegate { } private func bindViewModel() { - // Inputs - rootView.contentView.editButton.rx.tap - .bind(to: viewModel.editButtonTapped) - .disposed(by: disposeBag) - - bindRowTapGesture(for: rootView.etcSettingView.logoutRow) - .bind(to: viewModel.logoutButtonTapped) - .disposed(by: disposeBag) - - bindRowTapGesture(for: rootView.etcSettingView.unsubscribeRow) - .bind(to: viewModel.unsubscribeButtonTapped) - .disposed(by: disposeBag) - - // Other rows - bindRowTapGesture(for: rootView.etcSettingView.versionInfoRow) - .subscribe(onNext: { print("버전정보 탭됨") }) - .disposed(by: disposeBag) - - bindRowTapGesture(for: rootView.etcSettingView.termsOfServiceRow) - .subscribe(onNext: { print("이용약관 탭됨") }) - .disposed(by: disposeBag) - - bindRowTapGesture(for: rootView.etcSettingView.inquiryRow) - .subscribe(onNext: { print("문의하기 탭됨") }) - .disposed(by: disposeBag) - - // Outputs - viewModel.pushEditProfileVC - .emit(onNext: { [weak self] in - self?.pushEditProfileViewController() - }) - .disposed(by: disposeBag) - - viewModel.showActionSheet - .emit(onNext: { [weak self] kind in - self?.showActionSheet(for: kind) - }) - .disposed(by: disposeBag) - - viewModel.performLogout - .emit(onNext: { [weak self] in - self?.viewModel.logout() - }) - .disposed(by: disposeBag) - - viewModel.performUnsubscribe - .emit(onNext: { [weak self] in - self?.viewModel.unsubscribe() - }) - .disposed(by: disposeBag) + // Inputs + rootView.contentView.editButton.rx.tap + .bind(to: viewModel.editButtonTapped) + .disposed(by: disposeBag) + + bindRowTapGesture(for: rootView.etcSettingView.logoutRow) + .bind(to: viewModel.logoutButtonTapped) + .disposed(by: disposeBag) + + bindRowTapGesture(for: rootView.etcSettingView.unsubscribeRow) + .bind(to: viewModel.unsubscribeButtonTapped) + .disposed(by: disposeBag) + + // Other rows + bindRowTapGesture(for: rootView.etcSettingView.versionInfoRow) + .subscribe(onNext: { print("버전정보 탭됨") }) + .disposed(by: disposeBag) + + bindRowTapGesture(for: rootView.etcSettingView.termsOfServiceRow) + .subscribe(onNext: { print("이용약관 탭됨") }) + .disposed(by: disposeBag) + + bindRowTapGesture(for: rootView.etcSettingView.inquiryRow) + .subscribe(onNext: { print("문의하기 탭됨") }) + .disposed(by: disposeBag) + + // Outputs + viewModel.pushEditProfileVC + .emit(onNext: { [weak self] in + self?.pushEditProfileViewController() + }) + .disposed(by: disposeBag) + + viewModel.showActionSheet + .emit(onNext: { [weak self] kind in + self?.showActionSheet(for: kind) + }) + .disposed(by: disposeBag) + + viewModel.performLogout + .emit(onNext: { [weak self] in + self?.viewModel.logout() + }) + .disposed(by: disposeBag) + + viewModel.performUnsubscribe + .emit(onNext: { [weak self] in + self?.viewModel.unsubscribe() + }) + .disposed(by: disposeBag) + + viewModel.userInfo + .observe(on: MainScheduler.instance) + .subscribe(onNext: { [weak self] userInfo in + self?.updateUI(with: userInfo) + }) + .disposed(by: disposeBag) + } + + private func updateUI(with userInfo: MyPageUserInfo?) { + guard let userInfo = userInfo else { return } + + rootView.contentView.nameLabel.text = userInfo.name ?? "꾸물리안 님" + rootView.contentView.levelLabel.setText("Lv. \(userInfo.level) 지각대장 꾸물이", style: .body05, color: .white) + rootView.contentView.levelLabel.setHighlightText("Lv. \(userInfo.level)", style: .body05, color: .lightGreen) + + if let profileImageURL = userInfo.profileImg { + loadImage(from: profileImageURL, into: rootView.contentView.profileImageView) + } else { + rootView.contentView.profileImageView.image = UIImage.imgProfile } + } + + private func loadImage(from urlString: String, into imageView: UIImageView) { + + } + private func bindRowTapGesture(for view: UIView) -> Observable { - return view.gestureRecognizers? - .compactMap { $0 as? UITapGestureRecognizer } - .first? - .rx.event - .map { _ in } - ?? Observable.empty() - } + return view.gestureRecognizers? + .compactMap { $0 as? UITapGestureRecognizer } + .first? + .rx.event + .map { _ in } + ?? Observable.empty() + } private func pushEditProfileViewController() { let editProfileViewController = MyPageEditViewController() @@ -98,13 +125,12 @@ class MyPageViewController: BaseViewController, CustomActionSheetDelegate { } func actionButtonDidTap(for kind: ActionSheetKind) { - viewModel.actionSheetButtonTapped.accept(kind) - } + viewModel.actionSheetButtonTapped.accept(kind) + } private func showActionSheet(for kind: ActionSheetKind) { - let actionSheet = CustomActionSheetController(kind: kind) - actionSheet.delegate = self - present(actionSheet, animated: true, completion: nil) - } - + let actionSheet = CustomActionSheetController(kind: kind) + actionSheet.delegate = self + present(actionSheet, animated: true, completion: nil) + } } diff --git a/KkuMulKum/Source/MyPage/ViewModel/MyPageViewModel.swift b/KkuMulKum/Source/MyPage/ViewModel/MyPageViewModel.swift index 3065ab65..60fe3838 100644 --- a/KkuMulKum/Source/MyPage/ViewModel/MyPageViewModel.swift +++ b/KkuMulKum/Source/MyPage/ViewModel/MyPageViewModel.swift @@ -11,6 +11,8 @@ import RxSwift import RxCocoa class MyPageViewModel { + private let userService: MyPageUserServiceType + private let disposeBag = DisposeBag() let editButtonTapped = PublishRelay() let logoutButtonTapped = PublishRelay() @@ -21,10 +23,12 @@ class MyPageViewModel { let showActionSheet: Signal let performLogout: Signal let performUnsubscribe: Signal - private let disposeBag = DisposeBag() - + let userInfo: BehaviorRelay - init() { + init(userService: MyPageUserServiceType = MyPageUserService()) { + self.userService = userService + self.userInfo = BehaviorRelay(value: nil) + pushEditProfileVC = editButtonTapped.asSignal() showActionSheet = Observable.merge( @@ -45,6 +49,17 @@ class MyPageViewModel { .asSignal(onErrorJustReturn: ()) } + func fetchUserInfo() { + Task { + do { + let info = try await userService.getUserInfo() + userInfo.accept(info) + } catch { + print("Failed to fetch user info: \(error)") + } + } + } + func logout() { print("로그아웃 눌름 ㅂㅂ") } From f623ea539ed3b11884779500a83e7ebba1c4eb07 Mon Sep 17 00:00:00 2001 From: hooni Date: Thu, 22 Aug 2024 22:14:59 +0900 Subject: [PATCH 3/6] =?UTF-8?q?fix/#324=20=EC=9D=91=EB=8B=B5=EB=AA=A8?= =?UTF-8?q?=EB=8D=B8=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- KkuMulKum.xcodeproj/project.pbxproj | 12 ------------ .../DTO/Model/MyPage/MyPageIngoModel.swift | 18 ------------------ KkuMulKum/Network/Service/UserService.swift | 2 +- .../ServiceProtocol/ServiceProtocol.swift | 2 +- .../ViewController/MyPageViewController.swift | 4 ++-- .../MyPage/ViewModel/MyPageViewModel.swift | 4 ++-- 6 files changed, 6 insertions(+), 36 deletions(-) delete mode 100644 KkuMulKum/Network/DTO/Model/MyPage/MyPageIngoModel.swift diff --git a/KkuMulKum.xcodeproj/project.pbxproj b/KkuMulKum.xcodeproj/project.pbxproj index 9b420a3d..b1d8d6ef 100644 --- a/KkuMulKum.xcodeproj/project.pbxproj +++ b/KkuMulKum.xcodeproj/project.pbxproj @@ -29,7 +29,6 @@ 784824FE2C75F25900FE07A0 /* MyPageEditViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784824FD2C75F25900FE07A0 /* MyPageEditViewController.swift */; }; 784825092C77623C00FE07A0 /* MypageTargetType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784825082C77623C00FE07A0 /* MypageTargetType.swift */; }; 7848250B2C77627200FE07A0 /* UserService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7848250A2C77627200FE07A0 /* UserService.swift */; }; - 7848250E2C7762EF00FE07A0 /* MyPageIngoModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7848250D2C7762EF00FE07A0 /* MyPageIngoModel.swift */; }; 784825112C77666500FE07A0 /* ServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784825102C77666500FE07A0 /* ServiceProtocol.swift */; }; 784E4D942C3B1C7F00BC943C /* KakaoSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 784E4D932C3B1C7F00BC943C /* KakaoSDK */; }; 784E4D962C3B1C7F00BC943C /* KakaoSDKAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 784E4D952C3B1C7F00BC943C /* KakaoSDKAuth */; }; @@ -264,7 +263,6 @@ 784824FD2C75F25900FE07A0 /* MyPageEditViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageEditViewController.swift; sourceTree = ""; }; 784825082C77623C00FE07A0 /* MypageTargetType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MypageTargetType.swift; sourceTree = ""; }; 7848250A2C77627200FE07A0 /* UserService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserService.swift; sourceTree = ""; }; - 7848250D2C7762EF00FE07A0 /* MyPageIngoModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageIngoModel.swift; sourceTree = ""; }; 784825102C77666500FE07A0 /* ServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceProtocol.swift; sourceTree = ""; }; 789196332C486F6B00FF8CDF /* KeychainAccessible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainAccessible.swift; sourceTree = ""; }; 789196352C492F8600FF8CDF /* AuthTargetType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthTargetType.swift; sourceTree = ""; }; @@ -572,14 +570,6 @@ path = ViewModel; sourceTree = ""; }; - 7848250C2C7762A800FE07A0 /* MyPage */ = { - isa = PBXGroup; - children = ( - 7848250D2C7762EF00FE07A0 /* MyPageIngoModel.swift */, - ); - path = MyPage; - sourceTree = ""; - }; 7848250F2C77664400FE07A0 /* ServiceProtocol */ = { isa = PBXGroup; children = ( @@ -1463,7 +1453,6 @@ DE9E188E2C3BCC6B00DB76B4 /* Meetings */, DE9E188F2C3BCC7600DB76B4 /* Promises */, DE9E18902C3BCC8000DB76B4 /* Utils */, - 7848250C2C7762A800FE07A0 /* MyPage */, ); path = Model; sourceTree = ""; @@ -1881,7 +1870,6 @@ 78B9286C2C29402C006D9942 /* AppDelegate.swift in Sources */, DD3F9DD42C4858A3008E1FF7 /* UtilTargetType.swift in Sources */, DECB84562C43FB990022A003 /* AddPromiseViewModel.swift in Sources */, - 7848250E2C7762EF00FE07A0 /* MyPageIngoModel.swift in Sources */, DECB84602C4446930022A003 /* FindPlaceService.swift in Sources */, DEFBEBD52C46C86600437188 /* SelectPenaltyService.swift in Sources */, DEBA032F2C3C24F2002ED8F2 /* ModelType.swift in Sources */, diff --git a/KkuMulKum/Network/DTO/Model/MyPage/MyPageIngoModel.swift b/KkuMulKum/Network/DTO/Model/MyPage/MyPageIngoModel.swift deleted file mode 100644 index fa011293..00000000 --- a/KkuMulKum/Network/DTO/Model/MyPage/MyPageIngoModel.swift +++ /dev/null @@ -1,18 +0,0 @@ -// -// MyPageIngoModel.swift -// KkuMulKum -// -// Created by 이지훈 on 8/22/24. -// - -import Foundation - -struct MyPageUserInfo: ResponseModelType { - let userId: Int - let name: String? - let level: Int - let promiseCount: Int - let tardyCount: Int - let tardySum: Int - let profileImg: String? -} diff --git a/KkuMulKum/Network/Service/UserService.swift b/KkuMulKum/Network/Service/UserService.swift index cf6b0e75..1a36a334 100644 --- a/KkuMulKum/Network/Service/UserService.swift +++ b/KkuMulKum/Network/Service/UserService.swift @@ -16,7 +16,7 @@ final class MyPageUserService: MyPageUserServiceType { self.provider = provider } - func getUserInfo() async throws -> MyPageUserInfo { + func getUserInfo() async throws -> LoginUserModel { return try await performRequest(.getUserInfo) } diff --git a/KkuMulKum/Source/MyPage/ServiceProtocol/ServiceProtocol.swift b/KkuMulKum/Source/MyPage/ServiceProtocol/ServiceProtocol.swift index 36d39953..6c6c70f0 100644 --- a/KkuMulKum/Source/MyPage/ServiceProtocol/ServiceProtocol.swift +++ b/KkuMulKum/Source/MyPage/ServiceProtocol/ServiceProtocol.swift @@ -8,6 +8,6 @@ import Foundation protocol MyPageUserServiceType { - func getUserInfo() async throws -> MyPageUserInfo + func getUserInfo() async throws -> LoginUserModel func performRequest(_ target: UserTargetType) async throws -> T } diff --git a/KkuMulKum/Source/MyPage/ViewController/MyPageViewController.swift b/KkuMulKum/Source/MyPage/ViewController/MyPageViewController.swift index d4c42b07..a3ea5396 100644 --- a/KkuMulKum/Source/MyPage/ViewController/MyPageViewController.swift +++ b/KkuMulKum/Source/MyPage/ViewController/MyPageViewController.swift @@ -92,14 +92,14 @@ class MyPageViewController: BaseViewController, CustomActionSheetDelegate { .disposed(by: disposeBag) } - private func updateUI(with userInfo: MyPageUserInfo?) { + private func updateUI(with userInfo: LoginUserModel?) { guard let userInfo = userInfo else { return } rootView.contentView.nameLabel.text = userInfo.name ?? "꾸물리안 님" rootView.contentView.levelLabel.setText("Lv. \(userInfo.level) 지각대장 꾸물이", style: .body05, color: .white) rootView.contentView.levelLabel.setHighlightText("Lv. \(userInfo.level)", style: .body05, color: .lightGreen) - if let profileImageURL = userInfo.profileImg { + if let profileImageURL = userInfo.profileImageURL { loadImage(from: profileImageURL, into: rootView.contentView.profileImageView) } else { rootView.contentView.profileImageView.image = UIImage.imgProfile diff --git a/KkuMulKum/Source/MyPage/ViewModel/MyPageViewModel.swift b/KkuMulKum/Source/MyPage/ViewModel/MyPageViewModel.swift index 60fe3838..4e8eceee 100644 --- a/KkuMulKum/Source/MyPage/ViewModel/MyPageViewModel.swift +++ b/KkuMulKum/Source/MyPage/ViewModel/MyPageViewModel.swift @@ -23,11 +23,11 @@ class MyPageViewModel { let showActionSheet: Signal let performLogout: Signal let performUnsubscribe: Signal - let userInfo: BehaviorRelay + let userInfo: BehaviorRelay init(userService: MyPageUserServiceType = MyPageUserService()) { self.userService = userService - self.userInfo = BehaviorRelay(value: nil) + self.userInfo = BehaviorRelay(value: nil) pushEditProfileVC = editButtonTapped.asSignal() From 596fc2654c5d745e013319a6e17e34b66f5d57d4 Mon Sep 17 00:00:00 2001 From: hooni Date: Fri, 23 Aug 2024 14:48:33 +0900 Subject: [PATCH 4/6] =?UTF-8?q?fix/#324=20=EC=BD=94=EB=93=9C=EB=A6=AC?= =?UTF-8?q?=EB=B7=B0=EB=B0=98=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- KkuMulKum.xcodeproj/project.pbxproj | 14 +++++++------- .../MyPageUserServiceType.swift} | 2 +- 2 files changed, 8 insertions(+), 8 deletions(-) rename KkuMulKum/Source/MyPage/{ServiceProtocol/ServiceProtocol.swift => MyPageServiceProtocol/MyPageUserServiceType.swift} (89%) diff --git a/KkuMulKum.xcodeproj/project.pbxproj b/KkuMulKum.xcodeproj/project.pbxproj index b1d8d6ef..33537ba9 100644 --- a/KkuMulKum.xcodeproj/project.pbxproj +++ b/KkuMulKum.xcodeproj/project.pbxproj @@ -29,7 +29,7 @@ 784824FE2C75F25900FE07A0 /* MyPageEditViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784824FD2C75F25900FE07A0 /* MyPageEditViewController.swift */; }; 784825092C77623C00FE07A0 /* MypageTargetType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784825082C77623C00FE07A0 /* MypageTargetType.swift */; }; 7848250B2C77627200FE07A0 /* UserService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7848250A2C77627200FE07A0 /* UserService.swift */; }; - 784825112C77666500FE07A0 /* ServiceProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784825102C77666500FE07A0 /* ServiceProtocol.swift */; }; + 784825112C77666500FE07A0 /* MyPageUserServiceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784825102C77666500FE07A0 /* MyPageUserServiceType.swift */; }; 784E4D942C3B1C7F00BC943C /* KakaoSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 784E4D932C3B1C7F00BC943C /* KakaoSDK */; }; 784E4D962C3B1C7F00BC943C /* KakaoSDKAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 784E4D952C3B1C7F00BC943C /* KakaoSDKAuth */; }; 784E4D992C3B95A900BC943C /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 784E4D982C3B95A900BC943C /* KeychainAccess */; }; @@ -263,7 +263,7 @@ 784824FD2C75F25900FE07A0 /* MyPageEditViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageEditViewController.swift; sourceTree = ""; }; 784825082C77623C00FE07A0 /* MypageTargetType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MypageTargetType.swift; sourceTree = ""; }; 7848250A2C77627200FE07A0 /* UserService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserService.swift; sourceTree = ""; }; - 784825102C77666500FE07A0 /* ServiceProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServiceProtocol.swift; sourceTree = ""; }; + 784825102C77666500FE07A0 /* MyPageUserServiceType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageUserServiceType.swift; sourceTree = ""; }; 789196332C486F6B00FF8CDF /* KeychainAccessible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainAccessible.swift; sourceTree = ""; }; 789196352C492F8600FF8CDF /* AuthTargetType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthTargetType.swift; sourceTree = ""; }; 789196372C49697B00FF8CDF /* AuthError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthError.swift; sourceTree = ""; }; @@ -570,12 +570,12 @@ path = ViewModel; sourceTree = ""; }; - 7848250F2C77664400FE07A0 /* ServiceProtocol */ = { + 7848250F2C77664400FE07A0 /* MyPageServiceProtocol */ = { isa = PBXGroup; children = ( - 784825102C77666500FE07A0 /* ServiceProtocol.swift */, + 784825102C77666500FE07A0 /* MyPageUserServiceType.swift */, ); - path = ServiceProtocol; + path = MyPageServiceProtocol; sourceTree = ""; }; 789196392C49697F00FF8CDF /* Auth */ = { @@ -1250,7 +1250,7 @@ DE159D312C406E1600425101 /* MyPage */ = { isa = PBXGroup; children = ( - 7848250F2C77664400FE07A0 /* ServiceProtocol */, + 7848250F2C77664400FE07A0 /* MyPageServiceProtocol */, 784824FA2C75BF6800FE07A0 /* ViewModel */, DE159D2E2C406E1600425101 /* View */, DE159D302C406E1600425101 /* ViewController */, @@ -1902,7 +1902,7 @@ DE9E18892C3BC91000DB76B4 /* ResponseBodyDTO.swift in Sources */, 784825092C77623C00FE07A0 /* MypageTargetType.swift in Sources */, DD8626642C4606A300E4F980 /* ReadyPlanInfoView.swift in Sources */, - 784825112C77666500FE07A0 /* ServiceProtocol.swift in Sources */, + 784825112C77666500FE07A0 /* MyPageUserServiceType.swift in Sources */, DD3976682C41769400E2A4C4 /* CreateMeetingViewModel.swift in Sources */, DECB84582C43FBEB0022A003 /* AddPromiseViewController.swift in Sources */, DDE7D2C62C47D2BB005A921F /* MeetingTargetType.swift in Sources */, diff --git a/KkuMulKum/Source/MyPage/ServiceProtocol/ServiceProtocol.swift b/KkuMulKum/Source/MyPage/MyPageServiceProtocol/MyPageUserServiceType.swift similarity index 89% rename from KkuMulKum/Source/MyPage/ServiceProtocol/ServiceProtocol.swift rename to KkuMulKum/Source/MyPage/MyPageServiceProtocol/MyPageUserServiceType.swift index 6c6c70f0..7281e049 100644 --- a/KkuMulKum/Source/MyPage/ServiceProtocol/ServiceProtocol.swift +++ b/KkuMulKum/Source/MyPage/MyPageServiceProtocol/MyPageUserServiceType.swift @@ -1,5 +1,5 @@ // -// ServiceProtocol.swift +// MyPageUserServiceType.swift // KkuMulKum // // Created by 이지훈 on 8/22/24. From 0a8fb2da573df13acb12155ce38c990d0c575461 Mon Sep 17 00:00:00 2001 From: hooni Date: Fri, 23 Aug 2024 15:56:50 +0900 Subject: [PATCH 5/6] =?UTF-8?q?fix/#324=20=EA=B9=83=EC=98=A4=EB=A5=98=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- KkuMulKum.xcodeproj/project.pbxproj | 16 +++ .../Source/MyPage/View/MyPageEditView.swift | 76 +++++++++++ .../MyPageAskViewController.swift | 47 +++++++ .../MyPageEditViewController.swift | 123 ++++++++++++++++-- .../MyPageTermsViewController.swift | 47 +++++++ .../ViewController/MyPageViewController.swift | 27 +++- .../ViewModel/MyPageEditViewModel.swift | 96 ++++++++++++++ 7 files changed, 414 insertions(+), 18 deletions(-) create mode 100644 KkuMulKum/Source/MyPage/View/MyPageEditView.swift create mode 100644 KkuMulKum/Source/MyPage/ViewController/MyPageAskViewController.swift create mode 100644 KkuMulKum/Source/MyPage/ViewController/MyPageTermsViewController.swift create mode 100644 KkuMulKum/Source/MyPage/ViewModel/MyPageEditViewModel.swift diff --git a/KkuMulKum.xcodeproj/project.pbxproj b/KkuMulKum.xcodeproj/project.pbxproj index 33537ba9..d42b069d 100644 --- a/KkuMulKum.xcodeproj/project.pbxproj +++ b/KkuMulKum.xcodeproj/project.pbxproj @@ -30,6 +30,10 @@ 784825092C77623C00FE07A0 /* MypageTargetType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784825082C77623C00FE07A0 /* MypageTargetType.swift */; }; 7848250B2C77627200FE07A0 /* UserService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7848250A2C77627200FE07A0 /* UserService.swift */; }; 784825112C77666500FE07A0 /* MyPageUserServiceType.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784825102C77666500FE07A0 /* MyPageUserServiceType.swift */; }; + 784825012C77016400FE07A0 /* MyPageEditView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784825002C77016400FE07A0 /* MyPageEditView.swift */; }; + 784825032C77016F00FE07A0 /* MyPageEditViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784825022C77016F00FE07A0 /* MyPageEditViewModel.swift */; }; + 784825052C7716E900FE07A0 /* MyPageAskViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784825042C7716E900FE07A0 /* MyPageAskViewController.swift */; }; + 784825072C77215B00FE07A0 /* MyPageTermsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784825062C77215B00FE07A0 /* MyPageTermsViewController.swift */; }; 784E4D942C3B1C7F00BC943C /* KakaoSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 784E4D932C3B1C7F00BC943C /* KakaoSDK */; }; 784E4D962C3B1C7F00BC943C /* KakaoSDKAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 784E4D952C3B1C7F00BC943C /* KakaoSDKAuth */; }; 784E4D992C3B95A900BC943C /* KeychainAccess in Frameworks */ = {isa = PBXBuildFile; productRef = 784E4D982C3B95A900BC943C /* KeychainAccess */; }; @@ -264,6 +268,10 @@ 784825082C77623C00FE07A0 /* MypageTargetType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MypageTargetType.swift; sourceTree = ""; }; 7848250A2C77627200FE07A0 /* UserService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserService.swift; sourceTree = ""; }; 784825102C77666500FE07A0 /* MyPageUserServiceType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageUserServiceType.swift; sourceTree = ""; }; + 784825002C77016400FE07A0 /* MyPageEditView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageEditView.swift; sourceTree = ""; }; + 784825022C77016F00FE07A0 /* MyPageEditViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageEditViewModel.swift; sourceTree = ""; }; + 784825042C7716E900FE07A0 /* MyPageAskViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageAskViewController.swift; sourceTree = ""; }; + 784825062C77215B00FE07A0 /* MyPageTermsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPageTermsViewController.swift; sourceTree = ""; }; 789196332C486F6B00FF8CDF /* KeychainAccessible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeychainAccessible.swift; sourceTree = ""; }; 789196352C492F8600FF8CDF /* AuthTargetType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthTargetType.swift; sourceTree = ""; }; 789196372C49697B00FF8CDF /* AuthError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthError.swift; sourceTree = ""; }; @@ -566,6 +574,7 @@ isa = PBXGroup; children = ( 784824FB2C75BF7900FE07A0 /* MyPageViewModel.swift */, + 784825022C77016F00FE07A0 /* MyPageEditViewModel.swift */, ); path = ViewModel; sourceTree = ""; @@ -1234,6 +1243,7 @@ DE0137D22C43C5E50088C777 /* MyPageView.swift */, DE159D2B2C406E1600425101 /* MyPageContentView.swift */, DE159D2C2C406E1600425101 /* MyPageEtcSettingView.swift */, + 784825002C77016400FE07A0 /* MyPageEditView.swift */, ); path = View; sourceTree = ""; @@ -1243,6 +1253,8 @@ children = ( DE159D2F2C406E1600425101 /* MyPageViewController.swift */, 784824FD2C75F25900FE07A0 /* MyPageEditViewController.swift */, + 784825042C7716E900FE07A0 /* MyPageAskViewController.swift */, + 784825062C77215B00FE07A0 /* MyPageTermsViewController.swift */, ); path = ViewController; sourceTree = ""; @@ -1786,6 +1798,7 @@ DECB84622C44472F0022A003 /* FindPlaceViewModel.swift in Sources */, 789D73A72C46AF4900C7077D /* KeychainService.swift in Sources */, DD43937C2C412F4500EC1799 /* InviteCodeViewController.swift in Sources */, + 784825012C77016400FE07A0 /* MyPageEditView.swift in Sources */, DD4393782C412F4500EC1799 /* CheckInviteCodeView.swift in Sources */, DE6D4D102C3F14D80005584B /* InvitationCodePopUpView.swift in Sources */, 789196382C49697B00FF8CDF /* AuthError.swift in Sources */, @@ -1822,6 +1835,7 @@ A3FB184F2C3BF4BC001483E5 /* MakeMeetingsResponseModel.swift in Sources */, DEF725DB2C3F3BBF008C87C7 /* Toast.swift in Sources */, DD43937A2C412F4500EC1799 /* CreateSuccessViewController.swift in Sources */, + 784825032C77016F00FE07A0 /* MyPageEditViewModel.swift in Sources */, DE254AAC2C31192400A4015E /* UILabel+.swift in Sources */, DE254AB72C3119D000A4015E /* ReuseIdentifiable.swift in Sources */, A3DD9C412C41BAD000E58A13 /* MeetingListViewModel.swift in Sources */, @@ -1845,6 +1859,7 @@ A39F2B212C499CE5008DA5F5 /* SetReadyStatusInfoServiceType.swift in Sources */, DE6D4D132C3F14D80005584B /* MeetingMemberCell.swift in Sources */, DD3F9DD82C49C25D008E1FF7 /* PromiseServiceProtocol.swift in Sources */, + 784825072C77215B00FE07A0 /* MyPageTermsViewController.swift in Sources */, DDAF1C932C3D6E3D008A37D3 /* PromiseViewController.swift in Sources */, 789D73BE2C47FE0F00C7077D /* AuthInterceptor.swift in Sources */, 789D73B32C47CC6D00C7077D /* LocalNotificationManager.swift in Sources */, @@ -1863,6 +1878,7 @@ DD3072142C3BF87A00416D9F /* NearestPromiseResponseModel.swift in Sources */, 782B407F2C3E44B7008B0CA7 /* WelcomeViewModel.swift in Sources */, DD41BEFA2C41D4160095A068 /* TardyView.swift in Sources */, + 784825052C7716E900FE07A0 /* MyPageAskViewController.swift in Sources */, DD3072162C3BFE4E00416D9F /* UpcomingPromiseListResponseModel.swift in Sources */, A3FB18572C3BF704001483E5 /* MeetingListResponseModel.swift in Sources */, DE254AB22C31197B00A4015E /* UIButton+.swift in Sources */, diff --git a/KkuMulKum/Source/MyPage/View/MyPageEditView.swift b/KkuMulKum/Source/MyPage/View/MyPageEditView.swift new file mode 100644 index 00000000..3cbcc69b --- /dev/null +++ b/KkuMulKum/Source/MyPage/View/MyPageEditView.swift @@ -0,0 +1,76 @@ +// +// MyPageEditView.swift +// KkuMulKum +// +// Created by 이지훈 on 8/22/24. +// + +import UIKit + +import SnapKit +import Then + +class MyPageEditView: BaseView { + let titleLabel = UILabel().then { + $0.setText("프로필을 설정해 주세요", style: .head01, color: .gray8) + $0.textAlignment = .left + } + + let profileImageView = UIImageView().then { + $0.image = .imgProfile + $0.contentMode = .scaleAspectFill + $0.clipsToBounds = true + } + + let cameraButton = UIButton().then { + $0.setImage(.iconCamera, for: .normal) + $0.tintColor = .white + $0.setLayer(borderWidth: 0, borderColor: .clear, cornerRadius: 15) + } + + let skipButton = UIButton().then { + $0.setTitle("기본 프로필로 설정", style: .body05, color: .gray5) + $0.addUnderline() + } + + let confirmButton = UIButton().then { + $0.setTitle("확인", style: .body03, color: .white) + $0.backgroundColor = .maincolor + $0.setLayer(borderWidth: 0, borderColor: .clear, cornerRadius: 8) + } + + override func setupView() { + backgroundColor = .white + + addSubviews(titleLabel, profileImageView, cameraButton, skipButton, confirmButton) + } + + override func setupAutoLayout() { + titleLabel.snp.makeConstraints { + $0.top.equalTo(safeAreaLayoutGuide).offset(24) + $0.leading.trailing.equalToSuperview().inset(20) + } + + profileImageView.snp.makeConstraints { + $0.top.equalTo(titleLabel.snp.bottom).offset(120) + $0.centerX.equalToSuperview() + $0.size.equalTo(Screen.width(150)) + } + + cameraButton.snp.makeConstraints { + $0.bottom.trailing.equalTo(profileImageView) + $0.size.equalTo(Screen.width(42)) + } + + skipButton.snp.makeConstraints { + $0.bottom.equalTo(confirmButton.snp.top).offset(-20) + $0.centerX.equalToSuperview() + } + + confirmButton.snp.makeConstraints { + $0.bottom.equalTo(safeAreaLayoutGuide).offset(-20) + $0.leading.trailing.equalToSuperview().inset(20) + $0.height.equalTo(Screen.height(50)) + } + } +} diff --git a/KkuMulKum/Source/MyPage/ViewController/MyPageAskViewController.swift b/KkuMulKum/Source/MyPage/ViewController/MyPageAskViewController.swift new file mode 100644 index 00000000..b7091180 --- /dev/null +++ b/KkuMulKum/Source/MyPage/ViewController/MyPageAskViewController.swift @@ -0,0 +1,47 @@ +// +// MyPageAskViewController.swift +// KkuMulKum +// +// Created by 이지훈 on 8/22/24. +// + +import UIKit +import WebKit + +class MyPageAskViewController: BaseViewController { + + private let viewModel: MyPageViewModel + private var webView: WKWebView! + + init(viewModel: MyPageViewModel) { + self.viewModel = viewModel + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func loadView() { + let webConfiguration = WKWebViewConfiguration() + webView = WKWebView(frame: .zero, configuration: webConfiguration) + webView.uiDelegate = self + view = webView + } + + override func viewDidLoad() { + super.viewDidLoad() + + let myURL = URL(string: "https://docs.google.com/forms/d/e/1FAIpQLSdRR65ARe2M7JxQEAx8vpFz-I8tyEYwlpLwtSnJjniGOapPVQ/viewform") + let myRequest = URLRequest(url: myURL!) + webView.load(myRequest) + } + + override func setupView() { + super.setupView() + setupNavigationBarTitle(with: "문의하기") + setupNavigationBarBackButton() + } +} + +extension MyPageAskViewController: WKUIDelegate {} diff --git a/KkuMulKum/Source/MyPage/ViewController/MyPageEditViewController.swift b/KkuMulKum/Source/MyPage/ViewController/MyPageEditViewController.swift index b1fcb812..2c21c8e2 100644 --- a/KkuMulKum/Source/MyPage/ViewController/MyPageEditViewController.swift +++ b/KkuMulKum/Source/MyPage/ViewController/MyPageEditViewController.swift @@ -7,23 +7,122 @@ import UIKit -class MyPageEditViewController: BaseViewController { +import RxSwift +import RxCocoa +class MyPageEditViewController: BaseViewController { + private let rootView = MyPageEditView() + private let viewModel: MyPageEditViewModel + private let disposeBag = DisposeBag() + + init(viewModel: MyPageEditViewModel) { + self.viewModel = viewModel + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func loadView() { + view = rootView + } + override func viewDidLoad() { super.viewDidLoad() - - view.backgroundColor = .white + + setupNavigationBarTitle(with: "프로필 설정") + setupNavigationBarBackButton() + setupBindings() } - - /* - // MARK: - Navigation - - // In a storyboard-based application, you will often want to do a little preparation before navigation - override func prepare(for segue: UIStoryboardSegue, sender: Any?) { - // Get the new view controller using segue.destination. - // Pass the selected object to the new view controller. + private func setupBindings() { + let input = MyPageEditViewModel.Input( + profileImageTap: rootView.cameraButton.rx.tap.asObservable(), + confirmButtonTap: rootView.confirmButton.rx.tap.asObservable(), + newProfileImage: Observable.never() // This will be updated in imagePickerController + ) + + let output = viewModel.transform(input: input, disposeBag: DisposeBag()) + output.profileImage + .drive(rootView.profileImageView.rx.image) + .disposed(by: disposeBag) + + output.isConfirmButtonEnabled + .drive(rootView.confirmButton.rx.isEnabled) + .disposed(by: disposeBag) + + output.isConfirmButtonEnabled + .map { $0 ? 1.0 : 0.5 } + .drive(rootView.confirmButton.rx.alpha) + .disposed(by: disposeBag) + + output.serverResponse + .drive(onNext: { [weak self] response in + if let response = response { + self?.showAlert(message: response) + } + }) + .disposed(by: disposeBag) + + rootView.cameraButton.rx.tap + .subscribe(onNext: { [weak self] in + self?.showImagePicker() + }) + .disposed(by: disposeBag) + + navigationItem.leftBarButtonItem?.rx.tap + .subscribe(onNext: { [weak self] in + self?.navigateToMyPageViewController() + }) + .disposed(by: disposeBag) + } + private func showImagePicker() { + let imagePicker = UIImagePickerController() + imagePicker.delegate = self + imagePicker.sourceType = .photoLibrary + imagePicker.allowsEditing = true + present(imagePicker, animated: true) + } + + private func showAlert(message: String) { + let alert = UIAlertController(title: nil, message: message, preferredStyle: .alert) + alert.addAction(UIAlertAction(title: "확인", style: .default)) + present(alert, animated: true) + } + + private func navigateToMyPageViewController() { + navigationController?.popViewController(animated: true) } - */ +} +extension MyPageEditViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate { + func imagePickerController( + _ picker: UIImagePickerController, + didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any] + ) { + if let editedImage = info[.editedImage] as? UIImage ?? info[.originalImage] as? UIImage { + let croppedImage = cropToCircle(image: editedImage) + let input = MyPageEditViewModel.Input( + profileImageTap: Observable.never(), + confirmButtonTap: Observable.never(), + newProfileImage: Observable.just(croppedImage) + ) + _ = viewModel.transform(input: input, disposeBag: DisposeBag()) + } + dismiss(animated: true) + } + + private func cropToCircle(image: UIImage) -> UIImage { + let shorterSide = min(image.size.width, image.size.height) + let imageBounds = CGRect(x: 0, y: 0, width: shorterSide, height: shorterSide) + UIGraphicsBeginImageContextWithOptions(imageBounds.size, false, UIScreen.main.scale) + let context = UIGraphicsGetCurrentContext()! + context.addEllipse(in: imageBounds) + context.clip() + image.draw(in: imageBounds) + let circleImage = UIGraphicsGetImageFromCurrentImageContext()! + UIGraphicsEndImageContext() + return circleImage + } } diff --git a/KkuMulKum/Source/MyPage/ViewController/MyPageTermsViewController.swift b/KkuMulKum/Source/MyPage/ViewController/MyPageTermsViewController.swift new file mode 100644 index 00000000..49c2ad0c --- /dev/null +++ b/KkuMulKum/Source/MyPage/ViewController/MyPageTermsViewController.swift @@ -0,0 +1,47 @@ +// +// MyPageTermsViewController.swift +// KkuMulKum +// +// Created by 이지훈 on 8/22/24. +// + +import UIKit +import WebKit + +class MyPageTermsViewController: BaseViewController { + + private let viewModel: MyPageViewModel + private var webView: WKWebView! + + init(viewModel: MyPageViewModel) { + self.viewModel = viewModel + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func loadView() { + let webConfiguration = WKWebViewConfiguration() + webView = WKWebView(frame: .zero, configuration: webConfiguration) + webView.uiDelegate = self + view = webView + } + + override func viewDidLoad() { + super.viewDidLoad() + + let myURL = URL(string: "https://arrow-frog-4b9.notion.site/a66033a3ff4a40bfaa6eff0a5bee737d") + let myRequest = URLRequest(url: myURL!) + webView.load(myRequest) + } + + override func setupView() { + super.setupView() + setupNavigationBarTitle(with: "이용약관") + setupNavigationBarBackButton() + } +} + +extension MyPageTermsViewController: WKUIDelegate {} diff --git a/KkuMulKum/Source/MyPage/ViewController/MyPageViewController.swift b/KkuMulKum/Source/MyPage/ViewController/MyPageViewController.swift index a3ea5396..45590e52 100644 --- a/KkuMulKum/Source/MyPage/ViewController/MyPageViewController.swift +++ b/KkuMulKum/Source/MyPage/ViewController/MyPageViewController.swift @@ -22,7 +22,7 @@ class MyPageViewController: BaseViewController, CustomActionSheetDelegate { override func viewDidLoad() { super.viewDidLoad() view.backgroundColor = .green1 - + bindViewModel() viewModel.fetchUserInfo() } @@ -45,18 +45,19 @@ class MyPageViewController: BaseViewController, CustomActionSheetDelegate { bindRowTapGesture(for: rootView.etcSettingView.unsubscribeRow) .bind(to: viewModel.unsubscribeButtonTapped) .disposed(by: disposeBag) - // Other rows bindRowTapGesture(for: rootView.etcSettingView.versionInfoRow) .subscribe(onNext: { print("버전정보 탭됨") }) .disposed(by: disposeBag) bindRowTapGesture(for: rootView.etcSettingView.termsOfServiceRow) - .subscribe(onNext: { print("이용약관 탭됨") }) + .subscribe(onNext: { [weak self] in + self?.pushTermsViewController() }) .disposed(by: disposeBag) bindRowTapGesture(for: rootView.etcSettingView.inquiryRow) - .subscribe(onNext: { print("문의하기 탭됨") }) + .subscribe(onNext: { [weak self] in + self?.pushAskViewController() }) .disposed(by: disposeBag) // Outputs @@ -116,14 +117,28 @@ class MyPageViewController: BaseViewController, CustomActionSheetDelegate { .first? .rx.event .map { _ in } - ?? Observable.empty() + + ?? Observable.empty() } private func pushEditProfileViewController() { - let editProfileViewController = MyPageEditViewController() + let authService = AuthService() + let editProfileViewModel = MyPageEditViewModel(authService: authService) + let editProfileViewController = MyPageEditViewController(viewModel: editProfileViewModel) + navigationController?.pushViewController(editProfileViewController, animated: true) } + private func pushAskViewController() { + let askViewController = MyPageAskViewController(viewModel: self.viewModel) + navigationController?.pushViewController(askViewController, animated: true) + } + + private func pushTermsViewController() { + let askViewController = MyPageTermsViewController(viewModel: self.viewModel) + navigationController?.pushViewController(askViewController, animated: true) + } + func actionButtonDidTap(for kind: ActionSheetKind) { viewModel.actionSheetButtonTapped.accept(kind) } diff --git a/KkuMulKum/Source/MyPage/ViewModel/MyPageEditViewModel.swift b/KkuMulKum/Source/MyPage/ViewModel/MyPageEditViewModel.swift new file mode 100644 index 00000000..f7ab73ae --- /dev/null +++ b/KkuMulKum/Source/MyPage/ViewModel/MyPageEditViewModel.swift @@ -0,0 +1,96 @@ +// +// MyPageEditViewModel.swift +// KkuMulKum +// +// Created by 이지훈 on 8/22/24. +// + +import Foundation + +import UIKit +import Kingfisher +import RxSwift +import RxCocoa +import Moya + +class MyPageEditViewModel: ViewModelType { + private let authService: AuthServiceType + + struct Input { + let profileImageTap: Observable + let confirmButtonTap: Observable + let newProfileImage: Observable + } + + struct Output { + let profileImage: Driver + let isConfirmButtonEnabled: Driver + let serverResponse: Driver + } + + init(authService: AuthServiceType) { + self.authService = authService + } + + func transform(input: Input, disposeBag: DisposeBag) -> Output { + let imageDataRelay = BehaviorRelay(value: nil) + let serverResponseRelay = PublishRelay() + + + input.newProfileImage + .compactMap { $0?.jpegData(compressionQuality: 1.0) } + .bind(to: imageDataRelay) + .disposed(by: DisposeBag()) + + let profileImage = imageDataRelay + .map { data -> UIImage? in + guard let data = data else { return UIImage.imgProfile } + return UIImage(data: data) + } + .asDriver(onErrorJustReturn: UIImage.imgProfile) + + let isConfirmButtonEnabled = imageDataRelay + .map { $0 != nil } + .asDriver(onErrorJustReturn: false) + + + // TODO: api 연결시 다시 연결예정 +// input.confirmButtonTap +// .withLatestFrom(imageDataRelay) +// .flatMapLatest { [weak self] imageData -> Observable in +// guard let self = self, let imageData = imageData else { +// return .just("이미지 데이터가 없습니다.") +// } +// return self.authService.rx.request(.updateProfileImage(image: imageData, fileName: "profile_image.jpg", mimeType: "image/jpeg")) +// .filterSuccessfulStatusCodes() +// .map { _ in "프로필 이미지가 성공적으로 업로드되었습니다." } +// .catchError { error in +// let networkError = error as? NetworkError ?? .unknownError("알 수 없는 오류가 발생했습니다.") +// return .just(self.handleError(networkError)) +// } +// } +// .bind(to: serverResponseRelay) +// .disposed(by: disposeBag) + + return Output( + profileImage: profileImage, + isConfirmButtonEnabled: isConfirmButtonEnabled, + serverResponse: serverResponseRelay.asDriver(onErrorJustReturn: nil) + ) + } + + private func handleError(_ error: NetworkError) -> String { + switch error { + case .apiError(let code, let message): + return code == 413 ? "이미지 크기가 너무 큽니다. 더 작은 이미지를 선택해주세요." : "업로드 실패: \(message)" + case .networkError(let error): + return "네트워크 오류: \(error.localizedDescription)" + case .decodingError: + return "데이터 처리 중 오류가 발생했습니다." + default: + return "알 수 없는 오류가 발생했습니다." + } + } +} + + From 1b8acc588885404ebea84b3fff85b37455c6e835 Mon Sep 17 00:00:00 2001 From: hooni Date: Fri, 23 Aug 2024 15:59:24 +0900 Subject: [PATCH 6/6] =?UTF-8?q?fix/#324=20=EC=98=A4=EB=A5=98=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- KkuMulKum/Source/MyPage/ViewModel/MyPageEditViewModel.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/KkuMulKum/Source/MyPage/ViewModel/MyPageEditViewModel.swift b/KkuMulKum/Source/MyPage/ViewModel/MyPageEditViewModel.swift index f7ab73ae..8fcd0785 100644 --- a/KkuMulKum/Source/MyPage/ViewModel/MyPageEditViewModel.swift +++ b/KkuMulKum/Source/MyPage/ViewModel/MyPageEditViewModel.swift @@ -14,7 +14,7 @@ import RxCocoa import Moya class MyPageEditViewModel: ViewModelType { - private let authService: AuthServiceType + private let authService: AuthServiceProtocol struct Input { let profileImageTap: Observable @@ -28,7 +28,7 @@ class MyPageEditViewModel: ViewModelType { let serverResponse: Driver } - init(authService: AuthServiceType) { + init(authService: AuthServiceProtocol) { self.authService = authService }