diff --git a/SOPT-Stamp-iOS/Projects/Data/Sources/Repository/SignInRepository.swift b/SOPT-Stamp-iOS/Projects/Data/Sources/Repository/SignInRepository.swift index 737488612..bb1d08ca8 100644 --- a/SOPT-Stamp-iOS/Projects/Data/Sources/Repository/SignInRepository.swift +++ b/SOPT-Stamp-iOS/Projects/Data/Sources/Repository/SignInRepository.swift @@ -30,6 +30,7 @@ extension SignInRepository: SignInRepositoryInterface { public func requestSignIn(request: SignInRequest) -> AnyPublisher { networkService.requestSignIn(email: request.email, password: request.password).map { entity in UserDefaultKeyList.Auth.userId = entity.userId + UserDefaultKeyList.User.sentence = entity.message return entity.toDomain() }.eraseToAnyPublisher() } diff --git a/SOPT-Stamp-iOS/Projects/Data/Sources/Repository/SignUpRepository.swift b/SOPT-Stamp-iOS/Projects/Data/Sources/Repository/SignUpRepository.swift index c4212fe2f..55fe7cbd0 100644 --- a/SOPT-Stamp-iOS/Projects/Data/Sources/Repository/SignUpRepository.swift +++ b/SOPT-Stamp-iOS/Projects/Data/Sources/Repository/SignUpRepository.swift @@ -38,8 +38,11 @@ extension SignUpRepository: SignUpRepositoryInterface { } public func postSignUp(signUpRequest: SignUpModel) -> AnyPublisher { - return userService.postSignUp(nickname: signUpRequest.nickname, email: signUpRequest.email, password: signUpRequest.password).map { statusCode in - statusCode == 200 - }.eraseToAnyPublisher() + return userService.postSignUp(nickname: signUpRequest.nickname, email: signUpRequest.email, password: signUpRequest.password) + .map { entity in + guard let userId = entity?.userId else { return false } + UserDefaultKeyList.Auth.userId = userId + return true + }.eraseToAnyPublisher() } } diff --git a/SOPT-Stamp-iOS/Projects/Modules/DSKit/Sources/Components/CustomDimmerView.swift b/SOPT-Stamp-iOS/Projects/Modules/DSKit/Sources/Components/CustomDimmerView.swift index 74e4b6a65..9f606138b 100644 --- a/SOPT-Stamp-iOS/Projects/Modules/DSKit/Sources/Components/CustomDimmerView.swift +++ b/SOPT-Stamp-iOS/Projects/Modules/DSKit/Sources/Components/CustomDimmerView.swift @@ -13,7 +13,7 @@ import Core public class CustomDimmerView: UIView { // MARK: - Properties - private var vc: UIViewController? + private var view: UIView? // MARK: - UI Component private let blurEffect = UIBlurEffect(style: .light) @@ -24,7 +24,13 @@ public class CustomDimmerView: UIView { public init(_ vc: UIViewController) { super.init(frame: .zero) - self.vc = vc + self.view = vc.view + setViews() + } + + public init(_ view: UIView) { + super.init(frame: .zero) + self.view = view setViews() } @@ -36,8 +42,8 @@ public class CustomDimmerView: UIView { extension CustomDimmerView { private func setViews() { dimmerView.backgroundColor = .black.withAlphaComponent(0.55) - dimmerView.frame = self.vc?.view.frame ?? CGRect() - blurEffectView.frame = self.vc?.view.frame ?? CGRect() + dimmerView.frame = self.view?.frame ?? CGRect() + blurEffectView.frame = self.view?.frame ?? CGRect() self.addSubviews(blurEffectView, dimmerView) } } diff --git a/SOPT-Stamp-iOS/Projects/Modules/DSKit/Sources/Components/CustomLoadingView.swift b/SOPT-Stamp-iOS/Projects/Modules/DSKit/Sources/Components/CustomLoadingView.swift new file mode 100644 index 000000000..d640588de --- /dev/null +++ b/SOPT-Stamp-iOS/Projects/Modules/DSKit/Sources/Components/CustomLoadingView.swift @@ -0,0 +1,97 @@ +// +// CustomLoadingView.swift +// DSKit +// +// Created by Junho Lee on 2023/01/12. +// Copyright © 2023 SOPT-Stamp-iOS. All rights reserved. +// + +import Combine +import UIKit + +import Core + +import SnapKit + +public extension UIViewController { + var isLoading: Bool { + get { return CustomLoadingView.shared.isLoading } + set(startLoading) { + if startLoading { + CustomLoadingView.shared.show(self.view) + } else { + CustomLoadingView.shared.hide() + } + } + } + + func showLoading() { + self.isLoading = true + } + + func stopLoading() { + self.isLoading = false + } +} + +public class CustomLoadingView: UIView { + + // MARK: - Properties + + public static let shared = CustomLoadingView() + public var isLoading: Bool { + return self.activityIndicator.isAnimating + } + + // MARK: - UI Components + + lazy var activityIndicator: UIActivityIndicatorView = { + let activityIndicator = UIActivityIndicatorView() + activityIndicator.frame = CGRect(x: 0, y: 0, width: 50, height: 50) + activityIndicator.hidesWhenStopped = false + activityIndicator.style = .large + activityIndicator.tintColor = .white + return activityIndicator + }() + + // MARK: - View Life Cycle + + override init(frame: CGRect) { + super.init(frame: frame) + configureUI() + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } +} + +// MARK: - Extension + +extension CustomLoadingView { + private func configureUI() { + self.addSubview(activityIndicator) + } + + public func show(_ view: UIView) { + view.addSubview(self) + + UIView.animate(withDuration: 0.3, delay: 0.05) { + self.backgroundColor = .black.withAlphaComponent(0.55) + } + + self.snp.makeConstraints { + $0.edges.equalToSuperview() + } + self.activityIndicator.center = view.center + self.layoutIfNeeded() + self.activityIndicator.startAnimating() + } + + public func hide(completion: (() -> Void)? = nil) { + self.backgroundColor = .clear + self.activityIndicator.stopAnimating() + self.removeFromSuperview() + completion?() + } +} diff --git a/SOPT-Stamp-iOS/Projects/Modules/Network/Sources/Entity/SignUpResponse.swift b/SOPT-Stamp-iOS/Projects/Modules/Network/Sources/Entity/SignUpResponse.swift new file mode 100644 index 000000000..d98b637dc --- /dev/null +++ b/SOPT-Stamp-iOS/Projects/Modules/Network/Sources/Entity/SignUpResponse.swift @@ -0,0 +1,19 @@ +// +// SignUpResponse.swift +// Network +// +// Created by Junho Lee on 2023/01/13. +// Copyright © 2023 SOPT-Stamp-iOS. All rights reserved. +// + +import Foundation + +import Foundation + +public struct SignUpResponse: Codable{ + public let userId: Int + + init(userId: Int) { + self.userId = userId + } +} diff --git a/SOPT-Stamp-iOS/Projects/Modules/Network/Sources/Service/UserService.swift b/SOPT-Stamp-iOS/Projects/Modules/Network/Sources/Service/UserService.swift index fa80d076c..cb147b6a8 100644 --- a/SOPT-Stamp-iOS/Projects/Modules/Network/Sources/Service/UserService.swift +++ b/SOPT-Stamp-iOS/Projects/Modules/Network/Sources/Service/UserService.swift @@ -15,17 +15,16 @@ import Moya public typealias DefaultUserService = BaseService public protocol UserService { - func postSignUp(nickname: String, email: String, password: String) -> AnyPublisher + func postSignUp(nickname: String, email: String, password: String) -> AnyPublisher func requestSignIn(email: String, password: String) -> AnyPublisher } extension DefaultUserService: UserService { - public func postSignUp(nickname: String, email: String, password: String) -> AnyPublisher { - requestObjectInCombineNoResult(.signUp(nickname: nickname, + public func postSignUp(nickname: String, email: String, password: String) -> AnyPublisher { + requestObjectInCombine(.signUp(nickname: nickname, email: email, - password: password) - ) + password: password)) } public func requestSignIn(email: String, password: String) -> AnyPublisher { diff --git a/SOPT-Stamp-iOS/Projects/Presentation/Sources/MissionListScene/VC/MissionListVC.swift b/SOPT-Stamp-iOS/Projects/Presentation/Sources/MissionListScene/VC/MissionListVC.swift index b7d0cb705..53e7ddc1f 100644 --- a/SOPT-Stamp-iOS/Projects/Presentation/Sources/MissionListScene/VC/MissionListVC.swift +++ b/SOPT-Stamp-iOS/Projects/Presentation/Sources/MissionListScene/VC/MissionListVC.swift @@ -27,8 +27,8 @@ public class MissionListVC: UIViewController { } private var cancelBag = CancelBag() - private var missionTypeMenuSelected = CurrentValueSubject(.all) - private var viewWillAppear = PassthroughSubject() + private var missionTypeMenuSelected = CurrentValueSubject(.all) + private var viewWillAppear = PassthroughSubject() private let swipeHandler = PassthroughSubject() lazy var dataSource: UICollectionViewDiffableDataSource! = nil @@ -216,7 +216,7 @@ extension MissionListVC { private func bindViewModels() { let input = MissionListViewModel.Input(viewWillAppear: viewWillAppear.asDriver(), - missionTypeSelected: missionTypeMenuSelected.asDriver()) + missionTypeSelected: missionTypeMenuSelected) let output = self.viewModel.transform(from: input, cancelBag: self.cancelBag) output.$missionListModel diff --git a/SOPT-Stamp-iOS/Projects/Presentation/Sources/MissionListScene/ViewModel/MissionListViewModel.swift b/SOPT-Stamp-iOS/Projects/Presentation/Sources/MissionListScene/ViewModel/MissionListViewModel.swift index 598fd9188..9a40dc4d1 100644 --- a/SOPT-Stamp-iOS/Projects/Presentation/Sources/MissionListScene/ViewModel/MissionListViewModel.swift +++ b/SOPT-Stamp-iOS/Projects/Presentation/Sources/MissionListScene/ViewModel/MissionListViewModel.swift @@ -36,7 +36,7 @@ public class MissionListViewModel: ViewModelType { public struct Input { let viewWillAppear: Driver - let missionTypeSelected: Driver + let missionTypeSelected: CurrentValueSubject } // MARK: - Outputs @@ -61,7 +61,7 @@ extension MissionListViewModel { input.viewWillAppear .withUnretained(self) .sink { owner, _ in - owner.fetchMissionList() + owner.fetchMissionList(type: input.missionTypeSelected.value) }.store(in: cancelBag) input.missionTypeSelected @@ -74,13 +74,13 @@ extension MissionListViewModel { return output } - private func fetchMissionList() { + private func fetchMissionList(type: MissionListFetchType) { switch self.missionListsceneType { case .ranking(_, _, let userId): self.otherUserId = userId self.useCase.fetchOtherUserMissionList(type: .complete, userId: userId) default: - self.useCase.fetchMissionList(type: .all) + self.useCase.fetchMissionList(type: type) } } diff --git a/SOPT-Stamp-iOS/Projects/Presentation/Sources/SettingScene/VC/NicknameEditVC.swift b/SOPT-Stamp-iOS/Projects/Presentation/Sources/SettingScene/VC/NicknameEditVC.swift index bc07d9423..4769df1ae 100644 --- a/SOPT-Stamp-iOS/Projects/Presentation/Sources/SettingScene/VC/NicknameEditVC.swift +++ b/SOPT-Stamp-iOS/Projects/Presentation/Sources/SettingScene/VC/NicknameEditVC.swift @@ -20,6 +20,7 @@ public class NicknameEditVC: UIViewController { // MARK: - Properties public var viewModel: NicknameEditViewModel! + public var factory: ModuleFactoryInterface! private var cancelBag = CancelBag() // MARK: - UI Components diff --git a/SOPT-Stamp-iOS/Projects/Presentation/Sources/SettingScene/VC/SentenceEditVC.swift b/SOPT-Stamp-iOS/Projects/Presentation/Sources/SettingScene/VC/SentenceEditVC.swift index 2ddfc673f..2762b6e83 100644 --- a/SOPT-Stamp-iOS/Projects/Presentation/Sources/SettingScene/VC/SentenceEditVC.swift +++ b/SOPT-Stamp-iOS/Projects/Presentation/Sources/SettingScene/VC/SentenceEditVC.swift @@ -20,6 +20,7 @@ public class SentenceEditVC: UIViewController { // MARK: - Properties public var viewModel: SentenceEditViewModel! + public var factory: ModuleFactoryInterface! private var cancelBag = CancelBag() // MARK: - UI Components diff --git a/SOPT-Stamp-iOS/Projects/Presentation/Sources/SignInScene/VC/SignInVC.swift b/SOPT-Stamp-iOS/Projects/Presentation/Sources/SignInScene/VC/SignInVC.swift index 0840b93e2..da04269c7 100644 --- a/SOPT-Stamp-iOS/Projects/Presentation/Sources/SignInScene/VC/SignInVC.swift +++ b/SOPT-Stamp-iOS/Projects/Presentation/Sources/SignInScene/VC/SignInVC.swift @@ -174,9 +174,15 @@ extension SignInVC { private func bindViewModels() { - let signInButtonTapped = signInButton.publisher(for: .touchUpInside).map { _ in - SignInRequest(email: self.emailTextField.text, password: self.passwordTextField.text) - }.asDriver() + let signInButtonTapped = signInButton + .publisher(for: .touchUpInside) + .handleEvents(receiveOutput: { [weak self] _ in + guard let self = self else { return } + self.showLoading() + }) + .map { _ in + SignInRequest(email: self.emailTextField.text, password: self.passwordTextField.text) } + .asDriver() let input = SignInViewModel.Input(emailTextChanged: emailTextField.textChanged, passwordTextChanged: passwordTextField.textChanged, @@ -185,7 +191,9 @@ extension SignInVC { output.isFilledForm.assign(to: \.isEnabled, on: self.signInButton).store(in: self.cancelBag) - output.isSignInSuccess.sink { isSignInSuccess in + output.isSignInSuccess.sink { [weak self] isSignInSuccess in + guard let self = self else { return } + self.stopLoading() if isSignInSuccess { let navigation = UINavigationController(rootViewController: self.factory.makeMissionListVC(sceneType: .default)) ViewControllerUtils.setRootViewController(window: self.view.window!, viewController: navigation, withAnimation: true) diff --git a/SOPT-Stamp-iOS/Projects/SOPT-Stamp-iOS/Sources/ModuleFactory/ModuleFactory.swift b/SOPT-Stamp-iOS/Projects/SOPT-Stamp-iOS/Sources/ModuleFactory/ModuleFactory.swift index 1b154d815..9b816fd7a 100644 --- a/SOPT-Stamp-iOS/Projects/SOPT-Stamp-iOS/Sources/ModuleFactory/ModuleFactory.swift +++ b/SOPT-Stamp-iOS/Projects/SOPT-Stamp-iOS/Sources/ModuleFactory/ModuleFactory.swift @@ -137,6 +137,7 @@ extension ModuleFactory { let viewModel = NicknameEditViewModel(nicknameUseCase: signUpUseCase, editPostUseCase: settingUseCase) let nicknameEdit = NicknameEditVC() + nicknameEdit.factory = self nicknameEdit.viewModel = viewModel return nicknameEdit } @@ -147,6 +148,7 @@ extension ModuleFactory { let viewModel = SentenceEditViewModel(useCase: useCase) let sentenceEditVC = SentenceEditVC() sentenceEditVC.viewModel = viewModel + sentenceEditVC.factory = self return sentenceEditVC } diff --git a/SOPT-Stamp-iOS/Tuist/ProjectDescriptionHelpers/BaseInfoPlist.swift b/SOPT-Stamp-iOS/Tuist/ProjectDescriptionHelpers/BaseInfoPlist.swift index 17acadefb..b2f6650ac 100644 --- a/SOPT-Stamp-iOS/Tuist/ProjectDescriptionHelpers/BaseInfoPlist.swift +++ b/SOPT-Stamp-iOS/Tuist/ProjectDescriptionHelpers/BaseInfoPlist.swift @@ -3,9 +3,10 @@ import ProjectDescription public extension Project { static let baseinfoPlist: [String: InfoPlist.Value] = [ "CFBundleShortVersionString": "1.0.0", + "CFBundleDevelopmentRegion": "ko", "CFBundleVersion": "1", "CFBundleIdentifier": "com.sopt-stamp-iOS.release", - "CFBundleDisplayName": "SOPTAMP", + "CFBundleDisplayName": "SOPT", "UILaunchStoryboardName": "LaunchScreen", "UIApplicationSceneManifest": [ "UIApplicationSupportsMultipleScenes": false,