iOS

[Firebase] Firebase Cloud Messaging (FCM)

빨간체리반지 2020. 8. 16. 16:22

Apple Notification

- 앱 이름, 작은 앱 아이콘, 텍스트 형식의 메시지 정보를 포함

​- 사용자는 받은 푸시 메시지를 상호작용(Tab/Swipe)하면, 해당 푸시메시지는 화면에서 지워지고, 해당 앱을 열어 관련된 정보를 보여줍니다.

- 텍스트를 제외한 기타 소스(이미지, gif, 표 등등)의 경우 url의 형태로 추가 가능, 사용자가 푸시받은 메시지를 탭한 경우에만 화면에 띄울 수 있습니다. (iOS 10 이상 부터 지원)

- android와 달리 ios는 notification이 왔을 때, 사용자가 해당 푸시를 tab하지 않으면, 상호작용을 할 수 없다!

(notification으로 온 데이터를 앱내에 자동 저장하거나 그런기능을 지원하지 않음!)

 

 

FCM(iOS) 적용

1. Firebase 콘솔에 프로젝트 등록

2. GoogleService-Info.plist 파일 다운 후, 프로젝트에 Add하기

3. Firebase Cocoapod 적용

pod 'Firebase/Core'
pod 'Firebase/Messaging'
pod 'FirebaseInstanceID', "2.0.0"

4. Firebase Console > 설정 > 클라우드메시징 > APNs 인증서 파일 넣기

   (APNs Developer/Distribution Certificate 필요)

5. Xcode > Capabilities > Push Notification 활성화

6. Xcode > General > Linked Frameworks and Libraries > UserNotificatoins.framework 추가

7. 프로젝트 소스(AppDelegate)에 적용

 

- AppDelegate(Swift)

import UIKit
import FirebaseCore //import Firebase
import FirebaseMessaging
import FirebaseInstanceID 
import UserNotifications

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    
    var window: UIWindow?
    let gcmMessageIDKey = "gcm.message_id"
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
        UIApplication.shared.applicationIconBadgeNumber = UIApplication.shared.applicationIconBadgeNumber-1
        let token = InstanceID.instanceID()
        print("### \(token)")
        
        
        print("### didFinishLaunchingWithOptions")
        
        FirebaseApp.configure()
        
        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()
        Messaging.messaging().delegate = self
        return true
    }
    
    // MARK : device token
    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        print("APNs token retrieved: \(deviceToken.base64EncodedString())")
        
        // With swizzling disabled you must set the APNs token here.
        // subscribe topic in device
        Messaging.messaging().apnsToken = deviceToken
        print("\(deviceToken)")
    }
    
    // MARK : Push Delegate (<= iOS 9)
    // Receive_ Foreground
    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
        print("### didReceiveRemoteNotification : \(userInfo)")
        /* if let messageID = userInfo[gcmMessageIDKey] {
            print("Message ID: \(messageID)")
        } */
    }
    // Receive_ Background
    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
        print("### fetchCompletionHandler : \(userInfo)")
        /* if let messageID = userInfo[gcmMessageIDKey] {
         print("Message ID: \(messageID)")
         } */
        completionHandler(UIBackgroundFetchResult.newData)
    }
    func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
        print("Unable to register for remote notifications: \(error.localizedDescription)")
    }
}


