본문 바로가기
iOS_Swift 앱개발👍

[iOS_Swift] APNs 원격 푸시 알림 _ 33

by 개발하는윤기사 2023. 7. 31.
728x90
반응형

이번 글은 Apple Push Server를 이용한 원격 푸시 알림에 관한 글입니다!!

 

이 글에서는 원격 푸시 알림에 대한 기본 세팅 설정 내용은 없고, 원격 푸시 알림 설정이 완료된 후의 내용을 담고 있으니,

 

기본 설정을 마치고 글을 읽어주셨으면 좋겠습니다!

 

 

또한, 로컬 푸시 알림에 대한 글은 바로 전 포스팅에 정리해 놓았으니 아래 링크 타고 이동하시면 됩니다 ㅎㅎ

 

[iOS_Swift] 로컬 푸시 알림 _ 32

로컬 푸시 알림은 앱 내부에서 생성한 특정 메시지를 전달 유저에게 전달하는 알림입니다! 알림이 도착하면 앱 아이콘의 배지에 표시할 수 있어요. 메시지나 카카오톡 보면 메시지가 왔을 때 빨

swiftyun.tistory.com

 

 

Apple Push Notification service (APNs)

  • iOS의 원격 푸시 알림은 애플 서버(APNs)를 이용해 발송합니다.
    • 푸시 알림은 애플 서버를 이용해 발송한다고 했는데, 쉽게 생각하면 앱과 애플 푸시 서버 사이에 있는 APNs는 중간 다리역할을 하는 것입니다!
  • 애플 서버에서 디바이스 토큰을 통해 푸시 알림을 보내는 방식이기 때문에 이 디바이스 토큰을 애플서버에 알려주어야 합니다.
    • 사용자에게 푸시 알림 허용에 관한 Alert을 띄우고, 사용자가 푸시 알림을 허용한 경우, APNs에 디바이스 토큰이 등록되는 방식입니다.

APNs는 앱과 애플 서버 간의 중간 다리역할이라고 생각하면 됩니다!

 

 

원격 푸시 알림 Flow Chart

  1. 사용자에게 알림 권한을 요청합니다.
  2. 사용자가 알림 권한을 허용한다면, APNs에 알림을 허용한 디바이스 토큰 정보를 등록합니다.
  3. 디바이스로 푸시 알림을 보냅니다.
  4. APNs를 이용해 해당 디바이스로 푸시 알림을 전달합니다.
  5. 앱에서는 APNs를 통해 들어온 푸시 알림을 처리합니다.

 

 

이제 1번부터 5번까지의 흐름을 따라가면서, 정리를 해보겠습니다!

 

 

1. 사용자에게 알림 권한을 요청합니다.

- UNUserNotificationCenter.current().requestAuthorization() 메서드를 이용해서 사용자에게 어떠한 타입의 알림 권한을 요청할지 정합니다.

- registerForRemoteNotification()은 APNs 디바이스 토큰 저장을 위한 준비라고 보시면 됩니다!

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    
    UNUserNotificationCenter.current().requestAuthorization(options:[.badge, .alert, .sound]){ (granted, error) in }

    application.registerForRemoteNotifications() 

}

 

2. 사용자가 알림 권한을 허용한다면, APNs에 알림을 허용한 디바이스 토큰 정보를 등록합니다.

//유저디폴트에 토큰 저장
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {

    let deviceTokenString = deviceToken.reduce("", {$0 + String(format: "%02X", $1)})
    UserDefaultManager.deviceToken = deviceTokenString

}

//토큰 저장 실패시
func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
     
        print("APNs registration failed: \\(error)")        
        UserDefaultManager.deviceToken = "simulatorDeviceToken"
}

 

 

3, 4. 디바이스로 푸시 알림을 보냅니다. APNs를 이용해 해당 디바이스로 푸시 알림을 전달합니다.

 

- 저같은 경우에는 CloudKit Console > Push Notifications를 이용해서 알림을 보냈습니다!

 

- Push Notification Console로 들어오시면, 알림을 보낼 수 있는데, 여기서 필요한 건 DeviceToken만 필요하기 때문에 위 2번에서 받았던 deviceToken 정보를 콘솔창에 print 해서 직접 넣어주시면 됩니다.

디바이스 토큰 정보

 

5. 앱에서는 APNs를 통해 들어온 푸시 알림을 처리합니다.

 

- 푸시 알림을 처리하기 위해선 앱이 포그라운드 상태인지, 백그라운드 상태 혹은 종료된 상태인지에 따라 다르게 처리해 줄 수 있습니다!

 

여기서 aps 유무의 aps는 푸시 알림의 Payload json타입의 root key 값인데요,

 

APNs를 통해서 푸시 알림을 보낼 때는 아래와 같은 json 형식으로 보내주기 때문입니다.

 

여기서 유무에 따라 분기처리를 나눈 이유는, 제가 서비스하고 있는 앱의 푸시 알림이 원격과 로컬 두 가지가 다 있기 때문입니다 ^^

 

 

willPresent : 앱이 포그라운드에 있을 때의 동작

//앱이 foreground
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {

    //notification의 content : title, subtitle, body, sound
    let content = notification.request.content
    print(content)
    
    handleUserNotification(content: content)
    
    completionHandler([.alert, .sound])

}

 

didReceive : 앱이 백그라운드에 있을 때의 동작

//사용자가 푸시 알림 클릭 시 (앱이 백그라운드에 있을 때)
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
      
    //notification의 content : title, subtitle, body, sound
    let content = response.notification.request.content
    print(content)
    
    handleUserNotification(content: content)
    
    completionHandler()
}

 

* 공통 메서드를 하나 만들어서 Notification에 대한 동작 처리를 만들어 놓을 수 있습니다!

- 이렇게 하면 편하던데요... ^_^

func handleUserNotification(content: UNNotificationContent) {
    
    var title = ""
    var msg = ""
    
    //원격 알림에 대한 처리
    if let aps = content.userInfo["aps"] as? [String: Any] {
       
        title = content.userInfo["title"] as? String ?? ""
        
        if let alert = aps["alert"] as? [String: String] {
            msg = alert["body"] ?? ""
        } else {
            msg = aps["alert"] as? String ?? ""
        }
        
     	print(title)
        print(msg)
        
    } 
    //로컬 알림에 대한 처리
    else {
                   
            title = content.title
            msg = content.body
            
            print(title)
            print(msg)
          
          }
       
}

 

728x90
반응형