Notification Service Extension을 사용하면 푸시 알림이 사용자에게 표시되기 전에 가로채고 수정할 수 있습니다. 이를 통해 다음이 가능합니다:
- 백그라운드 데이터 처리
- 사용자 지정 스타일(색상, 아이콘, 진동)
- 리치 미디어 첨부 파일(이미지, 비디오)
- 확인된 전달/분석
- 액션 버튼 옵션
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” 경고는 무시하세요.
<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를 앱에 전달합니다. 이 메서드가 호출되기 전에 알림 페이로드를 가져와서 (원하는 경우) 사용자에게 표시되기 전에 업데이트할 수 있습니다.
이 예시에서는 다음 데이터와 함께 알림을 보냅니다:
{
"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 target과 OneSignalNotificationServiceExtension 타겟이 동일하고 올바른지 확인하세요:
- Supported Destinations
- Minimum Deployment(iOS 14.5 이상)
Cocoapods를 사용하는 경우 Podfile의 main target과 일치하는지 확인하여 빌드 오류를 방지하세요.
Xcode의 Main App Target 예시
Xcode의 OneSignalNotificationServiceExtension Target 예시
OneSignalNotificationServiceExtension > Info 탭을 계속 진행하여 NSExtension 키를 확장합니다. 다음이 표시되는지 확인하세요:
<dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.usernotifications.service</string>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).NotificationService</string>
</dict>
예시:
Objective-C를 사용하는 경우 $(PRODUCT_MODULE_NAME).NotificationService 대신 NotificationService를 사용하세요.
“Copy only when installing” 끄기
main app target > Build Phases > Embed App Extensions를 선택합니다. “Copy only when installing”이 선택되지 않았는지 확인하세요. 선택되어 있으면 선택 해제하세요:
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으로 설정합니다.
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 선택
이 장치에 메시지가 포함된 알림을 보냅니다(Create message API에서 보내는 경우 contents 속성 사용). 이 예시에서 페이로드는 다음과 같습니다:
//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"
]
}'
앱이 실행 중이거나 실행 중이 아닐 때 로그된 메시지가 표시되어야 합니다.
메시지가 표시되지 않으면 앱에서 OneSignal을 제거하고 Mobile SDK Setup을 다시 따라 OneSignal을 올바르게 설정했는지 확인하세요.