diff --git a/Clody_iOS/Clody_iOS.xcodeproj/project.pbxproj b/Clody_iOS/Clody_iOS.xcodeproj/project.pbxproj index 56b1425..9e14eab 100644 --- a/Clody_iOS/Clody_iOS.xcodeproj/project.pbxproj +++ b/Clody_iOS/Clody_iOS.xcodeproj/project.pbxproj @@ -116,10 +116,6 @@ 2CE6378B2C3D546200B6EC6D /* AccountViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CE6378A2C3D546200B6EC6D /* AccountViewController.swift */; }; 2CE6378D2C3D56A700B6EC6D /* AccountView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CE6378C2C3D56A700B6EC6D /* AccountView.swift */; }; 2CE637912C3D56C300B6EC6D /* AccountViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CE637902C3D56C300B6EC6D /* AccountViewModel.swift */; }; - 952EB1902C6A301C00D1E516 /* EmailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 952EB18F2C6A301C00D1E516 /* EmailViewController.swift */; }; - 952EB1922C6A309600D1E516 /* EmailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 952EB1912C6A309600D1E516 /* EmailView.swift */; }; - 952EB1942C6A30CC00D1E516 /* EmailViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 952EB1932C6A30CC00D1E516 /* EmailViewModel.swift */; }; - 952EB1962C6A8DCD00D1E516 /* SignUpInfoModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 952EB1952C6A8DCD00D1E516 /* SignUpInfoModel.swift */; }; 952EB1992C6BCF7E00D1E516 /* UIViewController+.swift in Sources */ = {isa = PBXBuildFile; fileRef = 952EB1982C6BCF7E00D1E516 /* UIViewController+.swift */; }; 95310F722C3C39FE00023C7B /* ClodyNavigationBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95310F712C3C39FE00023C7B /* ClodyNavigationBar.swift */; }; 95310F742C3C765C00023C7B /* ClodyTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 95310F732C3C765C00023C7B /* ClodyTextField.swift */; }; @@ -214,6 +210,7 @@ 0BABF7BD2C722313005A5CA1 /* AppVersionManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppVersionManager.swift; sourceTree = ""; }; 0BC373522C3FCC2600C45072 /* ViewModelType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModelType.swift; sourceTree = ""; }; 0BCD4FA22C35B59C006390D2 /* Clody_iOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Clody_iOS.entitlements; sourceTree = ""; }; + 0BDF13BF2C7D612800BA2E7C /* Config_Dev.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config_Dev.xcconfig; sourceTree = ""; }; 0BE7E9E92C45B0B8000C5358 /* UserManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserManager.swift; sourceTree = ""; }; 0BE7E9EE2C461D0F000C5358 /* NotificationPickerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationPickerView.swift; sourceTree = ""; }; 0BE7E9F02C4636DE000C5358 /* CalendarMonthlyResponseDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarMonthlyResponseDTO.swift; sourceTree = ""; }; @@ -256,10 +253,6 @@ 2CE6378A2C3D546200B6EC6D /* AccountViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountViewController.swift; sourceTree = ""; }; 2CE6378C2C3D56A700B6EC6D /* AccountView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountView.swift; sourceTree = ""; }; 2CE637902C3D56C300B6EC6D /* AccountViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountViewModel.swift; sourceTree = ""; }; - 952EB18F2C6A301C00D1E516 /* EmailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmailViewController.swift; sourceTree = ""; }; - 952EB1912C6A309600D1E516 /* EmailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmailView.swift; sourceTree = ""; }; - 952EB1932C6A30CC00D1E516 /* EmailViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmailViewModel.swift; sourceTree = ""; }; - 952EB1952C6A8DCD00D1E516 /* SignUpInfoModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpInfoModel.swift; sourceTree = ""; }; 952EB1982C6BCF7E00D1E516 /* UIViewController+.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+.swift"; sourceTree = ""; }; 95310F712C3C39FE00023C7B /* ClodyNavigationBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClodyNavigationBar.swift; sourceTree = ""; }; 95310F732C3C765C00023C7B /* ClodyTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClodyTextField.swift; sourceTree = ""; }; @@ -353,6 +346,7 @@ 0B20F3EA2C2C4DAB00F8D62D /* Info.plist */, 95310F892C401A1F00023C7B /* GoogleService-Info.plist */, 2CB129422C47F2CC009D1480 /* Config.xcconfig */, + 0BDF13BF2C7D612800BA2E7C /* Config_Dev.xcconfig */, ); path = Clody_iOS; sourceTree = ""; @@ -814,19 +808,10 @@ path = ViewModels; sourceTree = ""; }; - 952EB1972C6A8DD600D1E516 /* Models */ = { - isa = PBXGroup; - children = ( - 952EB1952C6A8DCD00D1E516 /* SignUpInfoModel.swift */, - ); - path = Models; - sourceTree = ""; - }; 95310F7D2C3FF37500023C7B /* Auth */ = { isa = PBXGroup; children = ( 95310FBC2C44346500023C7B /* ViewModels */, - 952EB1972C6A8DD600D1E516 /* Models */, 95310F812C3FF51600023C7B /* Views */, 95310F802C3FF50D00023C7B /* ViewControllers */, ); @@ -837,7 +822,6 @@ isa = PBXGroup; children = ( 95310F7E2C3FF44E00023C7B /* LoginViewController.swift */, - 952EB18F2C6A301C00D1E516 /* EmailViewController.swift */, 95310F8D2C401BED00023C7B /* TermsViewController.swift */, 95310F8F2C40705600023C7B /* NicknameViewController.swift */, 95310F932C40776200023C7B /* OnBoardingViewController.swift */, @@ -851,7 +835,6 @@ isa = PBXGroup; children = ( 95310F822C3FF53400023C7B /* LoginView.swift */, - 952EB1912C6A309600D1E516 /* EmailView.swift */, 95310F8B2C401BE200023C7B /* TermsView.swift */, 95310F912C40706600023C7B /* NicknameView.swift */, 95310F972C40777C00023C7B /* OnBoardingView.swift */, @@ -920,7 +903,6 @@ isa = PBXGroup; children = ( 95310FBD2C4434B500023C7B /* LoginViewModel.swift */, - 952EB1932C6A30CC00D1E516 /* EmailViewModel.swift */, 95310FBF2C44431800023C7B /* TermsViewModel.swift */, 95310FC12C45453F00023C7B /* NicknameViewModel.swift */, 95310FC32C45A31700023C7B /* DiaryNotificationViewModel.swift */, @@ -1069,7 +1051,6 @@ 95310F762C3C97A800023C7B /* ClodyBottomButton.swift in Sources */, 0B72DA2E2C46C13E00292173 /* NotificationViewModel.swift in Sources */, 0B6C074B2C3DC3070054A0B0 /* ListCollectionViewCell.swift in Sources */, - 952EB1962C6A8DCD00D1E516 /* SignUpInfoModel.swift in Sources */, 0B4A1BC22C453FE700E31EC4 /* APIConstant.swift in Sources */, 95310FAB2C42D9EC00023C7B /* ReplyDetailViewController.swift in Sources */, 0B6C075D2C3E7D660054A0B0 /* WritingDiaryView.swift in Sources */, @@ -1096,7 +1077,6 @@ 0B4A1BD52C4546F800E31EC4 /* Config.swift in Sources */, 0BAAC6532C43D74D00174A9D /* ClodyToast.swift in Sources */, 2CE6378B2C3D546200B6EC6D /* AccountViewController.swift in Sources */, - 952EB1922C6A309600D1E516 /* EmailView.swift in Sources */, 0B4EF9D12C48C39600CAF1F6 /* PermissionManager.swift in Sources */, 95310F7C2C3DD36100023C7B /* ClodyPickerView.swift in Sources */, 95310FB72C43B07100023C7B /* ReplyWaitingViewModel.swift in Sources */, @@ -1154,12 +1134,10 @@ 0BE7E9FE2C4660E7000C5358 /* GetReplyResponseDTO.swift in Sources */, 0B6C075B2C3E7D570054A0B0 /* WritingDiaryViewController.swift in Sources */, 0BE7EA002C4664EC000C5358 /* LoginRequestDTO.swift in Sources */, - 952EB1942C6A30CC00D1E516 /* EmailViewModel.swift in Sources */, 952EB1992C6BCF7E00D1E516 /* UIViewController+.swift in Sources */, 0B6C07432C3DC2DE0054A0B0 /* ListModel.swift in Sources */, 0B17F8052C368465008A9998 /* DailyCalendarCollectionViewCell.swift in Sources */, 0B190F0D2C38111500D46CFC /* CalendarDailyModel.swift in Sources */, - 952EB1902C6A301C00D1E516 /* EmailViewController.swift in Sources */, 0BE7E9EF2C461D0F000C5358 /* NotificationPickerView.swift in Sources */, 95310FC02C44431800023C7B /* TermsViewModel.swift in Sources */, 95310F942C40776200023C7B /* OnBoardingViewController.swift in Sources */, @@ -1208,7 +1186,7 @@ /* Begin XCBuildConfiguration section */ 0B20F3EB2C2C4DAB00F8D62D /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 2CB129422C47F2CC009D1480 /* Config.xcconfig */; + baseConfigurationReference = 0BDF13BF2C7D612800BA2E7C /* Config_Dev.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; @@ -1259,6 +1237,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_KEY_CFBundleDisplayName = "클로디Dev"; IPHONEOS_DEPLOYMENT_TARGET = 15.0; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; @@ -1316,6 +1295,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; + INFOPLIST_KEY_CFBundleDisplayName = "클로디"; IPHONEOS_DEPLOYMENT_TARGET = 15.0; LOCALIZATION_PREFERS_STRING_CATALOGS = YES; MTL_ENABLE_DEBUG_INFO = NO; @@ -1352,9 +1332,12 @@ PRODUCT_BUNDLE_IDENTIFIER = com.Clody.Clody; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Debug; }; @@ -1385,9 +1368,13 @@ PRODUCT_BUNDLE_IDENTIFIER = com.Clody.Clody; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = RELEASE; SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Release; }; diff --git a/Clody_iOS/Clody_iOS.xcodeproj/xcshareddata/xcschemes/Clody_iOS.xcscheme b/Clody_iOS/Clody_iOS.xcodeproj/xcshareddata/xcschemes/Clody_iOS.xcscheme new file mode 100644 index 0000000..bb5180b --- /dev/null +++ b/Clody_iOS/Clody_iOS.xcodeproj/xcshareddata/xcschemes/Clody_iOS.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Clody_iOS/Clody_iOS.xcodeproj/xcshareddata/xcschemes/Clody_iOS_Dev.xcscheme b/Clody_iOS/Clody_iOS.xcodeproj/xcshareddata/xcschemes/Clody_iOS_Dev.xcscheme new file mode 100644 index 0000000..afa24a5 --- /dev/null +++ b/Clody_iOS/Clody_iOS.xcodeproj/xcshareddata/xcschemes/Clody_iOS_Dev.xcscheme @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Clody_iOS/Clody_iOS/Global/Literals/String.swift b/Clody_iOS/Clody_iOS/Global/Literals/String.swift index 930f9ae..2960985 100644 --- a/Clody_iOS/Clody_iOS/Global/Literals/String.swift +++ b/Clody_iOS/Clody_iOS/Global/Literals/String.swift @@ -120,6 +120,7 @@ enum I18N { static let placeHolder = "일상 속 작은 감사함을 적어보세요." static let helpMessage = "신조어, 비속어, 이모지 작성은 불가능해요" static let replyButton = "답장 확인" + static let inputLimitError = "2~50자까지 입력할 수 있어요." } enum Calendar { diff --git a/Clody_iOS/Clody_iOS/Global/Manager/UserManager.swift b/Clody_iOS/Clody_iOS/Global/Manager/UserManager.swift index 68de5b3..9d746ca 100644 --- a/Clody_iOS/Clody_iOS/Global/Manager/UserManager.swift +++ b/Clody_iOS/Clody_iOS/Global/Manager/UserManager.swift @@ -42,6 +42,11 @@ final class UserManager { set { keychain["fcmToken"] = newValue } } + var appleEmail: String? { + get { return keychain["appleEmail"] } + set { keychain["appleEmail"] = newValue } + } + var hasAccessToken: Bool { return self.accessToken != nil } var accessTokenValue: String { return self.accessToken ?? "" } var refreshTokenValue: String { return self.refreshToken ?? "" } @@ -49,6 +54,7 @@ final class UserManager { var idTokenValue: String { return self.idToken ?? "" } var platformValue: String { return self.platform ?? "" } var fcmTokenValue: String { return self.fcmToken ?? "" } + var appleEmailValue: String { return self.appleEmail ?? "" } } extension UserManager { diff --git a/Clody_iOS/Clody_iOS/Presentation/Auth/Models/SignUpInfoModel.swift b/Clody_iOS/Clody_iOS/Presentation/Auth/Models/SignUpInfoModel.swift deleted file mode 100644 index aca3352..0000000 --- a/Clody_iOS/Clody_iOS/Presentation/Auth/Models/SignUpInfoModel.swift +++ /dev/null @@ -1,14 +0,0 @@ -// -// SignUpInfoModel.swift -// Clody_iOS -// -// Created by 김나연 on 8/13/24. -// - -import Foundation - -struct SignUpInfoModel { - var platform: String - var email: String - var name: String -} diff --git a/Clody_iOS/Clody_iOS/Presentation/Auth/ViewControllers/EmailViewController.swift b/Clody_iOS/Clody_iOS/Presentation/Auth/ViewControllers/EmailViewController.swift deleted file mode 100644 index 2205e78..0000000 --- a/Clody_iOS/Clody_iOS/Presentation/Auth/ViewControllers/EmailViewController.swift +++ /dev/null @@ -1,126 +0,0 @@ -// -// EmailViewController.swift -// Clody_iOS -// -// Created by 김나연 on 8/12/24. -// - -import UIKit - -import RxCocoa -import RxSwift -import Then - -final class EmailViewController: UIViewController { - - // MARK: - Properties - - private let viewModel = EmailViewModel() - private let disposeBag = DisposeBag() - private var signUpInfo: SignUpInfoModel - - // MARK: - UI Components - - private let rootView = EmailView() - private lazy var clodyTextField = rootView.textField - - // MARK: - Life Cycles - - init(signUpInfo: SignUpInfoModel) { - self.signUpInfo = signUpInfo - super.init(nibName: nil, bundle: nil) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - - override func loadView() { - super.loadView() - - view = rootView - } - - override func viewDidLoad() { - super.viewDidLoad() - - bindViewModel() - setUI() - } -} - -// MARK: - Extensions - -private extension EmailViewController { - - func bindViewModel() { - let textFieldDidEndEditing = clodyTextField.textField.rx.controlEvent(.editingDidEnd) - .map { - guard let text = self.clodyTextField.textField.text else { return false } - return !text.isEmpty - } - .asSignal(onErrorJustReturn: false) - - let input = EmailViewModel.Input( - textFieldInputEvent: clodyTextField.textField.rx.text.orEmpty.distinctUntilChanged().asSignal(onErrorJustReturn: ""), - textFieldDidBeginEditing: clodyTextField.textField.rx.controlEvent(.editingDidBegin).asSignal(), - textFieldDidEndEditing: textFieldDidEndEditing, - nextButtonTapEvent: rootView.nextButton.rx.tap.asSignal(), - backButtonTapEvent: rootView.navigationBar.backButton.rx.tap.asSignal() - ) - let output = viewModel.transform(from: input, disposeBag: disposeBag) - - output.isTextFieldFocused - .drive(onNext: { isFocused in - self.clodyTextField.setFocusState(to: isFocused) - }) - .disposed(by: disposeBag) - - output.nextButtonIsEnabled - .bind(onNext: { isEnabled in - self.rootView.nextButton.setEnabledState(to: isEnabled) - }) - .disposed(by: disposeBag) - - output.errorMessage - .drive(onNext: { inputResult in - switch inputResult { - case .empty: - self.clodyTextField.hideErrorMessage() - output.nextButtonIsEnabled.accept(false) - case .error: - self.clodyTextField.showErrorMessage(I18N.Common.emailError) - output.nextButtonIsEnabled.accept(false) - case .normal: - self.clodyTextField.hideErrorMessage() - output.nextButtonIsEnabled.accept(true) - } - }) - .disposed(by: disposeBag) - - output.pushViewController - .drive(onNext: { - guard let email = self.clodyTextField.textField.text else { return } - self.signUpInfo.email = email - self.navigationController?.pushViewController(TermsViewController(signUpInfo: self.signUpInfo), animated: true) - }) - .disposed(by: disposeBag) - - output.popViewController - .drive(onNext: { - self.navigationController?.popViewController(animated: true) - }) - .disposed(by: disposeBag) - - rootView.rx.tapGesture() - .when(.recognized) - .subscribe(onNext: { [weak self] _ in - self?.view.endEditing(true) - }) - .disposed(by: disposeBag) - } - - func setUI() { - self.navigationController?.isNavigationBarHidden = true - } -} diff --git a/Clody_iOS/Clody_iOS/Presentation/Auth/ViewControllers/LoginViewController.swift b/Clody_iOS/Clody_iOS/Presentation/Auth/ViewControllers/LoginViewController.swift index 029571d..d72f696 100644 --- a/Clody_iOS/Clody_iOS/Presentation/Auth/ViewControllers/LoginViewController.swift +++ b/Clody_iOS/Clody_iOS/Presentation/Auth/ViewControllers/LoginViewController.swift @@ -20,11 +20,6 @@ final class LoginViewController: UIViewController { private let viewModel = LoginViewModel() private let disposeBag = DisposeBag() - private lazy var signUpInfo = SignUpInfoModel( - platform: "", - email: "", - name: "" - ) // MARK: - UI Components @@ -96,7 +91,7 @@ private extension LoginViewController { } else { if let oauthToken = oauthToken { self.viewModel.signIn(authCode: oauthToken.accessToken) { statusCode in - self.handleResultForStatus(statusCode: statusCode, platform: .kakao) + self.handleResultForStatus(statusCode: statusCode) } } } @@ -107,7 +102,7 @@ private extension LoginViewController { func signInWithApple() { let appleIDProvider = ASAuthorizationAppleIDProvider() let request = appleIDProvider.createRequest() - request.requestedScopes = [] + request.requestedScopes = [.email] let authorizationController = ASAuthorizationController(authorizationRequests: [request]) authorizationController.delegate = self @@ -115,7 +110,7 @@ private extension LoginViewController { authorizationController.performRequests() } - func handleResultForStatus(statusCode: Int, platform: LoginPlatformType) { + func handleResultForStatus(statusCode: Int) { switch statusCode { case 200: /// 로그인 성공 @@ -124,21 +119,11 @@ private extension LoginViewController { } case 404: /// 존재하지 않는 유저 - self.signUpInfo.platform = UserManager.shared.platformValue - self.handleResultForPlatform(platform: platform) + self.navigationController?.pushViewController(TermsViewController(), animated: true) default: print("😵 서버 에러 - 로그인에 실패했습니다.") } } - - func handleResultForPlatform(platform: LoginPlatformType) { - switch platform { - case .apple: - self.navigationController?.pushViewController(EmailViewController(signUpInfo: self.signUpInfo), animated: true) - case .kakao: - self.navigationController?.pushViewController(TermsViewController(signUpInfo: self.signUpInfo), animated: true) - } - } } extension LoginViewController: ASAuthorizationControllerDelegate { @@ -149,11 +134,12 @@ extension LoginViewController: ASAuthorizationControllerDelegate { else { return } print("✅ 애플 로그인 성공") + let email = credential.email guard let idToken = String(data: identityToken, encoding: .utf8) else { return } - print("💳 idToken: \(idToken)") + print("📧 email: \(email ?? "❌")\n💳 idToken: \(idToken)") - viewModel.signIn(idToken: idToken) { statusCode in - self.handleResultForStatus(statusCode: statusCode, platform: .apple) + viewModel.signIn(email: email, idToken: idToken) { statusCode in + self.handleResultForStatus(statusCode: statusCode) } } diff --git a/Clody_iOS/Clody_iOS/Presentation/Auth/ViewControllers/NicknameViewController.swift b/Clody_iOS/Clody_iOS/Presentation/Auth/ViewControllers/NicknameViewController.swift index cd3f804..5f51663 100644 --- a/Clody_iOS/Clody_iOS/Presentation/Auth/ViewControllers/NicknameViewController.swift +++ b/Clody_iOS/Clody_iOS/Presentation/Auth/ViewControllers/NicknameViewController.swift @@ -18,7 +18,6 @@ final class NicknameViewController: UIViewController { private let viewModel = NicknameViewModel() private let disposeBag = DisposeBag() private let maxLength = 10 - private var signUpInfo: SignUpInfoModel // MARK: - UI Components @@ -27,15 +26,6 @@ final class NicknameViewController: UIViewController { // MARK: - Life Cycles - init(signUpInfo: SignUpInfoModel) { - self.signUpInfo = signUpInfo - super.init(nibName: nil, bundle: nil) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - override func loadView() { super.loadView() @@ -115,8 +105,7 @@ private extension NicknameViewController { .drive(onNext: { guard let nickname = self.clodyTextField.textField.text else { return } self.showLoadingIndicator() - self.signUpInfo.name = nickname - self.signUp() + self.signUp(nickname: nickname) }) .disposed(by: disposeBag) @@ -156,8 +145,8 @@ private extension NicknameViewController { extension NicknameViewController { - func signUp() { - viewModel.signUp(signUpInfo: signUpInfo) { statusCode in + func signUp(nickname: String) { + viewModel.signUp(nickname: nickname) { statusCode in self.hideLoadingIndicator() switch statusCode { diff --git a/Clody_iOS/Clody_iOS/Presentation/Auth/ViewControllers/TermsViewController.swift b/Clody_iOS/Clody_iOS/Presentation/Auth/ViewControllers/TermsViewController.swift index 3236245..54cda3b 100644 --- a/Clody_iOS/Clody_iOS/Presentation/Auth/ViewControllers/TermsViewController.swift +++ b/Clody_iOS/Clody_iOS/Presentation/Auth/ViewControllers/TermsViewController.swift @@ -17,7 +17,6 @@ final class TermsViewController: UIViewController { private let viewModel = TermsViewModel() private let disposeBag = DisposeBag() - private var signUpInfo: SignUpInfoModel // MARK: - UI Components @@ -25,15 +24,6 @@ final class TermsViewController: UIViewController { // MARK: - Life Cycles - init(signUpInfo: SignUpInfoModel) { - self.signUpInfo = signUpInfo - super.init(nibName: nil, bundle: nil) - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } - override func loadView() { super.loadView() @@ -131,7 +121,7 @@ private extension TermsViewController { output.pushViewController .drive(onNext: { _ in - self.navigationController?.pushViewController(NicknameViewController(signUpInfo: self.signUpInfo), animated: true) + self.navigationController?.pushViewController(NicknameViewController(), animated: true) }) .disposed(by: disposeBag) diff --git a/Clody_iOS/Clody_iOS/Presentation/Auth/ViewModels/EmailViewModel.swift b/Clody_iOS/Clody_iOS/Presentation/Auth/ViewModels/EmailViewModel.swift deleted file mode 100644 index 80ae82e..0000000 --- a/Clody_iOS/Clody_iOS/Presentation/Auth/ViewModels/EmailViewModel.swift +++ /dev/null @@ -1,71 +0,0 @@ -// -// EmailViewModel.swift -// Clody_iOS -// -// Created by 김나연 on 8/12/24. -// - -import UIKit - -import RxCocoa -import RxSwift - -enum TextFieldInputResult { - case empty - case error - case normal -} - -final class EmailViewModel: ViewModelType { - - struct Input { - let textFieldInputEvent: Signal - let textFieldDidBeginEditing: Signal - let textFieldDidEndEditing: Signal - let nextButtonTapEvent: Signal - let backButtonTapEvent: Signal - } - - struct Output { - let isTextFieldFocused: Driver - let nextButtonIsEnabled = BehaviorRelay(value: false) - let errorMessage: Driver - let pushViewController: Driver - let popViewController: Driver - } - - func transform(from input: Input, disposeBag: DisposeBag) -> Output { - let isTextFieldFocused = Signal - .merge( - input.textFieldDidBeginEditing.map { true }, - input.textFieldDidEndEditing - ) - .asDriver(onErrorJustReturn: false) - - let errorMessage = input.textFieldInputEvent - .map { text in - if text.count == 0 { return TextFieldInputResult.empty } - - let emailRegEx = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}" - if let _ = text.range(of: emailRegEx, options: .regularExpression) { - return TextFieldInputResult.normal - } else { - return TextFieldInputResult.error - } - } - .asDriver(onErrorJustReturn: TextFieldInputResult.empty) - - let pushViewController = input.nextButtonTapEvent - .asDriver(onErrorJustReturn: ()) - - let popViewController = input.backButtonTapEvent - .asDriver(onErrorJustReturn: ()) - - return Output( - isTextFieldFocused: isTextFieldFocused, - errorMessage: errorMessage, - pushViewController: pushViewController, - popViewController: popViewController - ) - } -} diff --git a/Clody_iOS/Clody_iOS/Presentation/Auth/ViewModels/LoginViewModel.swift b/Clody_iOS/Clody_iOS/Presentation/Auth/ViewModels/LoginViewModel.swift index 37ebb16..aab16be 100644 --- a/Clody_iOS/Clody_iOS/Presentation/Auth/ViewModels/LoginViewModel.swift +++ b/Clody_iOS/Clody_iOS/Presentation/Auth/ViewModels/LoginViewModel.swift @@ -11,7 +11,7 @@ import KakaoSDKAuth import RxCocoa import RxSwift -enum LoginPlatformType: String { +enum LoginPlatformType: String, CaseIterable { case kakao = "kakao" case apple = "apple" } @@ -54,7 +54,8 @@ extension LoginViewModel { } /// 애플 로그인 - func signIn(idToken: String, completion: @escaping (Int) -> ()) { + func signIn(email: String?, idToken: String, completion: @escaping (Int) -> ()) { + if let email = email { UserManager.shared.appleEmail = email } UserManager.shared.platform = LoginPlatformType.apple.rawValue APIConstants.authCode = idToken diff --git a/Clody_iOS/Clody_iOS/Presentation/Auth/ViewModels/NicknameViewModel.swift b/Clody_iOS/Clody_iOS/Presentation/Auth/ViewModels/NicknameViewModel.swift index 10586a5..9e1e477 100644 --- a/Clody_iOS/Clody_iOS/Presentation/Auth/ViewModels/NicknameViewModel.swift +++ b/Clody_iOS/Clody_iOS/Presentation/Auth/ViewModels/NicknameViewModel.swift @@ -10,6 +10,12 @@ import UIKit import RxCocoa import RxSwift +enum TextFieldInputResult { + case empty + case error + case normal +} + final class NicknameViewModel: ViewModelType { struct Input { @@ -73,13 +79,16 @@ final class NicknameViewModel: ViewModelType { extension NicknameViewModel { - func signUp(signUpInfo: SignUpInfoModel, completion: @escaping (Int) -> ()) { + func signUp(nickname: String, completion: @escaping (Int) -> ()) { + let platform = UserManager.shared.platformValue + let email = (platform == LoginPlatformType.apple.rawValue ? UserManager.shared.appleEmailValue : "") + Providers.authProvider.request( target: .signUp( data: SignUpRequestDTO( - platform: signUpInfo.platform, - email: signUpInfo.email, - name: signUpInfo.name + platform: platform, + email: email, + name: nickname ) ), instance: BaseResponse.self diff --git a/Clody_iOS/Clody_iOS/Presentation/Auth/Views/EmailView.swift b/Clody_iOS/Clody_iOS/Presentation/Auth/Views/EmailView.swift deleted file mode 100644 index f63e28e..0000000 --- a/Clody_iOS/Clody_iOS/Presentation/Auth/Views/EmailView.swift +++ /dev/null @@ -1,71 +0,0 @@ -// -// EmailView.swift -// Clody_iOS -// -// Created by 김나연 on 8/12/24. -// - -import UIKit - -import SnapKit -import Then - -final class EmailView: BaseView { - - // MARK: - UI Components - - let navigationBar = ClodyNavigationBar(type: .normal) - private let introLabel = UILabel() - let textField = ClodyTextField(type: .email) - let nextButton = ClodyBottomButton(title: I18N.Common.next) - - // MARK: - Methods - - override func setStyle() { - backgroundColor = .white - - introLabel.do { - $0.textColor = .grey01 - $0.attributedText = UIFont.pretendardString( - text: I18N.Auth.emailIntro, - style: .head1, - lineHeightMultiple: 1.5 - ) - $0.numberOfLines = 0 - } - - nextButton.do { - $0.isEnabled = false - $0.backgroundColor = .lightYellow - } - } - - override func setHierarchy() { - self.addSubviews(navigationBar, introLabel, textField, nextButton) - } - - override func setLayout() { - navigationBar.snp.makeConstraints { - $0.height.equalTo(ScreenUtils.getHeight(44)) - $0.top.equalTo(safeAreaLayoutGuide) - $0.horizontalEdges.equalToSuperview() - } - - introLabel.snp.makeConstraints { - $0.top.equalTo(navigationBar.snp.bottom).offset(ScreenUtils.getHeight(ScreenUtils.getWidth(40))) - $0.leading.equalToSuperview().inset(ScreenUtils.getWidth(24)) - } - - textField.snp.makeConstraints { - $0.height.equalTo(ScreenUtils.getHeight(51)) - $0.top.equalTo(introLabel.snp.bottom).offset(ScreenUtils.getHeight(ScreenUtils.getWidth(40))) - $0.horizontalEdges.equalToSuperview().inset(ScreenUtils.getWidth(24)) - } - - nextButton.snp.makeConstraints { - $0.height.equalTo(ScreenUtils.getHeight(48)) - $0.horizontalEdges.equalToSuperview().inset(ScreenUtils.getWidth(24)) - $0.bottom.equalTo(safeAreaLayoutGuide).inset(ScreenUtils.getHeight(5)) - } - } -} diff --git a/Clody_iOS/Clody_iOS/Presentation/Auth/Views/NicknameView.swift b/Clody_iOS/Clody_iOS/Presentation/Auth/Views/NicknameView.swift index d49cb39..59ab962 100644 --- a/Clody_iOS/Clody_iOS/Presentation/Auth/Views/NicknameView.swift +++ b/Clody_iOS/Clody_iOS/Presentation/Auth/Views/NicknameView.swift @@ -16,7 +16,7 @@ final class NicknameView: BaseView { let navigationBar = ClodyNavigationBar(type: .normal) private let introLabel = UILabel() - let textField = ClodyTextField(type: .nickname) + let textField = ClodyTextField() let nextButton = ClodyBottomButton(title: I18N.Common.next) // MARK: - Methods diff --git a/Clody_iOS/Clody_iOS/Presentation/Common/Component/ClodyTextField.swift b/Clody_iOS/Clody_iOS/Presentation/Common/Component/ClodyTextField.swift index 83f9240..9fe1a4d 100644 --- a/Clody_iOS/Clody_iOS/Presentation/Common/Component/ClodyTextField.swift +++ b/Clody_iOS/Clody_iOS/Presentation/Common/Component/ClodyTextField.swift @@ -10,63 +10,18 @@ import UIKit import SnapKit import Then -enum TextFieldType { - case nickname - case email -} - -final class ClodyTextField: UIView { +final class ClodyTextField: BaseView { // MARK: - UI Components let textField = UITextField() private let underline = UIView() - - private lazy var messageLabel = UILabel() - .then { - $0.textColor = .grey04 - $0.attributedText = UIFont.pretendardString( - text: type == .nickname ? I18N.Common.nicknameCondition : "", - style: .detail1_regular - ) - } - .then { - addSubview($0) - $0.snp.makeConstraints { - $0.top.equalTo(underline.snp.bottom).offset(ScreenUtils.getHeight(6)) - } - } - - public lazy var countLabel = UILabel() - .then { - $0.textColor = .grey04 - $0.attributedText = UIFont.pretendardString(text: "\(count)", style: .detail1_medium) - } - .then { - addSubview($0) - $0.snp.makeConstraints { - $0.trailing.equalTo(charLimitLabel.snp.leading).offset(-ScreenUtils.getWidth(3)) - $0.centerY.equalTo(charLimitLabel) - } - } - - private lazy var charLimitLabel = UILabel() - .then { - $0.textColor = .grey06 - $0.attributedText = UIFont.pretendardString(text: I18N.Common.charLimit, style: .detail1_medium) - } - .then { - addSubview($0) - $0.snp.makeConstraints { - $0.top.equalTo(underline.snp.bottom).offset(ScreenUtils.getHeight(4)) - $0.trailing.equalToSuperview().inset(ScreenUtils.getWidth(2)) - } - } + private let messageLabel = UILabel() + let countLabel = UILabel() + private let charLimitLabel = UILabel() // MARK: - Properties - let type: TextFieldType - private var includedComponents: [UIView] = [] private var errorMessage: String? { didSet { messageLabel.attributedText = UIFont.pretendardString( @@ -83,47 +38,19 @@ final class ClodyTextField: UIView { } } - // MARK: - Life Cycles - - init(type: TextFieldType) { - self.type = type - super.init(frame: .zero) - - setStyle() - setHierarchy() - setLayout() - - switch type { - case .nickname: - includedComponents = [textField, underline, messageLabel, countLabel, charLimitLabel] - case .email: - includedComponents = [textField, underline] - } - } - - required init?(coder: NSCoder) { - fatalError("init(coder:) has not been implemented") - } -} - -// MARK: - Extensions - -extension ClodyTextField { - // MARK: - Methods - private func setStyle() { + override func setStyle() { textField.do { - $0.autocapitalizationType = .none + $0.autocapitalizationType = .none $0.autocorrectionType = .no $0.spellCheckingType = .no - $0.keyboardType = (type == .email) ? .emailAddress : .default $0.backgroundColor = .clear $0.font = .pretendard(.body1_medium) $0.textColor = .grey03 $0.clearButtonMode = .always $0.attributedPlaceholder = NSAttributedString( - string: type == .nickname ? I18N.Common.enterNickname : I18N.Common.enterEmail, + string: I18N.Common.enterNickname, attributes: [NSAttributedString.Key.foregroundColor : UIColor.grey05] ) } @@ -131,13 +58,31 @@ extension ClodyTextField { underline.do { $0.backgroundColor = .grey07 } + + messageLabel.do { + $0.textColor = .grey04 + $0.attributedText = UIFont.pretendardString( + text: I18N.Common.nicknameCondition, + style: .detail1_regular + ) + } + + countLabel.do { + $0.textColor = .grey04 + $0.attributedText = UIFont.pretendardString(text: "\(count)", style: .detail1_medium) + } + + charLimitLabel.do { + $0.textColor = .grey06 + $0.attributedText = UIFont.pretendardString(text: I18N.Common.charLimit, style: .detail1_medium) + } } - private func setHierarchy() { - self.addSubviews(textField, underline) + override func setHierarchy() { + self.addSubviews(textField, underline, messageLabel, countLabel, charLimitLabel) } - private func setLayout() { + override func setLayout() { textField.snp.makeConstraints { $0.top.equalToSuperview() $0.horizontalEdges.equalToSuperview() @@ -148,13 +93,30 @@ extension ClodyTextField { $0.top.equalTo(textField.snp.bottom).offset(ScreenUtils.getHeight(4)) $0.horizontalEdges.equalTo(textField.snp.horizontalEdges) } + + messageLabel.snp.makeConstraints { + $0.top.equalTo(underline.snp.bottom).offset(ScreenUtils.getHeight(3)) + $0.leading.equalToSuperview() + $0.trailing.equalTo(countLabel.snp.leading).offset(-ScreenUtils.getWidth(16)) + } + + countLabel.snp.makeConstraints { + $0.trailing.equalTo(charLimitLabel.snp.leading).offset(-ScreenUtils.getWidth(3)) + $0.centerY.equalTo(charLimitLabel) + } + + charLimitLabel.snp.makeConstraints { + $0.top.equalTo(underline.snp.bottom).offset(ScreenUtils.getHeight(2)) + $0.trailing.equalToSuperview().inset(ScreenUtils.getWidth(2)) + } } +} + +// MARK: - Extensions + +extension ClodyTextField { func showErrorMessage(_ message: String) { - if !includedComponents.contains(messageLabel) { - includedComponents.append(messageLabel) - } - messageLabel.isHidden = false errorMessage = message } @@ -162,17 +124,12 @@ extension ClodyTextField { func hideErrorMessage() { underline.backgroundColor = .mainYellow - switch type { - case .email: - messageLabel.isHidden = true - case .nickname: - messageLabel.do { - $0.textColor = .grey04 - $0.attributedText = UIFont.pretendardString( - text: I18N.Common.nicknameCondition, - style: .detail1_regular - ) - } + messageLabel.do { + $0.textColor = .grey04 + $0.attributedText = UIFont.pretendardString( + text: I18N.Common.nicknameCondition, + style: .detail1_regular + ) } } diff --git a/Clody_iOS/Clody_iOS/Presentation/MyPage/Views/ChangeNicknameBottomSheet.swift b/Clody_iOS/Clody_iOS/Presentation/MyPage/Views/ChangeNicknameBottomSheet.swift index 71f9cb1..e74e914 100644 --- a/Clody_iOS/Clody_iOS/Presentation/MyPage/Views/ChangeNicknameBottomSheet.swift +++ b/Clody_iOS/Clody_iOS/Presentation/MyPage/Views/ChangeNicknameBottomSheet.swift @@ -15,7 +15,7 @@ final class ChangeNicknameBottomSheet: BaseView { // MARK: - UI Components let navigationBar = ClodyNavigationBar(type: .bottomSheet, title: I18N.MyPage.nickNameEdit) - let clodyTextField = ClodyTextField(type: .nickname) + let clodyTextField = ClodyTextField() let doneButton = ClodyBottomButton(title: I18N.MyPage.edit) // MARK: - Methods diff --git a/Clody_iOS/Clody_iOS/Presentation/Reply/ViewControllers/ReplyWaitingViewController.swift b/Clody_iOS/Clody_iOS/Presentation/Reply/ViewControllers/ReplyWaitingViewController.swift index e6982ce..d9788c3 100644 --- a/Clody_iOS/Clody_iOS/Presentation/Reply/ViewControllers/ReplyWaitingViewController.swift +++ b/Clody_iOS/Clody_iOS/Presentation/Reply/ViewControllers/ReplyWaitingViewController.swift @@ -149,12 +149,6 @@ private extension ReplyWaitingViewController { guard let self = self else { return } hideLoadingIndicator() - // 첫 답장이라면 - if data.isFirst { - totalSeconds = secondsToWaitForFirstReply - return - } - let todayYear = Date().dateToYearMonthDay().0 let todayMonth = Date().dateToYearMonthDay().1 let todayDay = Date().dateToYearMonthDay().2 @@ -162,20 +156,20 @@ private extension ReplyWaitingViewController { if date.0 == todayYear, date.1 == todayMonth, date.2 == todayDay { - // 오늘 작성한 일기라면 + /// 오늘 작성한 일기라면 let createdTime = (data.HH * 3600) + (data.MM * 60) + data.SS - let remainingTime = (createdTime + secondsToWaitForNormalReply) - Date().currentTimeSeconds() + let totalWaitingTime = createdTime + (data.isFirst ? secondsToWaitForFirstReply : secondsToWaitForNormalReply) + let remainingTime = totalWaitingTime - Date().currentTimeSeconds() totalSeconds = (remainingTime <= 0) ? 0 : remainingTime } else if date.0 == todayYear, date.1 == todayMonth, date.2 == todayDay - 1 { - // 어제 작성한 일기라면 + /// 어제 작성한 일기라면 let calendar = Calendar.current let yesterdayDate = calendar.date(byAdding: .day, value: -1, to: Date())! - let writingTime = calendar.date(bySettingHour: data.HH, minute: data.MM, second: data.SS, of: yesterdayDate)! - let twelveHoursLater = writingTime.addingTimeInterval(Double(secondsToWaitForNormalReply)) - - let remainingTime = Int(twelveHoursLater.timeIntervalSinceNow) + let createdTime = calendar.date(bySettingHour: data.HH, minute: data.MM, second: data.SS, of: yesterdayDate)! + let totalWaitingTime = createdTime.addingTimeInterval(Double(data.isFirst ? secondsToWaitForFirstReply : secondsToWaitForNormalReply)) + let remainingTime = Int(totalWaitingTime.timeIntervalSinceNow) totalSeconds = (remainingTime <= 0) ? 0 : remainingTime } else { totalSeconds = 0 diff --git a/Clody_iOS/Clody_iOS/Presentation/WritingDiary/Cells/WritingDiaryCell.swift b/Clody_iOS/Clody_iOS/Presentation/WritingDiary/Cells/WritingDiaryCell.swift index c58caf1..55058e3 100644 --- a/Clody_iOS/Clody_iOS/Presentation/WritingDiary/Cells/WritingDiaryCell.swift +++ b/Clody_iOS/Clody_iOS/Presentation/WritingDiary/Cells/WritingDiaryCell.swift @@ -106,7 +106,7 @@ final class WritingDiaryCell: UICollectionViewCell { limeErrorLabel.do { $0.attributedText = UIFont.pretendardString( - text: "숫자 2~50자 까지 입력할 수 있어요.", + text: I18N.WritingDiary.inputLimitError, style: .detail1_medium, lineHeightMultiple: 1.5 )