투케이2K

34. (TWOK/UTIL) [Ios/Swift] AppDelegate - 파이어베이스 푸시 토큰 및 알림 관리 본문

투케이2K 유틸파일

34. (TWOK/UTIL) [Ios/Swift] AppDelegate - 파이어베이스 푸시 토큰 및 알림 관리

투케이2K 2022. 4. 12. 13:49

[설 명]

프로그램 : Ios / Swift

설 명 : 파이어베이스 푸시 토큰 및 알림 관리

 

[파이어베이스 푸시 알림 처리 로직 과정]

1. APN 인증 키(.p8) 발급

2. 파이어베이스 콘솔에서 프로젝트 생성 및 등록 필요

3. Xcode 프로젝트 실행 >> 파이어베이스 SDK 설치를 진행

4. Xcode 프로젝트 >> 파이어베이스 콘솔에서 다운받은 GoogleService-Info 파일을 프로젝트에 적용

5. Xcode 프로젝트 >> Push Notifications와 Background Modes 를 추가

6. AppDelegate 쪽에서 파이어베이스 푸시 알림 처리 소스 코드 작성 실시

7. (debug , release) , (enterprise) 빌드 환경에서 번들 아이디 (패키지명) 이 다른 경우 :

- 파이어베이스에서 iOS 프로젝트 다중 생성 (ex: testApp / testAppEnz) 및 각 환경에 맞는 p8 파일로 등록 (p8 파일 2개)

- Xcode 프로젝트 >> 파이어베이스 콘솔에서 다운받은 GoogleService-Info 파일 이름 변경 실시

(GoogleService-Info-Dug / GoogleService-Info-Enz)

- AppDelegate 에서 FirebaseApp.configure() 라이브러리 초기화 부분에서 빌드 환경 체크 >> 각 GoogleService-Info 파일 적용 실시

 

[소스 코드]

