diff --git a/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj b/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj index a1b203d6..e20a920c 100644 --- a/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj +++ b/iOS-NOTTODO/iOS-NOTTODO.xcodeproj/project.pbxproj @@ -1551,7 +1551,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.1; + MARKETING_VERSION = 1.0.0; PRODUCT_BUNDLE_IDENTIFIER = "nottodo.iOS-NOTTODO"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; @@ -1589,7 +1589,7 @@ "$(inherited)", "@executable_path/Frameworks", ); - MARKETING_VERSION = 1.0.1; + MARKETING_VERSION = 1.0.0; PRODUCT_BUNDLE_IDENTIFIER = "nottodo.iOS-NOTTODO"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; diff --git a/iOS-NOTTODO/iOS-NOTTODO/Application/AppDelegate.swift b/iOS-NOTTODO/iOS-NOTTODO/Application/AppDelegate.swift index 897387f2..77f8729b 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Application/AppDelegate.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Application/AppDelegate.swift @@ -28,17 +28,14 @@ class AppDelegate: UIResponder, UIApplicationDelegate { KakaoSDK.initSDK(appKey: Bundle.main.kakaoAPIKey) FirebaseApp.configure() - if KeychainUtil.getAccessToken() != "" { - self.skipAuthView() - print("토큰 유효") - } + checkForUpdate() // 메시지 대리자 설정 Messaging.messaging().delegate = self // FCM 다시 사용 설정 Messaging.messaging().isAutoInitEnabled = true - + // device token 요청. application.registerForRemoteNotifications() @@ -83,22 +80,115 @@ func application(_ application: UIApplication, didDiscardSceneSessions sceneSess extension AppDelegate: MessagingDelegate { /// 현재 등록 토큰 가져오기. - func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) { - if let fcmToken = fcmToken { - KeychainUtil.setFcmToken(fcmToken) - } + func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) { + if let fcmToken = fcmToken { + KeychainUtil.setFcmToken(fcmToken) } + } } extension AppDelegate: UNUserNotificationCenterDelegate { - + /// foreground에서 러닝 중에 앱에 도착하는 알림을 다루는 메서드 func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { completionHandler([.list, .sound, .badge, .banner]) } - + /// 도착한 notification에 대한 유저의 반응을 다루는 메서드 func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { completionHandler() } } + +extension AppDelegate { + func checkForUpdate() { + // 앱스토어 버전 + guard let appstoreVersion = getAppstoreVersion() else { return } + + // 현재 설치된 앱의 버전 + guard let appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String else { return } + + if compareVersion(userVersion: appVersion, appstoreVersion: appstoreVersion) { + DispatchQueue.main.async { + self.showUpdateAlert() + } + } else { + if KeychainUtil.getAccessToken() != "" { + self.skipAuthView() + print("토큰 유효") + } + } + } + + /// 버전 비교하는 메서드 + func compareVersion(userVersion: String, appstoreVersion: String) -> Bool { + let userMajor = userVersion.split(separator: ".").map {Int($0)!}[0] + let appstoreMajor = appstoreVersion.split(separator: ".").map {Int($0)!}[0] + + if userMajor < appstoreMajor { + return true + } + + let userMinor = userVersion.split(separator: ".").map {Int($0)!}[1] + let appstoreMinor = appstoreVersion.split(separator: ".").map {Int($0)!}[1] + + if userMinor < appstoreMinor { + return true + } + + let userPatch = userVersion.split(separator: ".").map {Int($0)!}[2] + let appstorePatch = appstoreVersion.split(separator: ".").map {Int($0)!}[2] + + if userPatch < appstorePatch { + return true + } + + return false + } + + /// 앱스토어에 배포된 버전 가져오는 메서드 + func getAppstoreVersion() -> String? { + let appleID = Bundle.main.appleId + guard let url = URL(string: "https://itunes.apple.com/lookup?id=\(appleID)"), + let data = try? Data(contentsOf: url), + let json = try? JSONSerialization.jsonObject(with: data, options: .allowFragments) as? [String: Any], + let results = json["results"] as? [[String: Any]], + let appStoreVersion = results[0]["version"] as? String else { + return nil + } + return appStoreVersion + } + + /// 선택 업데이트 경고창 + func showUpdateAlert() { + let alertController = UIAlertController( + title: I18N.update, + message: I18N.updateAlert, + preferredStyle: .alert + ) + + let updateAction = UIAlertAction(title: I18N.update, style: .default) { _ in + // App Store로 이동 + if let appStoreURL = URL(string: "https://itunes.apple.com/app/\(Bundle.main.appleId)") { + UIApplication.shared.open(appStoreURL, options: [:], completionHandler: {_ in + if KeychainUtil.getAccessToken() != "" { + self.skipAuthView() + print("토큰 유효") + } + }) + } + } + + let cancelAction = UIAlertAction(title: I18N.later, style: .default) + + alertController.addAction(updateAction) + alertController.addAction(cancelAction) + + if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene { + if let keyWindow = windowScene.windows.first, + let rootViewController = keyWindow.rootViewController { + rootViewController.present(alertController, animated: true, completion: nil) + } + } + } +} diff --git a/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/Bundle+.swift b/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/Bundle+.swift index 89f96b1e..d752fe1d 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/Bundle+.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Global/Extensions/Bundle+.swift @@ -44,4 +44,16 @@ extension Bundle { } return value } + + var appleId: String { + guard let filePath = Bundle.main.path(forResource: "API_KEY", ofType: "plist") else { + fatalError("Could't find file 'API_KEY.plist'.") + } + let plist = NSDictionary(contentsOfFile: filePath) + + guard let value = plist?.object(forKey: "APPLE_ID") as? String else { + fatalError("Couldn't find key 'APPLE_ID' in 'API_KEY.plist'.") + } + return value + } } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Global/Literals/Strings.swift b/iOS-NOTTODO/iOS-NOTTODO/Global/Literals/Strings.swift index 934c13fe..7631d41d 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Global/Literals/Strings.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Global/Literals/Strings.swift @@ -162,6 +162,12 @@ struct I18N { static let delete = "삭제하기" static let deleteModalTitle = "삭제하시겠습니까?" static let deleteModalSubtitle = "한 번 삭제하면 되돌릴 수 없어요." + static let update = "업데이트" + static let updateAlert = """ + 최신 업데이트가 있습니다. + 업데이트하시겠습니까? + """ + static let later = "나중에" static let notiDialogTitle = """ 알림을 허용하면 낫투두가 더 잘 도울 수 있어요! diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfoAccount/ViewControllers/MyInfoAccountViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfoAccount/ViewControllers/MyInfoAccountViewController.swift index d2c1af64..5887ddce 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfoAccount/ViewControllers/MyInfoAccountViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/MyInfoAccount/ViewControllers/MyInfoAccountViewController.swift @@ -152,10 +152,7 @@ private extension MyInfoAccountViewController { nextView.modalPresentationStyle = .overFullScreen nextView.modalTransitionStyle = .crossDissolve nextView.pushToRootAction = { [weak self] in - if let window = self?.view.window?.windowScene?.keyWindow { - let rootViewController = AuthViewController() - self?.navigationController?.changeRootViewController(rootViewController) - } + self?.navigationController?.changeRootViewController(AuthViewController()) } self.present(nextView, animated: true) } diff --git a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/FifthOnboardingViewController.swift b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/FifthOnboardingViewController.swift index 01bfe566..6c89b37f 100644 --- a/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/FifthOnboardingViewController.swift +++ b/iOS-NOTTODO/iOS-NOTTODO/Presentation/Onboarding/ViewControllers/FifthOnboardingViewController.swift @@ -180,12 +180,9 @@ extension FifthOnboardingViewController { @objc private func ButtonTapped() { AmplitudeAnalyticsService.shared.send(event: AnalyticsEvent.OnboardingClick.clickOnboardingNext5) - - if let window = view.window?.windowScene?.keyWindow { - DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { - UIView.animate(withDuration: 0.01) { - SceneDelegate.shared?.changeRootViewControllerTo(AuthViewController()) - } + DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { + UIView.animate(withDuration: 0.01) { + SceneDelegate.shared?.changeRootViewControllerTo(AuthViewController()) } } }