프로그래밍/SWIFT

[IOS][SWIFT] AppDelegate에서 백그라운드/포그라운드 push 감지

p-a-r-k 2024. 8. 11. 04:15
반응형

AppDelegate 메서드

IOS에서 앱의 실행상태가 변화할 때 앱 객체는 AppDelegate에 정의된 특정 메서드를 호출한다.

내부에서 적절한 메서드를 작성하여 포그라운드와 백그라운드시에 수신에 따른 적절한 처리를 해줄 수 있었다.

 

application; didFinishLaunchingWithOptions 메서드에서 firebase 세팅과 apns 등록을 해준다.

추가로 사일런트 푸시 처리 메서드쪽에서 백그라운드인경우 수신을 감지하여 적절한 처리를 해줄 수 있었다.

class AppDelegate: NSObject, UIApplicationDelegate {
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        FirebaseApp.configure()
        
        // 원격알림 등록: 푸시든 로컬이든 알람 허용해야 그 이후에 가능
        UNUserNotificationCenter.current().delegate = self

        let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
        UNUserNotificationCenter.current().requestAuthorization(
          options: authOptions,
          completionHandler: { _, _ in }
        )
        
        // APNs 등록
        application.registerForRemoteNotifications()
        
        // FirebaseMessaging
        Messaging.messaging().delegate = self

        return true
    }
    
    
    // 사일런트 푸시 처리 메소드
    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
                     fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
        if application.applicationState == .background {
            // do tasks
            UpboxFirebaseMessagingService.shared.handleFirebasePushNotification(userInfo: userInfo)
        }
        
        completionHandler(UIBackgroundFetchResult.newData)
    }

    func applicationDidBecomeActive(_ application: UIApplication) {
        // 앱이 포그라운드로 돌아올 때 권한 체크
    }

    func applicationWillResignActive(_ application: UIApplication) {
        // 앱이 활성 상태를 떠나기 직전에 호출됩니다.
    }

    func applicationDidEnterBackground(_ application: UIApplication) {
        // 앱이 백그라운드 상태로 전환될 때 호출됩니다.
    }

    func applicationWillEnterForeground(_ application: UIApplication) {
        // 앱이 백그라운드에서 포그라운드로 전환될 때 호출됩니다.
    }

    func applicationWillTerminate(_ application: UIApplication) {
        // 앱이 종료될 때 호출됩니다.
    }

    func loadFCMToken() {
        Messaging.messaging().token { token, error in
            if let error = error {
                print("Error fetching FCM registration token: \(error)")
            } else if let token = token {
                print("FCM registration token: \(token)")
                UpboxFirebaseMessagingService.token = token
            }
        }
    }
}

 

본인의 경우에는 웹뷰측에 푸시내용을 저장했어야 하는데, 포그라운드인 경우는 바로 웹뷰에 전달해주면 됐고

백그라운드인경우에는 DataModel에 저장만 하고, Resume시에 DataModel에 저장된 내용을 웹뷰에 전달하였다.

(Resume은 ContentView에서 .onChange(of: appStateObserver.isActive) 이벤트 내에서 처리했다.)

 

포그라운드 수신시에는 아래처럼 익스텐션으로 추가해주면 된다

extension AppDelegate : UNUserNotificationCenterDelegate {
    // foreground에서 시스템 푸시를 수신했을 때 해당 메소드가 호출
    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                willPresent notification: UNNotification,
                                withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        let userInfo = notification.request.content.userInfo
        
        if let title = userInfo["title"] as? String,
           let body = userInfo["body"] as? String,
           !title.isEmpty,
           !body.isEmpty {
            UpboxFirebaseMessagingService.shared.handleFirebasePushNotificationForeground(userInfo: userInfo)
        }
        
        let presentationOptions: UNNotificationPresentationOptions
        if #available(iOS 14.0, *) {
            presentationOptions = [.sound, .badge, .banner, .list]
        } else {
            presentationOptions = [.alert, .sound, .badge]
        }
        
        completionHandler(presentationOptions)
    }
    
    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) }
        let token = tokenParts.joined()
        print("Device Token: \(token)")
        
        Messaging.messaging().apnsToken = deviceToken
    }
}

extension AppDelegate: MessagingDelegate {
    func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
        print("Firebase registration token: \(String(describing: fcmToken))")
        UpboxFirebaseMessagingService.token = fcmToken ?? ""
        loadFCMToken()
    }
}

 

참고로 push notification을 테스트 하기위해 사용한 FCM의 기존 HTTP API 전송예시는 다음과 같다. (이전 버전)

[POST] https://fcm.googleapis.com/fcm/send  

[HEADER] Authorization: key=${서버 키 문자열}

{
    "to": "테스트기기의 regist token",
    "notification": {
        "title": "표시될 푸시 타이틀",
        "body": "표시될 푸시 내용"
    },
    "data": {
        "title": "전송 데이터1",
        "body": "전송 데이터2",
        "notiType": "전송 데이터3",
        "member": 7734,
        "urn": "",
        "param": ""
    },
    "priority": "high",
    "content_available": true,
    "apns": {
        "payload": {
            "aps": {
                "content-available": 1
            }
        },
        "headers": {
            "apns-push-type": "background",
            "apns-priority": "5"
        }
    }
}
반응형