diff --git a/iOS/Projects/App/WeTri/Sources/Application/AppDelegate.swift b/iOS/Projects/App/WeTri/Sources/Application/AppDelegate.swift index 26d09378..1ffc70ad 100644 --- a/iOS/Projects/App/WeTri/Sources/Application/AppDelegate.swift +++ b/iOS/Projects/App/WeTri/Sources/Application/AppDelegate.swift @@ -1,7 +1,5 @@ import UIKit -import Keychain - @main final class AppDelegate: UIResponder, UIApplicationDelegate { func application( diff --git a/iOS/Projects/App/WeTri/Sources/TabBarScene/Coordinator/TabBarCoordinator.swift b/iOS/Projects/App/WeTri/Sources/TabBarScene/Coordinator/TabBarCoordinator.swift index 4a6150f0..45d432ff 100644 --- a/iOS/Projects/App/WeTri/Sources/TabBarScene/Coordinator/TabBarCoordinator.swift +++ b/iOS/Projects/App/WeTri/Sources/TabBarScene/Coordinator/TabBarCoordinator.swift @@ -63,7 +63,7 @@ final class TabBarCoordinator: TabBarCoordinating { recordCoordinator.start() case .profile: - let profileCoordinator = ProfileCoordinator(navigationController: pageNavigationViewController, isMockEnvironment: true) + let profileCoordinator = ProfileCoordinator(navigationController: pageNavigationViewController, isMockEnvironment: false) childCoordinators.append(profileCoordinator) profileCoordinator.finishDelegate = self profileCoordinator.start() diff --git a/iOS/Projects/Features/SignUp/Sources/Data/Repository/ImageFormRepository.swift b/iOS/Projects/Features/SignUp/Sources/Data/Repository/ImageFormRepository.swift index c4e4417e..c95fc999 100644 --- a/iOS/Projects/Features/SignUp/Sources/Data/Repository/ImageFormRepository.swift +++ b/iOS/Projects/Features/SignUp/Sources/Data/Repository/ImageFormRepository.swift @@ -7,6 +7,29 @@ import Trinet enum ImageFormRepositoryError: Error { case invalidImageFormRepository + case invalidResponseCode + case notAccessObjectStorage + case notAccessGreenEye + case invalidFileType + case fileSizeTooLarge + case invalidFileCountOrFieldName + + var code: Int { + switch self { + case .notAccessObjectStorage: + return 9000 + case .notAccessGreenEye: + return 9100 + case .invalidFileType: + return 9200 + case .fileSizeTooLarge: + return 9300 + case .invalidFileCountOrFieldName: + return 9400 + default: + return 0 + } + } } // MARK: - ImageFormRepository @@ -37,10 +60,31 @@ public final class ImageFormRepository: ImageFormRepositoryRepresentable { } } .decode(type: GWResponse<[ImageForm]>.self, decoder: JSONDecoder()) - .compactMap(\.data) - .map { imageForms in - return imageForms + .tryMap { gwResponse in + guard let code = gwResponse.code else { + return gwResponse.data + } + switch code { + case ImageFormRepositoryError.notAccessObjectStorage.code: + throw ImageFormRepositoryError.notAccessObjectStorage + + case ImageFormRepositoryError.notAccessGreenEye.code: + throw ImageFormRepositoryError.notAccessGreenEye + + case ImageFormRepositoryError.invalidFileType.code: + throw ImageFormRepositoryError.invalidFileType + + case ImageFormRepositoryError.fileSizeTooLarge.code: + throw ImageFormRepositoryError.fileSizeTooLarge + + case ImageFormRepositoryError.invalidFileCountOrFieldName.code: + throw ImageFormRepositoryError.invalidFileCountOrFieldName + + default: + return gwResponse.data + } } + .compactMap { $0 } .eraseToAnyPublisher() } } diff --git a/iOS/Projects/Features/SignUp/Sources/Presentation/SignUpProfileScene/SignUpProfileViewController.swift b/iOS/Projects/Features/SignUp/Sources/Presentation/SignUpProfileScene/SignUpProfileViewController.swift index 1680d014..35a79226 100644 --- a/iOS/Projects/Features/SignUp/Sources/Presentation/SignUpProfileScene/SignUpProfileViewController.swift +++ b/iOS/Projects/Features/SignUp/Sources/Presentation/SignUpProfileScene/SignUpProfileViewController.swift @@ -195,11 +195,11 @@ private extension SignUpProfileViewController { imageButtonTap: imageButtonTapSubject.eraseToAnyPublisher(), imageSetting: imageSetSubject.eraseToAnyPublisher(), completeButtonTap: completeButtonTapSubject.eraseToAnyPublisher(), - genderBirth: genderBirthSubject.eraseToAnyPublisher() + genderBirth: genderBirthSubject.setFailureType(to: Error.self).eraseToAnyPublisher() ) let output = viewModel.transform(input: input) output - .subscribe(on: DispatchQueue.main) + .receive(on: DispatchQueue.main) .sink { [weak self] state in self?.render(state: state) } @@ -224,10 +224,35 @@ private extension SignUpProfileViewController { completionButton.isEnabled = false case let .customError(error): Log.make().error("\(error)") + if let profileImageError = error as? ImageFormRepositoryError { + switch profileImageError { + case .notAccessObjectStorage: + showAlert(message: "앱의 상태가 불안정합니다.") + case .notAccessGreenEye: + showAlert(message: "건전한 사진을 부탁드립니다.") + case .invalidFileType: + showAlert(message: "사진 파일이 올바르지 않은 확장자입니다.") + case .fileSizeTooLarge: + showAlert(message: "파일의 크기가 너무 큽니다.") + default: + showAlert(message: "다시 시도 해주세요.") + } + } default: break } } + + private func showAlert(message: String) { + let alertVC = UIAlertController( + title: "잘못된 접근입니다.", + message: message, + preferredStyle: .alert + ) + let confirmAction = UIAlertAction(title: "확인", style: .default) + alertVC.addAction(confirmAction) + present(alertVC, animated: true, completion: nil) + } } // MARK: PHPhotoLibrary @@ -238,7 +263,7 @@ private extension SignUpProfileViewController { let alertVC = UIAlertController( title: "프로필 이미지 설정", message: "선택해주세요.", - preferredStyle: .alert + preferredStyle: .actionSheet ) let cameraAction = UIAlertAction(title: "카메라", style: .default) { [weak self] _ in self?.cameraAuth() diff --git a/iOS/Projects/Features/SignUp/Sources/Presentation/SignUpProfileScene/ViewModel/SignUpProfileViewModel.swift b/iOS/Projects/Features/SignUp/Sources/Presentation/SignUpProfileScene/ViewModel/SignUpProfileViewModel.swift index f7fb4a72..9e54b0c7 100644 --- a/iOS/Projects/Features/SignUp/Sources/Presentation/SignUpProfileScene/ViewModel/SignUpProfileViewModel.swift +++ b/iOS/Projects/Features/SignUp/Sources/Presentation/SignUpProfileScene/ViewModel/SignUpProfileViewModel.swift @@ -19,7 +19,7 @@ public struct SignUpProfileViewModelInput { let imageButtonTap: AnyPublisher let imageSetting: AnyPublisher let completeButtonTap: AnyPublisher - let genderBirth: AnyPublisher + let genderBirth: AnyPublisher } public typealias SignUpProfileViewModelOutput = AnyPublisher @@ -73,19 +73,13 @@ extension SignUpProfileViewModel: SignUpProfileViewModelRepresentable { var imageData: Data? - let imageFormSubject = PassthroughSubject() - let nickNameSubject = PassthroughSubject() - let completeSignUpSubject = PassthroughSubject() + let nickNameSubject = PassthroughSubject() - var imageFormPubliser: AnyPublisher { - return imageFormSubject.eraseToAnyPublisher() - } - - var nickNamePublisher: AnyPublisher { + var nickNamePublisher: AnyPublisher { return nickNameSubject.eraseToAnyPublisher() } - var genderBirthPublisher: AnyPublisher { + var genderBirthPublisher: AnyPublisher { return input.genderBirth.eraseToAnyPublisher() } @@ -116,34 +110,19 @@ extension SignUpProfileViewModel: SignUpProfileViewModelRepresentable { } .store(in: &subscriptions) - input.completeButtonTap + let imagePublisher = input.completeButtonTap .compactMap { imageData } .flatMap(imageTransmitUseCase.transmit) - .sink { completion in - switch completion { - case .finished: break - case let .failure(error): - Log.make().error("\(error)") - } - } receiveValue: { imageforms in - guard let imageForm = imageforms.first else { - return - } - imageFormSubject.send(imageForm) - Log.make().debug("이미지 폼스 : \(imageforms)") - } - .store(in: &subscriptions) + .compactMap(\.first) + .share() - Publishers + let completePublisher = Publishers .CombineLatest3( - imageFormPubliser, + imagePublisher, nickNamePublisher, genderBirthPublisher ) - .sink { [weak self] imageForm, nickName, genderBirth in - guard let newUserInformation = self?.newUserInformation else { - return - } + .map { [newUserInformation] (imageForm: ImageForm, nickName: String, genderBirth: GenderBirth) -> SignUpUser in let signUpUser = SignUpUser( provider: newUserInformation.provider.rawValue, nickname: nickName, @@ -152,20 +131,14 @@ extension SignUpProfileViewModel: SignUpProfileViewModelRepresentable { profileImage: imageForm.imageURL, mappedUserID: newUserInformation.mappedUserID ) - completeSignUpSubject.send(signUpUser) + return signUpUser } - .store(in: &subscriptions) - - completeSignUpSubject .flatMap(signUpUseCase.signUp(signUpUser:)) + .share() + + completePublisher .receive(on: DispatchQueue.main) - .sink(receiveCompletion: { completion in - switch completion { - case .finished: - break - case let .failure(error): - Log.make().error("\(error)") - } + .sink(receiveCompletion: { _ in }, receiveValue: { [weak self] token in if let accessToken = token.accessToken, let refreshToken = token.refreshToken { @@ -176,6 +149,14 @@ extension SignUpProfileViewModel: SignUpProfileViewModelRepresentable { }) .store(in: &subscriptions) + let completeError = completePublisher + .map { _ in + return SignUpProfileState.idle + } + .catch { error in + Just(SignUpProfileState.customError(error)) + } + let initialState: SignUpProfileViewModelOutput = Just(.idle) .eraseToAnyPublisher() @@ -190,7 +171,7 @@ extension SignUpProfileViewModel: SignUpProfileViewModelRepresentable { .eraseToAnyPublisher() return Publishers - .Merge4(initialState, nickNameCheckedResult, image, success) + .Merge5(initialState, nickNameCheckedResult, image, success, completeError) .eraseToAnyPublisher() } }