diff --git a/TILApp.xcodeproj/project.pbxproj b/TILApp.xcodeproj/project.pbxproj index 369d656..2f7576a 100644 --- a/TILApp.xcodeproj/project.pbxproj +++ b/TILApp.xcodeproj/project.pbxproj @@ -48,13 +48,14 @@ 33DC8E0A2ADFDAFF00580FA0 /* TestNetworkViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33DC8E092ADFDAFF00580FA0 /* TestNetworkViewController.swift */; }; 33DC8E0C2ADFFCAB00580FA0 /* Moya+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33DC8E0B2ADFFCAB00580FA0 /* Moya+Ext.swift */; }; 33DC8E0E2AE0029D00580FA0 /* TestLayoutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33DC8E0D2AE0029D00580FA0 /* TestLayoutViewController.swift */; }; - 33E1D1DF2B0133D0007DEEE8 /* WebViewWarmer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33E1D1DE2B0133D0007DEEE8 /* WebViewWarmer.swift */; }; 33E1D1D42B00F916007DEEE8 /* UIImageView+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33E1D1D32B00F916007DEEE8 /* UIImageView+Ext.swift */; }; 33E1D1D62B00FF58007DEEE8 /* File.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33E1D1D52B00FF58007DEEE8 /* File.swift */; }; 33E1D1D82B011C7C007DEEE8 /* UIColor+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33E1D1D72B011C7C007DEEE8 /* UIColor+Ext.swift */; }; 33E1D1DB2B0124CF007DEEE8 /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 33E1D1DA2B0124CF007DEEE8 /* Kingfisher */; }; 33E1D1DD2B012524007DEEE8 /* AvatarImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33E1D1DC2B012524007DEEE8 /* AvatarImageView.swift */; }; + 33E1D1DF2B0133D0007DEEE8 /* WebViewWarmer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33E1D1DE2B0133D0007DEEE8 /* WebViewWarmer.swift */; }; 33E1D1E12B016545007DEEE8 /* UIImage+Ext.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33E1D1E02B016545007DEEE8 /* UIImage+Ext.swift */; }; + 33E1D1E32B025984007DEEE8 /* AuthService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33E1D1E22B025984007DEEE8 /* AuthService.swift */; }; 9B859B2B2AE26C2600025E8B /* ProfileEditViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9B859B2A2AE26C2600025E8B /* ProfileEditViewController.swift */; }; 9BB3AD6F2AEEC99F004A25DB /* MyProfileTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BB3AD6E2AEEC99F004A25DB /* MyProfileTableViewCell.swift */; }; 9BE049B92AEB968E00A47CFC /* WebViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9BE049B82AEB968E00A47CFC /* WebViewController.swift */; }; @@ -138,12 +139,13 @@ 33DC8E092ADFDAFF00580FA0 /* TestNetworkViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestNetworkViewController.swift; sourceTree = ""; }; 33DC8E0B2ADFFCAB00580FA0 /* Moya+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Moya+Ext.swift"; sourceTree = ""; }; 33DC8E0D2AE0029D00580FA0 /* TestLayoutViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestLayoutViewController.swift; sourceTree = ""; }; - 33E1D1DE2B0133D0007DEEE8 /* WebViewWarmer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewWarmer.swift; sourceTree = ""; }; 33E1D1D32B00F916007DEEE8 /* UIImageView+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImageView+Ext.swift"; sourceTree = ""; }; 33E1D1D52B00FF58007DEEE8 /* File.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = File.swift; sourceTree = ""; }; 33E1D1D72B011C7C007DEEE8 /* UIColor+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Ext.swift"; sourceTree = ""; }; 33E1D1DC2B012524007DEEE8 /* AvatarImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AvatarImageView.swift; sourceTree = ""; }; + 33E1D1DE2B0133D0007DEEE8 /* WebViewWarmer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewWarmer.swift; sourceTree = ""; }; 33E1D1E02B016545007DEEE8 /* UIImage+Ext.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+Ext.swift"; sourceTree = ""; }; + 33E1D1E22B025984007DEEE8 /* AuthService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthService.swift; sourceTree = ""; }; 9B859B2A2AE26C2600025E8B /* ProfileEditViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileEditViewController.swift; sourceTree = ""; }; 9BB3AD6E2AEEC99F004A25DB /* MyProfileTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyProfileTableViewCell.swift; sourceTree = ""; }; 9BE049B82AEB968E00A47CFC /* WebViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebViewController.swift; sourceTree = ""; }; @@ -290,6 +292,7 @@ isa = PBXGroup; children = ( 3334E1D02AD9129600D853D7 /* APIService.swift */, + 33E1D1E22B025984007DEEE8 /* AuthService.swift */, 3373D56A2AFB7F820057A23B /* RssService.swift */, ); path = Services; @@ -594,6 +597,7 @@ 33A4E82C2AECBE3C00E3F55E /* MoreTableViewCell.swift in Sources */, D2CDB6FD2AEB677100BFD8C2 /* CustomTitleLabel.swift in Sources */, D2CDB6F52AEB66A900BFD8C2 /* CustomLargeButton.swift in Sources */, + 33E1D1E32B025984007DEEE8 /* AuthService.swift in Sources */, D24BB9952AE1104400ED7028 /* BlogEditViewController.swift in Sources */, D2CDB6F72AEB66EC00BFD8C2 /* CustomLargeBorderButton.swift in Sources */, D2BEB50B2AF8BE5200C1BF30 /* BlockedUserTableViewCell.swift in Sources */, diff --git a/TILApp/Services/AuthService.swift b/TILApp/Services/AuthService.swift new file mode 100644 index 0000000..ff61166 --- /dev/null +++ b/TILApp/Services/AuthService.swift @@ -0,0 +1,69 @@ +import AuthenticationServices + +final class AuthService: NSObject { + static let shared: AuthService = .init() + override private init() {} + + struct ApplePayload { + let username: String? + let provider: String + let providerId: String + } + + private var target: UIViewController? + private var onSuccess: ((ApplePayload) -> Void)? + private var onError: ((Error) -> Void)? + + func signInWithApple( + _ target: UIViewController, + onSuccess: @escaping (ApplePayload) -> Void, + onError: @escaping (Error) -> Void + ) { + self.target = target + self.onSuccess = onSuccess + let appleIDProvider = ASAuthorizationAppleIDProvider() + let request = appleIDProvider.createRequest() + request.requestedScopes = [.fullName] + + let authorizationController = ASAuthorizationController(authorizationRequests: [request]) + authorizationController.delegate = self + authorizationController.presentationContextProvider = self + authorizationController.performRequests() + } +} + +extension AuthService: ASAuthorizationControllerDelegate { + func authorizationController( + controller: ASAuthorizationController, + didCompleteWithAuthorization authorization: ASAuthorization + ) { + switch authorization.credential { + case let appleIDCredential as ASAuthorizationAppleIDCredential: + let userIdentifier = appleIDCredential.user + let fullName = appleIDCredential.fullName + + var username: String? + if let fullName { + username = "\(fullName.familyName ?? "") \(fullName.givenName ?? "")" + } + + onSuccess?(.init( + username: username, + provider: "APPLE", + providerId: userIdentifier + )) + default: + break + } + } + + func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) { + onError?(error) + } +} + +extension AuthService: ASAuthorizationControllerPresentationContextProviding { + func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor { + return target!.view.window! + } +} diff --git a/TILApp/Views/AuthView/SignInViewController.swift b/TILApp/Views/AuthView/SignInViewController.swift index 37149af..07ec2c8 100644 --- a/TILApp/Views/AuthView/SignInViewController.swift +++ b/TILApp/Views/AuthView/SignInViewController.swift @@ -31,7 +31,7 @@ final class SignInViewController: UIViewController { view.addSubview(authorizationButton) view.addSubview(label) view.addSubview(privacyLabel) - + let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(privacyLabelTapped)) privacyLabel.isUserInteractionEnabled = true privacyLabel.addGestureRecognizer(tapGestureRecognizer) @@ -48,63 +48,38 @@ final class SignInViewController: UIViewController { } @objc private func handleAuthorizationAppleIDButtonPress() { - let appleIDProvider = ASAuthorizationAppleIDProvider() - let request = appleIDProvider.createRequest() - request.requestedScopes = [.fullName, .email] - - let authorizationController = ASAuthorizationController(authorizationRequests: [request]) - authorizationController.delegate = self - authorizationController.presentationContextProvider = self - authorizationController.performRequests() - } - - @objc private func privacyLabelTapped() { - let webViewController = WebViewController() - webViewController.url = "https://plucky-fang-eae.notion.site/e951a2d004ac4bbdbee73ee6b8ea4d08" - webViewController.hidesBottomBarWhenPushed = true - present(webViewController, animated: true) - } -} - -extension SignInViewController: ASAuthorizationControllerDelegate { - // 로그인 성공 후 동작 - func authorizationController( - controller: ASAuthorizationController, - didCompleteWithAuthorization authorization: ASAuthorization - ) { - switch authorization.credential { - case let appleIDCredential as ASAuthorizationAppleIDCredential: - let userIdentifier = appleIDCredential.user - let fullName = appleIDCredential.fullName - - var username: String? - if fullName != nil { - username = (fullName?.familyName ?? "") + (fullName?.givenName ?? "") - } - + AuthService.shared.signInWithApple(self) { payload in AuthViewModel.shared.signIn(.init( - username: username ?? "이름없음", + username: payload.username ?? "이름없음", avatarUrl: nil, - provider: "APPLE", - providerId: userIdentifier + provider: payload.provider, + providerId: payload.providerId )) { [weak self] result in - guard let self, case .failure = result else { return } - let alert = UIAlertController(title: "로그인 실패", message: "\n다시 시도해 주세요.", preferredStyle: .alert) - alert.addAction(UIAlertAction(title: "확인", style: .default, handler: nil)) - self.present(alert, animated: true, completion: nil) + guard let self else { return } + switch result { + case .success: + break + case .failure(let error): + debugPrint(#function, error) + showAuthFailedAlert() + } } - default: - break + } onError: { [weak self] error in + debugPrint(#function, error) + self?.showAuthFailedAlert() } } - func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) { - // TODO: 로그인 실패 후 동작 + private func showAuthFailedAlert() { + let alert = UIAlertController(title: "로그인 실패", message: "\n다시 시도해 주세요.", preferredStyle: .alert) + alert.addAction(UIAlertAction(title: "확인", style: .default)) + present(alert, animated: true) } -} -extension SignInViewController: ASAuthorizationControllerPresentationContextProviding { - func presentationAnchor(for controller: ASAuthorizationController) -> ASPresentationAnchor { - return view.window! + @objc private func privacyLabelTapped() { + let webViewController = WebViewController() + webViewController.url = "https://plucky-fang-eae.notion.site/e951a2d004ac4bbdbee73ee6b8ea4d08" + webViewController.hidesBottomBarWhenPushed = true + present(webViewController, animated: true) } } diff --git a/TILApp/Views/CommunityView/CommunityViewController.swift b/TILApp/Views/CommunityView/CommunityViewController.swift index 8c19194..96b92be 100644 --- a/TILApp/Views/CommunityView/CommunityViewController.swift +++ b/TILApp/Views/CommunityView/CommunityViewController.swift @@ -41,8 +41,10 @@ final class CommunityViewController: UIViewController { communityViewModel.delegate = self communityViewModel.load { [weak self] _ in guard let self else { return } - loadingView.stopAnimating() - loadingView.isHidden = true + DispatchQueue.main.async { [weak self] in + self?.loadingView.stopAnimating() + self?.loadingView.isHidden = true + } } }