ReactNative - [ios] fcm remote notification

nueiin·2024년 3월 8일

React Native

목록 보기
7/7

1. 서론

알림 뱃지를 수정하던 중 버그를 발견하였다. ios는 백그라운드 상태에서 알림 배너는 나타나지만 data 페이로드를 수신하지 못하였다.
깃 이슈를 찾아보니 나와 같은 문제가 많았는데, 대부분의 사람들이 페이로드만 수정하여 해결되었고.. 나는 content_available만 추가하는 것으로는 해결되지 않았다.

2. 해결

2.1 원인

  • 페이로드
    기존에는 fcm 알림 전송 페이로드에서 data, notification을 모두 전송해주었는데, FCM 메시지 정보에 따르면, 알림과 데이터 페이로드가 모두 포함된 메시지를 수신한 경우의 앱 동작은 앱이 백그라운드 상태인지 아니면 포그라운드 상태인지에 따라 다르다고 한다. (공식 문서를 자세히 읽자👿)

    - 백그라운드 상태: 알림 페이로드가 앱의 알림 목록에 수신되며 사용자가 알림을 탭한 경우에만 앱에서 데이터 페이로드를 처리
    - 포그라운드 상태: 앱에서 페이로드가 둘 다 제공되는 메시지 객체를 수신

    이것 때문에 백그라운드 상태에서 배너가 나타나고, 알림을 탭하면 앱이 실행되면서 foreground event가 찍히는 것이었다.
    => notification을 삭제하고 content_available true를 전달해주었다.

  
    $notification = array(
            'title'=>$sender,
            'body'=>$message,
            'badge'=>0,
            'sound'=>'default',
            );
 
    $data       = array(
            'title'=>$sender,
            'message'=>$message,
            'body'=>$message,
            'chat_id'=>$chat_id,
            'topic_id'=>$topic_id,
            'sound'=>'default',
            );

    $params     = array(
            // 'notification'=>$notification,	=> 삭제
           	'priority'=>'high',
            'data'=>$data,
            'to'=>$token,
            'content_available'=>true	// 추가 (메시지 수신 시 데이터 처리를 위한 앱 실행을 위해)
            );

  • AppDelegate.mm
#import "AppDelegate.h"
#import <Firebase.h>
#import <React/RCTBundleURLProvider.h>
#import "RNSplashScreen.h"
#import <React/RCTLinkingManager.h>
#import <UserNotifications/UserNotifications.h>

// Implement UNUserNotificationCengerDelegate to receive display notification via APNS for devices
// running iOS 10 and above.
@interface AppDelegate () <UNUserNotificationCenterDelegate>
@end

@implementation AppDelegate

NSString *const kGCMMessageIdKey = @"gcm.message_id";

- (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options {
  return [RCTLinkingManager application:application openURL:url options:options];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
  [FIRApp configure];
  [FIRMessaging messaging].delegate = self;

  self.moduleName = @"messenger";
  // You can add your custom initial props in the dictionary below.
  // They will be passed down to the ViewController used by React Native.
  self.initialProps = @{};

  // Define UNUserNotificationCenter
  // UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];

  // center.delegate = self;

  [application registerForRemoteNotifications];

  return [super application:application didFinishLaunchingWithOptions:launchOptions];
  // return YES;
}

- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
  // If you are receivingh a notification message while your app is in the background,
  // this callback will not be fired till the user taps on the notification launching the application.
  // TODO: Handle data of notification

  // With swizzling disabled you must let Messaging know about the message, for Anlytics
  // [[FIRMessaging messaging] appDidReceiveMessage:userInfo];
  NSLog(@"didReceiveRemoteNotification userInfo ======================");
  if (userInfo[kGCMMessageIdKey]) {
    NSLog(@"Message ID: %@", userInfo[kGCMMessageIdKey]);
  }

  NSLog(@"%@", userInfo);
}

// Required for the notification event. You must call the completion handler after handling the remote notification.
// - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
// fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
// {
//   // If you are receiving a notification message while your app is in the background,
//   // this callback will not be fired till the user taps on the notification launching the application.
//   // TODO: Handle data of notification

//   // With swizzling disabled you must let Messaging know about the message, for Analytics
//   // [[FIRMessaging messaging] appDidReceiveMessage:userInfo];

//   NSLog(@"didReceiveRemoteNotification fetchCompleteHandler ==========");
//   if (userInfo[kGCMMessageIdKey]) {
//     NSLog(@"Message ID: %@", userInfo[kGCMMessageIdKey]);
//   }

//   NSLog(@"%@", userInfo);

//   // Change this to your preferred presentation option
//   completionHandler(UIBackgroundFetchResultNewData);
// }

// Receive displayed notifications for iOS 10 devices.
// Handle incoming notification messages while app is in the foreground.
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
  willPresentNotification:(UNNotification *)notification
    withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler
{
  NSLog(@"WillPresentNotification =============================");
  NSDictionary *userInfo = notification.request.content.userInfo;

  // With swizzling disabled you must let Messaging know about the message, for Analytics
  // [[FIRMessaging messaging] appDidReceiveMessage:userInfo];

  if (userInfo[kGCMMessageIdKey]) {
    NSLog(@"Message ID: %@", userInfo[kGCMMessageIdKey]);
  }

  NSLog(@"%@", userInfo);

  // Chagne this to your preferred presentation option
  completionHandler(UNNotificationPresentationOptionAlert | UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound);
}

// Handle notification message after display notification is tapped by the user.
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
         withCompletionHandler:(void (^)(void))completionHandler
{
  NSDictionary *userInfo = response.notification.request.content.userInfo;
  if (userInfo[kGCMMessageIdKey]) {
    NSLog(@"Message ID: %@", userInfo[kGCMMessageIdKey]);
  }

  // With swizzling eisabled you must let Messaging know about the message, for Analytics
  // [[FIRMessaging messaging] appDidReceiveMessage:userInfo];

  NSLog(@"%@", userInfo);

  completionHandler();
}

- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
  NSLog(@"Unable to register for remote notifications: %@", error);
}


- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge
{
#if DEBUG
  return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"];
#else
  return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"];
#endif
}

@end

  • AppDelegate.h
#import <RCTAppDelegate.h>
#import <UIKit/UIKit.h>
#import <UserNotifications/UNUserNotificationCenter.h>
#import <Firebase/Firebase.h>

@interface AppDelegate : RCTAppDelegate <UNUserNotificationCenterDelegate, FIRMessagingDelegate>

@end

  • Podfile
target 'messenger' do
	...
	  #pod 'Firebase/Core'	=> 삭제
    ...
end

  • Info.plist

    	<key>FirebaseMessagingAutoInitEnabled</key>
    	<false/>

  • ios/<프로젝트명>/<프로젝트명>.entitlements

    	<key>aps-environment</key>
    	<string>production</string><!-- <string>development</string> -->

2.2 결과

> LOG setBackgroundMessageHandler=========
> LOG {"contentAvailable": true, "data": {"body": "안녕?", "chat_id": "", "message": "안녕?", "sound": "default", "title": "TEST", "topic_id": "_"}, "from": "", "messageId": ""}

참고

profile
풀스택 개발자

0개의 댓글