import UIKit
import AVFoundation
import Firebase
import FirebaseAnalytics

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
    
    
    // MARK: - [클래스 설명]
    /*
    // -----------------------------------------
    1. 애플리케이션 딜리게이트
    // -----------------------------------------
    */
    
    
    
    
    
    // MARK: - [파이어베이스 푸시 알림]
    /*
    // -----------------------------------------
    1. Swift Package Manager를 사용해 Firebase 종속 항목을 설치하고 관리
       - https://github.com/firebase/firebase-ios-sdk
       - [Up to Next Major Version 8.0.0 < 9.0.0]
    2. FirebaseAnalytics를 추가 , 파이어베이스 메시징 추가
    // -----------------------------------------
    */
    
    
    
    
    
    // MARK: - [빠른 로직 찾기 : 주석 로직 찾기]
    // -----------------------------------------
    // [SEARCH FAST] : [프리퍼런스 값 초기화 실시]
    // [SEARCH FAST] : [푸시 알림 뱃지 카운트 초기화]
    // [SEARCH FAST] : [빌드 타입 확인 실시]
    // [SEARCH FAST] : [파이어베이스 초기화]
    // [SEARCH FAST] : [파이어베이스 등록 토큰 확인]
    // [SEARCH FAST] : [푸시 알림 수신 확인]
    // -----------------------------------------
    
    
    
    
    
    // MARK: - [전역 변수 선언 실시]
    var window: UIWindow? // [ios 13 미만 버전 제어 위해 선언]



    
    
    // MARK: - [앱 프로세스 완료 및 앱 실행 실시]
    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        print("")
        print("====================================")
        print("[AppDelegate >> didFinishLaunchingWithOptions]")
        print("-------------------------------")
        print("설 명 :: 앱 프로세스 완료 및 앱 실행 실시")
        print("====================================")
        print("")
        
        // -----------------------------------------
        // [SEARCH FAST] : [프리퍼런스 값 초기화 실시]
        S_Preference().proccessCreateClear()
        // -----------------------------------------
        
        
        // -----------------------------------------
        // [SEARCH FAST] : [푸시 알림 뱃지 카운트 초기화]
        // UIApplication.shared.applicationIconBadgeNumber = 0
        // -----------------------------------------
        
        
        // -----------------------------------------
        // [LaunchScreen 로딩화면 지연 실시 - 3초]
        // Thread.sleep(forTimeInterval: 3.0)
        // -----------------------------------------
        
        
        // -----------------------------------------
        // [SEARCH FAST] : [빌드 타입 확인 실시]
        // [SEARCH FAST] : [파이어베이스 초기화]
        #if DEBUG
            print("")
            print("====================================")
            print("[AppDelegate >> didFinishLaunchingWithOptions]")
            print("-------------------------------")
            print("설 명 :: ", "빌드 타입 확인 실시")
            print("-------------------------------")
            print("빌드 타입 :: ", "DEBUG 실행")
            print("====================================")
            print("")
            let fireBaseConfigFile = Bundle.main.path(forResource: "GoogleService-Info-Dug", ofType: "plist")!
            let firOptions = FirebaseOptions(contentsOfFile: fireBaseConfigFile)!
            FirebaseApp.configure(options: firOptions)
        #elseif ENTERPRISE
            print("")
            print("====================================")
            print("[AppDelegate >> didFinishLaunchingWithOptions]")
            print("-------------------------------")
            print("설 명 :: ", "빌드 타입 확인 실시")
            print("-------------------------------")
            print("빌드 타입 :: ", "ENTERPRISE 실행")
            print("====================================")
            print("")
            let fireBaseConfigFile = Bundle.main.path(forResource: "GoogleService-Info-Enz", ofType: "plist")!
            let firOptions = FirebaseOptions(contentsOfFile: fireBaseConfigFile)!
            FirebaseApp.configure(options: firOptions)
        #else
            print("")
            print("====================================")
            print("[AppDelegate >> didFinishLaunchingWithOptions]")
            print("-------------------------------")
            print("설 명 :: ", "빌드 타입 확인 실시")
            print("-------------------------------")
            print("빌드 타입 :: ", "RELEASE 실행")
            print("====================================")
            print("")
            let fireBaseConfigFile = Bundle.main.path(forResource: "GoogleService-Info-Dug", ofType: "plist")!
            let firOptions = FirebaseOptions(contentsOfFile: fireBaseConfigFile)!
            FirebaseApp.configure(options: firOptions)
        #endif
        // -----------------------------------------
        
        
        // -----------------------------------------
        Messaging.messaging().delegate = self // [메시징 딜리게이트 지정]
        UNUserNotificationCenter.current().delegate = self // [노티피케이션 알림 딜리게이트 지정]
        let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound] // [푸시 알림 권한]
        UNUserNotificationCenter.current().requestAuthorization(options: authOptions) { (success, error) in // [푸시 알림 권한 요청]
            // [success 부분에 권한을 허락하면 true / 권한을 허락하지 않으면 false 값이 들어갑니다]
            if let error = error {
                print("")
                print("====================================")
                print("[AppDelegate >> requestAuthorization() :: 노티피케이션 권한 요청 에러]")
                print("error :: \(error.localizedDescription)")
                print("====================================")
                print("")
            }
            else {
                print("")
                print("====================================")
                print("[AppDelegate >> requestAuthorization() :: 노티피케이션 권한 요청 응답 확인]")
                print("success :: \(success)")
                print("====================================")
                print("")
            }
        }
        application.registerForRemoteNotifications() // [원격 알림 앱 등록 : APNS 등록]
        print("")
        print("====================================")
        print("[AppDelegate >> didFinishLaunchingWithOptions]")
        print("설 명 :: ", "파이어베이스 초기화 수행 실시")
        print("====================================")
        print("")
        // -----------------------------------------

        
        // -----------------------------------------
        return true
        // -----------------------------------------
    }



    
    
    // MARK: - [Scene 만들기 위한 구성 객체 반환 : 스토리보드 , info]
    @available(iOS 13.0, *)
    func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
        print("")
        print("====================================")
        print("[AppDelegate >> configurationForConnecting]")
        print("-------------------------------")
        print("설 명 :: Scene 만들기 위한 구성 객체 반환 : 스토리보드 , info")
        print("====================================")
        print("")

        return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
    }

    
    
    
    
    // MARK: - [Scene 구성 객체 해제 실시]
    @available(iOS 13.0, *)
    func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
        print("")
        print("====================================")
        print("[AppDelegate >> didDiscardSceneSessions]")
        print("-------------------------------")
        print("설 명 :: Scene 구성 객체 해제 실시")
        print("====================================")
        print("")
    }
    
    
    
    
    
    // MARK: - [애플리케이션 사용자가 작업 태스크 날린 이벤트 감지]
    func applicationWillTerminate(_ application: UIApplication) {
        print("")
        print("====================================")
        print("[AppDelegate >> applicationWillTerminate]")
        print("-------------------------------")
        print("설 명 :: 애플리케이션 사용자가 작업 태스크 날린 이벤트 감지")
        print("====================================")
        print("")
        
        // -----------------------------------------
        // [SEARCH FAST] : [프리퍼런스 값 초기화 실시]
        // -----------------------------------------
        // [앱 종료 시 프리퍼런스 데이터 초기화]
        S_Preference().mainFinishClear()
        // -----------------------------------------
        // [프로세스 종료 시 프리퍼런스 데이터 초기화]
        S_Preference().proccessFinishClear()
        // -----------------------------------------
    }
    
    
    
    
    
    // MARK: - [디바이스 화면 세로 모드 고정 실시]
    func application(_ application: UIApplication, supportedInterfaceOrientationsFor window: UIWindow?) -> UIInterfaceOrientationMask {
        print("")
        print("====================================")
        print("[AppDelegate >> supportedInterfaceOrientationsFor]")
        print("-------------------------------")
        print("설 명 :: 디바이스 화면 세로 모드 고정 실시")
        print("====================================")
        print("")
        
        // [세로 방향 고정]
        return UIInterfaceOrientationMask.portrait
    }
    
    
    
    
    
    // MARK: - [원격 알림 앱 등록 : APNS 등록 후 >> apnsToken 매핑]
    func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        let deviceTokenString = deviceToken.reduce("", {$0 + String(format: "%02X", $1)}) // hex 데이터 확인
        print("")
        print("====================================")
        print("[AppDelegate >> didRegisterForRemoteNotificationsWithDeviceToken]")
        print("-------------------------------")
        print("설 명 :: 원격 알림 앱 등록 : APNS 등록 후 >> apnsToken 매핑")
        print("-------------------------------")
        print("deviceToken :: \(deviceToken)")
        print("====================================")
        print("")
        // -----------------------------------------
        // [APNS 토큰 매핑]
        Messaging.messaging().apnsToken = deviceToken
        // -----------------------------------------
    }
    
    
    
    
    
    // MARK: - [푸시 알림 전달 받음 상태 확인]
    func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any]){
        // [switch 문을 사용해서 분기 처리 실시]
        switch UIApplication.shared.applicationState {
        case .active:
            print("")
            print("====================================")
            print("[AppDelegate >> didReceiveRemoteNotification]")
            print("설 명 :: 포그라운드 상태에서 푸시알림 전달받음")
            print("====================================")
            print("")
            break
        case .background:
            print("")
            print("====================================")
            print("[AppDelegate >> didReceiveRemoteNotification]")
            print("설 명 :: 백그라운드 상태에서 푸시알림 전달받음")
            print("====================================")
            print("")
            break
        case .inactive:
            print("")
            print("====================================")
            print("[AppDelegate >> didReceiveRemoteNotification]")
            print("설 명 :: 푸시 클릭 접속 확인")
            print("====================================")
            print("")
            break
        default:
            print("")
            print("====================================")
            print("[AppDelegate >> didReceiveRemoteNotification]")
            print("설 명 :: default")
            print("====================================")
            print("")
            break
        }
    }
    

} // [클래스 종료]





