메인 콘텐츠로 건너뛰기

개요

Notification Service Extension을 사용하면 푸시 알림이 사용자에게 표시되기 전에 가로채고 수정할 수 있습니다. 이를 통해 다음이 가능합니다:
  • 백그라운드 데이터 처리
  • 사용자 지정 스타일(색상, 아이콘, 진동)
  • 리치 미디어 첨부 파일(이미지, 비디오)
  • 확인된 전달/분석
  • 액션 버튼 옵션
OSNotification 클래스를 통해 OneSignal에서 전송된 푸시 알림의 데이터에 액세스할 수 있습니다.

Android Notification Service Extension

사용자에게 표시되기 전에 알림을 처리할 수 있습니다. 일반적인 사용 사례는 다음과 같습니다:
  • 알림 표시 여부와 관계없이 백그라운드에서 데이터 수신
  • 사용자 지정 강조 색상, 진동 패턴 또는 기타 NotificationCompat 옵션과 같은 클라이언트 측 앱 로직에 따라 특정 알림 설정 재정의
자세한 내용은 Android의 NotificationCompat 옵션 문서를 참조하세요.

1. Service Extension용 클래스 생성

INotificationServiceExtension을 확장하는 클래스를 만들고 onNotificationReceived 메서드를 구현합니다. onNotificationReceived 메서드 매개변수는 INotificationReceivedEvent 유형의 event입니다.
package your.package.name

import androidx.annotation.Keep;
import com.onesignal.notifications.IActionButton;
import com.onesignal.notifications.IDisplayableMutableNotification;
import com.onesignal.notifications.INotificationReceivedEvent;
import com.onesignal.notifications.INotificationServiceExtension;

@Keep // Keep is required to prevent minification from renaming or removing your class
public class NotificationServiceExtension implements INotificationServiceExtension {

     @Override
     public void onNotificationReceived(INotificationReceivedEvent event) {
        IDisplayableMutableNotification notification = event.getNotification();

        if (notification.getActionButtons() != null) {
           for (IActionButton button : notification.getActionButtons()) {
              // you can modify your action buttons here
           }
        }

     /* Add customizations here. See examples below for additional methods to modify the notification*/
     }
}

2. 사용 사례 예시

다음은 위의 템플릿 Notification Service Extension 클래스에서 구현할 수 있는 일반적인 예시입니다.
  • 알림 표시 방지
  • 사용자 지정 필드 추가
  • 알림 색상 및 아이콘 변경
event.preventDefault();

//Do some async work, then decide to show or dismiss
new Thread(() -> {
    try {
        Thread.sleep(1000);
    } catch (InterruptedException ignored) {}

    //Manually show the notification
    event.getNotification().display();
}).start();

3. AndroidManifest.xml에 Service Extension 추가

AndroidManifest.xml 파일의 application 태그 내에 클래스 이름과 값을 meta-data로 추가합니다. “unused” 경고는 무시하세요.
XML
<application>
  <!-- Keep android:name as shown, set android:value toyour class fully name spaced-->
  <meta-data
    android:name="com.onesignal.NotificationServiceExtension"
    android:value="com.onesignal.example.NotificationServiceExtension" />
</application>

iOS Notification Service Extension

UNNotificationServiceExtension을 사용하면 푸시 알림이 사용자에게 표시되기 전에 콘텐츠를 수정할 수 있으며 다음과 같은 다른 중요한 기능에 필요합니다: 앱에 대한 Mobile SDK 설정 지침을 따랐다면 이미 설정했을 가능성이 높지만 이 섹션에서는 OneSignal 알림 페이로드 데이터에 액세스하고 발생할 수 있는 문제를 해결하는 방법을 설명합니다.

iOS 푸시 페이로드 가져오기

