본문 바로가기

Programming/Objective-C

[iOS] 이미지 푸시 적용 (Rich Push) - Notification Service Extension

Rich Push 적용 결과

오늘은 소셜커머스, 쇼핑 및 뉴스 등 앱들의 푸시 메세지에서 흔히 볼 수 있는 이미지 푸시(aka. Rich Push)에 대해서 알아보자.

 

WWDC2016에서 iOS 10과 함께 소개된 케케묵은 기술을 왜 이제와서 알아보냐고?

그야 이제와서 이미지 푸시 적용을 하려고 하는 여러분을 위해서라는걸 알랑가 몰라..

 

내가 설명하는 내용만 잘 따라오면 삽질하는 시간을 90%이상 줄일 수 있다는 것만 명심!

자자 거두절미하고 바로 들어갑시다. 

 

 

샘플 프로젝트

 

IronK89/ios-objc-richpush

This is sample project for richpush (iOS_OjbC). Contribute to IronK89/ios-objc-richpush development by creating an account on GitHub.

github.com

 

개발환경

Objective-C, iOS 12.4.1, Xcode 12.4

Notification Service Extension이란?

푸시로 들어온 정보를 사용자에게 표시하기 전에 커스텀한 후 표시할 수 있도록 도와주는 기능이야.

iOS에서도 AOS처럼 푸시 커스터마이징이 가능하다!! 이거지. (iOS 10 이전엔 불가능했음.)

 

기본 푸시와 Notification Service Extension을 적용한 푸시의 비교 이미지.

Notification Service Extension 구현

그럼 이번엔 실제 프로젝트에 적용해보자. 

더보기

[warning]

해당 글에는 푸시 설정이 완료된 프로젝트를 기반으로 구현방법을 가이드하고 있음.

따라서 푸시(Remote Notification) 설정에 대한 내용은 포함하지 않는다.

(!) 필요시 댓글로 남겨주면, 고려는 해드릴게...

1) 프로젝트에 Notification Service Extension 추가

1-1) Xcode > File > New > Target 클릭

1-2) Target 선택화면에서 Notification Service Extension 선택 후 Next 클릭

1-3) Product Name 입력 후 Finish 클릭

여기까지 진행하면, 프로젝트에 NotificationServiceExtension이 추가될거야.

2) Notification Service Extension 실행

실제로 Push 내용을 커스텀하려면 NotificationService.m의 didReceiveNotificationRequest 함수를 수정해야돼. 

생성된 기본코드로 Push를 받았을때 결과물을 먼저 확인해보자.

(기본코드를 보면 title 뒤에 [modified]가 추가될 걸 미리 예상할 수 있음)

 

기본코드

- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
    self.contentHandler = contentHandler;
    self.bestAttemptContent = [request.content mutableCopy];
    
    // Modify the notification content here...
    self.bestAttemptContent.title = [NSString stringWithFormat:@"%@ [modified]", self.bestAttemptContent.title];
    
    self.contentHandler(self.bestAttemptContent);
}

 

여기서 잠깐! Notification Service Extension을 사용하기 위해서는 Push Payload에 "mutable-content" : 1 이라는 값을 필수로 포함시켜야 돼.

 

Push Payload

{
	"aps": {
		"content-available": 1,
		"alert": {
			"title": "강처리의 푸시 테스트",
			"body": "오늘은 Rich Push에 대해서 알아보자!"
		},
		"badge": 9,
		"sound": "default",
		"mutable-content" : 1
	}
}

 

Push 전송 결과

위 Push 전송 결과를 보면 "title" 뒤에 [modified]가 추가된 걸 확인할 수 있어.

여기까지 큰 문제없이 잘 따왔을 거라고 생각되는데, 도움이 필요하면 댓글 gogo~!

3) Notification Service Extension 커스텀

title 이외에 body를 변경하고, 이미지를 추가할건데, UNMutableNotificationContent에 body와 attachments property를 변경하면돼.

*attachment에 푸시정보로 받은 URL이미지를 적용할 수도 있음. 샘플코드에는 로컬이미지로 표시

더보기

