적당한 고통은 희열이다

- 댄 브라운 '다빈치 코드' 중에서

초보 iOS 개발자의 일상/개발 업무

FCM Firebase 클라우드 메시징

hongssup_ 2021. 5. 13. 15:52
반응형

Configuring APNs with FCM 

Firebase ID 토큰 확인

Firebase를 사용하는 앱이 커스텀 백엔드 서버와 통신하는 경우, 서버에 현재 로그인한 사용자를 식별해야 할 수도 있다. 사용자를 안전하게 식별하기 위해서는 로그인이 정상적으로 이루어진 후, HTTPS를 사용하여 사용자의 ID 토큰을 서버로 전송합니다. 

 

iOS 앱에 FCM 추가 

iOS 클라이언트 앱 설정

* APNs란? 

: Apple 푸시 알림 서비스 (Apple Push Notification service) 

 

<Apple 개발자 계정>

Certificates, Identifiers & Profiles에서 APNs 인증 키를 가져온다. 

identifier에도 enabled capabilities에 push notifications 기능을 추가해주어야 함. 

<Xcode>

Signing & Capabilities 에서 Push Notifications 추가, Background Modes 추가 후 Remote notifications 체크. 

<Firebase>

iOS 앱 추가하여 GoogleService-info.plist 파일 다운받아 프로젝트에 넣어주기

프로젝트 설정 - 클라우드 메시징 탭에서 APN 인증키 업로드 (+ KEY ID, TEAM ID 필요)

 <Podfile>

pod 'Firebase/Analytics'

pod 'Firebase/Messaging'

추가해주고 터미널에서 pod install 

 

<코드>

앱에서 Firebase 초기화 코드 추가. Initialize Firebase in your app 

AppDelegate에  Firebase 모듈을 가져온 다음, 

import Firebase

application:didFinishLaunchingWithOptions: 메서드에서 FirebaseApp 공유 인스턴스를 구성해준다. 

FirebaseApp.configure()

원격 알림 등록 Register for remote notifications 

iOS 10.0 이상만 지원할 경우, 다음 코드를

applicationWillFinishLaunchingWithOptions: 또는 applicationDidFinishLaunchingWithOptions:

메서드에 입력하여 UNUserNotificationCenter 및 FIRMessaging의 delegate 속성을 할당해주어야 한다. 

UNUserNotificationCenter.current().delegate = self
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(
    options: authOptions,
    completionHandler: {_, _ in })
application.registerForRemoteNotifications()

등록 토큰 액세스 Access the registration token

기본적으로 FCM SDK는 앱을 시작할 때 MessagingDelegate의 messaging:didReceiveRegistrationToken: 메서드를 통해 클라이언트 앱 디바이스의 등록 토큰을 생성한다. 최초 앱 시작 시 및 토큰이 업데이트/무효화 될 때 마다 신규 또는 기존 토큰을 검색하여 어떤 경우에서나 유효한 토큰을 호출한다. (새 기기에서 앱 복원, 앱 삭제/재설치, 앱 데이터 소거 등의 경우에 등록 토큰이 변경될 수 있음)

메세지 대리자 설정 (FirebaseApp.configure() 밑에 추가)

Messaging.messaging().delegate = self

토큰 갱신 모니터링

등록 토큰은 MessagingDelegate의 messaging:didReceiveRegistrationToken: 메서드를 통해 전달됨. 일반적으로 앱 시작 시 등록 토큰을 사용하여 이 메서드를 한 번 호출한다. (호출 시 새 등록 토큰이라면 애플리케이션 서버에 전송, 등록 토큰을 구독 처리)

토큰이 업데이트될 때마다 알림을 받으려면 대리자를 등록

func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
    print("Firebase registration token: \(fcmToken!)")

    let dataDict:[String: String] = ["token": fcmToken!]
    NotificationCenter.default.post(name: Notification.Name("FCMToken"), object: nil, userInfo: dataDict)
    // If necessary send token to application server
}

APN 토큰을 명시적으로 FCM 등록 토큰에 매핑해야. 

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
    // 디바이스 토큰 요청 성공시
    print("deviceToken = \(deviceToken.debugDescription)")
    Messaging.messaging().apnsToken = deviceToken
}

 

iOS 앱에서 메시지 수신

UNUserNotificationCenterDelegate를 extension으로 추가하여 다음 코드로 FCM에서 디스플레이 알림을 수신. 

func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
    let userInfo = notification.request.content.userInfo
    print(userInfo)
    //푸시 받았을때
    completionHandler([.alert, .sound])
}

func userNotificationCenter(_ center: UNUserNotificationCenter,
                            didReceive response: UNNotificationResponse,
                            withCompletionHandler completionHandler: @escaping () -> Void) {        
    //푸시 클릭시
    let messageID = response.notification.request.identifier
    print(userInfo)
        
    let state = UIApplication.shared.applicationState
        
    if state == .background {            
    } else if state == .inactive {            
    } else if state == .active {            
    }        
    completionHandler()
}

자동 푸시 알림 처리

포그라운드 알림과 달리 백그라운드 알림의 경우 application(_:didReceiveRemoteNotification:fetchCompletionHandler:) 메서드를 통해 구현. 

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
                 fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
    if let messageID = userInfo[gcmMessageIDKey] {
        print("Message ID: \(messageID)")
    }
    print(userInfo)
    completionHandler(UIBackgroundFetchResult.newData)
}

 

 

 

 

더보기
더보기

전체 코드 

import UIKit
import Firebase

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        
        UNUserNotificationCenter.current().delegate = self
        let authOptions: UNAuthorizationOptions = [.alert, .sound]
        UNUserNotificationCenter.current().requestAuthorization(
            options: authOptions,
            completionHandler: {_, _ in })
        application.registerForRemoteNotifications()
        
        FirebaseApp.configure()
        Messaging.messaging().delegate = self

        window?.rootViewController = ViewController()
        return true
    }
}

extension AppDelegate : MessagingDelegate, UNUserNotificationCenterDelegate {
    
    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        print("deviceToken = \(deviceToken.debugDescription)")
        Messaging.messaging().apnsToken = deviceToken
    }
    
    func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
        print("FCMToken: \(fcmToken!)")
        
        let dataDict:[String: String] = ["token": fcmToken!]
        NotificationCenter.default.post(name: Notification.Name("FCMToken"), object: nil, userInfo: dataDict)
    }
    
    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        let userInfo = notification.request.content.userInfo
        print(userInfo)
        
        completionHandler([.alert, .sound])
    }
    // 백그라운드에서 알림
    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
                     fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {        
        if let messageID = userInfo["gcm.message_id"] {
            print(messageID)
        }
        completionHandler(UIBackgroundFetchResult.newData)
    }
    //푸시 클릭시 
    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                didReceive response: UNNotificationResponse,
                                withCompletionHandler completionHandler: @escaping () -> Void) {           
        let messageID = response.notification.request.identifier
        let userInfo = response.notification.request.content.userInfo
        print("\(messageID) - \(userInfo)")
        
        let state = UIApplication.shared.applicationState
        
        if state == .background {            
        } else if state == .inactive {            
        } else if state == .active {  
        }        
        completionHandler()
    }
}
728x90
반응형