// MARK : Push Delegate (>= iOS 10)
@available(iOS 10, *)
extension AppDelegate : UNUserNotificationCenterDelegate {
    // Receive_ Foreground
    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        let userInfo = notification.request.content.userInfo
        print("### willPresent userInfo\n\(userInfo)")
        /* if let messageID = userInfo[gcmMessageIDKey] {
            print("Message ID: \(messageID)")
        } */
        let json = userInfo as NSDictionary
        let message = json.object(forKey: "message") ?? ""
        print("### message : \(message)")
        completionHandler([])
    }
    // Receive_ Background
    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
        let notification = response.notification
        let userInfo = notification.request.content.userInfo
        print("### didReceive userInfo : \(userInfo)")
        // Category
        //print("### category : \(userInfo)")
        /*
        if(response.actionIdentifier == "imgPush"){
            print("### imgPush")
        } else if(response.actionIdentifier == "reply"){
            print("### reply")
        }else{
            print("### no category")
        }
        */
        /* if let messageID = userInfo[gcmMessageIDKey] os_10_data_message]
}

- AppDelegate(Objective-C)

#import <UIKit/UIKit.h>
#import <UserNotifications/UserNotifications.h>
@import Firebase;
@import FirebaseMessaging;
@import FirebaseInstanceID;
@import UserNotifications;

@interface AppDelegate : UIResponder <UIApplicationDelegate, UNUserNotificationCenterDelegate>

@property (strong, nonatomic) UIWindow *window;


@end
#import "AppDelegate.h"
@import UserNotifications;


@implementation AppDelegate

//----------didFinishLaunchingWithOptions
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Override point for customization after application launch.
    
    //firebaseApp 공유 인스턴스 구성
    [FIRApp configure];
    
    //메시지 대리자 설정
    [FIRMessaging messaging].delegate = self;
    
    // instance Token
    NSString *fcmToken = [FIRMessaging messaging].FCMToken;
    NSLog(@"iid : %@", fcmToken);
    
    /// register this App to Firebase Server
    if ([UNUserNotificationCenter class] != nil) {
        // iOS 10 or later
        // For iOS 10 display notification (sent via APNS)
        [UNUserNotificationCenter currentNotificationCenter].delegate = self;
        UNAuthorizationOptions authOptions = UNAuthorizationOptionAlert |
        UNAuthorizationOptionSound | UNAuthorizationOptionBadge;
        [[UNUserNotificationCenter currentNotificationCenter]
         requestAuthorizationWithOptions:authOptions
         completionHandler:^(BOOL granted, NSError * _Nullable error) {
             // ...
         }];
    } else {
        // iOS 10 notifications aren't available; fall back to iOS 8-9 notifications.
        UIUserNotificationType allNotificationTypes = (UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge);
        UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:allNotificationTypes categories:nil];
        [application registerUserNotificationSettings:settings];
    }
    [application registerForRemoteNotifications];
    
    return YES;
}

//------didReceiveRemoteNotification
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{
    NSLog(@"### Message Accepted!! : %@", userInfo);
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler{
    NSLog(@"### Message Accepted!! : %@", userInfo);
}

//-------didRegisterForRemoteNotificationsWithDeviceToken
- (void) application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{
    [FIRMessaging messaging].APNSToken = deviceToken;
    NSLog(@"### APNS token : %@", deviceToken);
}

//---------didFailToRegisterForRemoteNotificationsWithError
- (void) application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error{
    NSLog(@"### Register Notification Error : %@", error.localizedDescription);
}

//---------applicationDidEnterBackground
- (void)applicationDidEnterBackground:(UIApplication *)application {
    // 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.
    /// register this App to Firebase Server
    if ([UNUserNotificationCenter class] != nil) {
        // iOS 10 or later
        // For iOS 10 display notification (sent via APNS)
        [UNUserNotificationCenter currentNotificationCenter].delegate = self;
        UNAuthorizationOptions authOptions = UNAuthorizationOptionAlert |
        UNAuthorizationOptionSound | UNAuthorizationOptionBadge;
        [[UNUserNotificationCenter currentNotificationCenter]
         requestAuthorizationWithOptions:authOptions
         completionHandler:^(BOOL granted, NSError * _Nullable error) {
             // ...
         }];
    } else {
        // iOS 10 notifications aren't available; fall back to iOS 8-9 notifications.
        UIUserNotificationType allNotificationTypes =
        (UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge);
        UIUserNotificationSettings *settings =
        [UIUserNotificationSettings settingsForTypes:allNotificationTypes categories:nil];
        [application registerUserNotificationSettings:settings];
    }
    
    [application registerForRemoteNotifications];
    
}
/*
 - (void)applicationWillResignActive:(UIApplication *)application {
 // Sent when the application is about to move from active to inactive state. This can  NSLog(@"### MSG : %@", userInfo);
    completionHandler();
}

//------- refresh token
-(void)messaging:(FIRMessaging *)messaging didReceiveRegistrationToken:(NSString *)fcmToken{
    NSLog(@"### FCM token : %@", fcmToken);
}

//------start ios 10 data message
-(void)messaging:(FIRMessaging *)messaging didReceiveMessage:(FIRMessagingRemoteMessage *)remoteMessage{
    NSLog(@"### Received data MSG : %@", remoteMessage.appData);
}

@end

 

 

 

Error

 

* firebase linker command failed with exit code 1 오류 날 때 해결법

: 프로젝트 Target > Build Phases > Link Binary With Libraries 에서 libPods-프젝명 이렇게 되어있는거 없애주기

* 터미널에서 pod init 다음과 같이 오류 날 때 해결법

/Library/Ruby/Gems/2.3.0/gems/cocoapods-1.6.1/lib/cocoapods/command.rb:118:in `git_version': Failed to extract git version from `git --version` ("xcrun: error: active developer path (\"/Applications/Xcode10.1.app/Contents/Developer\") does not exist\nUse `sudo xcode-select --switch path/to/Xcode.app` to specify the Xcode that you wish to use for command line developer tools, or use `xcode-select --install` to install the standalone command line developer tools.\nSee `man xcode-select` for more details.\n") (RuntimeError)

from /Library/Ruby/Gems/2.3.0/gems/cocoapods-1.6.1/lib/cocoapods/command.rb:130:in `verify_minimum_git_version!'

from /Library/Ruby/Gems/2.3.0/gems/cocoapods-1.6.1/lib/cocoapods/command.rb:49:in `run'

from /Library/Ruby/Gems/2.3.0/gems/cocoapods-1.6.1/bin/pod:55:in `<top (required)>'

from /usr/local/bin/pod:22:in `load'

from /usr/local/bin/pod:22:in `<main>'

// 아래와 같이 입력해주면 된다
$ sudo xcode-select -switch /Applications/Xcode.app/Contents/Developer/

// (잘 설정되었는지 확인하는 방법....)
$ xcode-select --print-path // /Applications/Xcode.app/Contents/Developer 나오면 성공

 

 

 

 

참고 사이트

 

 

- Apple Developer (Notifications)

 

Notifications - System Capabilities - iOS - Human Interface Guidelines - Apple Developer

Notifications Apps can use notifications to provide timely and important information anytime, whether the device is locked or in use. For example, notifications can signal when a message has arrived, an event is about to occur, new data is available, or th

developer.apple.com

 

- FCM (iOS)

 

iOS 프로젝트에 Firebase 추가

기본 요건 Xcode 10.1 이상을 설치합니다. CocoaPods 1.4.0 이상을 설치합니다. Xcode에서 프로젝트를 엽니다. 프로젝트에서 iOS 8 이상을 타겟팅해야 합니다. Swift 프로젝트에서 Swift 3.0 이상을 사용해야 ��

firebase.google.com