다른 property는 없냐고? 아래 애플 공식 문서에서 확인해.

https://developer.apple.com/documentation/usernotifications/unmutablenotificationcontent

 

수정코드

- (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler {
    self.contentHandler = contentHandler;
    self.bestAttemptContent = [request.content mutableCopy];
    
    // Modify the notification content here...
    self.bestAttemptContent.title = [NSString stringWithFormat:@"%@ [modified]", self.bestAttemptContent.title];
    self.bestAttemptContent.body = [NSString stringWithFormat:@"%@ [modified]", self.bestAttemptContent.body];
    
    NSError *error;
    
    UNNotificationAttachment *attachment = [UNNotificationAttachment attachmentWithIdentifier:@"image" URL:[[NSBundle mainBundle] URLForResource:@"KANGCHEOLE_BANNER" withExtension:@"png"] options:nil error:&error];
    
    if (error) {
        NSLog(@"Notification Extension Error : %@",error);
    } else {
        self.bestAttemptContent.attachments = @[attachment];
    }
    
    self.contentHandler(self.bestAttemptContent);
}

 

Push 전송 결과

4) Notification Service Extension 예외상황

만약 didReceiveNotificationRequest에서 제한시간(30초) 안에 데이터 처리를 못한다면, serviceExtensionTimeWillExpire가 호출될거야.

 

어떤 경우냐고? 예를들어 Push Payload에 이미지 링크를 포함시키고, 해당 이미지를 다운받아서 표시해야하는 상황이야. 그런데 이미지 사이즈가 너무 크거나 모바일 네트워크 속도가 너무 느리다고 가정해보자.

그 이미지를 모두 다운로드 받을때까지 무한정 기다려줄가? No! 제한시간 30초가 초과되면, serviceExtensionTimeWillExpire 함수가 바로 호출돼.

 

이 함수에서 마지막으로 변경할 수 있는 기회를 한 번 더 주는거야. Last Chance!

 

이 함수를 기본 소스로 사용하면, Extension이 적용되지 않은 기본 Push로 표시되고, didReceiveNotificationRequest처럼 property를 수정하면 커스텀 Push로 표시돼.

- (void)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.
    self.contentHandler(self.bestAttemptContent);
}

 

FAQ

Notification Service Extension을 적용했는데, Push를 받아도 커스텀되지 않아요.

더보기

1. Extension의 Deployment Target 버전을 확인해볼 것. 

iOS 14.4로 설정돼있다면, 14.4 이상인 아이폰에서만 Extension이 적용된다는 건 상식으로 알아두면 좋아.

TMI로 만일 Deployment Target이 13이라면, 스토어에서 iOS12인 유저가 앱 설치시 Extension은 제외되고 설치된다는건 다 알지? 그리고 Notification Service Extension은 iOS 10.0 이상부터 지원가능 함

2. Push Payload를 확인해볼 것.

위에서 언급했듯이 "mutable-content" : 1가 반드시 포함돼야함

 

3. Remote notification 표시 설정 확인.

Notification Service Extension은 Remote notification이 Alert로 표시되도록 설정된 경우만 적용됨.

silent notification에도 적용불가

Notification Service Extension을 적용하고, 배포하려는데 아카이브시 에러가 나요.

더보기

App Extension 배포시 개발자 사이트에서 App Identifier를 추가하고, Provisioning Profile도 추가해야함.

추가로 아카이브전 메인 프로젝트의 Version과 Build를 Extension에도 동일하게 설정해야 경고가 발생하지 않음

P.S.

다음과 같은 상황에서는 댓글 남겨주는 센스! 🙏

- 작성된 글의 내용을 퍼가기(and 본인글 작성시 출처 표시)

- 궁금한 점이나 오류, 내용 보강 요청

- 평소 본인이 궁금하거나 포스팅이 필요한 주제

참고자료

https://developer.apple.com/documentation/usernotifications/unnotificationserviceextension?language=objc
https://developer.apple.com/documentation/usernotifications/modifying_content_in_newly_delivered_notifications?language=objc#overview
 

https://developer.apple.com/documentation/usernotifications/unmutablenotificationcontent