SwiftUI

[SwiftUI] Local Notification 앱의 로컬알림 기능 구현

하이D:) 2023. 7. 17. 17:39

로컬 알림에 대해 이해하고 구현하기 위해서는 먼저 우리가 푸시 알림이라고 부르는 local notification과 remote notification의 차이점에 대해 알아볼 필요가 있다. remote notification는 서버 측에서 보내는 서버 푸시이며 기기 자체에서 앱으로 알림을 보내는 것은 local notification이다. 두 케이스는 각기 다르지만  local notification는 서버가 없어도 앱 자체만으로 해결이 가능하기 때문에 구현하기 비교적 간단하다.

이 글에서는 Local Notification을 구현하기 위한 과정을 각각 설명하고자 한다.

 

#🥨 사용자로부터 푸시알림 사용 권한 요청

앱에서 알림 기능을 사용자에게 제공하기 위해서는 가장 먼저 사용자가 알림을 받고 싶은지의 여부를 먼저 파악해야 하기 때문에 푸시 알림 권한 요청을 위한 코드가 필요하다. 이때 필요한 UNUserNotificationCenter 는 알림(notification) 관련 활동을 관리하기 위한 중앙 객체이며 아래와 같은 기능을 가능하게 한다.

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

  • 경고, 소리 및 아이콘 배지를 통해 사용자와 상호 작용할 수 있는 권한을 요청할 수 있다.
  • 알림에 대한 타입을 선언할 수 있고, 앱이 알림을 전달했을 때 사용자가 수행할 수 있는 맞춤 작업을 선언할 수 있다
  • 알림 전달을 예약할 수 있다.
  • APN(Apple 푸시 알림 서비스)에서 시스템이 제공하는 원격 알림의 페이로드를 처리할 수 있다.
  • 알림 센터에서 표시하는 이미 전달된 알림을 관리한다.
  • 지정 알림타입과 연결된 사용자 선택 작업을 처리한다.
  • 앱의 알림 관련 설정을 불러 수 있다

권한 요청할 때 .current()로 객체를 반환받은 후 requestAuthorization 메서드를 활용해야 하며, 앱이 시작될 때 권한 요청을 받아야 하기 때문에 AppDelegate에 작성하도록 한다.

class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDelegate {
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        remoteNotificationsRegistration(application)
        return true
    }
    
    func remoteNotificationsRegistration(_ application: UIApplication) {
        let center = UNUserNotificationCenter.current()
        center.requestAuthorization(options: [.alert, .badge, .sound]) { success, error in
            guard error == nil else {
                print("Error")
                return
            }
            print("Success")
            
        }
    }
}

 

#🥨 알림 내용을 설정

UNMutableNotificationContent 라는 클래스는 알림 내용에 대해 편집 가능하게 하며 title, subtitle, body, badge, sound 등 다양한 항목들을 설정해 줄 수 있다.

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

let content = UNMutableNotificationContent()
content.title = "로컬 알림 테스트 제목!! "
content.subtitle = "서브타이틀"
content.body = "로컬 알림 테스트입니다"

 

#🥨 알림이 발동되는 조건인 트리거를 설정한다

알림을 전달하기 위해 다양한 트리거들이 존재한다. 이러한 알림이 발동되는 조건인 트리거는 크게 시간 간격(time interval), 특정한 시간(calendar), 위치(location)로 나눌 수 있다.

  • UNTimeIntervalNotificationTrigger : 특정 시간간격을 두고 알림을 전달할 때 사용하는 트리거이다
  • UNCalendarNotificationTrigger : 시스템이 특정 날짜 및 시간에 전달하는 알림을 유발하는 트리거 조건이다.
  • UNLocationNotificationTrigger : 사용자의 장치가 지정한 지리적 영역에 들어가거나 나갈 시스템이 알림을 전달하도록 하는 트리거 조건이다.

 

#🥨 알림 요청

위에서 것 처럼 알림 내용과 트리거를 설정해주었다면 알림을 요청하면되는데, 이때, UNNotificationRequest 클래스를 사용하여 알림을 요청할 수 있다.

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

func sendNoti() {

	let content = UNMutableNotificationContent()
	content.title = "로컬 알림 테스트 제목!! "
	content.subtitle = "서브타이틀"
	content.body = "로컬 알림 테스트입니다 ^^"
            
	let trigger = UNTimeIntervalNotificationTrigger(timeInterval: 2, repeats: false)
            

	let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
            
	notificationCenter.add(request) { (error) in
		if error != nil {
			// handle errors
		}
		print("로컬 푸쉬알림 요청 성공")
	}
}

 

#🥨 forground 상태일 때도 알림 처리를 해주려면

추가적으로 forground 상태일 (앱을 켜서 보고 있을)에도알림 처리를해주려면 AppDelegate 해당 메서드를 작성해주어야 한다. didFinishLaunchingWithOptions 메서드에서 delegate를 할당해 주기 위해서는 AppDelegate class에서 UNUserNotificationCenterDelegate 프로토콜을 채택하고 있어야 하는 점을 유의하자.

func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
    completionHandler([.list, .banner])
}

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
    UNUserNotificationCenter.current().delegate = self
    return true
}

 

 

#🥨  예시 코드와 화면

class AppDelegate: NSObject, UIApplicationDelegate, UNUserNotificationCenterDelegate { 

    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        completionHandler([.list, .banner])
    }
    
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
        remoteNotificationsRegistration(application)
        UNUserNotificationCenter.current().delegate = self
        return true
    }
    
    func remoteNotificationsRegistration(_ application: UIApplication) {
        let center = UNUserNotificationCenter.current()
        center.requestAuthorization(options: [.alert, .badge, .sound]) { success, error in
            guard error == nil else {
                print("Error")
                return
            }
            print("Succes")
        }
    }
}
struct PushNotificationPracticeView: View {
    func sendNoti() {
        let notificationCenter = UNUserNotificationCenter.current()
        notificationCenter.removeAllPendingNotificationRequests()
        
        let content = UNMutableNotificationContent()
        content.title = "로컬 알림 테스트 제목!! "
        content.subtitle = "서브타이틀"
        content.body = "로컬 알림 테스트입니다 ^^"
        
        let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: trigger)
        
        notificationCenter.add(request) { (error) in
            if error != nil {
                // handle errors
            }
            print("로컬 푸쉬알림 요청 성공")
        }
    }
    
    var body: some View {
        VStack{
            Button{
                self.sendNoti()
            }label: {
                Text("로컬 알림 와랏!!")
            }
            .asGreenToggleButton(isGreen: true)
        }
    }
}