diff --git a/Cartfile.resolved b/Cartfile.resolved index b20170e..f013dae 100644 --- a/Cartfile.resolved +++ b/Cartfile.resolved @@ -9,7 +9,7 @@ github "Ekhoo/Device" "3.2.1" github "ReactiveCocoa/ReactiveCocoa" "10.3.0" github "ReactiveCocoa/ReactiveSwift" "6.3.0" github "SnapKit/SnapKit" "5.0.1" -github "airbnb/lottie-ios" "4e5877425dae5c10792fc9d22d53dc6bf6824dc1" +github "airbnb/lottie-ios" "eebcf80fa5c502159f9f61cbf84551766d8e2832" github "alickbass/CodableFirebase" "0.2.2" github "ninjaprox/NVActivityIndicatorView" "4.8.0" github "raulriera/TextFieldEffects" "1.7.0" diff --git a/DDD.Attendance.xcodeproj/project.pbxproj b/DDD.Attendance.xcodeproj/project.pbxproj index ed61308..78a7b45 100644 --- a/DDD.Attendance.xcodeproj/project.pbxproj +++ b/DDD.Attendance.xcodeproj/project.pbxproj @@ -61,7 +61,7 @@ BC1014932333D9BB0096E962 /* HomeHeaderCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC1014912333D9BB0096E962 /* HomeHeaderCell.xib */; }; BC1014982333DD4E0096E962 /* WelcomeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC1014962333DD4E0096E962 /* WelcomeCell.swift */; }; BC1014992333DD4E0096E962 /* WelcomeCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = BC1014972333DD4E0096E962 /* WelcomeCell.xib */; }; - BC381F302349F4760040202D /* Firebase.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC381F2F2349F4760040202D /* Firebase.swift */; }; + BC381F302349F4760040202D /* FirebaseClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC381F2F2349F4760040202D /* FirebaseClient.swift */; }; BC3E50AE232A5E4600B56EBF /* Login.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BC3E50AD232A5E4600B56EBF /* Login.storyboard */; }; BC3E50B0232A5E5600B56EBF /* LoginViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BC3E50AF232A5E5600B56EBF /* LoginViewController.swift */; }; BC3E50B3232A5EF600B56EBF /* SignUp.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BC3E50B2232A5EF600B56EBF /* SignUp.storyboard */; }; @@ -107,6 +107,7 @@ BCE1B4102320034F001BB684 /* AttendanceListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCE1B40E2320034F001BB684 /* AttendanceListCell.swift */; }; BCE1B4112320034F001BB684 /* AttendanceListCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = BCE1B40F2320034F001BB684 /* AttendanceListCell.xib */; }; BCE1B4132320054F001BB684 /* AttendanceListModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = BCE1B4122320054F001BB684 /* AttendanceListModel.swift */; }; + E53AF1E624E4312B00E517A4 /* FirebasePath.swift in Sources */ = {isa = PBXBuildFile; fileRef = E53AF1E524E4312B00E517A4 /* FirebasePath.swift */; }; E571F75F2323F08700816C04 /* SnapKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E571F75E2323F08700816C04 /* SnapKit.framework */; }; E571F7652324024D00816C04 /* Storyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = E571F7642324024D00816C04 /* Storyboard.swift */; }; E571F767232402D200816C04 /* ReuseableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E571F766232402D200816C04 /* ReuseableView.swift */; }; @@ -118,6 +119,8 @@ E571F77723241F5300816C04 /* Account.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E571F77623241F5300816C04 /* Account.storyboard */; }; E571F77A2324345400816C04 /* UIView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = E571F7792324345400816C04 /* UIView+Extension.swift */; }; E571F77C23243D7600816C04 /* HomeTransitionCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = E571F77B23243D7600816C04 /* HomeTransitionCoordinator.swift */; }; + E5821C6524D5902F00A89DBE /* FirebaseDecoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5821C6424D5902F00A89DBE /* FirebaseDecoder.swift */; }; + E5821C6724D596E100A89DBE /* FirebaseResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5821C6624D596E100A89DBE /* FirebaseResult.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -175,7 +178,7 @@ BC1014912333D9BB0096E962 /* HomeHeaderCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = HomeHeaderCell.xib; sourceTree = ""; }; BC1014962333DD4E0096E962 /* WelcomeCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeCell.swift; sourceTree = ""; }; BC1014972333DD4E0096E962 /* WelcomeCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = WelcomeCell.xib; sourceTree = ""; }; - BC381F2F2349F4760040202D /* Firebase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Firebase.swift; sourceTree = ""; }; + BC381F2F2349F4760040202D /* FirebaseClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirebaseClient.swift; sourceTree = ""; }; BC3E50AD232A5E4600B56EBF /* Login.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Login.storyboard; sourceTree = ""; }; BC3E50AF232A5E5600B56EBF /* LoginViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginViewController.swift; sourceTree = ""; }; BC3E50B2232A5EF600B56EBF /* SignUp.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = SignUp.storyboard; sourceTree = ""; }; @@ -225,6 +228,7 @@ BCE1B40E2320034F001BB684 /* AttendanceListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttendanceListCell.swift; sourceTree = ""; }; BCE1B40F2320034F001BB684 /* AttendanceListCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AttendanceListCell.xib; sourceTree = ""; }; BCE1B4122320054F001BB684 /* AttendanceListModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttendanceListModel.swift; sourceTree = ""; }; + E53AF1E524E4312B00E517A4 /* FirebasePath.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirebasePath.swift; sourceTree = ""; }; E571F75E2323F08700816C04 /* SnapKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SnapKit.framework; path = Carthage/Build/iOS/SnapKit.framework; sourceTree = ""; }; E571F7642324024D00816C04 /* Storyboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Storyboard.swift; sourceTree = ""; }; E571F766232402D200816C04 /* ReuseableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReuseableView.swift; sourceTree = ""; }; @@ -236,6 +240,8 @@ E571F77623241F5300816C04 /* Account.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Account.storyboard; sourceTree = ""; }; E571F7792324345400816C04 /* UIView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Extension.swift"; sourceTree = ""; }; E571F77B23243D7600816C04 /* HomeTransitionCoordinator.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeTransitionCoordinator.swift; sourceTree = ""; }; + E5821C6424D5902F00A89DBE /* FirebaseDecoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirebaseDecoder.swift; sourceTree = ""; }; + E5821C6624D596E100A89DBE /* FirebaseResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FirebaseResult.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -344,8 +350,11 @@ BC381F2D2349F4600040202D /* Repository */ = { isa = PBXGroup; children = ( - BC381F2F2349F4760040202D /* Firebase.swift */, + BC381F2F2349F4760040202D /* FirebaseClient.swift */, BCDE474F2379A990009CEAC2 /* Curriculum.swift */, + E5821C6424D5902F00A89DBE /* FirebaseDecoder.swift */, + E5821C6624D596E100A89DBE /* FirebaseResult.swift */, + E53AF1E524E4312B00E517A4 /* FirebasePath.swift */, ); path = Repository; sourceTree = ""; @@ -737,16 +746,18 @@ E571F77C23243D7600816C04 /* HomeTransitionCoordinator.swift in Sources */, 4648F78324BB1AEC0084B2A2 /* MenuModel.swift in Sources */, BCE1B3ED231BE383001BB684 /* BaseDataSource.swift in Sources */, + E5821C6524D5902F00A89DBE /* FirebaseDecoder.swift in Sources */, BC3E50B5232A5F0100B56EBF /* SignUpViewController.swift in Sources */, BCE1B3F3231BE57F001BB684 /* HomeViewModel.swift in Sources */, BC10148D232E32810096E962 /* LoginTransitionCoordinator.swift in Sources */, 4654355224C1FA0300541E14 /* AttendanceStatusHeader.swift in Sources */, 46481EA3249DF80100F3DB02 /* SetAttendanceViewController.swift in Sources */, - BC381F302349F4760040202D /* Firebase.swift in Sources */, + BC381F302349F4760040202D /* FirebaseClient.swift in Sources */, E571F76E2324085C00816C04 /* InteractiveAnimator.swift in Sources */, 466B86BF24C475FE00855C94 /* NameHeaderCell.swift in Sources */, 4648F77724BB16040084B2A2 /* ManagerHomeViewController.swift in Sources */, 46AD886324A4DAFA0036B4AF /* BannerModel.swift in Sources */, + E5821C6724D596E100A89DBE /* FirebaseResult.swift in Sources */, BC3E50C8232A6D6C00B56EBF /* Optional+Extension.swift in Sources */, BCE1B3F5231BE67A001BB684 /* AccountView.swift in Sources */, 4654355424C2020000541E14 /* AttendanceStatusModel.swift in Sources */, @@ -765,6 +776,7 @@ BC3E50B0232A5E5600B56EBF /* LoginViewController.swift in Sources */, 2074DB1D2348C6BE007D7377 /* SignUpViewModel.swift in Sources */, E571F77423241D3300816C04 /* AccountViewController.swift in Sources */, + E53AF1E624E4312B00E517A4 /* FirebasePath.swift in Sources */, BC8863FA234E36FE0031C7F1 /* PosterCell.swift in Sources */, 462EF76424B9B9590009768C /* ImageScrollViewController.swift in Sources */, E571F76A2324033B00816C04 /* PresentingViewAnimator.swift in Sources */, diff --git a/DDD.Attendance/AppDelegate.swift b/DDD.Attendance/AppDelegate.swift index af84264..bc31d07 100644 --- a/DDD.Attendance/AppDelegate.swift +++ b/DDD.Attendance/AppDelegate.swift @@ -52,11 +52,12 @@ private extension AppDelegate { let userDefault = UserDefaults.standard if userDefault.value(forKey: "wasOpendApp") == nil { userDefault.set(true, forKey: "wasOpendApp") - Firebase().signOut { isSuccess in - if isSuccess { + FirebaseClient().requestSignOut { result in + switch result { + case .success(_): print("Success - Unauthenticate Firebase") - } else { - print("Failure - Unauthenticate Firebase") + case .failure(let error): + print(error) } } } diff --git a/DDD.Attendance/Common/QRCode/ScannerViewController.swift b/DDD.Attendance/Common/QRCode/ScannerViewController.swift index 8105ad4..6b72e94 100644 --- a/DDD.Attendance/Common/QRCode/ScannerViewController.swift +++ b/DDD.Attendance/Common/QRCode/ScannerViewController.swift @@ -14,7 +14,7 @@ class ScannerViewController: BaseViewController { private var captureSession: AVCaptureSession! private var previewLayer: AVCaptureVideoPreviewLayer! - private let firebase = Firebase() + private let firebase = FirebaseClient() var attendanceTimeStamp: Int64? { didSet { descriptionLabel.text = "설정한 출석체크 시간은 \n\(Date().timeStampToString(timeStamp: attendanceTimeStamp ?? 0).dateAndTimetoString())입니다." @@ -138,7 +138,7 @@ private extension ScannerViewController { guard let attendanceTimeStamp = attendanceTimeStamp else { return } let currentTimeStamp = Date().getTimeStamp() let isLate = currentTimeStamp > attendanceTimeStamp - firebase.attendance(userId: userId, isLate: isLate, timeStamp: currentTimeStamp) { [weak self] result in + firebase.requestAttendance(userId: userId, isLate: isLate, timeStamp: currentTimeStamp) { [weak self] result in self?.showResult(result: result) } } diff --git a/DDD.Attendance/Home/HomeViewController.swift b/DDD.Attendance/Home/HomeViewController.swift index 86f5a64..68ad144 100644 --- a/DDD.Attendance/Home/HomeViewController.swift +++ b/DDD.Attendance/Home/HomeViewController.swift @@ -40,11 +40,6 @@ class HomeViewController: BaseViewController { $0.separatorInset = UIEdgeInsets(top: 0, left: UIScreen.main.bounds.width, bottom: 0, right: 0) $0.dataSource = dataSource } - -// profileButton.then { -// $0.action = #selector(signOut) -// $0.target = self -// } } override func bindStyle() { @@ -143,17 +138,6 @@ private extension HomeViewController { present(viewControllerToPresent, animated: true) } } - - @objc func signOut() { - Firebase().signOut { [weak self] isSuccess in - if isSuccess { - let loginVC = LoginViewController.instantiateViewController() - UIApplication.shared.keyWindow?.rootViewController = loginVC - } else { - self?.showAlert(title: "로그아웃 실패", message: "로그아웃에 실패하였습니다.") - } - } - } } // MARK: - Reactive diff --git a/DDD.Attendance/Home/HomeViewModel.swift b/DDD.Attendance/Home/HomeViewModel.swift index d4a75dc..2ffa219 100644 --- a/DDD.Attendance/Home/HomeViewModel.swift +++ b/DDD.Attendance/Home/HomeViewModel.swift @@ -36,12 +36,12 @@ protocol HomeViewModelTypes { class HomeViewModel { - private let firebase: Firebase + private let firebase: FirebaseClient private let accountModelProperty = MutableProperty(nil) private let curriculumListProperty = MutableProperty<[Curriculum]?>(nil) private let bannerProperty = MutableProperty(nil) - init(firebase: Firebase = Firebase()) { + init(firebase: FirebaseClient = FirebaseClient()) { self.firebase = firebase } } diff --git a/DDD.Attendance/Login/LoginPopupViewController.swift b/DDD.Attendance/Login/LoginPopupViewController.swift index 997a964..aaab31a 100644 --- a/DDD.Attendance/Login/LoginPopupViewController.swift +++ b/DDD.Attendance/Login/LoginPopupViewController.swift @@ -26,18 +26,22 @@ class LoginPopupViewController: BaseViewController { override func bindViewModel() { super.bindViewModel() - loginPopupView.resultHandler = { [weak self] status in + loginPopupView.loginSuccessHandler = { [weak self] status in NVActivityIndicatorPresenter.sharedInstance.stopAnimating() switch status { case .admin: self?.moveManagerHomeViewController() case .default: self?.moveHomeViewController() - case .failure: - self?.loginFailureAction(with: "Email 또는 Password를 확인해주세요.") } } - + + loginPopupView.loginFailureHandler = { [weak self] error in + NVActivityIndicatorPresenter.sharedInstance.stopAnimating() + print(error) + self?.loginFailureAction(with: "Email 또는 Password를 확인해주세요.") + } + reactive.keyboardWillShow <~ NotificationCenter.default.reactive .keyboard(.willShow) diff --git a/DDD.Attendance/Login/View/LoginPopupView.swift b/DDD.Attendance/Login/View/LoginPopupView.swift index 1bc438f..7d00e3a 100644 --- a/DDD.Attendance/Login/View/LoginPopupView.swift +++ b/DDD.Attendance/Login/View/LoginPopupView.swift @@ -19,7 +19,8 @@ class LoginPopupView: BaseView { private let viewModel = LoginPopupViewModel() - var resultHandler: ((Firebase.LoginStatus) -> Void)? + var loginSuccessHandler: ((FirebaseClient.AccountType) -> Void)? + var loginFailureHandler: ((Error) -> Void)? override func bindData() { super.bindData() @@ -45,7 +46,9 @@ class LoginPopupView: BaseView { reactive.requestFirebaseAuth <~ viewModel.outputs.loginAccount - reactive.loginResultHandler <~ viewModel.outputs.loginResult + reactive.loginSuccessHandler <~ viewModel.outputs.loginSuccess + + reactive.loginFailureHandler <~ viewModel.outputs.loginFailure reactive.isEnabledLoginButton <~ viewModel.outputs.isValidAccount @@ -113,11 +116,17 @@ extension Reactive where Base: LoginPopupView { }) } - var loginResultHandler: BindingTarget { + var loginSuccessHandler: BindingTarget { return makeBindingTarget({ base, status in - base.resultHandler?(status) + base.loginSuccessHandler?(status) }) } + + var loginFailureHandler: BindingTarget { + return makeBindingTarget { base, error in + base.loginFailureHandler?(error) + } + } var checkValidAccount: BindingTarget<(String, String)> { return makeBindingTarget({ base, account in diff --git a/DDD.Attendance/Login/View/LoginPopupViewModel.swift b/DDD.Attendance/Login/View/LoginPopupViewModel.swift index fb637b7..8f27348 100644 --- a/DDD.Attendance/Login/View/LoginPopupViewModel.swift +++ b/DDD.Attendance/Login/View/LoginPopupViewModel.swift @@ -22,9 +22,11 @@ protocol LoginPopupViewModelOutputs { var loginAccount: Signal { get } - var loginResult: Signal { get } + var loginSuccess: Signal { get } var isValidAccount: Signal { get } + + var loginFailure: Signal { get } } protocol LoginPopupViewModelTypes { @@ -38,16 +40,17 @@ class LoginPopupViewModel { typealias Account = (String, String) - private let firebase: Firebase + private let firebase: FirebaseClient private let isValidAccountProperty = MutableProperty(nil) private let pressLoginButtonProperty = MutableProperty(nil) private let emailProperty = MutableProperty("") private let passwordProperty = MutableProperty("") - private let loginResultProperty = MutableProperty(nil) + private let accountTypeProperty = MutableProperty(nil) + private let loginFailureProperty = MutableProperty(nil) - init(firebase: Firebase = Firebase()) { + init(firebase: FirebaseClient = FirebaseClient()) { self.firebase = firebase - self.checkLoginSession() +// self.checkLoginSession() } } @@ -86,49 +89,46 @@ extension LoginPopupViewModel: LoginPopupViewModelOutputs { } } - var loginResult: Signal { - return loginResultProperty.signal.skipNil() + var loginSuccess: Signal { + return accountTypeProperty.signal.skipNil() } var isValidAccount: Signal { return emailProperty.signal .combineLatest(with: passwordProperty.signal) - .filterMap { [unowned self] (email, password) in + .compactMap { [unowned self] (email, password) in let isValid = self.validateEmail(from: email) && self.validatePassword(from: password) self.isValidAccountProperty.value = isValid return isValid } } + + var loginFailure: Signal { + return loginFailureProperty.signal.skipNil() + } } private extension LoginPopupViewModel { func loginFirebase(with email: String, _ password: String) { - firebase.login(with: email, password) { [weak self] result in - if result?.user != nil { - self?.fetchLoginStatus { status in - self?.loginResultProperty.value = status - } - } else { - self?.loginResultProperty.value = .failure + firebase.requestSignIn(with: email, password) { [weak self] result in + switch result { + case .success(_): + self?.fetchLoginStatus() + case .failure(let error): + self?.loginFailureProperty.value = error } } } - func fetchLoginStatus(completion: @escaping (Firebase.LoginStatus) -> Void) { - firebase.checkAdminAccunt { status in - completion(status) - } - } - - func checkLoginSession() { - let activityData = ActivityData(type: .pacman) - NVActivityIndicatorPresenter.sharedInstance.startAnimating(activityData) - fetchLoginStatus { [weak self] status in - if status != .failure { - self?.loginResultProperty.value = status + func fetchLoginStatus() { + firebase.verifyAccountType { [weak self] result in + switch result { + case .success(let accountType): + self?.accountTypeProperty.value = accountType + case .failure(let error): + self?.loginFailureProperty.value = error } - NVActivityIndicatorPresenter.sharedInstance.stopAnimating() } } diff --git a/DDD.Attendance/Manager/ManagerHomeViewController.swift b/DDD.Attendance/Manager/ManagerHomeViewController.swift index 946ff84..4c4de4f 100644 --- a/DDD.Attendance/Manager/ManagerHomeViewController.swift +++ b/DDD.Attendance/Manager/ManagerHomeViewController.swift @@ -89,11 +89,13 @@ extension ManagerHomeViewController: UITableViewDelegate { } private func signOut() { - Firebase().signOut { [weak self] isSuccess in - if isSuccess { + FirebaseClient().requestSignOut { [weak self] result in + switch result { + case .success(_): let loginVC = LoginViewController.instantiateViewController() UIApplication.shared.keyWindow?.rootViewController = loginVC - } else { + case .failure(let error): + print(error) self?.showAlert(title: "로그아웃 실패", message: "로그아웃에 실패하였습니다.") } } diff --git a/DDD.Attendance/Manager/SearchUsersViewModel.swift b/DDD.Attendance/Manager/SearchUsersViewModel.swift index 7fa203d..8d3b0c2 100644 --- a/DDD.Attendance/Manager/SearchUsersViewModel.swift +++ b/DDD.Attendance/Manager/SearchUsersViewModel.swift @@ -24,10 +24,10 @@ protocol SearchUsersViewModelTypes { } class SearchUsersViewModel { - private let firebase: Firebase + private let firebase: FirebaseClient private let userProperty = MutableProperty(nil) - init(firebase: Firebase = Firebase()) { + init(firebase: FirebaseClient = FirebaseClient()) { self.firebase = firebase } } @@ -40,10 +40,10 @@ extension SearchUsersViewModel: SearchUsersViewModelTypes { extension SearchUsersViewModel: SearchUsersViewModelInputs { func remoteAttendanceStatus(name userName: String) { - firebase.getUser(name: userName) { [weak self] (result: APIAttendanceResult) in + firebase.fetchUserAttendanceList(name: userName) { [weak self] result in switch result { - case .success(let result): - self?.userProperty.value = result + case .success(let value): + self?.userProperty.value = value case .failure(let error): print("사용자 검색 에러 - \(error)") } diff --git a/DDD.Attendance/Repository/Curriculum.swift b/DDD.Attendance/Repository/Curriculum.swift index cab64c6..e99508d 100644 --- a/DDD.Attendance/Repository/Curriculum.swift +++ b/DDD.Attendance/Repository/Curriculum.swift @@ -17,6 +17,4 @@ struct Curriculum: Codable { let isDone: Bool let index: Int - -// let isAttend: Bool } diff --git a/DDD.Attendance/Repository/Firebase.swift b/DDD.Attendance/Repository/FirebaseClient.swift similarity index 52% rename from DDD.Attendance/Repository/Firebase.swift rename to DDD.Attendance/Repository/FirebaseClient.swift index 94d96c6..a8c12f4 100644 --- a/DDD.Attendance/Repository/Firebase.swift +++ b/DDD.Attendance/Repository/FirebaseClient.swift @@ -11,95 +11,117 @@ import FirebaseAuth import FirebaseDatabase import FirebaseStorage -class Firebase { +class FirebaseClient { let manager: Auth private let database = Database.database().reference() - enum LoginStatus { + enum AccountType { case admin case `default` - case failure + } + + enum ErrorCode: Error { + case notFoundUser + case notExistedUid + case notExistedAccountType } init(manager: Auth = Auth.auth()) { self.manager = manager } - func login(with email: String, _ password: String, completion: @escaping (AuthDataResult?) -> Void) { + func requestSignIn(with email: String, + _ password: String, + completion: @escaping (Result) -> Void + ) { manager.signIn(withEmail: email, password: password) { value, error in - guard let value = value else { - completion(nil) + guard let user = value?.user else { + completion(.failure(ErrorCode.notFoundUser)) return } - completion(value) + completion(.success(user)) } } - func signUp(with user: UserModel, _ password: String, completion: @escaping (AuthDataResult?) -> Void) { - manager.createUser(withEmail: user.email, password: password) { value, error in - guard let value = value else { - completion(nil) + func requestSignUp(with signUpUserModel: SignUpUserModel, + completion: @escaping (Result) -> Void + ) { + manager.createUser(withEmail: signUpUserModel.email, password: signUpUserModel.password) { value, error in + guard let uid = value?.user.uid else { + completion(.failure(ErrorCode.notFoundUser)) return } -// var attendance = [String: Bool]() -// (0..<10).forEach { -// attendance.updateValue(false, forKey: "\($0)") -// } - let userData: [String: Any] = [ - "email": user.email, - "name": user.name, - "position": user.position, - "isManager": false, - "attendance": [] - ] - Database.database().reference().child("users").child(value.user.uid).setValue(userData) - completion(value) + let userModel = StoredUserModel(email: signUpUserModel.email, + name: signUpUserModel.name, + position: signUpUserModel.position, + isManager: false, + uid: uid, + attendance: [String: String]()) + completion(.success(userModel)) } } - func signOut(completion: @escaping (Bool) -> Void) { + func requestSignOut(completion: @escaping (Result) -> Void) { do { try manager.signOut() - completion(true) + completion(.success(Void())) } catch { - print(error) - completion(false) + completion(.failure(error)) } } + + func storeUserAccount(with userModel: StoredUserModel) { + /// Firebase Database에 적재하기 위해서는 NSNumber, NSString, NSDictionary, and NSArray 을 준수하여야 함 + let userData: [String: Any] = [ + "email": userModel.email, + "name": userModel.name, + "position": userModel.position, + "isManager": userModel.isManager, + "attendance": userModel.attendance + ] + database + .child(FirebasePath.users.rawValue) + .child(userModel.uid) + .setValue(userData) + } - func checkAdminAccunt(completion: @escaping (LoginStatus) -> Void) { + func verifyAccountType(completion: @escaping (Result) -> Void) { guard let uid = manager.currentUser?.uid else { - completion(LoginStatus.failure) + completion(.failure(ErrorCode.notExistedUid)) return } database - .child("users") + .child(FirebasePath.users.rawValue) .child(uid) .observeSingleEvent(of: .value, with: { snapshot in let value = snapshot.value as? NSDictionary if let isManager = value?["isManager"] as? Bool { - isManager - ? completion(LoginStatus.admin) - : completion(LoginStatus.default) + let loginStatus = isManager + ? AccountType.admin + : AccountType.default + completion(.success(loginStatus)) } else { - completion(LoginStatus.failure) + completion(.failure(ErrorCode.notExistedAccountType)) } }) { error in - print(error.localizedDescription) - completion(LoginStatus.failure) + completion(.failure(error)) } } - func attendance(userId: String, isLate: Bool, timeStamp: Int64, completion: @escaping(Bool) -> Void) { + func requestAttendance(userId: String, + isLate: Bool, + timeStamp: Int64, + completion: @escaping(Bool) -> Void + ) { let attendance: [String: String] = [ "result": isLate ? "1" : "0" ] database - .child("users") + .child(FirebasePath.users.rawValue) .child(userId) - .child("attendance") + .child(FirebasePath.attendance.rawValue) .child("\(timeStamp)") .setValue(attendance, withCompletionBlock: { error, _ in completion(error == nil) @@ -107,7 +129,7 @@ class Firebase { } func fetchCurriculumList(completion: @escaping ([Curriculum]?) -> Void) { - database.child("curriculum") + database.child(FirebasePath.curriculum.rawValue) .observeSingleEvent(of: .value) { snapshot in guard let value = snapshot.value, @@ -130,7 +152,7 @@ class Firebase { let storage = Storage.storage() let pathReference = storage.reference(withPath: "banner/banner.png") - database.child("banner") + database.child(FirebasePath.banner.rawValue) .observeSingleEvent(of: .value) { snapshot in guard let value = snapshot.value, let result = value as? [String: String] else { completion(nil) @@ -146,10 +168,12 @@ class Firebase { } } } - - func getUser(name userName: String, completion: @escaping(APIAttendanceResult) -> Void) { + + func fetchUserAttendanceList(name userName: String, + completion: @escaping(Result) -> Void + ) { database - .child("users") + .child(FirebasePath.users.rawValue) .observeSingleEvent(of: .value, with: { snapshot in guard let value = snapshot.value, @@ -163,15 +187,14 @@ class Firebase { if let targetUser = targetUser { do { - let jsonData = try JSONSerialization.data(withJSONObject: targetUser, options: .prettyPrinted) - let decoded = try JSONDecoder().decode(AttendanceStatusModel.self, from: jsonData) - completion(.success(decoded)) + let decodedValue = try FirebaseDecoder(from: targetUser) + .decode() + completion(.success(decodedValue)) } catch { - completion(.failure(.data)) + completion(.failure(error)) } } else { - print("not found user") - completion(.failure(.data)) + completion(.failure(ErrorCode.notFoundUser)) } }) } diff --git a/DDD.Attendance/Repository/FirebaseDecoder.swift b/DDD.Attendance/Repository/FirebaseDecoder.swift new file mode 100644 index 0000000..d2fa25a --- /dev/null +++ b/DDD.Attendance/Repository/FirebaseDecoder.swift @@ -0,0 +1,55 @@ +// +// FirebaseDecoder.swift +// DDD.Attendance +// +// Created by seongjun.park on 2020/08/01. +// Copyright © 2020 DDD. All rights reserved. +// + +import Foundation + +class FirebaseDecoder { + + private let object: [String: Any]? + + enum ErrorCode: Error { + case notExistData + case failSerialization + case failDecoding + } + + init(from object: [String: Any]?) { + self.object = object + } + + func decode() throws -> T { + do { + let serializationData = try serialize() + return try decode(from: serializationData) + } catch { + throw error + } + } +} + +// MARK: - Private +private extension FirebaseDecoder { + func serialize() throws -> Data { + guard let object = object else { + throw ErrorCode.notExistData + } + do { + return try JSONSerialization.data(withJSONObject: object, options: .prettyPrinted) + } catch { + throw ErrorCode.failSerialization + } + } + + func decode(from serializationData: Data) throws -> T { + do { + return try JSONDecoder().decode(T.self, from: serializationData) + } catch { + throw ErrorCode.failDecoding + } + } +} diff --git a/DDD.Attendance/Repository/FirebasePath.swift b/DDD.Attendance/Repository/FirebasePath.swift new file mode 100644 index 0000000..218a949 --- /dev/null +++ b/DDD.Attendance/Repository/FirebasePath.swift @@ -0,0 +1,16 @@ +// +// FirebasePath.swift +// DDD.Attendance +// +// Created by seongjun.park on 2020/08/12. +// Copyright © 2020 DDD. All rights reserved. +// + +import Foundation + +enum FirebasePath: String { + case users + case attendance + case curriculum + case banner +} diff --git a/DDD.Attendance/Repository/FirebaseResult.swift b/DDD.Attendance/Repository/FirebaseResult.swift new file mode 100644 index 0000000..46d8c2f --- /dev/null +++ b/DDD.Attendance/Repository/FirebaseResult.swift @@ -0,0 +1,26 @@ +// +// FirebaseResult.swift +// DDD.Attendance +// +// Created by seongjun.park on 2020/08/01. +// Copyright © 2020 DDD. All rights reserved. +// + +enum Result { + case success(Value) + case failure(Error) + + var value: Value? { + switch self { + case .success(let value): return value + case .failure: return nil + } + } + + var error: Error? { + switch self { + case .success: return nil + case .failure(let error): return error + } + } +} diff --git a/DDD.Attendance/SignUp/Model/UserModel.swift b/DDD.Attendance/SignUp/Model/UserModel.swift index 57afc00..90d0cce 100644 --- a/DDD.Attendance/SignUp/Model/UserModel.swift +++ b/DDD.Attendance/SignUp/Model/UserModel.swift @@ -6,9 +6,19 @@ // Copyright © 2019 DDD. All rights reserved. // -struct UserModel { +struct SignUpUserModel { let email: String + let password: String let name: String let position: String let isManager: Bool } + +struct StoredUserModel { + let email: String + let name: String + let position: String + let isManager: Bool + let uid: String + let attendance: [String: String] +} diff --git a/DDD.Attendance/SignUp/SignUpViewModel.swift b/DDD.Attendance/SignUp/SignUpViewModel.swift index 6404663..9723c54 100644 --- a/DDD.Attendance/SignUp/SignUpViewModel.swift +++ b/DDD.Attendance/SignUp/SignUpViewModel.swift @@ -106,9 +106,9 @@ class SignUpViewModel { let (alertSignal, alertObserver) = Signal.pipe() - private let firebase: Firebase + private let firebase: FirebaseClient - init(firebase: Firebase = Firebase()) { + init(firebase: FirebaseClient = FirebaseClient()) { self.firebase = firebase } @@ -117,21 +117,25 @@ class SignUpViewModel { } func pressSignUpButton() { - let user = UserModel(email: email.value ?? "", - name: (lastName.value ?? "") + (firstName.value ?? ""), - position: position.value.name, - isManager: false) - signUpFirebase(with: user, password.value ?? "") + let user = SignUpUserModel(email: email.value ?? "", + password: password.value ?? "", + name: (lastName.value ?? "") + (firstName.value ?? ""), + position: position.value.name, + isManager: false) + postSignUp(with: user) } } private extension SignUpViewModel { - func signUpFirebase(with user: UserModel, _ password: String) { - firebase.signUp(with: user, password) { [weak self] result in - if result?.user != nil { + func postSignUp(with signUpUserModel: SignUpUserModel) { + firebase.requestSignUp(with: signUpUserModel) { [weak self] result in + switch result { + case .success(let userModel): self?.step.value = .StepFour - } else { + self?.firebase.storeUserAccount(with: userModel) + case .failure(let error): self?.alertObserver.send(value: "서버 오류입니다. 잠시 후에 다시 시도해주세요.") + print(error) } } }