// MARK: - [노티피케이션 알림 딜리게이트 추가]
extension AppDelegate: UNUserNotificationCenterDelegate {
    
    // -----------------------------------------
    // [SEARCH FAST] : [푸시 알림 수신 확인]
    // -----------------------------------------
    // MARK: [앱이 foreground 상태 일 때, 알림이 온 경우]
    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                willPresent notification: UNNotification,
                                withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
        print("")
        print("====================================")
        print("[AppDelegate >> willPresent]")
        print("-------------------------------")
        print("설 명 :: 앱 포그라운드 상태 푸시 알림 확인")
        print("-------------------------------")
        print("userInfo :: \(notification.request.content.userInfo)") // [푸시 정보 가져옴]
        print("-------------------------------")
        print("title :: \(notification.request.content.title)") // [타이틀]
        print("-------------------------------")
        print("body :: \(notification.request.content.body)") // [내용]
        print("-------------------------------")
        print("sound :: \(notification.request.content.sound!)") // [소리]
        print("-------------------------------")
        print("data [keys] :: \(notification.request.content.userInfo.keys)") // [커스텀 json 데이터]
        print("-------------------------------")
        print("data [sort] [푸시 타입] :: \(String(describing: notification.request.content.userInfo["sort"] ?? ""))") // [무음, 진동, 소리 발생 여부]
        print("-------------------------------")
        print("data [msgType] [메시지 타입] :: \(String(describing: notification.request.content.userInfo["msgType"] ?? ""))") // [메시지 타입]
        print("-------------------------------")
        print("data [messageId] [메시지 아이디] :: \(String(describing: notification.request.content.userInfo["messageId"] ?? ""))") // [메시지 아이디]
        print("====================================")
        print("")
        
        
        // [프리퍼런스에 저장된 타입이 있는지 확인]
        var checkPushType = S_Preference().getString(_sKey: S_FinalData.PRE_PUSH_SORT)
        if checkPushType != nil && checkPushType.count>0 && checkPushType.equals(_string: "") == false { // [프리퍼런스 값이 널이 아닌 경우]
            
            let type = checkPushType
            if type == "0" || type == "1" || type == "2" {
                // [정상적으로 값을 포함한 경우]
            }
            else {
                checkPushType = "\(String(describing: notification.request.content.userInfo["sort"] ?? ""))"
                
                if checkPushType == "0" || checkPushType == "1" || checkPushType == "2" {
                    
                    // [프리퍼런스에 데이터 저장]
                    S_Preference().setString(_sKey: S_FinalData.PRE_PUSH_SORT, _sValue: checkPushType)
                }
                else {
                    checkPushType = "2" // [기본 값 설정 : 소리 + 진동]
                }
            }
        }
        else { // [프리퍼런스 값이 널인 경우]
            checkPushType = "\(String(describing: notification.request.content.userInfo["sort"] ?? ""))"
            
            if checkPushType == "0" || checkPushType == "1" || checkPushType == "2" {
                
                // [프리퍼런스에 데이터 저장]
                S_Preference().setString(_sKey: S_FinalData.PRE_PUSH_SORT, _sValue: checkPushType)
            }
            else {
                checkPushType = "2" // [기본 값 설정 : 소리 + 진동]
            }
        }
        print("")
        print("====================================")
        print("[AppDelegate >> willPresent]")
        print("-------------------------------")
        print("설 명 :: 노티피케이션 알림 타입 확인")
        print("-------------------------------")
        print("checkPushType :: ", checkPushType)
        print("====================================")
        print("")
        
        
        // [푸시 알림 타입별 분기 처리 실시]
        let sort = checkPushType
        if sort == "0" { // [무음]
            print("")
            print("====================================")
            print("[AppDelegate >> willPresent]")
            print("설 명 :: 노티피케이션 알림 [무음] 모드")
            print("====================================")
            print("")
            
            // [알림 표시 수행]
            completionHandler([.alert, .badge])
        }
        else if sort == "1" { // [진동]
            print("")
            print("====================================")
            print("[AppDelegate >> willPresent]")
            print("설 명 :: 노티피케이션 알림 [진동] 모드")
            print("====================================")
            print("")
            
            // [진동 기능 활성]
            UIDevice.vibrate()
            
            // [알림 표시 수행]
            completionHandler([.alert, .badge])
        }
        else if sort == "2" { // [소리 + 진동]
            print("")
            print("====================================")
            print("[AppDelegate >> willPresent]")
            print("설 명 :: 노티피케이션 알림 [소리 + 진동] 모드")
            print("====================================")
            print("")
            
            // [진동 기능 활성]
            UIDevice.vibrate()
            
            // [알림 표시 수행]
            completionHandler([.alert, .badge, .sound])
        }
        else { // [기본 : 소리 + 진동]
            print("")
            print("====================================")
            print("[AppDelegate >> willPresent]")
            print("설 명 :: 노티피케이션 알림 [기본 : 소리 + 진동] 모드")
            print("====================================")
            print("")
            
            // [알림 표시 수행]
            completionHandler([.alert, .badge, .sound])
        }
        
        
        // [AppDelegate 포그라운드 푸시 메시지 송신]
        DispatchQueue.main.async {
            // [포그라운드 메시지 전송 데이터 결합 실시]
            var sendDic : Dictionary<String, Any> = [String : Any]()
            sendDic["title"] = "\(String(describing: notification.request.content.title))" // [제목]
            sendDic["message"] = "\(String(describing: notification.request.content.body))" // [내용]
            sendDic["msgType"] = "\(String(describing: notification.request.content.userInfo["msgType"] ?? ""))" // [커스텀 : 메시지 타입]
            sendDic["messageId"] = "\(String(describing: notification.request.content.userInfo["messageId"] ?? ""))" // [커스텀 : 메시지 아이디]
            print("")
            print("====================================")
            print("[AppDelegate >> willPresent]")
            print("-------------------------------")
            print("설 명 :: 노티피케이션 포그라운드 알림 전달 실시")
            print("-------------------------------")
            print("message :: ", sendDic.description)
            print("====================================")
            print("")
            
            // [브로드 캐스팅 포그라운드 상태 푸시 알림 메시지 전달]
            NotificationCenter.default.post(
                name: NSNotification.Name(rawValue: S_FinalData.NOTI_RECEIVE_PUSH_MESSAGE), // [알림을 식별하는 태그]
                object: nil, // [발송자가 옵저버에게 보내려고 하는 객체]
                userInfo: ["message" : sendDic] // [객체의 저장소 [AnyHashable: Any] 형태]
            )
        }
    }
    // -----------------------------------------
    // MARK: [앱이 살아있고 background 상태 일 때, 알림이 온 경우 >> 포그라운드 전환 상태]
    func userNotificationCenter(_ center: UNUserNotificationCenter,
                                didReceive response: UNNotificationResponse,
                                withCompletionHandler completionHandler: @escaping () -> Void) {
        print("")
        print("====================================")
        print("[AppDelegate >> didReceive]")
        print("-------------------------------")
        print("설 명 :: 앱 백그라운드 >> 포그라운드 상태 푸시 알림 확인")
        print("-------------------------------")
        print("response :: \(response)") // [응답 전체]
        print("-------------------------------")
        print("title :: \(response.notification.request.content.title)") // [타이틀]
        print("-------------------------------")
        print("body :: \(response.notification.request.content.body)") // [내용]
        print("====================================")
        print("")
        
        // [completionHandler : 푸시 알림 상태창 처리]
        completionHandler()
    }
    // -----------------------------------------
}





