Skip to content

Commit 892a13b

Browse files
committed
fix: FCMTokenSyncHandler에서 토큰 backfill 흐름을 처리하도록 수정
1 parent 99da5e8 commit 892a13b

4 files changed

Lines changed: 97 additions & 7 deletions

File tree

Application/DevLogApp/Sources/App/Assembler/AppLayerAssembler.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ final class AppLayerAssembler: Assembler {
1212
func assemble(_ container: any DIContainer) {
1313
container.register(FCMTokenSyncHandler.self) {
1414
FCMTokenSyncHandler(
15+
messagingService: container.resolve(PushMessagingService.self),
1516
userService: container.resolve(UserService.self)
1617
)
1718
}

Application/DevLogApp/Sources/App/Handler/FCMTokenSyncHandler.swift

Lines changed: 62 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,27 +11,82 @@ import DevLogData
1111
import Foundation
1212

1313
final class FCMTokenSyncHandler {
14+
private let messagingService: PushMessagingService
1415
private let userService: UserService
16+
private let notificationCenter: NotificationCenter
1517
private let logger = Logger(category: "FCMTokenSyncHandler")
1618
private var cancellables = Set<AnyCancellable>()
1719

1820
init(
21+
messagingService: PushMessagingService,
1922
userService: UserService,
2023
notificationCenter: NotificationCenter = .default
2124
) {
25+
self.messagingService = messagingService
2226
self.userService = userService
27+
self.notificationCenter = notificationCenter
2328

2429
notificationCenter.publisher(for: .didRefreshFCMToken)
2530
.compactMap { $0.userInfo?["fcmToken"] as? String }
2631
.sink { [weak self] fcmToken in
27-
Task {
28-
do {
29-
try await self?.userService.updateFCMToken(fcmToken)
30-
} catch {
31-
self?.logger.error("Failed to sync refreshed FCM token", error: error)
32-
}
33-
}
32+
self?.syncFCMToken(fcmToken)
33+
}
34+
.store(in: &cancellables)
35+
36+
notificationCenter.publisher(for: .didRequestFCMTokenSync)
37+
.sink { [weak self] _ in
38+
self?.requestFCMTokenSync()
3439
}
3540
.store(in: &cancellables)
41+
42+
notificationCenter.publisher(for: .didReceiveAPNSToken)
43+
.compactMap { $0.userInfo?["deviceToken"] as? Data }
44+
.sink { [weak self] deviceToken in
45+
self?.handleAPNSToken(deviceToken)
46+
}
47+
.store(in: &cancellables)
48+
}
49+
}
50+
51+
private extension FCMTokenSyncHandler {
52+
func requestFCMTokenSync() {
53+
Task { [weak self] in
54+
guard let self else { return }
55+
guard await messagingService.isNotificationAuthorized() else {
56+
return
57+
}
58+
notificationCenter.post(name: .didRequestRemoteNotificationRegistration, object: nil)
59+
syncCurrentFCMToken()
60+
}
61+
}
62+
63+
func handleAPNSToken(_ deviceToken: Data) {
64+
messagingService.setAPNSToken(deviceToken)
65+
syncCurrentFCMToken()
66+
}
67+
68+
func syncCurrentFCMToken() {
69+
Task { [weak self] in
70+
guard let self else { return }
71+
72+
do {
73+
guard let fcmToken = try await messagingService.fetchFCMToken() else {
74+
return
75+
}
76+
try await userService.updateFCMToken(fcmToken)
77+
} catch {
78+
logger.error("Failed to sync current FCM token", error: error)
79+
}
80+
}
81+
}
82+
83+
func syncFCMToken(_ fcmToken: String) {
84+
Task { [weak self] in
85+
do {
86+
try await self?.userService.updateFCMToken(fcmToken)
87+
} catch {
88+
self?.logger.error("Failed to sync refreshed FCM token", error: error)
89+
}
90+
}
3691
}
3792
}

Application/DevLogData/Sources/Protocol/PushMessagingService.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import Foundation
1010
public protocol PushMessagingService: AnyObject {
1111
func setDelegate(_ delegate: PushMessagingServiceDelegate?)
1212
func setAPNSToken(_ deviceToken: Data)
13+
func isNotificationAuthorized() async -> Bool
14+
func fetchFCMToken() async throws -> String?
1315
}
1416

1517
public protocol PushMessagingServiceDelegate: AnyObject {

Application/DevLogInfra/Sources/Service/PushMessagingServiceImpl.swift

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
import Foundation
99
import DevLogData
1010
import FirebaseMessaging
11+
import UserNotifications
1112

1213
final class PushMessagingServiceImpl: NSObject, PushMessagingService {
1314
private weak var delegate: PushMessagingServiceDelegate?
@@ -20,10 +21,41 @@ final class PushMessagingServiceImpl: NSObject, PushMessagingService {
2021
func setAPNSToken(_ deviceToken: Data) {
2122
Messaging.messaging().apnsToken = deviceToken
2223
}
24+
25+
func isNotificationAuthorized() async -> Bool {
26+
let settings = await UNUserNotificationCenter.current().notificationSettings()
27+
28+
switch settings.authorizationStatus {
29+
case .authorized, .provisional, .ephemeral:
30+
return true
31+
case .denied, .notDetermined:
32+
return false
33+
@unknown default:
34+
return false
35+
}
36+
}
37+
38+
func fetchFCMToken() async throws -> String? {
39+
do {
40+
return try await Messaging.messaging().token()
41+
} catch {
42+
if error.isMissingAPNSTokenForFCMToken {
43+
return nil
44+
}
45+
throw error
46+
}
47+
}
2348
}
2449

2550
extension PushMessagingServiceImpl: MessagingDelegate {
2651
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
2752
delegate?.pushMessagingService(self, didReceiveRegistrationToken: fcmToken)
2853
}
2954
}
55+
56+
private extension Error {
57+
var isMissingAPNSTokenForFCMToken: Bool {
58+
let nsError = self as NSError
59+
return nsError.domain == "com.google.fcm" && nsError.code == 505
60+
}
61+
}

0 commit comments

Comments
 (0)