23 03 2018
iOS 에서 Swift에 FCM을 이용한 Push Topics 전송
- Firebase의 가이드,
https://firebase.google.com/docs/cloud-messaging/ios/client?authuser=0
- 1)프로젝트를 만든다
developer.apple.com에 해당 프로젝트의 정보를 등록해서
App IDS가 필요하고 Push Notifications 의 Certificates에 등록되어야 한다.
여기서 발급한 키는 이제 사용하지 않아도 된다
Apple에서 Keys라는 메뉴를 만들어서 하나의 Key로 Firebase의 인증을 처리 할수 있다
iOS의 프로젝트를 추가 위해서는
(1)bundle id (2)앱 ID prefix가 필요함
- 2)앱에 Firebase 추가
https://console.firebase.google.com/?pli=1에 접속 프로젝트를 추가한다.
화면을 보면 알수 있습니다.
여기에 bundle ID와 앱 ID 프리픽스를 입력하고 GoogelService-Info.plist를 다운받아서 프로젝트에 포함한다.
- 3)작업프로젝트에 Firebase SDK추가
아직 Xcode 프로젝트가 없으면 지금 만듭니다.
Podfile이 없으면 새로 만듭니다. (터미널을 오픈해서 처리)
1 2 |
$ cd your project directory $ pod init |
설치할 pod를 추가합니다. 다음과 같이 Podfile
에 Pod를 포함할 수 있습니다.
1 2 |
pod 'Firebase/Core' pod 'Firebase/Messaging' |
이렇게 하면 iOS 앱에서 Firebase 및 Firebase용 Google 애널리틱스를 사용하기 위한 필수 라이브러리가 추가됩니다. 현재 사용 가능한 pod 및 하위 스펙은 아래를 참조하세요. 기능별 설정 가이드에도 이 내용이 링크되어 있습니다.
pod를 설치하고 .xcworkspace 파일을 열어 Xcode에서 프로젝트를 확인합니다.
1 2 |
$ pod install $ open your project xcworkspace |
다음부터 Project를 실행할 경우 *.xcworkspace로 실행하면 된다.
- 4)APN 인증 키 업로드
Firebase에 APN 인증 키를 업로드합니다. 아직 APN 인증 키가 없으면 FCM에서 APN 구성을 참조하세요.
- Firebase 콘솔의 프로젝트 내에서 톱니바퀴 아이콘을 선택하고 프로젝트 설정을 선택한 다음 클라우드 메시징 탭을 선택합니다.
- iOS 앱 구성의 APN 인증 키에서 업로드 버튼을 클릭합니다.
- 키를 저장한 위치로 이동하여 키를 선택하고 열기를 클릭합니다. 키에 해당하는 키 ID( Apple Developer Member Center의 Certificates, Identifiers & Profiles에서 확인 가능)를 추가하고 업로드를 클릭합니다.
Apnkey다운받으면 Key명에 아디가 같이 있게 된다. (.AuthKey_아이디.p8형태)
- 5) AppDelegate.swift
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 |
import UIKit import UserNotifications import Firebase import FirebaseMessaging @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { var window: UIWindow? let gcmMessageIDKey = "gcm.message_id" func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { // Override point for customization after application launch. print("didFinishLaunchingWithOptions") FirebaseApp.configure() Messaging.messaging().delegate = self Messaging.messaging().shouldEstablishDirectChannel = true // [START register_for_notifications] if #available(iOS 10.0, *) { UNUserNotificationCenter.current().delegate = self let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound] UNUserNotificationCenter.current().requestAuthorization(options: authOptions, completionHandler: {_, _ in }) } else { let settings: UIUserNotificationSettings = UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil) application.registerUserNotificationSettings(settings) } application.registerForRemoteNotifications() // [END register_for_notifications] return true } func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]) { print("didReceiveRemoteNotification") Messaging.messaging().appDidReceiveMessage(userInfo) if let messageID = userInfo[gcmMessageIDKey] { print("Message ID: \(messageID)") } print(userInfo) } func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { print("didReceiveRemoteNotification: fetchCompletionHandler") //Messaging.messaging().appDidReceiveMessage(userInfo) if let messageID = userInfo[gcmMessageIDKey] { print("Message ID: \(messageID)") } print(userInfo) completionHandler(UIBackgroundFetchResult.newData) } func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { // Convert token to string print("APNs device token: \(deviceToken)") //Messaging.messaging().apnsToken = deviceToken //Messaging.messaging().subscribe(toTopic: "GOLFCLUB_NEWS") // Print it to console // Persist it in your backend in case it's new } func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { // Print the error to console (you should alert the user that registration failed) print("Unable to register for remote notifications: \(error.localizedDescription)") } func applicationWillResignActive(_ application: UIApplication) { // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. } func applicationDidEnterBackground(_ application: UIApplication) { // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. print("applicationDidEnterBackground") Messaging.messaging().shouldEstablishDirectChannel = false } func applicationWillEnterForeground(_ application: UIApplication) { // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background. print("applicationWillEnterForeground") } func applicationDidBecomeActive(_ application: UIApplication) { // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. print("applicationDidBecomeActive") FBHandler() } func applicationWillTerminate(_ application: UIApplication) { // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. } func FBHandler(){ Messaging.messaging().shouldEstablishDirectChannel = true } } // [START ios_10_message_handling] @available(iOS 10, *) extension AppDelegate : UNUserNotificationCenterDelegate { // Receive displayed notifications for iOS 10 devices. func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { let userInfo = notification.request.content.userInfo print("userNotificationCenter-UNNotificationPresentationOptions") // With swizzling disabled you must let Messaging know about the message, for Analytics //Messaging.messaging().appDidReceiveMessage(userInfo) if let messageID = userInfo[gcmMessageIDKey] { print("Message ID: \(messageID)") } // Print full message. print(userInfo) // Change this to your preferred presentation option completionHandler([.alert, .sound, .badge]) } func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { let userInfo = response.notification.request.content.userInfo print("userNotificationCenter") // Messaging.messaging().appDidReceiveMessage(userInfo) // Print message ID. if let messageID = userInfo[gcmMessageIDKey] { print("Message ID: \(messageID)") } // Print full message. print(userInfo) completionHandler() } } // [END ios_10_message_handling] extension AppDelegate : MessagingDelegate { func messaging(_ messaging: Messaging, didRefreshRegistrationToken fcmToken: String) { print("Firebase registration token: \(fcmToken)") if Messaging.messaging().fcmToken != nil { Messaging.messaging().subscribe(toTopic: "GOLFCLUB_NEWS") } // TODO: If necessary send token to application server. // Note: This callback is fired at each app startup and whenever a new token is generated. } // Receive data messages on iOS 10+ directly from FCM (bypassing APNs) when the app is in the foreground. // To enable direct data messages, you can set Messaging.messaging().shouldEstablishDirectChannel to true. func messaging(_ messaging: Messaging, didReceive remoteMessage: MessagingRemoteMessage) { print("Received data message: \(remoteMessage.appData)") } } |
- 6)Code Signing Identity도 수정한다.
- 7)실제 테스트 Firebase 의 GROW의 Notification에서 테스트 하시면 됩니다.
이렇게 하시면 잘됩니다.
TroubleShooting
만약 FirebaseMessaging이 컴파일이 안될경우 Podfile에 use_frameworks!를 포함하고 pod install을 다시해보면 됩니다.
다시 설치할 경우
1 2 3 4 |
<span class="pln">$ sudo gem install cocoapods</span><span class="pun">-</span><span class="pln">deintegrate cocoapods</span><span class="pun">-</span><span class="pln">clean $ pod deintegrate $ pod clean $ rm </span><span class="typ">Podfile</span> |
IMP-00038: Could not convert to environment character set’s handle Android push topics