[Swift] UNUserNotificationCenter - 3
정말 무궁무진한 UNUserNotificationCenter
의 세계 ~ 🎶 이번 주 안으로 끝내고 싶은데 될지 모르겠다 ㅋㅋ 저번 글에 이어서 다음 메소드를 또 얼른 봐야 하기 때문에 잡담 없이 바로 시작 ..!
그 다음에 볼
open func add(_ request: UNNotificationRequest, withCompletionHandler completionHandler: ((Error?) -> Void)? = nil)
이 메소드는 로컬 알림 전달을 예약하는 역할을 하고, 인자로 request
와 completionHandler
를 갖는다.
request
는 UNNotificationRequest
타입이고, 알림 Payload 와 트리거 정보를 포함하는 요청 객체이다. 주의해야할 점은 이 인자에는 nil
값이 들어갈 수 없다는 것이다.
completionHandler
는 add
의 결과와 함께 백그라운드 Thread 에서 실행되는 코드 블록이다. 이 블록은 반환값이 없고, 실행 중에 오류가 발생하면 해당 오류로 설정되는 Error
타입의 인자만 가지고 있다. 알림 예약이 성공적으로 되면 오류가 없다는 뜻이기 때문에 이 인자값은 nil
이 된다.
참고로 이 메소드는 로컬 알림만 예약 가능하고, 원격 알림 예약에는 사용할 수 없다고 한다. 이 메소드가 호출되면 시스템은 request
에 있는 트리거 조건을 추적하기 시작하고, 추적하다가 트리거 조건을 만족하게 되면 시스템은 알림을 전달한다. 만약 request
가 UNNotificationTrigger
객체를 가지고 있지 않다면 트리거 조건이 없다는 뜻이기 때문에 알림이 바로 전달된다. 이 메소드를 써서 알림을 예약하는 예시는
let content = UNMutableNotificationContent()
content.title = "알림 제목"
content.body = "알림 내용"
let notification = UNNotificationRequest(identifier: "com.example.mynotification", content: content, trigger: nil)
UNUserNotificationCenter.current().add(notification) { error in
if let error = error {
print("알림 예약 실패 이유 : \(error.localizedDescription)")
} else {
print("알림 예약 성공")
}
}
이런 식으로 구성할 수 있다. 이쯤 되니까 UNNotificationRequest
랑 UNNotificationTrigger
가 무엇인지 궁금해졌다 ㅋㅋ
UNNotificationRequest
는 로컬 알림을 예약하기 위한 요청에 해당하고,
@available(iOS 10.0, *)
open class UNNotificationRequest : NSObject, NSCopying, NSSecureCoding {
open var identifier: String { get }
@NSCopying open var content: UNNotificationContent { get }
@NSCopying open var trigger: UNNotificationTrigger? { get }
public convenience init(identifier: String, content: UNNotificationContent, trigger: UNNotificationTrigger?)
}
이렇게 구성되어 있다. 이 객체는 identifier
, content
, trigger
라는 인자를 통해 초기화되는데, 이 3가지에 대해 하나씩 살펴보면
identifier
는 앱에서 알림을 식별하기 위해 사용하는 알림 요청 고유 식별자이고, 이 식별자를
removePendingNotificationRequests(withIdentifiers:)
removeDeliveredNotifications(withIdentifiers:)
와 같은 메소드의 identifiers
인자에 넣어서 대기 중인 알림 요청이나 전달된 알림을 대체하거나 삭제할 수 있다. (위의 2가지 메소드는 이따가 하나씩 설명할 예정이다 ㅎ) 만약 새로운 알림을 예약할 때 기존에 이미 등록되어 있던 식별자를 사용하면 시스템은 기존에 그 식별자로 예약되어 있던 알림을 삭제하고 새로운 알림으로 대체하기 때문에 아예 새로운 알림을 예약하려고 하는 경우에는 기존에 사용하지 않은 식별자를 써야 한다. 로컬 알림의 경우에는 UNNotificationRequest
를 초기화할 때 사용하는 init(identifier:content:trigger:)
메소드를 사용해서 identifier
를 설정하지만, 원격 알림의 경우에는 원격 알림을 생성할 때 내가 APNs 요청 Header 에 지정했던 apns-collapse-id
Key 의 값으로 identifier
를 설정한다. 식별자가 따로 설정되지 않은 경우에는 시스템이 자동으로 할당해준다.
content
는 알림에 보일 콘텐츠에 해당하고, UNNotificationContent
타입의 변수이다. UNNotificationContent
에 대해서 들어본 적이 있는 것 같은데 ? 익숙한데 ? 했더니 저번 글에 공식 문서 링크만 놓고 갔던 친구였다 ㅋㅋ UNNotificationContent
내용이 조금 많아서 나중에 더 자세히 알아보는 것으로 하고, 편집할 수 없는 알림 컨텐츠들을 나타내는 친구라는 것만 일단 알아두자 ..!
trigger
는 알림 전달을 발생하도록 하는 트리거이고, UNNotificationTrigger
타입의 변수이다. 이 값이 nil
이라는 것은 트리거가 없다는 뜻이기 때문에 알림이 곧바로 전달된다. UNNotificationTrigger
내용도 꽤 (?) 있기 때문에 나중에 제대로 소개하는 것으로 하고, 로컬 알림 전달이나 원격 알림 전달이 발생하도록 하는 이벤트를 대표하는 추상 클래스라는 것 정도만 알고 넘어가자 !
UNNotificationRequest
객체는
let content = UNMutableNotificationContent()
content.title = "Lunch time"
content.body = "Food is cooked... let's eat!"
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 60.0, repeats: false)
let request = UNNotificationRequest(identifier: "com.example.mynotification", content: content, trigger: trigger)
이런 식으로 만들 수 있고, 이 코드는 60초 후에 Lunch time 이라는 제목과 Food is cooked… let’s eat! 이라는 내용의 알림이 전달되도록 한다.
UNNotificationTrigger
는 위에서도 말했듯이 로컬 알림 전달이나 원격 알림 전달이 발생하도록 하는 이벤트를 대표하는 추상 클래스이고, 객체를 만들 때 직접 만들지 않고 구체적인 하위 클래스를 객체화하는 방식을 사용한다. 그 다음에 바로 위에서 UNNotificationRequest
객체 만드는 예시 코드처럼 UNNotificationTrigger
객체를 UNNotificationTrigger
객체 만들 때 trigger
파라미터에 넣어주면 알림을 예약할 수 있다. UNNotificationTrigger
는
@available(iOS 10.0, *)
open class UNNotificationTrigger : NSObject, NSCopying, NSSecureCoding {
open var repeats: Bool { get }
}
이렇게 간단하게 repeats
라는 변수 하나만 가지고 있는데, 이 친구는 알림이 전달된 뒤에 시스템이 그 알림을 새로 예약하는지를 나타내는 Bool
타입의 변수이다. 만약 이 프로퍼티가 false
이면 시스템은 그 알림을 한번만 전달하고, 이 프로퍼티가 true
이면 시스템은 그 알림 요청이 자동으로 다시 예약되어서 트리거 조건을 만족할 때마다 알림이 전달된다. 이쯤에서 방금 전에 언급한 구체적인 하위 클래스가 무엇인지 궁금해졌다 ㅋㅋ 구체적인 하위 클래스에는
UNTimeIntervalNotificationTrigger
UNCalendarNotificationTrigger
UNLocationNotificationTrigger
UNPushNotificationTrigger
이렇게 4가지가 있다고 하고, 하나씩 살펴보면
UNTimeIntervalNotificationTrigger
는 특정 시간이 흐른 후 시스템이 로컬 알림을 전달하도록 하는 트리거 조건이고,
@available(iOS 10.0, *)
open class UNTimeIntervalNotificationTrigger : UNNotificationTrigger {
open var timeInterval: TimeInterval { get }
public convenience init(timeInterval: TimeInterval, repeats: Bool)
open func nextTriggerDate() -> Date?
}
이렇게 구성되어 있다. timeInterval
이라는 TimeInterval
타입의 변수와 Date
타입을 반환하는 nextTriggerDate()
메소드를 가지고 있고, timeInterval
은 시스템이 로컬 알림을 전달하도록 하는 트리거가 생성되는 시간 간격 (초 단위) 에 해당한다. 이 프로퍼티의 값은 타이머처럼 시간이 지남에 따라 업데이트되는 것이 아니기 때문에 다음 트리거가 언제 실행되는지 알려면 다음 트리거 조건을 만족하는 날짜를 반환하는 nextTriggerDate()
메소드를 호출해야 한다. UNTimeIntervalNotificationTrigger
객체는
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: (30*60), repeats: false)
이런 식으로 만들 수 있고, 이 코드는 1,800초 (30분) 뒤에 트리거가 생성되도록 한다 (알림이 전달된다) 는 뜻이다. repeats
인자는 상위 클래스인 UNNotificationTrigger
를 통해 받아온 것이고, 생성된 트리거를 반복할지를 나타내는데 위 코드에서는 이 인자에 false
를 넣었기 때문에 이 트리거가 반복되지 않는다.
UNCalendarNotificationTrigger
는 특정 날짜와 시간에 시스템이 로컬 알림을 전달하도록 하는 트리거 조건이고,
@available(iOS 10.0, *)
open class UNCalendarNotificationTrigger : UNNotificationTrigger {
open var dateComponents: DateComponents { get }
public convenience init(dateMatching dateComponents: DateComponents, repeats: Bool)
open func nextTriggerDate() -> Date?
}
이렇게 구성되어 있다. dateComponents
라는 DateComponents
타입의 변수와 Date
타입을 반환하는 nextTriggerDate()
메소드를 가지고 있고, dateComponents
는 시스템이 로컬 알림을 전달하도록 하는 트리거가 생성되는 특정 날짜와 시간에 해당한다. UNTimeIntervalNotificationTrigger
와 마찬가지로 다음 트리거가 언제 실행되는지 알려면 다음 트리거 조건을 만족하는 날짜를 반환하는 nextTriggerDate()
메소드를 호출하면 되고, 상위 클래스에서 받아온 repeats
에 어떤 값이 설정되어 있는지에 따라 이 트리거의 반복 여부가 결정된다. 예를 들어, 매일 오전 8시 30분마다 알림을 전달하도록 하고 싶으면
var date = DateComponents()
date.hour = 8
date.minute = 30
let trigger = UNCalendarNotificationTrigger(dateMatching: date, repeats: true)
이런 식으로 UNCalendarNotificationTrigger
객체를 만들면 되고, 하루만 오전 8시 30분에 알림이 전달되게 하고 싶으면 위의 코드에서 repeats
를 false
로 바꾸어 주면 된다.
UNLocationNotificationTrigger
는 사용자의 기기가 특정 지역에 도착하거나 떠날 때 시스템이 로컬 알림을 전달하도록 하는 트리거 조건이고,
@available(iOS 10.0, *)
open class UNLocationNotificationTrigger : UNNotificationTrigger {
@NSCopying open var region: CLRegion { get }
public convenience init(region: CLRegion, repeats: Bool)
}
이렇게 구성되어 있다. region
이라는 변수 하나만 가지고 있고, 이 변수는 시스템이 로컬 알림을 전달하도록 하는 트리거가 생성되는 특정 지역에 해당한다. 이때 서로 다른 지역으로 알림 요청을 예약할 때 같은 식별자의 CLRegion
을 쓰게 되면 예상하지 못 하게 동작할 수 있기 때문에 CLRegion
의 식별자는 고유한 특성을 띄고 있어야 한다. (말 그대로 식별자이기 때문에 어떻게 보면 고유한 특성을 띄는게 당연하다 ㅎ) 알림 전달되는 시점을 사용자의 기기가 그 지역에 도달했을 때만으로 할지, 떠났을 때만으로 할지, 이 2가지 경우 모두로 할지 정하려면 지역 정보 (region
) 를 구성할 때 CLRegion
의 notifyOnEntry
프로퍼티와 notifyOnExit
프로퍼티를 쓰면 된다. notifyOnEntry
프로퍼티는 기기가 그 지역에 도달했을 때의 알림 전달 가능 여부를 나타내는 Bool
타입의 변수이고, notifyOnExit
프로퍼티는 기기가 그 지역을 떠났을 때의 알림 전달 가능 여부를 나타내는 Bool
타입의 변수이다. 이 트리거를 사용한 예시 코드는
let center = CLLocationCoordinate2D(latitude: 37.335400, longitude: -122.009201)
let region = CLCircularRegion(center: center, radius: 2000.0, identifier: "Headquarters")
region.notifyOnEntry = true
region.notifyOnExit = false
let trigger = UNLocationNotificationTrigger(region: region, repeats: false)
이런 식으로 구성할 수 있고, 이 코드는 위도 37.335400, 경도 -122.009201 인 지점을 기준으로 2km 반경에 있는 지역에 기기가 들어가면 트리거가 생성되도록 한다는 뜻이다. 참고로 이 트리거는 동시에 예약할 수 있는 개수가 제한되어 있고, Core Location 사용 권한이 있으면서 앱을 사용하는 동안 (when-in-use permission) 허용하도록 설정되어 있어야 이 트리거가 정상적으로 작동한다.
UNPushNotificationTrigger
는 Apple Push Notification service (APNs) 가 알림을 보냈음을 나타내는 트리거 조건이고,
@available(iOS 10.0, *)
open class UNPushNotificationTrigger : UNNotificationTrigger {
}
이렇게 아무 것도 없는 형태로 구성되어 있다 😅 참고로 이 객체는 내가 직접 만드는 것이 아니라 시스템이 만들고, 앱에 전달된 알림 요청을 관리할 때 이 객체를 만날 수 있다. 공식 문서에서도 딱히 이 친구에 대해 더 자세히 다루고 있지 않기 때문에 여기까지만 알아보겠다 ㅎ
그 다음에 알아볼 메소드는
open func add(_ request: UNNotificationRequest) async throws
인데, 이것도 마찬가지로 async
가 붙어있기 때문에 위에서 잔뜩 설명한
open func add(_ request: UNNotificationRequest, withCompletionHandler completionHandler: ((Error?) -> Void)? = nil)
메소드의 Concurrency 버전이라는 것만 알아두고 Concurrency 에 대해 공부하고 나서 제대로 알아보도록 하자 🧐
원래 계획은 UNNotificationContent
에 대한 내용까지 다루고 글 마무리하기였는데 역시 내 계획대로 되지 않았다 ^^ 다음 글에서 이 친구에 대해 알아보는걸로 하고 이쯤에서 마무리 !!