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()
}
}
'초보 iOS 개발자의 일상 > 개발 업무' 카테고리의 다른 글
[iOS] Firebase Hosting - ipa 파일 다운로드 링크 생성 (AdHoc & Enterprise) (0) | 2021.05.17 |
---|---|
유지보수 작업들 (0) | 2021.05.17 |
[Objective-C] UIWebView -> WKWebView 마이그레이션 (0) | 2021.05.12 |
[Swift iOS] AVPlayer 플레이어 재생 컨트롤 (0) | 2021.05.11 |
AppStore Connect Operation Error - 외부 Framework Architecture 제거 (0) | 2021.05.07 |