[iOS] - Push 알림, Notification 시뮬레이터에서 띄우기



iOS에서 push 알림을 띄우는 방법을 알아보자.


image

위와 같은 알림을 띄워보자. Xcode를 simulator로 실행시킬 때 device token이 발행이 되지 않기 때문에 push 알림을 simulator에 띄울 수 없을 것처럼 보이지만 테스트 하는 방법이 있었다🎉

먼저 push 알림을 띄우는데 가장 중요한 것은 아래 3가지이며, 다 굉장히 간단하므로 쉽게 push 알림을 테스트 할 수 있다.

  1. 사용자에게 알림을 띄울 권한 요청하기
  2. apns 파일 생성하기
  3. UNUserNotificationCenterDelegate 구현해서 알림에 대한 동작 처리하기 (생략해도 되긴 하다.)

먼저 Simulator에서도 Push 기능을 테스트하려면 Xcode 11.4버전 이상이 설치되어 있어야 한다. 만약 Xcode 버전이 11.4 이상이면 아래의 과정들을 따라해보자.

1. 사용자에게 알림을 띄울 권한 요청하기

iOS 배포 버전이 13 이상이면 AppDelegate에 추가로 SceneDelegate가 Xcode 프로젝트에 추가가 되어 있는 상태일텐데, 나는 AppDelegate에 push 알림과 관련된 코드를 작성했다.

AppDelegate.swift에서 앱이 실행되면 호출되는 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool 함수 안에 아래와 같이 작성한다.

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.
    let center = UNUserNotificationCenter.current()
    center.requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in // 알림을 띄울 때 alert, sound, badge를 띄울 권한을 사용자에게 요청한다.
        print("Permission granted : \(granted)") // 만약 사용자가 권한을 허용했다면 granted는 true로, 그렇지 않으면 false로 출력된다.
    }

    UIApplication.shared.registerForRemoteNotifications()
    return true
}

이 상태에서 앱을 실행시키면 아래와 같이 앱 권한을 요청하는 팝업이 뜰 것이다. 우리는 알림을 띄울 것이므로 권한을 허용해주면 된다.

image

여기서 한 번 선택하면 시스템은 이 사용자의 선택을 기억하기 때문에 다음부터 앱에 대한 알림을 띄울 권한을 또 요청하지 않는다. 만약 이 알림을 띄울 권한을 수정하거나 재 요청하려면 시뮬레이터의 설정의 알림에 대한 권한을 수정하거나 Xcode에서 앱을 삭제하고 다시 실행시킨다. 여기까지 잘 떴다면 앱이 알림을 띄울 권한을 사용자에게 요청하는 부분은 잘 설정이 되었다.

2. apns 파일 생성하기

APNs는 Apple Push Notification service의 약자로, 앱 외부에서 보내는 알림을 사용자 기기에 전달하는 것을 관리하는 곳이라고 생각하면 된다. 자세한 개념은 여기에서 확인할 수 있다 외부에서 기기에 push notification을 보낼 때는 json 형식의 payload를 작성하는데, 여기서도 같은 작업을 해준다고 생각하면 된다.

하여간 simulator에서 apns 라는 확장자를 가진 파일을 생성한 후, 이를 Xcode simulator에 드래그-드랍하면 알림을 띄울 수 있다. 먼저 아래와 같이 확장자가 apns인 파일을 만든다. 이를 만드는 방법은 텍스트 편집기를 열어 아무말이나 입력하고 나서 저장 후, 파일 아이콘에서 우클릭 > 정보 더 가져오기 > 이름 및 확장자 부분에서 ~~.apns로 수정한다. 파일 이름은 상관 없고, 확장자가 apns이기만 하면 된다.

image

그리고 나서 파일을 연 다음, 내용을 아래와 같이 json 형식의 텍스트로 수정한다.

{
  "aps": {
    "alert": {
      "title": "Push 테스트",
      "body": "🥳 Woohoo! Push notification in simulator! 🎉",
      "sound": "default"
    },
    "badge": 10
  },
  "Simulator Target Bundle": "여기에 자신 앱의 Bundle Identifier를 넣는다." 
}

위에도 적어놨지만 Simulator Target Bundle 부분에는 자신 앱의 Bundle Identifier를 넣어줘야 한다. 내 앱의 Bundle Identifier는 Xcode의 프로젝트 설정 > General > Identity에서 확인할 수 있다. 아래의 파란색으로 하이라이트 된 부분이 내 앱의 Bundle Identifier다.

image