// MARK: - [MessagingDelegate 딜리게이트 추가]
extension AppDelegate: MessagingDelegate {
    func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String?) {
        print("")
        print("====================================")
        print("[AppDelegate >> messaging]")
        print("-------------------------------")
        print("설 명 :: 파이어베이스 푸시 토큰 등록 확인")
        print("-------------------------------")
        print("로 직 :: ", "프리퍼런스에 푸시 토큰 값 저장 실시")
        print("-------------------------------")
        print("fcmToken :: \(String(describing: fcmToken ?? ""))")
        print("====================================")
        print("")
        
        
        // -----------------------------------------
        // [SEARCH FAST] : [파이어베이스 등록 토큰 확인]
        // -----------------------------------------
        
        
        // -----------------------------------------
        // [프리퍼런스에 푸시 토큰 저장 실시]
        S_Preference().setString(_sKey: S_FinalData.PRE_PUSH_TOKEN, _sValue: "\(String(describing: fcmToken ?? ""))") // 파이어베이스 푸시 토큰
        // -----------------------------------------
        
        
        // -----------------------------------------
        /*
        print("")
        print("====================================")
        print("[AppDelegate >> messaging]")
        print("-------------------------------")
        print("설 명 :: 파이어베이스 푸시 토큰 등록 확인")
        print("-------------------------------")
        print("로 직 :: ", "푸시 토큰 생성 브로드 캐스트 알림 전달 실시")
        print("-------------------------------")
        print("fcmToken :: \(String(describing: fcmToken ?? ""))")
        print("====================================")
        print("")
        
        NotificationCenter.default.post(
            name: NSNotification.Name(rawValue: S_FinalData.NOTI_PUSH_TOKEN_CREATE), // [알림을 식별하는 태그]
            object: nil, // [발송자가 옵저버에게 보내려고 하는 객체]
            userInfo: nil // [객체의 저장소 [AnyHashable: Any] 형태]
        )
        // */
        // -----------------------------------------
    }
}
 
 

반응형
Comments