본격적인 프로젝트 작업을 하기 전, 몇 가지 기능에 대한 확인 작업이 필요했는데 그 중 하나가 푸시 메세지의 UI의 커스텀이었다. 푸시가 왔을 때, 앱 진입만 하는 것이 아니라 해당 푸시에서 버튼을 클릭하여 원하는 작업을 진행하도록 해주는 기능이었는데 이를 구현하면서 삽질했던 내용을 정리해보고자 한다.
결론적으로 푸시의 UI는 위의 기능이 충족되는 형태로 충분히 커스텀이 가능하긴 했다. 다만 푸시 메세지가 오자마자 해당 UI로 오는 것이 아니라, 메세지를 밑으로 당기거나 꾹 눌렀을 때 커스텀된 UI가 표시된다.
NotificationContentExtension
은 [File-New-Target]에서 선택할 수 있고, 생성하게 되면 아래와 같이 타겟에 추가가 된다.
이렇게 추가된 NotificationContentExtension
은 여러 파일을 생성하는데 그중에 우리가 UI를 커스텀할 수 있는 스토리보드가 존재한다. 평소 코드로 뷰를 짜는데 익숙해서 스토리보드를 보고 당황하기는 했지만 그냥 차근차근 기존에 UIViewController
에서 뷰를 구현하듯이 해주면 된다.
여기서 중요한 부분이 didReceive(_ notification:)
구문인데 APNs를 수신하면 해당 구문이 호출되고 여기서 수신한 데이터를 원하는 형태로 출력되도록 가공해주면 된다.
이와 함께 UI는 크게 어렵지 않은 부분이나, 데이터 통신이나 교환이 필요할 경우에 기존 타겟의 것을 사용하고자 할 텐데 이럴 경우 타겟 멤버십이 달라 필요한 인스턴스 등을 사용하지 못 하게 된다. 따라서 기존 타겟의 인스턴스를 사용하고 싶다면 해당 파일의 멤버십에 NotificationContentExtension
의 타겟도 지정해주어야 한다.
NotificationContentExtension
으로 커스텀한 Push UI가 아무런 작업 없이 그대로 반영되면 좋겠지만, 커스텀이다보니 약간의 작업이 필요하다.
let category: UNNotificationCategory = .init(identifier: "myNotificationCategory", actions: [], intentIdentifiers: [], options: .customDismissAction)
UNUserNotificationCenter.current().setNotificationCategories([category])
본인이 식별할 수 있는 identifier를 하나 정하고 이를 토대로 UNNotificationCategory
인스턴스를 생성해준다. 해당 객체는 앱에서 액션 가능한 푸시 노티를 정의하기 위한 객체이고, 생성 시에 주입해준 identifier를 통해 노티 푸시가 넘어왔을 때 해당 푸시가 커스텀용인지 디폴트인지 구분해줄 수 있다.
해당 방법은 시뮬레이터에서 바로 푸시 테스트를 할 수 있다는 이점이 있다.
{
"Simulator Target Bundle" : "Bundle Identifier",
"aps" : {
"category": "myNotificationCategory",
"content-available": 1,
"alert" : {
"title" : "테스트 중",
"body" : "이거슨 그냥 테스트",
},
},
}
위와 같은 형식을 가진 aspn 파일을 구현하기만 하면 된다. 여기서 중요한 점은 타겟 번들과 aps 모두 작성이 되어야 하며, 특히 category 항목이 있어야 한다. 혹여 category 항목이 빠질 경우에는 우리가 만든 커스텀 푸시가 아닌 디폴트 푸시가 발송된다.
이렇게 작성한 aspn 파일을 시뮬레이터에 드래그하기만 하면 손쉽게 푸시 테스트를 할 수 있다. 다만 시뮬레이터의 한계인지 해당 작업으로는 잠금 화면에서의 푸시 테스트 시, 커스텀 UI가 출력되지 않는다. 따라서 실기기에서 테스트를 하는 방법도 진행할 수 밖에 없었다.
보통 로컬 푸시 관련해서 서칭하면 금세 나올 수 있는 방법으로 트리거를 만들어서 실행시켜주는 것이다.
func triggerTest() {
let notiContent: UNMutableNotificationContent = .init()
notiContent.categoryIdentifier = "myNotificationCategory"
notiContent.title = "테스트입니다"
notiContent.body = "제발 되면 좋겠다"
notiContent.userInfo = ["content-available": 1]
let trigger: UNTimeIntervalNotificationTrigger = .init(timeInterval: 3, repeats: false)
let request: UNNotificationRequest = .init(identifier: "Bundle Identifier", content: notiContent, trigger: trigger)
UNUserNotificationCenter.current().add(request) { error in
if let error {
print(error.localizedDescription)
}
}
}
먼저 푸시 노티에 들어갈 콘텐츠를 지정할 수 있게 해주는 UNMutableNotificationContent
인스턴스를 생성한다. 이후 타이틀이나 바디 등 본인이 넣고 싶은 내용을 넣어주는데 우리는 커스텀 UI를 테스트해보고 싶은 것이니 무조건 categoryIdentifier
에 UNNotificationCategory
인스턴스를 생성할 때 지정했던 identifier를 등록해주어야 한다.
이후에는 타이머에 대한 트리거를 만들고 해당 인스턴스들을 기반으로 UNNotificationRequest
를 생성해주고 UNUserNotificationCenter
에 등록해주면 실기기에서도 푸시 테스트를 해볼 수 있는 조건이 충족된다.
이렇게 만든 트리거는 본인이 원하는 시기에 실행되도록 액션만 등록해주면 된다.
프로젝트를 진행하면서 푸시를 써보기는 했어도 푸시에서 유저 인터렉트가 되도록 커스텀하고 이를 테스트하는 법도 처음이다 보니 자료 찾는 것조차도 버벅이었지만 결국에는 해결해낼 때 얻는 뿌듯함이 개발의 즐거움이지 않을까 싶다.
특히 푸시 테스트는 앞으로도 많이 해보게 될 내용일 듯 하여, 저 두가지 방법을 잘 기억하고 요긴하게 쓸 수 있도록 해야겠다.