다 입력했으면 저장을 눌러 파일을 저장한다. 굳이 이렇게 apns 파일을 만드는 이유는 텍스트 편집기에서 바로 위의 Json 텍스트를 입력하고 저장 후 확장자를 apns로 바꾸면 아래와 같이 기괴하게 파일의 내용이 바뀌기 때문이다.

image

정상적으로 apns 파일이 만들어졌다면 파일을 열었을 때 아래와 같이 나와야 한다.

image

이제 시뮬레이터를 실행시키고, command + shift + h를 누르거나 시뮬레이터 상단의 홈 화면 버튼을 눌러 앱을 background로 보내준다. 그리고 방금 생성한 apns 파일을 시뮬레이터의 화면으로 드래그-드롭한다. 그럼 아래와 같이 Push 알림이 잘 뜨는 것을 확인할 수 있다!🥳

image

여기까지만 하면 앱이 background, 즉 화면에서 꺼졌을 때 알림을 띄우는 것을 해본 것이다.

이제는 앱이 화면에 떠 있을때 알림을 띄우는 것과, Push 알림을 터치했을 때 request로 들어오는 payload의 값을 추출해 특정 동작을 하게 하는 것도 구현해보자.

3. UNUserNotificationCenterDelegate 구현해서 알림에 대한 동작 처리하기

바로 위에서 말한 앱이 화면에 떠 있을 때(foreground) 알림이 오면 push 알림을 띄우는 것, 그리고 push 알림을 터치했을 때 특정 동작을 하게끔 하려면 UNUserNotificationCenterDelegate를 구현해야 한다.

먼저 1번에서 작성한 func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool 함수 안에 다음과 같이 delegate를 설정하는 부분을 추가해준다.

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.
    let center = UNUserNotificationCenter.current()
    center.delegate = self // 여기 추가
    center.requestAuthorization(options: [.alert, .sound, .badge]) { granted, error in
        print("Permission granted : \(granted)")
    }

    UIApplication.shared.registerForRemoteNotifications()
    return true
}

그리고 AppDelegate가 UNUserNotificationCenterDelegate 프로토콜을 따르도록 구현한다. 나는 extension으로 구현했다.

extension AppDelegate: UNUserNotificationCenterDelegate {
    func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        // 앱이 foreground에 있을 때 push 알림이 오면 이 메서드가 호출된다.
    }
    
    func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
        // 사용자가 push 알림을 터치하면 이 메서드가 호출된다.
    }
}

앱이 foreground에 있을 때 push 알림 뜨게 하기

위의 코드의 첫 번째 메서드를 아래와 같이 구현한다.

func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
    // 앱이 foregorund일 때 푸시가 오면 banner, badge, sound표시를 하라는 의미. 참고로 .alert은 ios 14에서 deprecated 되었다.
    completionHandler([.banner, .badge, .sound])
}

그리고 시뮬레이터를 실행시키고, 앱이 화면에 떠 있는 상태에서 위에서 만든 apns 파일을 시뮬레이터의 화면으로 드래그-드롭 하면 아래와 같이 push 알림이 뜨는 것을 확인할 수 있다.

image

앱의 push 알림을 터치했을 때 특정 동작 처리하기

두 번째 메서드를 구현할 때 하고 싶은 동작을 하도록 구현하면 된다. apns 파일에 작성했던 json의 형식을 좀 더 다르게 해서 request를 보내고, 이를 앱에서 받아서 처리할 수도 있다.

예를 들어 내가 apns 파일을 아래와 같이 작성했다고 해보자.

{
  "aps": {
    "alert": {
      "title": "Push 테스트",
      "body": "🥳 Woohoo! Push notification in simulator! 🎉",
      "sound": "default"
    },
    "badge": 10
  },
  "Simulator Target Bundle": "내 앱의 Bundle Identifier",
  "url" : "text인데요~" 
}

임의로 url이라는 필드를 추가했다. 어떤 값을 추가할 지는 마음대로 설정해도 된다. 그리고 메서드를 아래와 같이 구현하고 시뮬레이터에서 push 알림이 떴을 때 push 알림을 클릭하면 Xcode의 콘솔에 Optional("text인데요~")라고 뜨게 된다.

func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
    // 사용자의 push 알림에 대한 response 처리
    let url = response.notification.request.content.userInfo["url"] // 여기에서 내가 원하는 키 값으로 값을 불러올 수 있다.
    print(url)
}

이렇게 json의 Payload에 원하는 값을 맘대로 구성해서 이에 따라 앱에서 push를 클릭했을 때 앱이 다르게 동작하도록 구현할 수도 있다.