사용자한테 알림을 표시하는 방법
User Notification은 사용자한테 알림을 주는 UNUserNotificationCenter 를 사용합니다.
사용자한테 알림을 주는 방법은 2가지 방법이 있습니다 (서버 푸쉬, 로컬 푸쉬)
서버 푸쉬는 서버로부터 알림을 받는 것 입니다.
로컬 푸쉬는 앱 자체에서 사용자한테 알림을 주는 것 입니다.
User Notification은 user notification 프레임워크 클래스들로부터 만들어지고 관리됩니다. 여기서 프레임워크는 UNUserNotificationCenter 클래스를 포함하여 user notification들을 스케줄하고 관리합니다.
UNUserNotificationCenter 객체에서는 notification을 관리하는데 첫 번째 단계로는 유저로부터 권한을 요청해야합니다.
사용자한테 알람 권한 받기
UNUserNotificationCenter 를 사용하여 알림을 받기 위해서는 우선 사용자에게 알람에 대한 권한을 받아야합니다.
권한을 받기 위해서는 다음과 같이 코드를 작성합니다.
options는 사용자한테 알람을 표시할 때 사용될 내용들이며 아래는 alert(사용자한테 표시), sound(알람에 소리), badge(알람에 표시할 배지)에 대한 권한을 받도록 options을 설정한 후 requestAuthorization 메서드를 사용하여 옵션을 포함하여 권한을 요청합니다.
import UserNotifications
class NotificationManager {
static let instance = NotificationManager() // 싱글톤: 어디서든 사용할 수 있는 유일한 인스턴스
func requestAuthorization() {
let options: UNAuthorizationOptions = [.alert, .sound, .badge]
UNUserNotificationCenter.current().requestAuthorization(options: options) { (success, error) in
if let error = error {
print("ERROR: \(error)")
} else {
print("SUCCESS")
}
}
}
}
알림 내용 설정하기
알림 설정하는 클래스
방법1. UNNotificationContent
UNNotificationContent 를 사용하면 내용을 수정할 수 없는 알림을 만들 수 있습니다.
UNNotificationContent 객체는 알림과 관련된 데이터를 포함합니다. 앱이 알림을 수신하면 연결된 UNNotificationRequest 객체에는 앱이 수신한 내용이 포함된 이 유형의 객체가 있습니다. content 객체를 사용하여 알림의 세부 정보를 가져올 수 있습니다. 시스템이 전달한 알림의 유형, 알림을 예약하기 전에 userInfo dictionary에 저장한 사용자 지정 데이터 및 첨부 파일을 포함하여 알림의 세부 정보를 얻을 수 있습니다.
추가로 UNNotificationContent 클래스의 인스턴스를 직접 만들면 안됩니다. 원격 알림같은 경우 시스템은 이 객체의 내용을 서버가 APNS 서버로 보내는 JSON 페이로드에서 파생됩니다. 로컬 알림의 경우 UNMutableNotificationContent 객체를 생성하고 해당 객체의 내용을 구성합니다.
방법2. UNMutableNotificationContent
UNMutableNotificationContent 를 사용하면 내용을 수정할 수 있는 알림을 만들 수 있습니다
UNMutableNotificationContent 는 UNNotificationConent 로부터 상속받는 하위 클래스입니다. trigger에서 timeInterval를 10으로 설정하면 알림을 설정하고 앱을 종료한지 10초 뒤에 알림이 울리게 됩니다.
badge 설정하기
사용자가 앱을 열었을 때 badge가 1로 표시되어 있는 것을 없애고 싶을 때는 다음과 같이 설정합니다.
struct ContentView: View {
@Environment(\.scenePhase) var scenePhase
var body: some View {
VStack(spacing: 40) {
Button("권한 받기") {
NotificationManager.instance.requestAuthorization()
}
Button("알람 설정") {
NotificationManager.instance.scheduleNotification()
}
}
.onChange(of: scenePhase, { oldValue, newValue in
if newValue == .active {
UNUserNotificationCenter.current().setBadgeCount(0) { error in
if let error = error {
print("에러발생:\(error)")
}
}
}
})
}
}
파일에 있는 데이터 알림으로 표시하기
알림으로 표시할 때 보여줄 메시지의 내용을 NSLocalizedString 메크로를 사용하면 앱의 리소스 파일들에 접근하여 좀 더 구체적인 문자열을 표시할 수 있습니다. localizedUserNotificationString(forkey: arguments:) 메서드는 시스템이 알림을 전달할 때까지 현지화된 문자열의 로딩을 지연시킵니다. 시스템이 알림을 전달하기 전에 사용자가 언어 설정을 변경하면, 시스템은 시스템이 알림을 예약할 때 사용 중인 언어 대신 현재 언어로 경고 텍스트를 업데이트합니다.
알림에 포함할 내용들
사용자한테 알림을 주기 위해서는 알림의 내용을 채워야합니다. 다음과 같은 종류의 내용들이 있습니다.
titile - 알림의 제목
subtitle - 알림의 부제목
body - 알림으로 전달할 메시지
badge - 몇개의 알림이 있는지 표시하는 숫자
sound - UNNotificationSound 타입으로 알림으로부터 받을 소리
unserinfo - set 또는 dictionary 로 알림으로부터 띄울 내용
알림에서 사용자한테 소리를 들려줘야하는 경우
sound 같은 경우 UNNotificationSound 객체를 만들어 사용자한테 소리를 알리는 것 입니다. UNNotificationSound를 사용하기 위해서는 다음과 같은 initializer를 만들어야합니다.
UNNotificationSound(named: UNNotificationSoundName): 이 초기자는 UNNotificationSoundName으로 지정된 소리를 UNNotificationSound 객체를 만듭니다.
var sound = UNNotificationSound.default
var sound = UNNotificationSound.defaultCritical
var sound = UNNotificationSound.defaultRingtone
var sound = UNNotificationSound.supportsSecureCoding
UNNotificationSoundName(rawValue: String): 이 초기자는 알림으로 들려줄 소리를 포함하는 파일의 이름으로 UNNotificationSoundName 객체를 만듭니다.
var sound = UNNotificationSoundName(rawValue: "filename")
알림에 이미지를 넣어야하는 경우
이미지를 넣어야 하는 경우 UNNotificationAttachment 를 사용해야 합니다. 이 클래스는 오디오, 이미지 또는 비디오를 알림에 추가할 수 있게 합니다. 사용하고자 하는 미디어는 반드시 디스크에 존재해야하며 지원하는 미디어 타입을 사용해야한다는 특징이 있습니다.
알림이 표시되기 전에 시스템이 알림의 경고를 표시하기 위해 첨부 파일을 제공하는 것은 개발자의 책임입니다. 로컬 알림의 경우 알림 콘텐츠를 만들 때 첨부 파일을 추가합니다. 원격 알림의 경우 notification service app extension를 사용하여 첨부된 파일을 다운로드한 다음 전달 전에 알림 콘텐츠에 추가합니다.
시스템은 관련된 알림을 표시하기 전에 첨부 파일을 유효성 검사합니다. 로컬 알림 요청에 손상된, 잘못된 또는 지원되지 않는 파일 형식의 파일을 첨부하면 시스템이 요청을 예약하지 않습니다. 원격 알림의 경우 시스템은 notification service app extension이 완료된 후에 첨부 파일을 유효성 검사합니다. 유효성이 검사된 후 시스템은 첨부된 파일을 첨부 데이터 저장소로 이동시켜 적절한 프로세스가 파일에 액세스할 수 있도록 합니다. 시스템은 앱 번들 내부에 위치한 첨부 파일을 복사합니다.
다음은 이미지를 추가하는 예시 코드입니다. 알림을 추가하는 코드로 UNNotificationAttachment 클래스를 사용하여 url 을 통해 이미지를 추가하였습니다.
func sendNotification() async {
let content = UNMutableNotificationContent()
content.title = "Reminder"
content.body = inputMessage
let idImage = "attach-\(UUID())"
if let urlImage = await getThumbnail(id: idImage) {
if let attachment = try? UNNotificationAttachment(identifier: idImage, url: urlImage, options: nil) {
content.attachments = [attachment]
}
}
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 10, repeats: false)
let id = "reminder-\(UUID())"
let request = UNNotificationRequest(identifier: id, content: content, trigger: trigger)
do {
let center = UNUserNotificationCenter.current()
try await center.add(request)
} catch {
print("Error: \(error)")
}
}
추가로 아래 코드는 위에 함수에서 이미지의 url 를 받기 위한 함수입니다. 이미지를 png 타입으로 변형한 후 저장한 다음 저장된 url를 반환합니다.
func getThumbnail(id: String) async -> URL? {
let manager = FileManager.default
if let docURL = manager.urls(for: .documentDirectory, in: .userDomainMask).first {
let fileURL = docURL.appendingPathComponent("\(id).png")
if let image = UIImage(named: "husky") {
if let thumbnail = await image.byPreparingThumbnail(ofSize: CGSize(width: 100, height: 100)) {
if let imageData = thumbnail.pngData() {
if let _ = try? imageData.write(to: fileURL) {
return fileURL
}
}
}
}
}
return nil
}
로컬 알림
알림을 띄우는 트리거
방법 1. UNTimeIntervalNotificationTrigger
UNTimeIntervalNotificationTrigger 를 사용하먄 일정 시간이 지났을 때 알림을 표시하도록 지정할 수 있습니다.
UNTimeIntervalNotificationTrigger(timeInterval: TimeInterval, repeats: Bool)
func scheduleNotification() {
let content = UNMutableNotificationContent()
content.title = "첫 알람"
content.subtitle = "서브 타이틀"
content.sound = .default
content.badge = 1
// time
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 10, repeats: false)
let requeset = UNNotificationRequest(
identifier: UUID().uuidString,
content: content,
trigger: trigger)
UNUserNotificationCenter.current().add(requeset)
}
방법 2. UNCalendarNotificationTrigger
UNCalendarNotificationTrigger 를 사용하면 특정한 날짜에 알림을 표시하도록 지정할 수 있습니다.
UNCalendarNotificationTrigger(dateMatching: DateComponents, repeats: Bool)
func scheduleNotification() {
let content = UNMutableNotificationContent() //
content.title = "첫 알람"
content.subtitle = "서브 타이틀"
content.sound = .default
content.badge = 1
// calendar
var dateComponents = DateComponents() // 매일 설정한 시간에 알림
dateComponents.hour = 19
dateComponents.minute = 16
dateComponents.weekday = 2
let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true)
let requeset = UNNotificationRequest(
identifier: UUID().uuidString,
content: content,
trigger: trigger)
UNUserNotificationCenter.current().add(requeset)
}
방법 3. UNLocationNotificationTrigger
UNLocationNotificationTrigger 를 사용하면 특정 지역에서 알림을 표시하도록 지정할 수 있습니다.
UNLocationNotificationTrigger(region: CLCircularRegion, repeats: Bool)
다음과 같이 위치 기반으로 특정 지역에 들어가거나 나갈 때 알림이 뜨도록 설정할 수 있습니다.
func scheduleNotification() {
let content = UNMutableNotificationContent() //
content.title = "첫 알람"
content.subtitle = "서브 타이틀"
content.sound = .default
content.badge = 1
// location
let coordinates = CLLocationCoordinate2D(latitude: 40.00, longitude: 50.00)
let region = CLCircularRegion(center: coordinates, radius: 100, identifier: UUID().uuidString)
let trigger = UNLocationNotificationTrigger(region: region, repeats: true)
region.notifyOnEntry = true // radius 반경 안에 들어갔을 때 트리거 작동 여부
region.notifyOnExit = false // radius 반경 밖으로 나갔을 때 트리가 작동 여부
let requeset = UNNotificationRequest(
identifier: UUID().uuidString,
content: content,
trigger: trigger)
UNUserNotificationCenter.current().add(requeset)
}
여러개의 알림을 그룹으로 만들기
여러개의 알림이 있으면 마지막에 있는 알림만 유저한테 알림으로 표시됩니다. 그러기 위해서는 identifier를 사용하여 여러개의 그룹으로 분리할 수 있습니다. UNMutableNotificationContent 클래스는 이런 목적으로 threadIdentifier 프로퍼티가 있습니다. 그리고 같은 identifier 알람들끼리 그룹으로 모이게 됩니다.
다음은 2개의 그룹에 각각 3개의 총 6개의 알림을 만드는 것입니다.
func sendNotification() async {
let listGroups = ["Group One", "Group Two"]
for group in listGroups {
for index in 1...3 {
let content = UNMutableNotificationContent()
content.title = "Reminder \(group)"
content.body = "\(index) - \(inputMessage)"
content.threadIdentifier = group
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 10, repeats: false)
let id = "reminder-\(UUID())"
let request = UNNotificationRequest(identifier: id, content: content, trigger: trigger)
do {
let center = UNUserNotificationCenter.current()
try await center.add(request)
} catch {
print("Error: \(error)")
}
}
}
}
알림과 상호작용하기
UNNotificationAction 클래스를 사용하면 알림을 아래로 드래그하여 사용자와 알림간의 상호작용을 할 수 있게 됩니다. 예를 들어 미팅 앱같은 경우에 앱을 들어가지 않고도 미팅의 초대를 수락하거나 거절할 수 있습니다. 그러기 위해서는 action 객체를 설정한 후UNNotificationCategory 객체에 더하여 시스템에 해당 UNNotificationCategory를 등록합니다.
예제 코드
UNNotificationAction으로 삭제를 위한 버튼을 만들어줍니다, 이후 터치를 했을 때 텍스트를 입력받을 수 있는 버튼 UNTextInputNotificationAction 객체를 만들어줍니다. 다음으로 만든 2개의 Action들을 UNNotificationCategory 에 추가한 후 center 에 설정합니다.
func sendNotification() async {
let center = UNUserNotificationCenter.current()
let groupID = "listActions"
let actionDelete = UNNotificationAction(identifier: "deleteButton", title: "Delete", options: .destructive)
let actionInput = UNTextInputNotificationAction(identifier: "inputField", title: "Message", options: [])
let category = UNNotificationCategory(identifier: groupID, actions: [actionDelete, actionInput], intentIdentifiers: [], options: [])
center.setNotificationCategories([category])
let content = UNMutableNotificationContent()
content.title = "Reminder"
content.body = inputMessage
content.categoryIdentifier = groupID
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 10, repeats: false)
let id = "reminder-\(UUID())"
let request = UNNotificationRequest(identifier: id, content: content, trigger: trigger)
do {
try await center.add(request)
} catch {
print("Error: \(error)")
}
}
import SwiftUI
import UserNotifications
import Observation
@Observable class ApplicationData: NSObject, UNUserNotificationCenterDelegate {
override init() {
super.init()
let center = UNUserNotificationCenter.current()
center.delegate = self
}
@MainActor
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse) async {
let identifier = response.actionIdentifier
if identifier == "deleteButton" {
print("Delete Message")
} else if identifier == "inputField" {
print("Send: \((response as! UNTextInputNotificationResponse).userText)")
}
}
}
import SwiftUI
import UserNotifications
struct ContentView: View {
@State private var inputMessage: String = ""
@State private var isButtonDisabled: Bool = false
var body: some View {
VStack(spacing: 12) {
HStack {
Text("Message:")
TextField("Insert Message", text: $inputMessage)
.textFieldStyle(.roundedBorder)
}
HStack {
Spacer()
Button("Post Notification") {
let message = inputMessage.trimmingCharacters(in: .whitespaces)
if !message.isEmpty {
Task(priority: .background) {
let center = UNUserNotificationCenter.current()
let authorization = await center.notificationSettings()
if authorization.authorizationStatus == .authorized {
inputMessage = ""
await sendNotification()
}
}
}
}.disabled(isButtonDisabled)
}
Spacer()
}.padding()
.task(priority: .background) {
do {
let center = UNUserNotificationCenter.current()
let authorized = try await center.requestAuthorization(options: [.alert, .sound])
await MainActor.run {
isButtonDisabled = !authorized
}
} catch {
print("Error: \(error)")
}
}
}
func sendNotification() async {
let center = UNUserNotificationCenter.current()
let groupID = "listActions"
let actionDelete = UNNotificationAction(identifier: "deleteButton", title: "Delete", options: .destructive)
let actionInput = UNTextInputNotificationAction(identifier: "inputField", title: "Message", options: [])
let category = UNNotificationCategory(identifier: groupID, actions: [actionDelete, actionInput], intentIdentifiers: [], options: [])
center.setNotificationCategories([category])
let content = UNMutableNotificationContent()
content.title = "Reminder"
content.body = inputMessage
content.categoryIdentifier = groupID
let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 10, repeats: false)
let id = "reminder-\(UUID())"
let request = UNNotificationRequest(identifier: id, content: content, trigger: trigger)
do {
try await center.add(request)
} catch {
print("Error: \(error)")
}
}
}
#Preview {
ContentView()
}
'SwiftUI' 카테고리의 다른 글
addingPercentEncoding - 특수 기호 URL에 포함시키기 (0) | 2024.01.17 |
---|---|
AppDelegate (UIWindowSceneDelegate, UIApplicationDelegate) (0) | 2024.01.16 |
System Notifications (0) | 2024.01.15 |
TapGesture (0) | 2024.01.06 |
RotateGesture (0) | 2024.01.06 |