@@ -11,10 +11,15 @@ import FirebaseAuth
1111import FirebaseFirestore
1212import FirebaseMessaging
1313import GoogleSignIn
14+ import Combine
1415import UserNotifications
1516
1617class AppDelegate : UIResponder , UIApplicationDelegate , MessagingDelegate {
1718 private let logger = Logger ( category: " AppDelegate " )
19+ private var store : Firestore { Firestore . firestore ( ) }
20+ private var authStateListenerHandle : AuthStateDidChangeListenerHandle ?
21+ private var cancellable : AnyCancellable ?
22+
1823 func application(
1924 _ app: UIApplication ,
2025 open url: URL ,
@@ -47,6 +52,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, MessagingDelegate {
4752
4853 // Firebase Messaging 설정
4954 Messaging . messaging ( ) . delegate = self
55+ observeAuthState ( )
5056
5157 // 앱이 완전 종료되어도, 알림을 통해 앱이 시작된 경우 처리
5258 if let remoteNotification = launchOptions ? [ . remoteNotification] as? [ AnyHashable : Any ] {
@@ -73,6 +79,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate, MessagingDelegate {
7379 ) {
7480 logger. error ( " Failed to register APNs token " , error: error)
7581 }
82+
83+ func applicationDidBecomeActive( _ application: UIApplication ) {
84+ syncBadgeCount ( )
85+ }
7686
7787 // FCMToken 갱신
7888 func messaging(
@@ -83,8 +93,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate, MessagingDelegate {
8393 NotificationCenter . default. post ( name: . fcmToken, object: nil , userInfo: [ " fcmToken " : fcmToken] )
8494 }
8595 }
96+ }
8697
87- private func updateUserTimeZone( ) {
98+ private extension AppDelegate {
99+ func updateUserTimeZone( ) {
88100 Task {
89101 do {
90102 guard let uid = Auth . auth ( ) . currentUser? . uid else { return }
@@ -96,6 +108,117 @@ class AppDelegate: UIResponder, UIApplicationDelegate, MessagingDelegate {
96108 }
97109 }
98110 }
111+
112+ func observeAuthState( ) {
113+ authStateListenerHandle = Auth . auth ( ) . addStateDidChangeListener { [ weak self] _, user in
114+ guard let self else { return }
115+
116+ self . cancellable? . cancel ( )
117+
118+ guard user != nil else {
119+ self . updateBadgeCount ( 0 )
120+ return
121+ }
122+
123+ self . startObservingBadgeCount ( )
124+ self . syncBadgeCount ( )
125+ }
126+ }
127+
128+ func startObservingBadgeCount( ) {
129+ cancellable = try ? observeUnreadNotificationCount ( )
130+ . receive ( on: DispatchQueue . main)
131+ . sink (
132+ receiveCompletion: { [ weak self] completion in
133+ guard let self else { return }
134+
135+ if case . failure( let error) = completion {
136+ self . logger. error ( " Failed to observe unread notification count " , error: error)
137+ }
138+ } ,
139+ receiveValue: { [ weak self] count in
140+ self ? . updateBadgeCount ( count)
141+ }
142+ )
143+ }
144+
145+ func fetchUnreadNotificationCount( ) async throws -> Int {
146+ logger. info ( " Fetching unread notification count " )
147+
148+ guard let uid = Auth . auth ( ) . currentUser? . uid else {
149+ logger. error ( " User not authenticated " )
150+ throw AuthError . notAuthenticated
151+ }
152+
153+ do {
154+ let snapshot = try await store. collection ( " users/ \( uid) /notifications " )
155+ . whereField ( " isRead " , isEqualTo: false )
156+ . getDocuments ( )
157+
158+ let unreadNotificationCount = snapshot. documents. count
159+ logger. info ( " Unread notification count: \( unreadNotificationCount) " )
160+ return unreadNotificationCount
161+ } catch {
162+ logger. error ( " Failed to fetch unread notification count " , error: error)
163+ throw error
164+ }
165+ }
166+
167+ func observeUnreadNotificationCount( ) throws -> AnyPublisher < Int , Error > {
168+ logger. info ( " Observing unread notification count " )
169+
170+ guard let uid = Auth . auth ( ) . currentUser? . uid else {
171+ logger. error ( " User not authenticated " )
172+ throw AuthError . notAuthenticated
173+ }
174+
175+ let subject = PassthroughSubject < Int , Error > ( )
176+ let listener = store. collection ( " users/ \( uid) /notifications " )
177+ . whereField ( " isRead " , isEqualTo: false )
178+ . addSnapshotListener { [ weak self] snapshot, error in
179+ if let error {
180+ self ? . logger. error ( " Failed to observe unread notification count " , error: error)
181+ subject. send ( completion: . failure( error) )
182+ return
183+ }
184+
185+ guard let snapshot else { return }
186+
187+ let unreadNotificationCount = snapshot. documents. count
188+ self ? . logger. info ( " Observed unread notification count: \( unreadNotificationCount) " )
189+ subject. send ( unreadNotificationCount)
190+ }
191+
192+ return subject
193+ . handleEvents ( receiveCancel: { listener. remove ( ) } )
194+ . eraseToAnyPublisher ( )
195+ }
196+
197+ func syncBadgeCount( ) {
198+ Task { @MainActor [ weak self] in
199+ guard let self else { return }
200+ guard Auth . auth ( ) . currentUser != nil else {
201+ self . updateBadgeCount ( 0 )
202+ return
203+ }
204+
205+ do {
206+ let unreadNotificationCount = try await self . fetchUnreadNotificationCount ( )
207+ self . updateBadgeCount ( unreadNotificationCount)
208+ } catch {
209+ self . logger. error ( " Failed to fetch unread notification count " , error: error)
210+ }
211+ }
212+ }
213+
214+ @MainActor
215+ private func updateBadgeCount( _ count: Int ) {
216+ UNUserNotificationCenter . current ( ) . setBadgeCount ( count) { [ weak self] error in
217+ if let error {
218+ self ? . logger. error ( " Failed to update badge count " , error: error)
219+ }
220+ }
221+ }
99222}
100223
101224extension AppDelegate : UNUserNotificationCenterDelegate {
0 commit comments