고난과 역경의 시간이었다.

 

Flutter - iOS device app badge

 

플러터로 개발하는 아이폰 앱 배지에 알림 숫자를 컨트롤 하기 위해서는 수동으로 swift를 수정해주어야한다.

안타까운 플러터 개발자들을 위해 왜 xcode로 직접 코드를 짜야하는 배경을 설명하자면,

 

1. FlutterLocalNotificaionPlugin package

: 이 패키지에 badgeNumber가 있지만 전혀 소용이 없다.

final NotificationDetails notiDetail = const NotificationDetails(
  android: AndroidNotificationDetails(
    'high_importance_channel',
    'High Importance Notifications',
    importance: Importance.high,
    priority: Priority.high,
    playSound: true,
    enableVibration: true,
    icon: "mipmap/m_logo",
  ),
  iOS: DarwinNotificationDetails(
    presentAlert: true,
    presentBadge: true,
    presentSound: true,
    presentBanner: true,
    badgeNumber: 3
  ),
);

 

이에 관련한 개발자의 답변:

 

https://github.com/MaikuB/flutter_local_notifications/issues/81

 

Support for notification badges on iOS and Android? · Issue #81 · MaikuB/flutter_local_notifications

Do you plan to support app icon notification badges for iOS and Android? :) Or maybe I just overlooked the functionality in the current plugin. Anyhow, would love to see the feature built in. My us...

github.com

 

 

2. FlutterAppBadger package

: discontinued. 더이상의 업데이트가 안되어있어서 최근 개발을 진행하는 프로젝트에는 소용이없다.

 

3. 서버단에서 해결

https://developer.apple.com/documentation/usernotifications/generating-a-remote-notification

ios badge는 서버단에서 해결하면 아주... 쉽게 해결된다.

 

24/09/02 수정 - 코드는 앱이 백그라운드일때만 작동됩니다.

앱이 종료되었을땐 실행이 안됩니다. 

 

포스트를 삭제하려다가 빅데이터를 위해 남겨두기로 결정.

앱 종료시 실행이 안되고 버그처럼 나타나서 저는 서버에서 핸들링하기로 결정했습니다.

 

기존 포스트 내용은 접어두었습니다.

 

더보기

위와 같은 배경으로 나는 swift를 통해 iOS badge count 개발을 진행해야했다.

 

AppDelegate.swift 파일을 수정하면된다.

FCM message 를 전달받았을때 native 에서 받은 메시지를 바로 반영해준다.

 

import Flutter
import UIKit
import Firebase
import UserNotifications
import os.log

@main
@objc class AppDelegate: FlutterAppDelegate {

    override func application(
        _ application: UIApplication,
        didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
    ) -> Bool {
        
        if #available(iOS 10.0, *) {
            UNUserNotificationCenter.current().delegate = self
            UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { granted, error in
                if granted {
                    os_log("iOS: Notification permission granted.", type: .info)
                } else if let error = error {
                    os_log("iOS: Notification permission error: %@", type: .error, error.localizedDescription)
                }
            }
        }
        
        application.registerForRemoteNotifications()
        
        FirebaseApp.configure()
        
        GeneratedPluginRegistrant.register(with: self)
        return super.application(application, didFinishLaunchingWithOptions: launchOptions)
    }

    // This method will be called when the app receives a notification in background or terminated state
    override func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
        os_log("### iOS: Received a remote notification", type: .info)
        
        // Handle the notification data (e.g., for Firebase Analytics)
        Messaging.messaging().appDidReceiveMessage(userInfo)
        
        // Increment the badge count and update the app icon
        if let badgeCount = userInfo["badge"] as? Int {
            setBadgeCount(badgeCount, application: application)
        } else {
            // Increment the badge count locally if "badge" not provided in payload
            let currentBadgeCount = UserDefaults.standard.integer(forKey: "badgeCount")
            let newBadgeCount = currentBadgeCount + 1
            UserDefaults.standard.set(newBadgeCount, forKey: "badgeCount")
            setBadgeCount(newBadgeCount, application: application)
        }
        
        // Call the completion handler to let the system know the fetch is complete
        completionHandler(.newData)
    }
    
    // method to set the badge count
    func setBadgeCount(_ count: Int, application: UIApplication) {
        os_log("### iOS: Setting badge count to %d", type: .info, count)
        if #available(iOS 16.0, *) {
            UNUserNotificationCenter.current().setBadgeCount(count) { error in
                if let error = error {
                    os_log("Failed to set badge count: %@", type: .error, error.localizedDescription)
                } else {
                    os_log("Badge count set to %d", type: .info, count)
                }
            }
        } else {
            application.applicationIconBadgeNumber = count
            os_log("Badge count set to %d using deprecated method", type: .info, count)
        }
    }
    
    // 앱이 실행될때 쌓아둔 알림 카운트를 모두 초기화
    override func applicationDidBecomeActive(_ application: UIApplication) {
        UserDefaults.standard.set(0, forKey: "badgeCount")
        setBadgeCount(0, application: application)
    }
}

 

 

os_log 는 콘솔로 앱이 background 상태일때 로그를 찍기위해 사용했으니 필요 없다면 생략하면 된다.

 

그리고 더 중요한 점,

 

서버단에서 FCM payload를 수정해주어야한다.

 

{
  "message": {
    "topic": "테스트토픽",
    "data": {
        "title": "TEST 3",
        "body": "data - body message"
    },
    "apns": {
      "payload": {
        "aps": {
          "alert": {
            "title": "Breaking News ",
            "body": "New news story available."
          },
          "content-available": 1,
          "interruption-level": "active" 
        }
      }
    }
  }
}

 

 

나는 notification을 사용하지 않아 빼두었지만, 필요하다면 추가해서 사용하면 된다.

중요한 점은 apns 안의 구조이므로 참조해서 사용하시길...

 

+ Recent posts