diff --git a/.gitignore b/.gitignore index 16c1cab..c85b3cb 100644 --- a/.gitignore +++ b/.gitignore @@ -144,3 +144,5 @@ Derived/ ### Tuist managed dependencies ### Tuist/Dependencies + +GoogleService-info.plist diff --git a/Projects/App/DodamDodam.entitlements b/Projects/App/DodamDodam.entitlements new file mode 100644 index 0000000..903def2 --- /dev/null +++ b/Projects/App/DodamDodam.entitlements @@ -0,0 +1,8 @@ + + + + + aps-environment + development + + diff --git a/Projects/App/Project.swift b/Projects/App/Project.swift index bb5aa8d..d1c5117 100644 --- a/Projects/App/Project.swift +++ b/Projects/App/Project.swift @@ -6,7 +6,7 @@ let project = Project( options: .options( defaultKnownRegions: ["en", "ko"], developmentRegion: "ko" - ), + ), settings: .settings( base: .init() .otherLinkerFlags(["$(inherited) -ObjC"]), @@ -30,11 +30,13 @@ let project = Project( "CFBundleVersion": "1", "UISupportedInterfaceOrientations": ["UIInterfaceOrientationPortrait"], "UIMainStoryboardFile": "", - "UILaunchStoryboardName": "LaunchScreen" + "UILaunchStoryboardName": "LaunchScreen", + "FirebaseAppDelegateProxyEnabled": .boolean(false) ] ), sources: ["iOS/Source/**"], resources: ["iOS/Resource/**"], +// entitlements: .file(path: "iOS/Resource/DodamDodam.entitlements"), scripts: [.swiftLint], dependencies: [ .project(target: "Feature", path: .relativeToRoot("Projects/Feature")), diff --git a/Projects/App/iOS/Source/AppDelegate.swift b/Projects/App/iOS/Source/AppDelegate.swift new file mode 100644 index 0000000..c8a3eaa --- /dev/null +++ b/Projects/App/iOS/Source/AppDelegate.swift @@ -0,0 +1,103 @@ +// +// AppDelegate.swift +// DodamDodam +// +// Created by dgsw8th61 on 9/19/24. +// + +import SwiftUI +import Firebase +import FirebaseMessaging +import DIContainer +import Domain + +class AppDelegate: NSObject, UIApplicationDelegate { + + let gcmMessageIDKey = "gcm.message_id" + + // 앱이 켜졌을 때 + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { + + // 파이어베이스 설정 + FirebaseApp.configure() + + // Setting Up Notifications... + // 원격 알림 등록 + if #available(iOS 10.0, *) { + // For iOS 10 display notification (sent via APNS) + UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { _, _ in } + } else { + let settings: UIUserNotificationSettings = + UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil) + application.registerUserNotificationSettings(settings) + } + + application.registerForRemoteNotifications() + + // Setting Up Cloud Messaging... + // 메세징 델리겟 + Messaging.messaging().delegate = self + UNUserNotificationCenter.current().delegate = self + UIApplication.shared.registerForRemoteNotifications() + return true + } + + // fcm 토큰이 등록 되었을 때 + func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { + print("✅ AppDelegate.application.didRegisterForRemoteNotificationsWithDeviceToken - apnsToken \(deviceToken)") + Messaging.messaging().apnsToken = deviceToken + } +} + +// Cloud Messaging... +extension AppDelegate: MessagingDelegate { + + // fcm 등록 토큰이 갱신되었을 때 + func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) { + guard let fcmToken else { + print("❌ AppDelegate.messaging.didReceiveRegistrationToken - FCM token is nil") + return + } + print("✅ AppDelegate.Fmessaging.didReceiveRegistrationToken - fcmToken \(fcmToken)") + } +} + +// User Notifications...[AKA InApp Notification...] + +@available(iOS 10, *) +extension AppDelegate: UNUserNotificationCenterDelegate { + + // 푸시 메세지가 앱이 켜져 있을 때 나올 때 + func userNotificationCenter( + _ center: UNUserNotificationCenter, + willPresent notification: UNNotification + ) async -> UNNotificationPresentationOptions { + let userInfo = notification.request.content.userInfo + + // Do Something With MSG Data... + if let messageID = userInfo[gcmMessageIDKey] { + print("✅ AppDelegate.userNotificationCenter.didReceive - Message ID: \(messageID)") + } + print("✅ AppDelegate.userNotificationCenter.didReceive - \(userInfo)") + + return if #available(iOS 14.0, *) { + [[.list, .banner, .sound]] + } else { + [[.alert, .sound]] + } + } + + // 푸시메세지를 받았을 떄 + func userNotificationCenter( + _ center: UNUserNotificationCenter, + didReceive response: UNNotificationResponse + ) async { + let userInfo = response.notification.request.content.userInfo + + // Do Something With MSG Data... + if let messageID = userInfo[gcmMessageIDKey] { + print("✅ AppDelegate.userNotificationCenter.didReceive - Message ID: \(messageID)") + } + print("✅ AppDelegate.userNotificationCenter.didReceive - \(response)") + } +} diff --git a/Projects/App/iOS/Source/AppMain.swift b/Projects/App/iOS/Source/AppMain.swift index f289e10..52139ac 100644 --- a/Projects/App/iOS/Source/AppMain.swift +++ b/Projects/App/iOS/Source/AppMain.swift @@ -16,6 +16,8 @@ import Realm @main struct AppMain: App { + @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate + init() { Pretendard.register() DependencyProvider.shared.register() diff --git a/Projects/Data/Network/Source/Remote/RemoteInterceptor.swift b/Projects/Data/Network/Source/Remote/RemoteInterceptor.swift index 59f1ecf..88cd937 100644 --- a/Projects/Data/Network/Source/Remote/RemoteInterceptor.swift +++ b/Projects/Data/Network/Source/Remote/RemoteInterceptor.swift @@ -10,6 +10,7 @@ import Alamofire import SignKit import Domain import DIContainer +import FirebaseMessaging class RemoteInterceptor: RequestInterceptor { @@ -73,11 +74,11 @@ class RemoteInterceptor: RequestInterceptor { if let id = Sign.id, let pw = Sign.password { do { + let pushToken = try await Messaging.messaging().token() _ = try await authRepository.postLogin( - .init(id: id, pw: pw) + .init(id: id, pw: pw, pushToken: pushToken) ) DispatchQueue.main.async { -// self.retryCount += 1 completion(.retry) } } catch let error { diff --git a/Projects/Data/Project.swift b/Projects/Data/Project.swift index 738ca35..a896fee 100644 --- a/Projects/Data/Project.swift +++ b/Projects/Data/Project.swift @@ -43,6 +43,7 @@ let project = Project( .project(target: "Domain", path: .relativeToRoot("Projects/Domain")), .external(name: "Moya"), .external(name: "SignKit"), + .external(name: "FirebaseMessaging"), .project(target: "Shared", path: .relativeToRoot("Projects/Shared")) ] ), diff --git a/Projects/Domain/Source/Request/Auth/PostLoginRequest.swift b/Projects/Domain/Source/Request/Auth/PostLoginRequest.swift index 7d984a1..9080778 100644 --- a/Projects/Domain/Source/Request/Auth/PostLoginRequest.swift +++ b/Projects/Domain/Source/Request/Auth/PostLoginRequest.swift @@ -9,9 +9,11 @@ public struct PostLoginRequest: RequestProtocol { public let id: String public let pw: String + public let pushToken: String - public init(id: String, pw: String) { + public init(id: String, pw: String, pushToken: String) { self.id = id self.pw = pw + self.pushToken = pushToken } } diff --git a/Projects/Feature/Project.swift b/Projects/Feature/Project.swift index c464958..0ca470c 100644 --- a/Projects/Feature/Project.swift +++ b/Projects/Feature/Project.swift @@ -17,7 +17,8 @@ let project = Project( .project(target: "DIContainer", path: .relativeToRoot("Projects/DIContainer")), .external(name: "CachedAsyncImage"), .external(name: "SwiftUIIntrospect"), - .project(target: "Shared", path: .relativeToRoot("Projects/Shared")) + .project(target: "Shared", path: .relativeToRoot("Projects/Shared")), + .external(name: "FirebaseMessaging") ] ) ] diff --git a/Projects/Feature/Source/All/AllView.swift b/Projects/Feature/Source/All/AllView.swift index 5bc52f3..f15b215 100644 --- a/Projects/Feature/Source/All/AllView.swift +++ b/Projects/Feature/Source/All/AllView.swift @@ -64,7 +64,6 @@ struct AllView: View { .padding(.bottom, 8) } else { Button { - dump(flow) flow.push(LoginView()) } label: { HStack(spacing: 16) { diff --git a/Projects/Feature/Source/Auth/Login/LoginViewModel.swift b/Projects/Feature/Source/Auth/Login/LoginViewModel.swift index fac4e79..91f1a20 100644 --- a/Projects/Feature/Source/Auth/Login/LoginViewModel.swift +++ b/Projects/Feature/Source/Auth/Login/LoginViewModel.swift @@ -9,6 +9,7 @@ import Combine import SignKit import DIContainer import Domain +import FirebaseMessaging class LoginViewModel: ObservableObject { @@ -28,8 +29,9 @@ class LoginViewModel: ObservableObject { func postLogin(_ completion: @escaping () -> Void) async { isShowingAlert = false do { + let pushToken = try await Messaging.messaging().token() _ = try await authRepository.postLogin( - .init(id: idText, pw: pwText) + .init(id: idText, pw: pwText, pushToken: pushToken) ) completion() } catch let error { diff --git a/Tuist/Package.resolved b/Tuist/Package.resolved index a7f4d36..1b623ae 100644 --- a/Tuist/Package.resolved +++ b/Tuist/Package.resolved @@ -1,5 +1,14 @@ { "pins" : [ + { + "identity" : "abseil-cpp-binary", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/abseil-cpp-binary.git", + "state" : { + "revision" : "194a6706acbd25e4ef639bcaddea16e8758a3e27", + "version" : "1.2024011602.0" + } + }, { "identity" : "alamofire", "kind" : "remoteSourceControl", @@ -9,6 +18,15 @@ "version" : "5.9.1" } }, + { + "identity" : "app-check", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/app-check.git", + "state" : { + "revision" : "3b62f154d00019ae29a71e9738800bb6f18b236d", + "version" : "10.19.2" + } + }, { "identity" : "dds-ios", "kind" : "remoteSourceControl", @@ -18,6 +36,15 @@ "version" : "0.2.20" } }, + { + "identity" : "firebase-ios-sdk", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/firebase-ios-sdk.git", + "state" : { + "revision" : "eca84fd638116dd6adb633b5a3f31cc7befcbb7d", + "version" : "10.29.0" + } + }, { "identity" : "flowkit", "kind" : "remoteSourceControl", @@ -27,6 +54,69 @@ "revision" : "71ee3f7414747e16f320a6f6b424152e22d9d694" } }, + { + "identity" : "googleappmeasurement", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleAppMeasurement.git", + "state" : { + "revision" : "fe727587518729046fc1465625b9afd80b5ab361", + "version" : "10.28.0" + } + }, + { + "identity" : "googledatatransport", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleDataTransport.git", + "state" : { + "revision" : "a637d318ae7ae246b02d7305121275bc75ed5565", + "version" : "9.4.0" + } + }, + { + "identity" : "googleutilities", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/GoogleUtilities.git", + "state" : { + "revision" : "57a1d307f42df690fdef2637f3e5b776da02aad6", + "version" : "7.13.3" + } + }, + { + "identity" : "grpc-binary", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/grpc-binary.git", + "state" : { + "revision" : "e9fad491d0673bdda7063a0341fb6b47a30c5359", + "version" : "1.62.2" + } + }, + { + "identity" : "gtm-session-fetcher", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/gtm-session-fetcher.git", + "state" : { + "revision" : "a2ab612cb980066ee56d90d60d8462992c07f24b", + "version" : "3.5.0" + } + }, + { + "identity" : "interop-ios-for-google-sdks", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/interop-ios-for-google-sdks.git", + "state" : { + "revision" : "2d12673670417654f08f5f90fdd62926dc3a2648", + "version" : "100.0.0" + } + }, + { + "identity" : "leveldb", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/leveldb.git", + "state" : { + "revision" : "a0bc79961d7be727d258d33d5a6b2f1023270ba1", + "version" : "1.22.5" + } + }, { "identity" : "moya", "kind" : "remoteSourceControl", @@ -36,6 +126,24 @@ "version" : "15.0.3" } }, + { + "identity" : "nanopb", + "kind" : "remoteSourceControl", + "location" : "https://github.com/firebase/nanopb.git", + "state" : { + "revision" : "b7e1104502eca3a213b46303391ca4d3bc8ddec1", + "version" : "2.30910.0" + } + }, + { + "identity" : "promises", + "kind" : "remoteSourceControl", + "location" : "https://github.com/google/promises.git", + "state" : { + "revision" : "540318ecedd63d883069ae7f1ed811a2df00b6ac", + "version" : "2.4.0" + } + }, { "identity" : "reactiveswift", "kind" : "remoteSourceControl", @@ -81,6 +189,15 @@ "version" : "0.0.2" } }, + { + "identity" : "swift-protobuf", + "kind" : "remoteSourceControl", + "location" : "https://github.com/apple/swift-protobuf.git", + "state" : { + "revision" : "edb6ed4919f7756157fe02f2552b7e3850a538e5", + "version" : "1.28.1" + } + }, { "identity" : "swiftui-cached-async-image", "kind" : "remoteSourceControl", diff --git a/Tuist/Package.swift b/Tuist/Package.swift index 3cd5144..5fa0b0a 100644 --- a/Tuist/Package.swift +++ b/Tuist/Package.swift @@ -30,6 +30,7 @@ let package = Package( .package(url: "https://github.com/Mercen-Lee/FlowKit", branch: "main"), .package(url: "https://github.com/Mercen-Lee/SignKit", exact: "0.0.2"), .package(url: "https://github.com/lorenzofiamingo/swiftui-cached-async-image", exact: "2.1.1"), + .package(url: "https://github.com/firebase/firebase-ios-sdk.git", from: "10.0.0"), .package(url: "https://github.com/realm/realm-swift", exact: "20.0.0"), .package(url: "https://github.com/siteline/SwiftUI-Introspect", exact: "1.3.0") ]