Mobile SDK 설정을 따르면 OneSignalNotificationServiceExtension에 코드를 추가하는 섹션이 나옵니다. 해당 코드에는 OneSignalExtension.didReceiveNotificationExtensionRequest 메서드가 있습니다. 여기서 사용자에게 표시되기 전에 알림의 bestAttemptContent를 앱에 전달합니다. 이 메서드가 호출되기 전에 알림 페이로드를 가져와서 (원하는 경우) 사용자에게 표시되기 전에 업데이트할 수 있습니다. 이 예시에서는 다음 데이터와 함께 알림을 보냅니다:
JSON
{
  "app_id": "YOUR_APP_ID",
  "target_channel": "push",
  "headings": {"en": "The message title"},
  "contents": {"en": "The message contents"},
  "data":{
    "additional_data_key_1":"value_1",
    "additional_data_key_2":"value_2"
    },
  "include_subscription_ids": ["SUBSCRIPTION_ID_1"]
}
a 매개변수를 사용하여 custom 객체를 통해 OneSignalNotificationServiceExtension 내에서 이 추가 data에 액세스할 수 있습니다.
// Check if `bestAttemptContent` exists
if let bestAttemptContent = bestAttemptContent {

    // Try to retrieve the "custom" data from the notification payload
    if let customData = bestAttemptContent.userInfo["custom"] as? [String: Any],
       let additionalData = customData["a"] as? [String: Any] {

        // Convert the `additionalData` dictionary to a JSON string for logging
        if let jsonData = try? JSONSerialization.data(withJSONObject: additionalData, options: .prettyPrinted),
           let jsonString = String(data: jsonData, encoding: .utf8) {
            // Successfully converted to JSON; log the formatted JSON string
            print("The additionalData dictionary in JSON format:\n\(jsonString)")
        } else {
            // Failed to convert the `additionalData` dictionary to JSON
            print("Failed to convert additionalData to JSON format.")
        }
    }

    // Try to retrieve the "aps" data from the notification payload
    if let messageData = bestAttemptContent.userInfo["aps"] as? [String: Any],
       let apsData = messageData["alert"] as? [String: Any],
       let body = apsData["body"] as? String,  // Extract the "body" of the alert
       let title = apsData["title"] as? String {  // Extract the "title" of the alert
        // Successfully retrieved the body and title; log the values
        print("The message contents is: \(body), message headings is: \(title)")
    } else {
        // Failed to retrieve the "aps" data or its contents
        print("Unable to retrieve apsData")
    }

    // Pass the notification to OneSignal for further processing
    OneSignalExtension.didReceiveNotificationExtensionRequest(self.receivedRequest,
                                                              with: bestAttemptContent,
                                                              withContentHandler: self.contentHandler)
}
콘솔 출력 예시:
The additionalData dictionary in JSON format:
{
  "additional_data_key_1" : "value_1",
  "additional_data_key_2" : "value_2"
}
The message contents is: The message contents, message headings is: The message title

iOS Notification Service Extension 문제 해결

이 가이드는 iOS 모바일 앱에서 이미지, 액션 버튼 또는 확인된 전달이 표시되지 않는 문제를 디버깅하기 위한 것입니다. Xcode 설정 확인 General > Targets에서 main app targetOneSignalNotificationServiceExtension 타겟이 동일하고 올바른지 확인하세요:
  • Supported Destinations
  • Minimum Deployment(iOS 14.5 이상)
Cocoapods를 사용하는 경우 Podfile의 main target과 일치하는지 확인하여 빌드 오류를 방지하세요.

Xcode의 Main App Target 예시

Xcode의 OneSignalNotificationServiceExtension Target 예시

OneSignalNotificationServiceExtension > Info 탭을 계속 진행하여 NSExtension 키를 확장합니다. 다음이 표시되는지 확인하세요:
XML
 <dict>
   <key>NSExtensionPointIdentifier</key>
   <string>com.apple.usernotifications.service</string>
   <key>NSExtensionPrincipalClass</key>
   <string>$(PRODUCT_MODULE_NAME).NotificationService</string>
 </dict>
예시:

Info 탭의 NSExtension 키 예시

Objective-C를 사용하는 경우 $(PRODUCT_MODULE_NAME).NotificationService 대신 NotificationService를 사용하세요.
“Copy only when installing” 끄기 main app target > Build Phases > Embed App Extensions를 선택합니다. “Copy only when installing”이 선택되지 않았는지 확인하세요. 선택되어 있으면 선택 해제하세요:

Main app target 빌드 단계 설정

iOS Notification Service Extension 디버깅

다음 단계에 따라 Notification Service Extension이 올바르게 설정되었는지 확인하세요.

