본문 바로가기
IOS App Programming/IOS 연습

FCM 푸시알림 사용기2 - 뱃지 카운팅 하기

by B_Tori 2024. 2. 21.

https://bang-tori.tistory.com/73

 

FCM 푸시알림 사용기1 - 특정 사용자에게 푸시알림 보내기

프로젝트를 진행하면서 FCM을 이용한 푸시 알림을 구현하게 되었다. 고민했던 부분을 바탕으로 구현 내용을 정리해 참고로 프로젝트는 백엔드 개발 없이 파이어베이스의 firestore 서비스를 이용

bang-tori.tistory.com

이전 게시물에서 푸시 알림을 사용자에게 보내는 것까지 정리해 보았다.

푸시 알림을 보내는 것을 끝으로 할 수 있지만

  • 알림 중요도 강조
  • 사용자에게 관심 유도

등과 같은 이유로 조금 더 나은 사용자 경험을 제공하기 위해 받은 알림 수만큼 뱃지를 표시하기로 결정하였다.

푸시알림 뱃지 예시 - 카카오톡

푸시 알림의 뱃지 카운팅을 하기 위해서는 서버에서 카운팅을 계산하고 프론트에서는 전달받은 숫자대로 표시하기만 할 수 있다.

하지만 백엔드 개발은 따로 이루어지지 않고, 파이어베이스 서비스를 사용하였기에 어떻게 구현을 해야 할지 고민이 되었다.

여러 방법을 찾아보니 두 가지 방법으로 구현을 할 수 있을 것 같았다.

  1. Notification Service Extension 사용
  2. firebase firestore 데이터에 토큰과 함께 뱃지 카운팅 값 저장

 

남은 프로젝트 기간이 얼마 남지 않은 상태라 사용해 본 적 없어 이해도가 떨어지는 Notification Service Extension 방법을 이용하기에는 여러 예외상황 등을 마주칠 리스크가 있어 2번 방법을 사용하였다.

 

2번 방법을 사용하기 위해 생각한 뱃지 카운팅 알고리즘은 다음과 같다.

편하게 firestore 데이터를 서버로 칭하였다.

  1. 유저 아이디 하나당 기기 토큰값과 뱃지 카운팅을 저장하는 Token 서버 데이터 생성
  2. 앱을 활성화시킬 때마다 디스플레이 카운트를 0으로 만들어주고, 해당 유저의 서버 데이터도 0으로 변경한다.
  3. 푸시 알림을 보낼 때는 서버에서 알림을 받는 유저의 뱃지 정보를 불러와서 1 올린 값을 뱃지 파라미터로 전달하여 Notification request를 보냄
  4. 리퀘스트를 보내면서 서버에는 똑같이 알림을 받는 유저의 뱃지 정보를 1 올려 다시 저장

뱃지 카운팅 관련 서비스 함수

func displayResetBadge() {
        if #available(iOS 16.0, *) {
            UNUserNotificationCenter.current().setBadgeCount(0)
        } else {
            UIApplication.shared.applicationIconBadgeNumber = 0
        }
        NotificationService.shared.resetBadgeCount()
    }
    
private func resetBadgeCount() {
    FirestoreService.shared.loadDocument(collectionId: .tokens, documentId: UserDefaultsManager.shared.getUserData().userId, dataType: Token.self) { result in
        switch result {
        case .success(let data):
            guard let token = data else { return }
            let resetToken = Token(fcmToken: token.fcmToken, badgeCount: 0)
            FirestoreService.shared.saveDocument(collectionId: .tokens, documentId: UserDefaultsManager.shared.getUserData().userId, data: resetToken) { result in
                switch result {
                case .success:
                    print("뱃지 카운트 리셋 성공")
                case .failure(let error):
                    print("뱃지 카운트 리셋 실패 : \\(error)")
                }
            }
        case .failure(let error):
            print("토큰 정보 가져오기 실패 : \\(error)")
            return
        }
    }
}
    
private func updateBadgeCount(userId: String) {
    FirestoreService.shared.loadDocument(collectionId: .tokens, documentId: userId, dataType: Token.self) { result in
        switch result {
        case .success(let data):
            guard let token = data else { return }
            let newToken = Token(fcmToken: token.fcmToken, badgeCount: token.badgeCount + 1)
            FirestoreService.shared.saveDocument(collectionId: .tokens, documentId: userId, data: newToken) { result in
                switch result {
                case .success:
                    print("뱃지 카운트 업데이트 성공")
                case .failure(let error):
                    print("뱃지 카운트 업데이트 실패 : \\(error)")
                }
            }
        case .failure(let error):
            print("토큰 정보 가져오기 실패 : \\(error)")
            return
        }
    }
}
  • displayResetBadge() : 화면에 보이는 뱃지를 0으로 만들어주는 함수
  • resetBadgeCount(): 토큰 데이터의 뱃지 카운트를 0으로 리셋하여 데이터베이스에 저장하는 함수
  • updateBadgeCount(userId:String) : 푸시 알림을 보낼 때 수신자의 토큰 데이터 구조에 뱃지 카운트를 1 추가하여 업데이트하여 저장해 주는 함수

뱃지를 리셋하는 경우는

  1. 앱을 foreground상태로 실행할 경우
  2. 앱이 foreground 상태일 때 알림이 왔을 경우이다.

따라서 didFinishLaunchingWithOptions , userNotificationCenter(_:willPresent:withCompletionHandler)에서 displayResetBadge()를 호출해 주었다.

카운팅 된 뱃지 표시하기

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
    if let aps = userInfo["aps"] as? [String: Any], let badge = aps["badge"] as? Int {
        if #available(iOS 16.0, *) {
            UNUserNotificationCenter.current().setBadgeCount(badge)
        } else {
            UIApplication.shared.applicationIconBadgeNumber = badge
        }
    }
    completionHandler(UIBackgroundFetchResult.newData)
}

didReceiveRemoteNotification : foreground/background/killed 상태 모든 푸시 알림을 처리

UNUserNotificationCenterDelegate 함수에 뱃지 카운팅을 설정할 수 있었지만 Killed 상태일 땐 푸시알림이 처리되지 않는 에러가 있어서 didReceiveRemoteNotification에 설정해 두었다.

댓글