diff --git a/DDooing/DDooing.xcodeproj/project.pbxproj b/DDooing/DDooing.xcodeproj/project.pbxproj index 976f3ab..9814581 100644 --- a/DDooing/DDooing.xcodeproj/project.pbxproj +++ b/DDooing/DDooing.xcodeproj/project.pbxproj @@ -7,6 +7,15 @@ objects = { /* Begin PBXBuildFile section */ + 10DDE2562C00F07C0084E1BF /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 10DDE2552C00F07C0084E1BF /* GoogleService-Info.plist */; }; + 10DDE2572C00F07C0084E1BF /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 10DDE2552C00F07C0084E1BF /* GoogleService-Info.plist */; }; + 10DDE2582C00F07C0084E1BF /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 10DDE2552C00F07C0084E1BF /* GoogleService-Info.plist */; }; + 10DDE2592C00F07C0084E1BF /* GoogleService-Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 10DDE2552C00F07C0084E1BF /* GoogleService-Info.plist */; }; + 10DDE25B2C00F7F40084E1BF /* NotificationDataModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10DDE25A2C00F7F40084E1BF /* NotificationDataModel.swift */; }; + 10DDE25C2C00F7F40084E1BF /* NotificationDataModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10DDE25A2C00F7F40084E1BF /* NotificationDataModel.swift */; }; + 10DDE25D2C00F7F40084E1BF /* NotificationDataModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10DDE25A2C00F7F40084E1BF /* NotificationDataModel.swift */; }; + 10DDE25E2C00F7F40084E1BF /* NotificationDataModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 10DDE25A2C00F7F40084E1BF /* NotificationDataModel.swift */; }; + 10DDE2602C00F9AA0084E1BF /* FirebaseMessaging in Frameworks */ = {isa = PBXBuildFile; productRef = 10DDE25F2C00F9AA0084E1BF /* FirebaseMessaging */; }; 4F1A593D2BF8892000C3EF2C /* Pretendard-ExtraBold.otf in Resources */ = {isa = PBXBuildFile; fileRef = 4F1A59342BF8892000C3EF2C /* Pretendard-ExtraBold.otf */; }; 4F1A593E2BF8892000C3EF2C /* Pretendard-ExtraBold.otf in Resources */ = {isa = PBXBuildFile; fileRef = 4F1A59342BF8892000C3EF2C /* Pretendard-ExtraBold.otf */; }; 4F1A593F2BF8892000C3EF2C /* Pretendard-ExtraBold.otf in Resources */ = {isa = PBXBuildFile; fileRef = 4F1A59342BF8892000C3EF2C /* Pretendard-ExtraBold.otf */; }; @@ -147,6 +156,9 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 10DDE24D2C00EF2D0084E1BF /* DDooingRelease.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DDooingRelease.entitlements; sourceTree = ""; }; + 10DDE2552C00F07C0084E1BF /* GoogleService-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; + 10DDE25A2C00F7F40084E1BF /* NotificationDataModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationDataModel.swift; sourceTree = ""; }; 4F1A59342BF8892000C3EF2C /* Pretendard-ExtraBold.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Pretendard-ExtraBold.otf"; sourceTree = ""; }; 4F1A59352BF8892000C3EF2C /* Pretendard-ExtraLight.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Pretendard-ExtraLight.otf"; sourceTree = ""; }; 4F1A59362BF8892000C3EF2C /* Pretendard-Regular.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Pretendard-Regular.otf"; sourceTree = ""; }; @@ -161,7 +173,6 @@ 4F3F813B2BFDDCEC008C920C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 4F3F813D2BFDDDE2008C920C /* WidgetColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WidgetColor.swift; sourceTree = ""; }; 4F3F81532BFE63CA008C920C /* AppModelContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppModelContainer.swift; sourceTree = ""; }; - 4F8D878C2BFF84DF006BEF4A /* GoogleService-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "GoogleService-Info.plist"; sourceTree = ""; }; 4FAD25AC2BF3339A00EBD37A /* DDooing.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DDooing.app; sourceTree = BUILT_PRODUCTS_DIR; }; 4FAD25AF2BF3339A00EBD37A /* DDooingApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DDooingApp.swift; sourceTree = ""; }; 4FAD25B12BF3339A00EBD37A /* MainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainView.swift; sourceTree = ""; }; @@ -202,6 +213,7 @@ files = ( 4FD812D32BF87AB900F16ED0 /* FirebaseDatabaseSwift in Frameworks */, 4FD812D12BF87AB900F16ED0 /* FirebaseDatabase in Frameworks */, + 10DDE2602C00F9AA0084E1BF /* FirebaseMessaging in Frameworks */, 4FD812CD2BF87AB900F16ED0 /* FirebaseAnalytics in Frameworks */, 4FD812D52BF87AB900F16ED0 /* FirebaseFirestore in Frameworks */, 4FD812CF2BF87AB900F16ED0 /* FirebaseAuth in Frameworks */, @@ -282,6 +294,7 @@ children = ( 9D79E9362BF5FAD700A05D95 /* MessageModel.swift */, 4F3F81532BFE63CA008C920C /* AppModelContainer.swift */, + 10DDE25A2C00F7F40084E1BF /* NotificationDataModel.swift */, ); path = Models; sourceTree = ""; @@ -345,7 +358,7 @@ 4FAD25A32BF3339A00EBD37A = { isa = PBXGroup; children = ( - 4F8D878C2BFF84DF006BEF4A /* GoogleService-Info.plist */, + 10DDE2552C00F07C0084E1BF /* GoogleService-Info.plist */, 4FBE64CB2BFDCB80008363A0 /* DDooingWidgetExtension.entitlements */, 4FAD25AE2BF3339A00EBD37A /* DDooing */, 4FAD25BF2BF3339B00EBD37A /* DDooingTests */, @@ -371,6 +384,7 @@ 4FAD25AE2BF3339A00EBD37A /* DDooing */ = { isa = PBXGroup; children = ( + 10DDE24D2C00EF2D0084E1BF /* DDooingRelease.entitlements */, 4F1A595C2BF8896A00C3EF2C /* Info.plist */, 4FAD25B52BF3339B00EBD37A /* Preview Content */, 4F3E652B2BFB14CE00996806 /* Views */, @@ -462,6 +476,7 @@ 4FD812D22BF87AB900F16ED0 /* FirebaseDatabaseSwift */, 4FD812D42BF87AB900F16ED0 /* FirebaseFirestore */, 4FD812D62BF87AB900F16ED0 /* FirebaseFirestoreSwift */, + 10DDE25F2C00F9AA0084E1BF /* FirebaseMessaging */, ); productName = DDooing; productReference = 4FAD25AC2BF3339A00EBD37A /* DDooing.app */; @@ -585,11 +600,11 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 4F8D878D2BFF864F006BEF4A /* GoogleService-Info.plist in Resources */, 4FAD25B72BF3339B00EBD37A /* Preview Assets.xcassets in Resources */, 4F1A59492BF8892000C3EF2C /* Pretendard-Bold.otf in Resources */, 4F3F813C2BFDDCEC008C920C /* Assets.xcassets in Resources */, 4F1A59432BF8892000C3EF2C /* Pretendard-Regular.otf in Resources */, + 10DDE2562C00F07C0084E1BF /* GoogleService-Info.plist in Resources */, 4F1A59552BF8892000C3EF2C /* Pretendard-SemiBold.otf in Resources */, 4F1A594C2BF8892000C3EF2C /* Pretendard-Thin.otf in Resources */, 4F1A594F2BF8892000C3EF2C /* Pretendard-Black.otf in Resources */, @@ -613,6 +628,7 @@ 4F1A59442BF8892000C3EF2C /* Pretendard-Regular.otf in Resources */, 4F1A59562BF8892000C3EF2C /* Pretendard-SemiBold.otf in Resources */, 4F1A59502BF8892000C3EF2C /* Pretendard-Black.otf in Resources */, + 10DDE2572C00F07C0084E1BF /* GoogleService-Info.plist in Resources */, 4F1A593E2BF8892000C3EF2C /* Pretendard-ExtraBold.otf in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -629,6 +645,7 @@ 4F1A59452BF8892000C3EF2C /* Pretendard-Regular.otf in Resources */, 4F1A59572BF8892000C3EF2C /* Pretendard-SemiBold.otf in Resources */, 4F1A59512BF8892000C3EF2C /* Pretendard-Black.otf in Resources */, + 10DDE2582C00F07C0084E1BF /* GoogleService-Info.plist in Resources */, 4F1A593F2BF8892000C3EF2C /* Pretendard-ExtraBold.otf in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -648,7 +665,7 @@ 4F6D48A22C00BF54008B2A7F /* Pretendard-Black.otf in Resources */, 4F6D48AA2C00BF67008B2A7F /* Pretendard-Thin.otf in Resources */, 4F6D48AD2C00BF8D008B2A7F /* Assets.xcassets in Resources */, - 4F8D878E2BFF8650006BEF4A /* GoogleService-Info.plist in Resources */, + 4F8D878E2BFF8650006BEF4A /* GoogleService-Info.plist in Resources */ ); runOnlyForDeploymentPostprocessing = 0; }; @@ -675,6 +692,7 @@ 4FAD25B02BF3339A00EBD37A /* DDooingApp.swift in Sources */, 4F3F81542BFE63CA008C920C /* AppModelContainer.swift in Sources */, 4F1A59592BF8894600C3EF2C /* FontManager.swift in Sources */, + 10DDE25B2C00F7F40084E1BF /* NotificationDataModel.swift in Sources */, 4FF515982BF33FD70056531C /* ShowMessageView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -684,6 +702,7 @@ buildActionMask = 2147483647; files = ( 4F1A595A2BF8894600C3EF2C /* FontManager.swift in Sources */, + 10DDE25C2C00F7F40084E1BF /* NotificationDataModel.swift in Sources */, 4FAD25C12BF3339B00EBD37A /* DDooingTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -694,6 +713,7 @@ files = ( 4FAD25CB2BF3339B00EBD37A /* DDooingUITests.swift in Sources */, 4F1A595B2BF8894600C3EF2C /* FontManager.swift in Sources */, + 10DDE25D2C00F7F40084E1BF /* NotificationDataModel.swift in Sources */, 4FAD25CD2BF3339B00EBD37A /* DDooingUITestsLaunchTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -716,6 +736,7 @@ 4FBE64BC2BFDC940008363A0 /* DDooingWidgetLiveActivity.swift in Sources */, 4F6D489B2C00BEEB008B2A7F /* LoginView.swift in Sources */, 4F3F81402BFDE116008C920C /* WidgetColor.swift in Sources */, + 10DDE25E2C00F7F40084E1BF /* NotificationDataModel.swift in Sources */, 4F3F81552BFE63CA008C920C /* AppModelContainer.swift in Sources */, 4F6D48AB2C00BF88008B2A7F /* UserStatusViewModel.swift in Sources */, 4F6D489D2C00BEFC008B2A7F /* PartnerNameView.swift in Sources */, @@ -875,7 +896,7 @@ CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"DDooing/Preview Content\""; - DEVELOPMENT_TEAM = U6CCD5BT22; + DEVELOPMENT_TEAM = 3GB5WJ43G4; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = DDooing/Info.plist; @@ -889,7 +910,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.Seodongwon.DDooing; + PRODUCT_BUNDLE_IDENTIFIER = com.iOSDevJoy.DDooing; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_EMIT_LOC_STRINGS = YES; @@ -904,12 +925,12 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_ENTITLEMENTS = DDooing/DDooing.entitlements; + CODE_SIGN_ENTITLEMENTS = DDooing/DDooingRelease.entitlements; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_ASSET_PATHS = "\"DDooing/Preview Content\""; - DEVELOPMENT_TEAM = U6CCD5BT22; + DEVELOPMENT_TEAM = 3GB5WJ43G4; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = DDooing/Info.plist; @@ -923,7 +944,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.Seodongwon.DDooing; + PRODUCT_BUNDLE_IDENTIFIER = com.iOSDevJoy.DDooing; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SWIFT_EMIT_LOC_STRINGS = YES; @@ -939,11 +960,11 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = U6CCD5BT22; + DEVELOPMENT_TEAM = 3GB5WJ43G4; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 17.4; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.Seodongwon.DDooingTests; + PRODUCT_BUNDLE_IDENTIFIER = com.iOSDevJoy.DDooingTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; @@ -959,11 +980,11 @@ BUNDLE_LOADER = "$(TEST_HOST)"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = U6CCD5BT22; + DEVELOPMENT_TEAM = 3GB5WJ43G4; GENERATE_INFOPLIST_FILE = YES; IPHONEOS_DEPLOYMENT_TARGET = 17.4; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.Seodongwon.DDooingTests; + PRODUCT_BUNDLE_IDENTIFIER = com.iOSDevJoy.DDooingTests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; @@ -978,10 +999,10 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = U6CCD5BT22; + DEVELOPMENT_TEAM = 3GB5WJ43G4; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.Seodongwon.DDooingUITests; + PRODUCT_BUNDLE_IDENTIFIER = com.iOSDevJoy.DDooingUITests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; @@ -996,10 +1017,10 @@ ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = U6CCD5BT22; + DEVELOPMENT_TEAM = 3GB5WJ43G4; GENERATE_INFOPLIST_FILE = YES; MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.Seodongwon.DDooingUITests; + PRODUCT_BUNDLE_IDENTIFIER = com.iOSDevJoy.DDooingUITests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_EMIT_LOC_STRINGS = NO; SWIFT_VERSION = 5.0; @@ -1017,7 +1038,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = U6CCD5BT22; + DEVELOPMENT_TEAM = 3GB5WJ43G4; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = DDooingWidget/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = DDooingWidget; @@ -1028,7 +1049,7 @@ "@executable_path/../../Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.Seodongwon.DDooing.DDooingWidget; + PRODUCT_BUNDLE_IDENTIFIER = com.iOSDevJoy.DDooing.DDooingWidget; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; @@ -1046,7 +1067,7 @@ CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; - DEVELOPMENT_TEAM = U6CCD5BT22; + DEVELOPMENT_TEAM = 3GB5WJ43G4; GENERATE_INFOPLIST_FILE = YES; INFOPLIST_FILE = DDooingWidget/Info.plist; INFOPLIST_KEY_CFBundleDisplayName = DDooingWidget; @@ -1057,7 +1078,7 @@ "@executable_path/../../Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.Seodongwon.DDooing.DDooingWidget; + PRODUCT_BUNDLE_IDENTIFIER = com.iOSDevJoy.DDooing.DDooingWidget; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_EMIT_LOC_STRINGS = YES; @@ -1128,6 +1149,11 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ + 10DDE25F2C00F9AA0084E1BF /* FirebaseMessaging */ = { + isa = XCSwiftPackageProductDependency; + package = 4FD812C72BF8798A00F16ED0 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; + productName = FirebaseMessaging; + }; 4FBE64D12BFDD572008363A0 /* FirebaseAnalytics */ = { isa = XCSwiftPackageProductDependency; package = 4FD812C72BF8798A00F16ED0 /* XCRemoteSwiftPackageReference "firebase-ios-sdk" */; diff --git a/DDooing/DDooing/DDooing.entitlements b/DDooing/DDooing/DDooing.entitlements index cd16938..544e831 100644 --- a/DDooing/DDooing/DDooing.entitlements +++ b/DDooing/DDooing/DDooing.entitlements @@ -2,13 +2,15 @@ + aps-environment + development com.apple.developer.applesignin Default com.apple.security.application-groups - group.com.Seodongwon.DDooing + group.com.iOSDevJoy.DDooing diff --git a/DDooing/DDooing/DDooingRelease.entitlements b/DDooing/DDooing/DDooingRelease.entitlements new file mode 100644 index 0000000..544e831 --- /dev/null +++ b/DDooing/DDooing/DDooingRelease.entitlements @@ -0,0 +1,16 @@ + + + + + aps-environment + development + com.apple.developer.applesignin + + Default + + com.apple.security.application-groups + + group.com.iOSDevJoy.DDooing + + + diff --git a/DDooing/DDooing/Info.plist b/DDooing/DDooing/Info.plist index cf8c929..3655ad9 100644 --- a/DDooing/DDooing/Info.plist +++ b/DDooing/DDooing/Info.plist @@ -2,6 +2,21 @@ + FirebaseAppDelegateProxyEnabled + + NSAppTransportSecurity + + NSExceptionDomains + + 114.70.193.152 + + NSExceptionAllowsInsecureHTTPLoads + + NSIncludesSubdomains + + + + UIAppFonts Pretendard-Black.otf @@ -14,5 +29,11 @@ Pretendard-SemiBold.otf Pretendard-Thin.otf + UIBackgroundModes + + fetch + processing + remote-notification + diff --git a/DDooing/DDooing/Models/NotificationDataModel.swift b/DDooing/DDooing/Models/NotificationDataModel.swift new file mode 100644 index 0000000..b8f6f9e --- /dev/null +++ b/DDooing/DDooing/Models/NotificationDataModel.swift @@ -0,0 +1,22 @@ +// +// NotificationData.swift +// DDooing +// +// Created by kimjihee on 5/25/24. +// + +import SwiftData +import SwiftUI + +@Model +final class NotificationDataModel { + @Attribute(.unique) var id: UUID + var body: String + var title: String + + init(body: String, title: String) { + self.id = UUID() + self.body = body + self.title = title + } +} diff --git a/DDooing/DDooing/Views/Auth/RandomCodeView.swift b/DDooing/DDooing/Views/Auth/RandomCodeView.swift index f4dbe88..9c8549d 100644 --- a/DDooing/DDooing/Views/Auth/RandomCodeView.swift +++ b/DDooing/DDooing/Views/Auth/RandomCodeView.swift @@ -13,6 +13,8 @@ struct RandomCodeView: View { @State private var isConnectionMode = true @State private var code = "" @State private var randomCode = "" + @AppStorage("userDeviceToken") private var userDeviceToken: String = "" + var body: some View { NavigationStack{ @@ -88,6 +90,7 @@ struct RandomCodeView: View { sendRandomCodeToFirebase(for: user, with: randomCode) viewModel.observeUserConnectionStatus(userId: user.uid) } + print("Device Token in HomeView: \(userDeviceToken)") } .navigationBarBackButtonHidden(true) } @@ -95,25 +98,26 @@ struct RandomCodeView: View { let lettersAndDigits = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" return String((0.. Bool { + FirebaseApp.configure() + + // Setting cloud messaging + Messaging.messaging().delegate = self + + // Setting notifications + UNUserNotificationCenter.current().delegate = self + + let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound] + UNUserNotificationCenter.current().requestAuthorization( + options: authOptions, + completionHandler: { _, _ in } + ) + + application.registerForRemoteNotifications() + return true } + + func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], + fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { + + // Do something with message data here. + + UIImpactFeedbackGenerator(style: .medium).impactOccurred() + + if let messageID = userInfo[gcmMessageIDKey] { + print("Message ID: \(messageID)") + } + + // Print full message. + print(userInfo) + + completionHandler(UIBackgroundFetchResult.newData) + } + + // In order to receive notifications you need implement these methods. + func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: any Error) { + print(error.localizedDescription) + } + + func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { + Messaging.messaging().apnsToken = deviceToken + print("----- \(deviceToken)") + } + + +} + +// Cloud messaging +extension AppDelegate: MessagingDelegate { + func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) { + print("Firebase register action token:") + + // Store this token to firebase and retrieve when to send message to someone. + let dataDict: [String: String] = ["token": fcmToken ?? ""] + + if let user = Auth.auth().currentUser { + setUsersFCMToken(token: fcmToken!, userAUID: user.uid) + UserDefaults.standard.set(fcmToken, forKey: "userDeviceToken") + } + + // Store token in firestore for sending notifications from server in future + print(dataDict) + } + + private func setUsersFCMToken(token : String, userAUID: String) { + let db = Firestore.firestore() + + db.collection("Users").document(userAUID).updateData([ + "deviceToken": token + ]) { err in + if let err = err { + print("Error updating document: \(err)") + } else { + print("토큰 저장 성공 : \(token)") + } + } + } + +} + +// User notifications (InApp Notifications) +extension AppDelegate: UNUserNotificationCenterDelegate { + func userNotificationCenter(_ center: UNUserNotificationCenter, + willPresent notification: UNNotification, + withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { + let userInfo = notification.request.content.userInfo + + // Haptics + // UIImpactFeedbackGenerator(style: .medium).impactOccurred() + + // Do something with message data. + print("User Info: \(userInfo)") + if let aps = userInfo["aps"] as? [String: Any], + let alert = aps["alert"] as? [String: String], + let body = alert["body"], + let title = alert["title"] { + // body와 title 값을 추출했습니다. + // 여기서 SwiftData를 사용하여 값을 저장합니다. +// saveNotificationData(body: body, title: title) + print("User alert: \(alert)") + print("User body: \(body)") + print("User title: \(title)") + } + + completionHandler([[.banner, .badge, .sound]]) + } + + + 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("User Info: \(userInfo)") + + completionHandler() + } } diff --git a/DDooing/DDooing/Views/Home/HomeView.swift b/DDooing/DDooing/Views/Home/HomeView.swift index 665f752..992de21 100644 --- a/DDooing/DDooing/Views/Home/HomeView.swift +++ b/DDooing/DDooing/Views/Home/HomeView.swift @@ -12,26 +12,38 @@ import SwiftData struct HomeView: View { @State var name: String = "" + @State var partnerName: String = "" @Environment(\.modelContext) private var modelContext @Query private var messages: [MessageModel] - @State private var randomMessages : String = "" + @State private var pushMessage : String = "" @State private var showContextMenu = false let partnerUID: String! @GestureState private var isPressed = false @State private var isLongPressed = false + @Query private var notificationDataList: [NotificationDataModel] + @State var partnerDeviceToken = "" init(partnerUID: String?) { self.partnerUID = partnerUID if messages.randomElement() != nil { - _randomMessages = State(initialValue: randomMessages) + _pushMessage = State(initialValue: pushMessage) } else { - _randomMessages = State(initialValue: "") + _pushMessage = State(initialValue: "") } } var body: some View { NavigationStack{ VStack { + HStack { + Text("DDooing") + .font(.largeTitle.bold()) + Spacer() + } + .padding(.vertical) + + Spacer() + Image("Heart button") .resizable() .aspectRatio(contentMode: .fit) @@ -53,7 +65,9 @@ struct HomeView: View { ForEach(messages) { mess in if mess.isStarred { Button (action: { - sendMessage(messageText: mess.message, isStarred: true) + pushMessage = mess.message + sendMessage(messageText: pushMessage, isStarred: true) + fetchAccessTokenAndSendPushNotification() }, label: { Text(mess.message) }) @@ -65,10 +79,11 @@ struct HomeView: View { if !isLongPressed { print("짧게누름") if let randomMessage = messages.randomElement() { - randomMessages = randomMessage.message + pushMessage = randomMessage.message } - saveRandomMessage() print("메시지 입력") + sendMessage(messageText: pushMessage, isStarred: false) + fetchAccessTokenAndSendPushNotification() } isLongPressed = false } @@ -76,29 +91,56 @@ struct HomeView: View { Text("\(postPositionText(name)) 생각하며 눌러보세요.") .font(.headline) .padding(.bottom,60) - - - - - - - - + Spacer() } .padding() - .navigationTitle("DDooing") .onAppear { fetchMyConnectedNickname { fetchedName in name = fetchedName } + + } + } + } + func fetchPartnerDeviceToken(completion: @escaping (String) -> Void) { + let db = Firestore.firestore() + db.collection("Users").document(partnerUID).getDocument { document, error in + if let document = document, document.exists { + partnerDeviceToken = document.data()?["deviceToken"] as? String ?? "Unknown" + completion(partnerDeviceToken) + } else { + completion("Unknown") } } } - func saveRandomMessage() { - guard let partnerUID = partnerUID else { return } - sendMessage(messageText: randomMessages, isStarred: false) + func fetchPartnerConnectedNickname(completion: @escaping (String) -> Void) { + let db = Firestore.firestore() + db.collection("Users").document(partnerUID).getDocument { document, error in + if let document = document, document.exists { + partnerName = document.data()?["ConnectedNickname"] as? String ?? "Unknown" + completion(partnerName) + } else { + completion("Unknown") + } + } + } + private func fetchMyConnectedNickname(completion: @escaping (String) -> Void) { + let db = Firestore.firestore() + guard let currentUid = Auth.auth().currentUser?.uid else { + completion("Unknown") + return + } + + db.collection("Users").document(currentUid).getDocument { document, error in + if let document = document, document.exists { + name = document.data()?["ConnectedNickname"] as? String ?? "Unknown" + completion(name) + } else { + completion("Unknown") + } + } } func sendMessage(messageText: String, isStarred: Bool) { @@ -106,52 +148,120 @@ struct HomeView: View { guard let currentUid = Auth.auth().currentUser?.uid else { return } - let currenrUserRef = db.collection("Received-Messages") - .document(currentUid).collection(partnerUID).document() - - let PartnerRef = db.collection("Received-Messages") - .document(partnerUID).collection(currentUid) + let currentUserRef = db.collection("Received-Messages") + .document(partnerUID).collection(currentUid).document() // let recentCurrentUserRef = db.collection("Received-Messages") -// .document(currentUid).collection("recent-messages") -// .document(partnerUID) +// .document(partnerUID).collection("recent-messages") +// .document(currentUid) - let recentPartnerRef = db.collection("Received-Messages") - .document(partnerUID).collection("recent-messages") - .document(currentUid) - - let messageId = currenrUserRef.documentID + let messageId = currentUserRef.documentID let messageData: [String: Any] = [ - "fromId": currentUid, - "toId": partnerUID!, + "fromId": partnerUID!, + "toId": currentUid, "messageText": messageText, "timeStamp": Timestamp(date: Date()), "isStarred": isStarred, "messageId": messageId ] -// currenrUserRef.setData(messageData) - PartnerRef.document(messageId).setData(messageData) + currentUserRef.setData(messageData) // recentCurrentUserRef.setData(messageData) - recentPartnerRef.setData(messageData) } - private func fetchMyConnectedNickname(completion: @escaping (String) -> Void) { - let db = Firestore.firestore() - guard let currentUid = Auth.auth().currentUser?.uid else { - completion("Unknown") + func fetchAccessTokenAndSendPushNotification() { + fetchPartnerDeviceToken { fetchedtoken in + partnerDeviceToken = fetchedtoken + } + fetchPartnerConnectedNickname { fetchedName in + partnerName = fetchedName + } + + // 서버로부터 OAuth 2.0 액세스 토큰 가져오기 + guard let url = URL(string: "") else { + print("Invalid URL for token") return } + + var request = URLRequest(url: url) + request.httpMethod = "GET" - db.collection("Users").document(currentUid).getDocument { document, error in - if let document = document, document.exists { - name = document.data()?["ConnectedNickname"] as? String ?? "Unknown" - completion(name) + let session = URLSession(configuration: .default) + session.dataTask(with: request) { data, response, err in + if let err = err { + print(err.localizedDescription) + return + } + guard let data = data else { + print("No data received") + return + } + + // 서버로부터 받은 응답을 문자열로 변환하여 출력 + if let accessToken = String(data: data, encoding: .utf8) { + print("Access Token String: \(accessToken)") + sendPushNotification(with: accessToken) } else { - completion("Unknown") + print("Invalid token response") } + }.resume() + } + + func sendPushNotification(with accessToken: String) { + guard !accessToken.isEmpty else { + print("Access token is empty") + return + } + + // HTTP v1 API의 엔드포인트 URL + guard let url = URL(string: "https://fcm.googleapis.com/v1/projects/ddooing-8881b/messages:send") else { + print("Invalid URL for FCM") + return } + print("partnerdevicetoken >>> \(partnerDeviceToken)") + print("pushMessage >>> \(pushMessage)") + print("parname >>> \(partnerName)") + let json: [String: Any] = [ + "message": [ + "token": partnerDeviceToken, + "notification": [ + "body": pushMessage, + "title": partnerName + ] + ] + ] + + var request = URLRequest(url: url) + request.httpMethod = "POST" + request.httpBody = try? JSONSerialization.data(withJSONObject: json, options: [.prettyPrinted]) + request.setValue("Bearer \(accessToken)", forHTTPHeaderField: "Authorization") + request.setValue("application/json", forHTTPHeaderField: "Content-Type") + + let session = URLSession(configuration: .default) + session.dataTask(with: request) { data, response, err in + if let err = err { + print("Error sending push notification: \(err.localizedDescription)") + return + } + + guard let httpResponse = response as? HTTPURLResponse else { + print("Invalid response") + return + } + + print("Push notification response status code: \(httpResponse.statusCode)") + + if let data = data, let responseBody = String(data: data, encoding: .utf8) { + print("Response Body: \(responseBody)") + } + + if httpResponse.statusCode == 200 { + print("Push notification sent successfully") + } else { + print("Failed to send push notification") + } + }.resume() } } diff --git a/DDooing/DDooingWidgetExtension.entitlements b/DDooing/DDooingWidgetExtension.entitlements index 849f0cc..1dfcb4c 100644 --- a/DDooing/DDooingWidgetExtension.entitlements +++ b/DDooing/DDooingWidgetExtension.entitlements @@ -4,7 +4,7 @@ com.apple.security.application-groups - group.com.Seodongwon.DDooing + group.com.iOSDevJoy.DDooing diff --git a/DDooing/GoogleService-Info.plist b/DDooing/GoogleService-Info.plist new file mode 100644 index 0000000..47231dc --- /dev/null +++ b/DDooing/GoogleService-Info.plist @@ -0,0 +1,36 @@ + + + + + CLIENT_ID + 752780593979-1avvvfte69oookeoh9lqjk6o2mmtu7b3.apps.googleusercontent.com + REVERSED_CLIENT_ID + com.googleusercontent.apps.752780593979-1avvvfte69oookeoh9lqjk6o2mmtu7b3 + API_KEY + AIzaSyAwf1U7zVB9zrctnZmQtRqbAaE7BdMYA7k + GCM_SENDER_ID + 752780593979 + PLIST_VERSION + 1 + BUNDLE_ID + com.iOSDevJoy.DDooing + PROJECT_ID + ddooing-8881b + STORAGE_BUCKET + ddooing-8881b.appspot.com + IS_ADS_ENABLED + + IS_ANALYTICS_ENABLED + + IS_APPINVITE_ENABLED + + IS_GCM_ENABLED + + IS_SIGNIN_ENABLED + + GOOGLE_APP_ID + 1:752780593979:ios:10788a37714cbc50dac51e + DATABASE_URL + https://ddooing-8881b-default-rtdb.firebaseio.com + + \ No newline at end of file