Remote notification
- Device에서 APNs에 푸쉬 토큰을 요청하고 APNS는 푸쉬 토큰을 디바이스에 발급해줌, 이후 다비아시는 Provider에 푸쉬 토큰을 전달해줌
- 푸쉬 토큰을 전달 받은 Provider는 추후 디바이스에 푸쉬를 전송할 대 APNs에 요청을 하고 APNs가 디바이스에 푸쉬를 전송함
Remote Notification 구현하기
- 1. 실제 iOS기기 준비
- 시뮬레이터로 테스트 불가능했지만 Xcode 11.4부터 시뮬레이터에서 푸쉬를 수신할 수 있는 기능을 지원하기 시작했음
- 2. 애플 개발자 사이트 설정하기
- App Identifier 등록하기
- Bundle Identifier 및 Description 추가하기
- Capabilities -> Push Notification On
- 3. Apple push 알림 인증 키 발급받기
- 유료 애플 개발자 프로그램을 등록해야 푸시 키를 발급받을 수 있음
- 푸시 인증 키는 여러 앱에서 동시에 사용이 가능함
- p8 인증키를 발급 받음
- 4. Xcode의 App > Capabilities(기능)에서 푸시 알림 사용 설정
- Debug/Release Mode 모두 설정해주어야 함
- Push Notification
- Background Modes -> Remote Notification On
- 5. Firebase Cloud System 연동
- Firebases는 푸쉬를 Cloud Messaging Service라고 지칭하고 있음
- 푸시 인증키 및 개발자 팀 키 등록
- 프로젝트 설정 -> 클라우드 메시징 탭 - > APN 인증 키 업로드
- 6. 코드 구현
- 원격 알림 권한을 등록해야 함
- iOS 시스템 정책으로 원격 알림 시스템에 앱 등록하는 과정이 필요함
- 파이어베이스도 푸쉬를 전송할 때 APNs 서버에 요청하게 됨으로 실질적 발송 및 관리자는 애플 시스템의 영향을 받음
- UNUserNotificationCenterDelegate
- 메시지 대리자 설정
- 파이어베이스가 푸시 메시지를 대신 전송할 수 있도록 대리자를 설정하는 과정이 필요
- MessagingDelegate
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
UIViewController.swizzleMethod()
FirebaseApp.configure()
//알림 시스템에 앱을 등록
if #available(iOS 10.0, *) {
// For iOS 10 display notification (sent via APNS)
UNUserNotificationCenter.current().delegate = self
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(
options: authOptions,
completionHandler: { _, _ in }
)
} else {
let settings: UIUserNotificationSettings =
UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
application.registerUserNotificationSettings(settings)
}
application.registerForRemoteNotifications()
//메시지 대리자 설정
Messaging.messaging().delegate = self
//현재 등록된 토큰 가져오기
Messaging.messaging().token { token, error in
if let error = error {
print("Error fetching FCM registration token: \(error)")
} else if let token = token {
print("FCM registration token: \(token)")
}
}
return true
}
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
}
extension AppDelegate: UNUserNotificationCenterDelegate {
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
Messaging.messaging().apnsToken = deviceToken
}
//포그라운드 알람 수신: 로컬/푸쉬 동일
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
//.banner, .list: ios 14+
guard let viewController = (UIApplication.shared.connectedScenes.first?.delegate as? SceneDelegate)?.window?.rootViewController?.topViewController else { return }
if viewController is SettingViewController {
} else {
completionHandler([.badge, .sound, .banner, .list])
}
}
//푸시 클릭: 호두과자 장바구니 담는 화면
//유저가 푸시를 클릭했을 때에만 수신 확인 가능
//특정 푸시를 클릭하면 특정 상세화면으로 화면 전환 ->
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
print("사용자가 푸쉬를 클릭했습니다.")
print(response.notification.request.content.body)
print(response.notification.request.content.userInfo)
let userInfo = response.notification.request.content.userInfo
if userInfo[AnyHashable("sesac2")] as? String == "project2" {
print("Sesac PROJECT")
} else {
print("EMPTY")
}
guard let viewController = (UIApplication.shared.connectedScenes.first?.delegate as? SceneDelegate)?.window?.rootViewController?.topViewController else { return }
print(viewController)
if viewController is ViewController {
viewController.navigationController?.pushViewController(SettingViewController(), animated: true)
} else if viewController is ProfileViewController {
viewController.dismiss(animated: true)
}
}
}
extension AppDelegate: MessagingDelegate {
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
print("Firebase registration token: \(String(describing: fcmToken))")
let dataDict: [String: String] = ["token": fcmToken ?? ""]
NotificationCenter.default.post(
name: Notification.Name("FCMToken"),
object: nil,
userInfo: dataDict
)
// TODO: If necessary send token to application server.
// Note: This callback is fired at each app startup and whenever a new token is generated.
}
}
- 7. 테스트 메시지 전송
- 콘솔에서 토큰 확인 후 파이어베이스에서 테스트 후 전송
추가 사항
- Foreground
- 출시 앱 프로젝트를 할 때도 겪었던 이슈 백그라운에서는 기본적으로 동작하지만 포그라운드 수신은 동작하지 않음
- 별도 처리를 해주어야 로컬/푸시 알림이 포그라운드 상태에서도 수신을 처리할 수 있음
- 출시 앱 프로젝트를 할 때도 겪었던 이슈 백그라운에서는 기본적으로 동작하지만 포그라운드 수신은 동작하지 않음
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
completionHandler([.badge, .sound, .banner, .list])
}
- completionHandler([.badge, .sound, .alert]는 iOS14부터 Deprecated 됨
- 따라서 최소 버전에 따른 분기 처리가 필요함
- Receive Response
- iOS에서는 푸시가 수신되었는지 여부를 확인할 수 없음
- 대신 사용자가 특정 푸시를 클릭했을 때에만 수신 확인이 가능
- 특정 푸시를 클릭했을 때 별도의 로직 구현을 하지 않으면 앱을 Active 상태로 올려주는 정도로 작동
- Interruption Lebel
- iOS15부터 알림 수준을 다르게 설정할 수 있음
- Ciritical은 재난 문자 알림과 같은 레벨 -> 따라서 거의 사용허지 않음
- TimeSensitive는 중요한 일정이나 보안 알림 등에서 사용
- 사용자가 방해금지 모드를 사용하고 있더라도 알림이 전달됨
- TimeSensitive 레벨의 푸시를 여러 번 받게 되면 사용자에게 단계를 내릴지 alert을 시스템에서 띄워줌
- 아이폰 설정에서 각 앱에 대한 긴급한 알림 수신 여부를 거부하면 TimeSensitive 상태의 푸시도 Acitive 상태로 전달됨
- Active는 일반적인 알림임
- Passive는 주로 광고성 알림에 해당 화면을 켜거나 소리르 재생하지 않은 채 알림만 전달됨
'IS' 카테고리의 다른 글
iOS Swift 카카오 소셜 로그인(Kakao Social Login) (0) | 2023.02.13 |
---|---|
iOS Swift - Service Level Project(SLP) 회고 (0) | 2023.02.13 |
RxSwift(Observable, Observe, Subject, Relay etc) (0) | 2022.10.29 |
iOS Collection View APIs (1) | 2022.10.18 |
iOS 다국어 대응 하는 법(Localization, 현지화) (1) | 2022.10.18 |