1. OneSignalNotificationServiceExtension 코드 업데이트

NotificationService.m 또는 NotificationService.swift를 열고 전체 파일 내용을 아래 코드로 바꾸세요. (코드는 원래 설정 코드와 동일하며 추가 로깅만 추가됩니다.) YOUR_BUNDLE_ID를 실제 Bundle ID로 바꾸세요.
import UserNotifications
import OneSignalExtension
import os.log

class NotificationService: UNNotificationServiceExtension {

    var contentHandler: ((UNNotificationContent) -> Void)?
    var receivedRequest: UNNotificationRequest!
    var bestAttemptContent: UNMutableNotificationContent?

    override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
        self.receivedRequest = request
        self.contentHandler = contentHandler
        self.bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent)

        let userInfo = request.content.userInfo
        let custom = userInfo["custom"]
        print("Running NotificationServiceExtension: userInfo = \(userInfo.description)")
        print("Running NotificationServiceExtension: custom = \(custom.debugDescription)")
        //debug log types need to be enabled in Console > Action > Include Debug Messages
        //Replace YOUR_BUNDLE_ID with your actual Bundle ID
        os_log("%{public}@", log: OSLog(subsystem: "YOUR_BUNDLE_ID", category: "OneSignalNotificationServiceExtension"), type: OSLogType.debug, userInfo.debugDescription)

        if let bestAttemptContent = bestAttemptContent {
            /* DEBUGGING: Uncomment the 2 lines below to check this extension is executing
                          Note, this extension only runs when mutable-content is set
                          Setting an attachment or action buttons automatically adds this */
            print("Running NotificationServiceExtension")
            bestAttemptContent.body = "[Modified] " + bestAttemptContent.body

            OneSignalExtension.didReceiveNotificationExtensionRequest(self.receivedRequest, with: bestAttemptContent, withContentHandler: self.contentHandler)
        }
    }

    override func serviceExtensionTimeWillExpire() {
        // Called just before the extension will be terminated by the system.
        // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used.
        if let contentHandler = contentHandler, let bestAttemptContent =  bestAttemptContent {
            OneSignalExtension.serviceExtensionTimeWillExpireRequest(self.receivedRequest, with: self.bestAttemptContent)
            contentHandler(bestAttemptContent)
        }
    }

}

2. Active Scheme 변경

Active Scheme을 OneSignalNotificationServiceExtension으로 설정합니다.

Xcode Active Scheme 선택

3. 프로젝트 빌드 및 실행

Xcode에서 실제 장치에서 프로젝트를 빌드하고 실행합니다.

4. Console 열기

Xcode에서 Window > Devices and Simulators를 선택합니다.

Xcode Devices and Simulators 창

연결된 장치가 표시되어야 합니다. Open Console을 선택합니다.

장치 콘솔 액세스 버튼

5. Console 확인

Console에서:
  • Action > Include Debug Messages 선택
  • CATEGORY로 OneSignalNotificationServiceExtension 검색
  • Start 선택

Console 디버깅 구성

이 장치에 메시지가 포함된 알림을 보냅니다(Create message API에서 보내는 경우 contents 속성 사용). 이 예시에서 페이로드는 다음과 같습니다:
cURL
  //Replace with your own app data:
  //YOUR_API_KEY, YOUR_APP_ID, SUBSCRIPTION_ID_1

curl --request POST \
 --url 'https://api.onesignal.com/notifications' \
 --header 'Authorization: Key YOUR_API_KEY' \
 --header 'accept: application/json' \
 --header 'content-type: application/json' \
 --data '
{
"app_id": "YOUR_APP_ID",
"target_channel": "push",
"headings": {"en": "The message title"},
"contents": {"en": "The message contents"},
"data":{"additional_data_key_1":"value_1","additional_data_key_2":"value_2"},
"include_subscription_ids": [
"SUBSCRIPTION_ID_1"
]
}'
앱이 실행 중이거나 실행 중이 아닐 때 로그된 메시지가 표시되어야 합니다.

Console 디버그 출력 예시

메시지가 표시되지 않으면 앱에서 OneSignal을 제거하고 Mobile SDK Setup을 다시 따라 OneSignal을 올바르게 설정했는지 확인하세요.