diff --git a/HappyAnding/HappyAnding.xcodeproj/project.pbxproj b/HappyAnding/HappyAnding.xcodeproj/project.pbxproj index 9d62a893..c4d97b6a 100644 --- a/HappyAnding/HappyAnding.xcodeproj/project.pbxproj +++ b/HappyAnding/HappyAnding.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ 4D061BBA2A475EE800F76835 /* ExploreShortcutViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D061BB92A475EE800F76835 /* ExploreShortcutViewModel.swift */; }; 4D3DBB88292E67E600DE8160 /* EditNicknameView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D3DBB87292E67E500DE8160 /* EditNicknameView.swift */; }; 4D3DBB962934E31A00DE8160 /* ShowProfileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D3DBB952934E31A00DE8160 /* ShowProfileView.swift */; }; + 4D5889E82AA36A52000C4849 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D5889E72AA36A52000C4849 /* AppDelegate.swift */; }; 4D61A767291E1EE8000EF531 /* NavigationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D61A766291E1EE8000EF531 /* NavigationViewModel.swift */; }; 4D6A9EFF29A36E9C00D02522 /* WrappingHStack in Frameworks */ = {isa = PBXBuildFile; productRef = 4D6A9EFE29A36E9C00D02522 /* WrappingHStack */; }; 4D6A9F0129A3A92F00D02522 /* wrappinghstack+license.txt in Resources */ = {isa = PBXBuildFile; fileRef = 4D6A9F0029A3A92E00D02522 /* wrappinghstack+license.txt */; }; @@ -19,6 +20,7 @@ 4D7D16082986BBDE008B3332 /* TextLiteral.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D7D16062986BBD7008B3332 /* TextLiteral.swift */; }; 4D93D06F2A5956E60042CBA8 /* ShowProfileViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D93D06E2A5956E60042CBA8 /* ShowProfileViewModel.swift */; }; 4D93D0752A61D0D10042CBA8 /* ReadShortcutViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4D93D0742A61D0D10042CBA8 /* ReadShortcutViewModel.swift */; }; + 4D93D0772A73C9330042CBA8 /* FirebaseMessaging in Frameworks */ = {isa = PBXBuildFile; productRef = 4D93D0762A73C9330042CBA8 /* FirebaseMessaging */; }; 4DAD635E292AB61700ABF8C1 /* UpdateShortcutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DAD635D292AB61700ABF8C1 /* UpdateShortcutView.swift */; }; 4DF15D732A4ECC7D0014F854 /* ListShortcutViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DF15D722A4ECC7D0014F854 /* ListShortcutViewModel.swift */; }; 4DF15D752A4ECE1F0014F854 /* ListCategoryShortcutViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4DF15D742A4ECE1F0014F854 /* ListCategoryShortcutViewModel.swift */; }; @@ -200,6 +202,7 @@ 4D061BB92A475EE800F76835 /* ExploreShortcutViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExploreShortcutViewModel.swift; sourceTree = ""; }; 4D3DBB87292E67E500DE8160 /* EditNicknameView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EditNicknameView.swift; sourceTree = ""; }; 4D3DBB952934E31A00DE8160 /* ShowProfileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShowProfileView.swift; sourceTree = ""; }; + 4D5889E72AA36A52000C4849 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 4D61A766291E1EE8000EF531 /* NavigationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationViewModel.swift; sourceTree = ""; }; 4D6A9F0029A3A92E00D02522 /* wrappinghstack+license.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "wrappinghstack+license.txt"; sourceTree = ""; }; 4D778A33290A53BA00C15AC4 /* UIApplication+Keyboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIApplication+Keyboard.swift"; sourceTree = ""; }; @@ -323,6 +326,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 4D93D0772A73C9330042CBA8 /* FirebaseMessaging in Frameworks */, F94B435D2907B19A00987819 /* FirebaseAnalytics in Frameworks */, F94B43632907B19A00987819 /* FirebaseFirestoreCombine-Community in Frameworks */, F94B435F2907B19A00987819 /* FirebaseAuth in Frameworks */, @@ -359,6 +363,14 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 4DB674822AF11B7900A83C0B /* Support */ = { + isa = PBXGroup; + children = ( + 4D5889E72AA36A52000C4849 /* AppDelegate.swift */, + ); + path = Support; + sourceTree = ""; + }; 8786B2DD29A7F8C8000B46A1 /* String */ = { isa = PBXGroup; children = ( @@ -475,6 +487,7 @@ 3D41EE06290A458B008BE986 /* Info.plist */, 87E99CC22901454D009B691F /* Extensions */, A39504852AD46ACB0019895E /* Support */, + 4DB674822AF11B7900A83C0B /* Support */, 87E99C9828FFF1D2009B691F /* Views */, 87E99CD729042503009B691F /* Model */, A0F822AA2910B8B900AF4448 /* ViewModel */, @@ -775,6 +788,7 @@ F94B43602907B19A00987819 /* FirebaseFirestore */, F94B43622907B19A00987819 /* FirebaseFirestoreCombine-Community */, 4D6A9EFE29A36E9C00D02522 /* WrappingHStack */, + 4D93D0762A73C9330042CBA8 /* FirebaseMessaging */, ); productName = HappyAnding; productReference = 87E99C6A28F94EA6009B691F /* HappyAnding.app */; @@ -996,6 +1010,7 @@ A39504872AD46B9B0019895E /* HapticManager.swift in Sources */, F91A72C32999160E00CA135A /* Alerter.swift in Sources */, 87E99CAD28FFF261009B691F /* ReadShortcutView.swift in Sources */, + 4D5889E82AA36A52000C4849 /* AppDelegate.swift in Sources */, A33F74AE2908D8C800B8D0D0 /* CheckBoxShortcutCell.swift in Sources */, 87E606B22910649B00C3DA13 /* SignInWithAppleView.swift in Sources */, F91F09DF29AE0B5E00E04FA0 /* GradeAlertView.swift in Sources */, @@ -1526,6 +1541,11 @@ package = 4D6A9EFD29A36E9C00D02522 /* XCRemoteSwiftPackageReference "WrappingHStack" */; productName = WrappingHStack; }; + 4D93D0762A73C9330042CBA8 /* FirebaseMessaging */ = { + isa = XCSwiftPackageProductDependency; + package = F94B435B2907B19A00987819 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseMessaging; + }; A3FC4746292A61550058BF26 /* FirebaseAnalytics */ = { isa = XCSwiftPackageProductDependency; package = F94B435B2907B19A00987819 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; diff --git a/HappyAnding/HappyAnding/Info.plist b/HappyAnding/HappyAnding/Info.plist index dea0a695..aa2b2c93 100644 --- a/HappyAnding/HappyAnding/Info.plist +++ b/HappyAnding/HappyAnding/Info.plist @@ -2,8 +2,6 @@ - Localization native development region - Korea CFBundleURLTypes @@ -18,11 +16,18 @@ + Localization native development region + Korea NSAppTransportSecurity NSAllowsArbitraryLoads + UIBackgroundModes + + fetch + remote-notification + URL Schemes shortcutsZip diff --git a/HappyAnding/HappyAnding/Support/AppDelegate.swift b/HappyAnding/HappyAnding/Support/AppDelegate.swift new file mode 100644 index 00000000..51539fcc --- /dev/null +++ b/HappyAnding/HappyAnding/Support/AppDelegate.swift @@ -0,0 +1,93 @@ +// +// AppDelegate.swift +// HappyAnding +// +// Created by kimjimin on 2023/09/02. +// + +import Firebase +import FirebaseMessaging + +class AppDelegate: NSObject, UIApplicationDelegate { + + let gcmMessageIDKey = "gcm.message_id" + + // APN 등록 및 사용자 권한 받기 + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { + + // 원격 알림 등록 + UNUserNotificationCenter.current().delegate = self + + let authOption: UNAuthorizationOptions = [.alert, .badge, .sound] + UNUserNotificationCenter.current().requestAuthorization( + options: authOption, + completionHandler: { _, _ in } + ) + + application.registerForRemoteNotifications() + + // 메시지 대리자 설정 + Messaging.messaging().delegate = self + + return true + } + + // APNs 토큰과 등록 토큰 매핑 + func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { + Messaging.messaging().apnsToken = deviceToken + } + + // 주제 메시지 수신 및 처리 + func application(_ application: UIApplication, + didReceiveRemoteNotification userInfo: [AnyHashable: Any], + fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { + + Messaging.messaging().appDidReceiveMessage(userInfo) + + completionHandler(UIBackgroundFetchResult.newData) + } +} + +// 토큰 갱신 모니터링 +extension AppDelegate: MessagingDelegate { + + func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) { + + let dataDict: [String: String] = ["token": fcmToken ?? ""] + NotificationCenter.default.post( + name: Notification.Name("FCMToken"), + object: nil, + userInfo: dataDict + ) + + // TODO: If necessary send token to application server. + + } +} + +// 알림 처리 +extension AppDelegate: UNUserNotificationCenterDelegate { + + func userNotificationCenter(_ center: UNUserNotificationCenter, + willPresent notification: UNNotification, + withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { + + let userInfo = notification.request.content.userInfo + + // 알림 수신 정보를 애널리틱스에 전달 + Messaging.messaging().appDidReceiveMessage(userInfo) + + completionHandler([[.banner, .badge, .sound]]) + } + + func userNotificationCenter(_ center: UNUserNotificationCenter, + didReceive response: UNNotificationResponse, + withCompletionHandler completionHandler: @escaping () -> Void) { + + let userInfo = response.notification.request.content.userInfo + + Messaging.messaging().appDidReceiveMessage(userInfo) + + completionHandler() + } +} diff --git a/HappyAnding/HappyAnding/Views/HappyAndingApp.swift b/HappyAnding/HappyAnding/Views/HappyAndingApp.swift index cc3daae6..94c882e5 100644 --- a/HappyAnding/HappyAnding/Views/HappyAndingApp.swift +++ b/HappyAnding/HappyAnding/Views/HappyAndingApp.swift @@ -8,8 +8,6 @@ import SwiftUI import FirebaseCore -import FirebaseFirestore - @main struct HappyAndingApp: App { @@ -30,6 +28,8 @@ struct HappyAndingApp: App { FirebaseApp.configure() } + @UIApplicationDelegateAdaptor(AppDelegate.self) var delegate + var body: some Scene { WindowGroup { if isShowingLaunchScreen {