diff --git a/BMDJ.xcodeproj/project.pbxproj b/BMDJ.xcodeproj/project.pbxproj index a55560a..749d7a9 100644 --- a/BMDJ.xcodeproj/project.pbxproj +++ b/BMDJ.xcodeproj/project.pbxproj @@ -29,10 +29,12 @@ 6554B36B2691BAC90080D3C5 /* MemoDatabaseService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6554B36A2691BAC90080D3C5 /* MemoDatabaseService.swift */; }; 6559820C264416720011D5BC /* MenuViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6559820B264416720011D5BC /* MenuViewController.swift */; }; 6559820F264418140011D5BC /* MenuViewReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6559820E264418140011D5BC /* MenuViewReactor.swift */; }; - 655A85DC26CADAF100781C17 /* FirebaseAnalytics in Frameworks */ = {isa = PBXBuildFile; productRef = 655A85DB26CADAF100781C17 /* FirebaseAnalytics */; }; - 655A85DE26CADAF100781C17 /* FirebaseCrashlytics in Frameworks */ = {isa = PBXBuildFile; productRef = 655A85DD26CADAF100781C17 /* FirebaseCrashlytics */; }; - 655A85E026CADAF100781C17 /* FirebaseMessaging in Frameworks */ = {isa = PBXBuildFile; productRef = 655A85DF26CADAF100781C17 /* FirebaseMessaging */; }; - 655A85E226CADAF100781C17 /* FirebaseStorage in Frameworks */ = {isa = PBXBuildFile; productRef = 655A85E126CADAF100781C17 /* FirebaseStorage */; }; + 655AB69826E2AC9000A7D9BC /* FirebaseAnalytics in Frameworks */ = {isa = PBXBuildFile; productRef = 655AB69726E2AC9000A7D9BC /* FirebaseAnalytics */; }; + 655AB69A26E2AC9000A7D9BC /* FirebaseCrashlytics in Frameworks */ = {isa = PBXBuildFile; productRef = 655AB69926E2AC9000A7D9BC /* FirebaseCrashlytics */; }; + 655AB69C26E2AC9000A7D9BC /* FirebaseMessaging in Frameworks */ = {isa = PBXBuildFile; productRef = 655AB69B26E2AC9000A7D9BC /* FirebaseMessaging */; }; + 655AB69E26E2AC9000A7D9BC /* FirebaseAuth in Frameworks */ = {isa = PBXBuildFile; productRef = 655AB69D26E2AC9000A7D9BC /* FirebaseAuth */; }; + 655AB6A026E2AC9000A7D9BC /* FirebaseStorage in Frameworks */ = {isa = PBXBuildFile; productRef = 655AB69F26E2AC9000A7D9BC /* FirebaseStorage */; }; + 655AB6A226E2AC9000A7D9BC /* FirebaseRemoteConfig in Frameworks */ = {isa = PBXBuildFile; productRef = 655AB6A126E2AC9000A7D9BC /* FirebaseRemoteConfig */; }; 655DC07D267DD5B9000A44A0 /* DanjiCellDelegateProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 655DC07C267DD5B9000A44A0 /* DanjiCellDelegateProxy.swift */; }; 655DC081267DFF1E000A44A0 /* RxOptional in Frameworks */ = {isa = PBXBuildFile; productRef = 655DC080267DFF1E000A44A0 /* RxOptional */; }; 6560409A2680F4FF00F9A725 /* SettingTItleHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 656040992680F4FF00F9A725 /* SettingTItleHeader.swift */; }; @@ -206,17 +208,19 @@ buildActionMask = 2147483647; files = ( 65B377D9262AE6C4000D03E7 /* RxKingfisher in Frameworks */, + 655AB69E26E2AC9000A7D9BC /* FirebaseAuth in Frameworks */, 65048A7F2656B73800B275D0 /* Lottie in Frameworks */, - 655A85E026CADAF100781C17 /* FirebaseMessaging in Frameworks */, - 655A85DC26CADAF100781C17 /* FirebaseAnalytics in Frameworks */, + 655AB69C26E2AC9000A7D9BC /* FirebaseMessaging in Frameworks */, + 655AB6A226E2AC9000A7D9BC /* FirebaseRemoteConfig in Frameworks */, + 655AB69826E2AC9000A7D9BC /* FirebaseAnalytics in Frameworks */, + 655AB6A026E2AC9000A7D9BC /* FirebaseStorage in Frameworks */, 65FD602326B070430049023F /* GoogleSignIn in Frameworks */, 655DC081267DFF1E000A44A0 /* RxOptional in Frameworks */, 65B377DD262AE729000D03E7 /* RxAlamofire in Frameworks */, 65B377E1262AE781000D03E7 /* RxRealm in Frameworks */, - 655A85E226CADAF100781C17 /* FirebaseStorage in Frameworks */, 658CC9F9263D499B000286D3 /* RxDataSources in Frameworks */, 6575A010267E61E40004ACEF /* Realm in Frameworks */, - 655A85DE26CADAF100781C17 /* FirebaseCrashlytics in Frameworks */, + 655AB69A26E2AC9000A7D9BC /* FirebaseCrashlytics in Frameworks */, 659625E2262BF51D00277B80 /* ReactorKit in Frameworks */, 6575A012267E61E40004ACEF /* RealmSwift in Frameworks */, 65B377C9262AE687000D03E7 /* SnapKit in Frameworks */, @@ -567,10 +571,12 @@ 6575A00F267E61E40004ACEF /* Realm */, 6575A011267E61E40004ACEF /* RealmSwift */, 65FD602226B070430049023F /* GoogleSignIn */, - 655A85DB26CADAF100781C17 /* FirebaseAnalytics */, - 655A85DD26CADAF100781C17 /* FirebaseCrashlytics */, - 655A85DF26CADAF100781C17 /* FirebaseMessaging */, - 655A85E126CADAF100781C17 /* FirebaseStorage */, + 655AB69726E2AC9000A7D9BC /* FirebaseAnalytics */, + 655AB69926E2AC9000A7D9BC /* FirebaseCrashlytics */, + 655AB69B26E2AC9000A7D9BC /* FirebaseMessaging */, + 655AB69D26E2AC9000A7D9BC /* FirebaseAuth */, + 655AB69F26E2AC9000A7D9BC /* FirebaseStorage */, + 655AB6A126E2AC9000A7D9BC /* FirebaseRemoteConfig */, ); productName = BMDJ; productReference = 65B377A2262AE513000D03E7 /* BMDJ.app */; @@ -610,7 +616,7 @@ 655DC07F267DFF1E000A44A0 /* XCRemoteSwiftPackageReference "RxOptional" */, 6575A00E267E61E40004ACEF /* XCRemoteSwiftPackageReference "realm-cocoa" */, 65FD602126B070430049023F /* XCRemoteSwiftPackageReference "GoogleSignIn-iOS" */, - 655A85DA26CADAF100781C17 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */, + 655AB69626E2AC9000A7D9BC /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */, ); productRefGroup = 65B377A3262AE513000D03E7 /* Products */; projectDirPath = ""; @@ -889,7 +895,7 @@ "$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)", "$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)", ); - MARKETING_VERSION = 1.0.4; + MARKETING_VERSION = 1.0.5; OTHER_LDFLAGS = "-ObjC"; PRODUCT_BUNDLE_IDENTIFIER = com.kakaotocs.danji; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -915,7 +921,7 @@ "$(TOOLCHAIN_DIR)/usr/lib/swift-5.0/$(PLATFORM_NAME)", "$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)", ); - MARKETING_VERSION = 1.0.4; + MARKETING_VERSION = 1.0.5; OTHER_LDFLAGS = "-ObjC"; PRODUCT_BUNDLE_IDENTIFIER = com.kakaotocs.danji; PRODUCT_NAME = "$(TARGET_NAME)"; @@ -956,12 +962,12 @@ minimumVersion = 3.2.3; }; }; - 655A85DA26CADAF100781C17 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = { + 655AB69626E2AC9000A7D9BC /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/firebase/firebase-ios-sdk.git"; requirement = { kind = upToNextMajorVersion; - minimumVersion = 8.5.0; + minimumVersion = 8.6.1; }; }; 655DC07F267DFF1E000A44A0 /* XCRemoteSwiftPackageReference "RxOptional" */ = { @@ -1044,26 +1050,36 @@ package = 65048A7D2656B73800B275D0 /* XCRemoteSwiftPackageReference "lottie-ios" */; productName = Lottie; }; - 655A85DB26CADAF100781C17 /* FirebaseAnalytics */ = { + 655AB69726E2AC9000A7D9BC /* FirebaseAnalytics */ = { isa = XCSwiftPackageProductDependency; - package = 655A85DA26CADAF100781C17 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + package = 655AB69626E2AC9000A7D9BC /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; productName = FirebaseAnalytics; }; - 655A85DD26CADAF100781C17 /* FirebaseCrashlytics */ = { + 655AB69926E2AC9000A7D9BC /* FirebaseCrashlytics */ = { isa = XCSwiftPackageProductDependency; - package = 655A85DA26CADAF100781C17 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + package = 655AB69626E2AC9000A7D9BC /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; productName = FirebaseCrashlytics; }; - 655A85DF26CADAF100781C17 /* FirebaseMessaging */ = { + 655AB69B26E2AC9000A7D9BC /* FirebaseMessaging */ = { isa = XCSwiftPackageProductDependency; - package = 655A85DA26CADAF100781C17 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + package = 655AB69626E2AC9000A7D9BC /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; productName = FirebaseMessaging; }; - 655A85E126CADAF100781C17 /* FirebaseStorage */ = { + 655AB69D26E2AC9000A7D9BC /* FirebaseAuth */ = { isa = XCSwiftPackageProductDependency; - package = 655A85DA26CADAF100781C17 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + package = 655AB69626E2AC9000A7D9BC /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseAuth; + }; + 655AB69F26E2AC9000A7D9BC /* FirebaseStorage */ = { + isa = XCSwiftPackageProductDependency; + package = 655AB69626E2AC9000A7D9BC /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; productName = FirebaseStorage; }; + 655AB6A126E2AC9000A7D9BC /* FirebaseRemoteConfig */ = { + isa = XCSwiftPackageProductDependency; + package = 655AB69626E2AC9000A7D9BC /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseRemoteConfig; + }; 655DC080267DFF1E000A44A0 /* RxOptional */ = { isa = XCSwiftPackageProductDependency; package = 655DC07F267DFF1E000A44A0 /* XCRemoteSwiftPackageReference "RxOptional" */; diff --git a/BMDJ/AppDelegate.swift b/BMDJ/AppDelegate.swift index 292c860..96773db 100644 --- a/BMDJ/AppDelegate.swift +++ b/BMDJ/AppDelegate.swift @@ -11,13 +11,21 @@ import Firebase @main class AppDelegate: UIResponder, UIApplicationDelegate { - + + let gcmMessageIDKey = "634NV3MK2C" var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { print(UserDefaultService.shared.token) FirebaseApp.configure() + UNUserNotificationCenter.current().delegate = self + Messaging.messaging().delegate = self + + let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound] + UNUserNotificationCenter.current().requestAuthorization(options: authOptions) { _, _ in } + application.registerForRemoteNotifications() + window = UIWindow(frame: UIScreen.main.bounds) window?.makeKeyAndVisible() @@ -39,3 +47,35 @@ class AppDelegate: UIResponder, UIApplicationDelegate { } } +extension AppDelegate: UNUserNotificationCenterDelegate { + func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { + let userInfo = notification.request.content.userInfo + + if let messageID = userInfo[gcmMessageIDKey] { + print("Message ID: \(messageID)") + } + completionHandler([]) + } + + func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { + let userInfo = response.notification.request.content.userInfo + + if let messageID = userInfo[gcmMessageIDKey] { + print("Message ID: \(messageID)") + } + + print(userInfo) + + completionHandler() + } +} + +extension AppDelegate: MessagingDelegate { + func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) { + print("Firebase registration token: \(fcmToken)") + if let fcmToken = fcmToken { + let dataDict: [String: String] = ["token": fcmToken] + NotificationCenter.default.post(name: Notification.Name("FCMToken"), object: nil, userInfo: dataDict) + } + } +} diff --git a/BMDJ/ViewController/DanjiPlantViewController.swift b/BMDJ/ViewController/DanjiPlantViewController.swift index 53d3025..58801b8 100644 --- a/BMDJ/ViewController/DanjiPlantViewController.swift +++ b/BMDJ/ViewController/DanjiPlantViewController.swift @@ -268,6 +268,8 @@ final class DanjiPlantViewController: UIViewController, View { super.viewDidLoad() setLayout() + + NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil) } // MARK: - Method @@ -531,7 +533,7 @@ final class DanjiPlantViewController: UIViewController, View { .map { Reactor.Action.plant } .bind(to: reactor.action) .disposed(by: disposeBag) - } + } private func bindState(_ reactor: DanjiPlantViewReactor) { reactor.state.asObservable().map { $0.colorSections } @@ -575,4 +577,9 @@ final class DanjiPlantViewController: UIViewController, View { .disposed(by: disposeBag) } + + @objc private func keyboardWillShow(_ notification: Notification) { + let topOffset = infoView.frame.minY + nicknameLabel.frame.minY + scrollView.setContentOffset(.init(x: 0, y: topOffset), animated: true) + } } diff --git a/BMDJ/ViewController/LoginViewController.swift b/BMDJ/ViewController/LoginViewController.swift index f69d549..642bd4f 100644 --- a/BMDJ/ViewController/LoginViewController.swift +++ b/BMDJ/ViewController/LoginViewController.swift @@ -13,6 +13,7 @@ import RxCocoa import SnapKit import GoogleSignIn +import FirebaseAuth final class LoginViewController: UIViewController, View { @@ -208,10 +209,13 @@ final class LoginViewController: UIViewController, View { GIDSignIn.sharedInstance.signIn(with: signInConfig, presenting: self) { user, error in guard error == nil else { return } print("Google User ID: \(user?.userID ?? "nil")") - if let idToken = user?.authentication.idToken { + if let idToken = user?.authentication.idToken, + let accessToken = user?.authentication.accessToken { AuthClient.shared.google(token: idToken) .subscribe(onNext: { token in print("Google: \(token)") + let credential = GoogleAuthProvider.credential(withIDToken: idToken, accessToken: accessToken) + Auth.auth().signIn(with: credential) self.loginSuccess(token: token) }) .disposed(by: self.disposeBag) @@ -271,6 +275,9 @@ extension LoginViewController: ASAuthorizationControllerDelegate { AuthClient.shared.apple(userID: token) .subscribe(onNext: { id in print("Apple: \(id)") + let nonce = self.randomNonceString() + let credential = OAuthProvider.credential(withProviderID: "apple.com", idToken: token, rawNonce: nonce) + Auth.auth().signIn(with: credential) self.loginSuccess(token: id) }) .disposed(by: disposeBag) @@ -282,4 +289,36 @@ extension LoginViewController: ASAuthorizationControllerDelegate { func authorizationController(controller: ASAuthorizationController, didCompleteWithError error: Error) { print("Apple Login Fail") } + + private func randomNonceString(length: Int = 32) -> String { + precondition(length > 0) + let charset: Array = + Array("0123456789ABCDEFGHIJKLMNOPQRSTUVXYZabcdefghijklmnopqrstuvwxyz-._") + var result = "" + var remainingLength = length + + while remainingLength > 0 { + let randoms: [UInt8] = (0 ..< 16).map { _ in + var random: UInt8 = 0 + let errorCode = SecRandomCopyBytes(kSecRandomDefault, 1, &random) + if errorCode != errSecSuccess { + fatalError("Unable to generate nonce. SecRandomCopyBytes failed with OSStatus \(errorCode)") + } + return random + } + + randoms.forEach { random in + if remainingLength == 0 { + return + } + + if random < charset.count { + result.append(charset[Int(random)]) + remainingLength -= 1 + } + } + } + + return result + } }