xcode에서 푸시메시지 기능을 적용시켜 놓았는데, xcode, 맥OS 버전등을 올리고 다시하면 뭔가 바뀌었는지 안된다.

이번에 새로 작업한 내용을 정리한다. (2024년 4월 버전)

 

1. FCM 사이트 설정 에 들어가서 아이폰앱 등록한다.

https://console.firebase.google.com/

 

로그인 - Google 계정

이메일 또는 휴대전화

accounts.google.com

1) FCM 사이트에 들어가서 아이폰앱 등록한다.

2) 정해진 절차대로 진행하고, GoogleService-Info.plist 파일을 저장한다.

     Firebase SDK를 추가하는데, Swift Package Manager 를 이용하는 방식이 새로 생겼다. 예전 pod 방식보다 쉬우니 이용 권장.

3) 번들ID, 팀ID 등을 넣는다.

4) 앱스토어 설정에 가서 APN key를 등록후에 FCM에 업로드 한다.

 

2. 앱스토어 설정(프로파일, certificate 설정하는곳)

1) APN Key 등록후 다운로드한다.

https://developer.apple.com/account/resources/authkeys/list

 

로그인 - Apple

 

idmsa.apple.com

Apple Push Key 생성

 

 

2) APN Key 를 FCM 사이트에 업로드 한다.

 

3) 번들ID, 팀ID 를 찾아서 확인한다.

번들ID는 Identifiers 메뉴나, xcode General 메뉴의 identity 항목의 Bundle Identifier 에서 확인 할 수 있다.

팀ID는 

https://appstoreconnect.apple.com/access/users

 

https://appstoreconnect.apple.com/login?targetUrl=%2Faccess%2Fusers&authResult=FAILED

 

appstoreconnect.apple.com

여기가 "사용자 및 액세스" 화면인데, 우측 상단에 사용자 정보가 있는데 Edit Profile을 누르면 팀ID 조회가 가능하다.

팀ID

 

3. xcode 프로젝트 설정

예전에는 xcode를 사용하기 위해서 pod 설치해서 이용했는데, 이번에 Swift Package Manager를 이용하여

Firebase SDK를 추가하는 방식이 새로 생겼는데, 훨씬 쉽고 간편하다. 이용 추천.

 

1) GoogleService-Info.plist 파일을 저장

2) Firebase SDK 추가 (Swift Package Manager 이용)

 

3) 초기화 코드 추가 ( AppDelegate 파일)

import UIKit
import FirebaseCore


@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

  var window: UIWindow?

  func application(_ application: UIApplication,
    didFinishLaunchingWithOptions launchOptions:
      [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    FirebaseApp.configure()

    return true
  }
}

 

4) 푸시 수신 처리 등

FCM 등록 문서 참조

https://firebase.google.com/docs/ios/setup?authuser=0&hl=ko

 

Apple 프로젝트에 Firebase 추가  |  Firebase for Apple platforms

Google I/O 2023에서 Firebase의 주요 소식을 확인하세요. 자세히 알아보기 의견 보내기 Apple 프로젝트에 Firebase 추가 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요

firebase.google.com

샘플 Firebase 앱 참조

https://github.com/firebase/quickstart-ios/tree/main/messaging/MessagingExampleSwift

 

quickstart-ios/messaging/MessagingExampleSwift at main · firebase/quickstart-ios

Firebase Quickstart Samples for iOS. Contribute to firebase/quickstart-ios development by creating an account on GitHub.

github.com

5) xcode 푸시 수신 위한 설정

Signing & Capabilities 탭에서 Background Modes 와 Push Notifications 를 추가한다.

6) xcode 푸시 수신위한 예제소스

 

AppDelegate

//
//  AppDelegate.swift
//
//  아이폰앱 FCM 설정 방법 : https://dokit.tistory.com/49
//
//  
//

import UIKit
import UserNotifications
import FirebaseCore
import FirebaseMessaging

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {

    var window: UIWindow?
    let gcmMessageIDKey = "gcm.message_id"
    let myLink = "link"
    var mTimer = Timer()
    var link_org = "";

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Override point for customization after application launch.
        
        FirebaseApp.configure()
        // [START set_messaging_delegate]
        Messaging.messaging().delegate = self
        // [END set_messaging_delegate]

        // Register for remote notifications. This shows a permission dialog on first run, to
        // show the dialog at a more appropriate time move this registration accordingly.
        // [START register_for_notifications]

        UNUserNotificationCenter.current().delegate = self

        let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
        UNUserNotificationCenter.current().requestAuthorization(
          options: authOptions,
          completionHandler: { _, _ in }
        )

        application.registerForRemoteNotifications()

        // [END register_for_notifications]
        
        return true
    }

    func application(_ application: UIApplication,
                     didReceiveRemoteNotification userInfo: [AnyHashable: Any]) {
      // If you are receiving a notification message while your app is in the background,
      // this callback will not be fired till the user taps on the notification launching the application.
      // TODO: Handle data of notification

      // With swizzling disabled you must let Messaging know about the message, for Analytics
      // Messaging.messaging().appDidReceiveMessage(userInfo)

      // Print message ID.
      if let messageID = userInfo[gcmMessageIDKey] {
        print("Message ID: \(messageID)")
      }

      // Print full message.
      print(userInfo)
    }
    
    // [START receive_message]
    func application(_ application: UIApplication,
                     didReceiveRemoteNotification userInfo: [AnyHashable: Any]) async
      -> UIBackgroundFetchResult {
      // If you are receiving a notification message while your app is in the background,
      // this callback will not be fired till the user taps on the notification launching the application.
      // TODO: Handle data of notification

      // With swizzling disabled you must let Messaging know about the message, for Analytics
      // Messaging.messaging().appDidReceiveMessage(userInfo)

      // Print message ID.
      if let messageID = userInfo[gcmMessageIDKey] {
        print("Message ID: \(messageID)")
      }

      // Print full message.
      print(userInfo)

      return UIBackgroundFetchResult.newData
    }

    // [END receive_message]
    
    func application(_ application: UIApplication,
                     didFailToRegisterForRemoteNotificationsWithError error: Error) {
      print("Unable to register for remote notifications: \(error.localizedDescription)")
    }

    // This function is added here only for debugging purposes, and can be removed if swizzling is enabled.
    // If swizzling is disabled then this function must be implemented so that the APNs token can be paired to
    // the FCM registration token.
    func application(_ application: UIApplication,
                     didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
      print("APNs token retrieved: \(deviceToken)")

      // With swizzling disabled you must set the APNs token here.
      // Messaging.messaging().apnsToken = deviceToken
    }

    
    // 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.
    }

}

// [START ios_10_message_handling]

extension AppDelegate: UNUserNotificationCenterDelegate {
  // Receive displayed notifications for iOS 10 devices.
  func userNotificationCenter(_ center: UNUserNotificationCenter,
                              willPresent notification: UNNotification) async
    -> UNNotificationPresentationOptions {
    let userInfo = notification.request.content.userInfo

    // With swizzling disabled you must let Messaging know about the message, for Analytics
    // Messaging.messaging().appDidReceiveMessage(userInfo)

    // [START_EXCLUDE]
    // Print message ID.
    if let messageID = userInfo[gcmMessageIDKey] {
      print("Message ID: \(messageID)")
    }
    // [END_EXCLUDE]

    // Print full message.
    print(userInfo)

    // Change this to your preferred presentation option
    return [[.alert, .sound]]
  }

  func userNotificationCenter(_ center: UNUserNotificationCenter,
                              didReceive response: UNNotificationResponse) async {
    let userInfo = response.notification.request.content.userInfo

    // [START_EXCLUDE]
    // Print message ID.
    if let messageID = userInfo[gcmMessageIDKey] {
      print("Message ID: \(messageID)")
    }
    // [END_EXCLUDE]

    // With swizzling disabled you must let Messaging know about the message, for Analytics
    // Messaging.messaging().appDidReceiveMessage(userInfo)

    // Print full message.
    print(userInfo)

      //let link = userInfo[myLink]
      if let myLinkOrg = userInfo[myLink]{
          link_org = myLinkOrg as! String
          print("link_org: \(link_org)")
      }else if let aps = userInfo["aps"] as? NSDictionary{
          if let link = aps["link"] as? NSString{
              link_org = link as String
              print("link_org !!: \(link_org)")
          }
      }
      print("url link: \(link_org)")
      print("5555555")
//      mTimer = Timer.scheduledTimer(timeInterval: 2, target: self, selector: #selector(timerAction), userInfo: nil, repeats: false)
      
//  https://developer111.tistory.com/50
      if link_org != ""{
          if UIApplication.shared.applicationState == .active {
              NSLog("포그라운드에서 클릭")
              let vc = UIApplication.shared.windows.first!.rootViewController as! ViewController
              let myUrl = URL(string: link_org)
              let myRequest = URLRequest(url: myUrl!)
              vc.mainWebView.load(myRequest)
          }else{
              NSLog("백그라운드에서 클릭")
              let userDefault = UserDefaults.standard
              userDefault.set(link_org, forKey: "link")
              userDefault.synchronize()
          }
          
          

          
      }
      
  }
    
    /**
     * 앱 백그라운드로 이동처리 함수
     */
    func appMinimization() {
        print("앱 백그라운드로 이동")
        let selector = NSSelectorFromString("suspend")
        let sharedUIApplication = UIApplication.perform(NSSelectorFromString("sharedApplication"))?.takeUnretainedValue()
        _ = sharedUIApplication?.perform(selector, with: nil)
    }
    
    @objc func timerAction(timer: Timer){
        print("6666666666 xcode 15 부터 제대로 동작안됨 ㅠㅠ")
//        if let viewController = self.window?.rootViewController as? ViewController {
//            print("7777777777")
//            viewController.pushLoad(pushurl: link_org)
//        }

//https://stackoverflow.com/questions/26753925/set-initial-viewcontroller-in-appdelegate-swift
//https://zest1923.tistory.com/161  ==> 이거 참조해서  Main 스토리보드 UI에서  Identity > Storyboard ID 값을 ViewController 로 설정해야됨
        self.window = UIWindow(frame: UIScreen.main.bounds)

        // In project directory storyboard looks like Main.storyboard,
        // you should use only part before ".storyboard" as it's name,
        // so in this example name is "Main".
        let storyboard = UIStoryboard.init(name: "Main", bundle: nil)

        // controller identifier sets up in storyboard utilities
        // panel (on the right), it called Storyboard ID
//        let viewController = storyboard.instantiateViewController(withIdentifier: "ViewController") as! ViewController
        guard let viewController = storyboard.instantiateViewController(withIdentifier: "ViewController") as? ViewController else { return }


        self.window?.rootViewController = viewController
        if let viewController = self.window?.rootViewController as? ViewController {
            print("7777777777")
            viewController.pushLoad(pushurl: link_org)
        }
    }
    
    
}

// [END ios_10_message_handling]

extension AppDelegate: MessagingDelegate {
  // [START refresh_token]
  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.
  }

  // [END refresh_token]
}

 

 

ViewController

임시

//
//  ViewController.swift
//
//
//  
//


import UIKit
import WebKit
import FirebaseMessaging

class ViewController: UIViewController {

    let url = URL(string: "https://m.naver.com")   // 운영 시작 URL
    
    let myVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
    var latestVersion = "1"
    var myUserid : String = ""
    var myToken : String = ""
    let iosVersion = UIDevice.current.systemVersion
    let contentController = WKUserContentController()
    var mainWebView = WKWebView(frame: CGRect(x: 0.0, y: 0.0, width: 0.1, height: 0.1))
    
    @IBOutlet weak var myStatus: UIView!
    
    @IBOutlet weak var viewIntro: UIImageView!
    
    var createWebView: WKWebView?
    var popupWebView: WKWebView?
    let processPool: WKProcessPool = WKProcessPool()
    var firstLoad : Bool = false
    let MY_BUILD_TYPE : String = "release"    //TODO,    배포시는 release 로 변경, 개발시는 debug
    
    let bt_dismiss: UIButton = {
        let button = UIButton()
        button.setImage(UIImage(named: "ico_playhome.png"), for: UIControl.State.normal)
        button.layer.opacity = 1.0
        button.addTarget(self, action: #selector(handleDismiss), for: .touchUpInside)
        button.layer.cornerRadius = 20
        button.translatesAutoresizingMaskIntoConstraints = false
        return button
    }()
    
    @objc func handleDismiss() {    // 홈으로 가기
        if popupWebView != nil {
            popupWebView?.removeFromSuperview()
            popupWebView = nil
        }
        let request : URLRequest = URLRequest(url: url!)
        mainWebView.load(request)
        
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        view.backgroundColor = .white
        setupWebView()
        setupLayout()
        let request : URLRequest = URLRequest(url: url!)
        mainWebView.load(request)
        bt_dismiss.isHidden = true
        print("@@@@@@ start web @@@@@@@")
        whiteMode()
        
        if MY_BUILD_TYPE == "debug" {
                if let _myToken = Messaging.messaging().fcmToken{
                    myToken = _myToken
                    print("@@@ my playhome token : \(myToken)")
                }
        }
        
        //백그라운드에서 포그라운드로 전환되면 실행되는 함수
        NotificationCenter.default.addObserver(forName: UIApplication.didBecomeActiveNotification, object: nil, queue: nil) { (Notification) in
                   
                let userDefault = UserDefaults.standard
                let pushUrl:String? = userDefault.string(forKey: "link")
                
                //링크가 있는 푸시를 클릭하는 경우에만 실행
                if(pushUrl != nil){
                    NSLog(pushUrl!)
                    NSLog("푸시에서 전달받은 웹뷰로")
                    let myUrl = URL(string: pushUrl!)
                    let myRequest = URLRequest(url: myUrl!)
                    self.mainWebView.load(myRequest)
                    userDefault.removeObject(forKey: "link")
                    userDefault.synchronize()
                }
        }
    }

    func blueMode(){    // 파란바탕
        print("@@@ blueMode")
        if #available(iOS 13.0, *) {
            overrideUserInterfaceStyle = .dark  // 흰글씨
            setNeedsStatusBarAppearanceUpdate()
        }
        myStatus.backgroundColor = UIColor(red: 0, green: 0.441, blue: 0.742, alpha: 1.0)   // 배경색, 파란색
    }
    
    func whiteMode(){    // 흰바탕
        print("@@@ whiteMode")
        if #available(iOS 13.0, *) {
            overrideUserInterfaceStyle = .light  // 검은글씨
            setNeedsStatusBarAppearanceUpdate()
        }
        myStatus.backgroundColor = UIColor(red: 1.0, green: 1.0, blue: 1.0, alpha: 1.0)   // 배경색, 흰색
    }
    
    func pushLoad(pushurl : String){
        print("ViewController linkUrl 111: \(pushurl)")
        guard let linkUrl = URL(string: pushurl) else { return }
        print("ViewController linkUrl 222: \(String(describing: linkUrl))")
        
        let request = URLRequest(url: linkUrl)
        mainWebView.load(request)
        print("@@@@@@ link start web @@@@@@@")

    }
    
    func disappearIntro(){
        viewIntro.isHidden = true
    }
    
    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        if(!firstLoad){
            print("@@@@@@ end web @@@@@@@")
            firstLoad = true
            
            let time = DispatchTime.now() + .seconds(2)
            DispatchQueue.main.asyncAfter(deadline: time) {
                self.disappearIntro()
            }
        }
        
        webView.evaluateJavaScript("navigator.userAgent") { [weak webView] (result, error) in
            if let webView = webView, let userAgent = result as? String {
                webView.customUserAgent = userAgent + " iosplayhome”  // iOS앱 구분위해  useragent에 추가하고, Front에서 이를 사용한다.
            }
        }
        
        print("@@@ webView didFinish url : \(webView.url!.absoluteString)")
        
//        if let myPageName = webView.url?.absoluteString{
//            if myPageName.contains("/biz/") == false{
//                whiteMode()
//            }else if myPageName.contains("/biz/") == true{  // 나의 사업
//                blueMode()
//            }
//        }
        
        showToast(controller: self, message : "debug", seconds: 2.0)

    }
        
    func setupWebView() {
        
        let preferences = WKPreferences()
        preferences.javaScriptEnabled = true
        preferences.javaScriptCanOpenWindowsAutomatically = true
        
        let configuration = WKWebViewConfiguration()
        configuration.preferences = preferences
        configuration.processPool = WKProcessPool()
        configuration.allowsInlineMediaPlayback = true
        configuration.allowsPictureInPictureMediaPlayback = true
        configuration.processPool = processPool
        
        // js -> native call
        contentController.add(self, name: "getDeviceInfo")
        contentController.add(self, name: "getTokenInfo")
        contentController.add(self, name: "goWebUrl")
        contentController.add(self, name: "goAppUpdate")
        contentController.add(self, name: "goAppMarket")
        contentController.add(self, name: "putUserData")
        contentController.add(self, name: "getUserData")
        contentController.add(self, name: "sendBadaroEvent")

        configuration.userContentController = contentController
        
        // native call -> js (이건  html 로드시에만 가능)
        //        let mySource = "mergeAppUserInfo('\(myUserid)','\(myVersion!)','ios','\(iosVersion)')"
        //        let userScript = WKUserScript(source: mySource, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
        //        contentController.addUserScript(userScript)
        
        mainWebView.scrollView.bounces = false
        mainWebView = WKWebView(frame: .zero, configuration: configuration)
        mainWebView.scrollView.isScrollEnabled = true
        mainWebView.scrollView.bounces = false
        mainWebView.uiDelegate = self
        mainWebView.navigationDelegate = self
        mainWebView.allowsLinkPreview = true
        mainWebView.allowsBackForwardNavigationGestures = true
        mainWebView.translatesAutoresizingMaskIntoConstraints = false
        
    }
    
    func setupLayout() {

        view.addSubview(mainWebView)
        view.addSubview(bt_dismiss)
        view.addSubview(viewIntro)
        
        
        if #available(iOS 11.0, *) {
            let safeArea = self.view.safeAreaLayoutGuide
            NSLayoutConstraint.activate([
                
                // Safe area 처리 필요
                mainWebView.topAnchor.constraint(equalTo: safeArea.topAnchor),
                //                mainWebView.topAnchor.constraint(equalTo: statusView.bottomAnchor),
                mainWebView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
                mainWebView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
                mainWebView.bottomAnchor.constraint(equalTo: safeArea.bottomAnchor),
                
                bt_dismiss.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -30),
                bt_dismiss.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
                bt_dismiss.widthAnchor.constraint(equalToConstant: 40),
                bt_dismiss.heightAnchor.constraint(equalToConstant: 40)
                ])
        } else {
            NSLayoutConstraint.activate([
                
                mainWebView.topAnchor.constraint(equalTo: view.topAnchor),
                //                mainWebView.topAnchor.constraint(equalTo: statusView.bottomAnchor),
                mainWebView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
                mainWebView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
                mainWebView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
                
                bt_dismiss.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -30),
                bt_dismiss.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
                bt_dismiss.widthAnchor.constraint(equalToConstant: 40),
                bt_dismiss.heightAnchor.constraint(equalToConstant: 40)
                ])
        }
        
    }
    
    //Toast Message
    //How To Use : showToast(controller: self, message : "This is a test", seconds: 2.0)
    func showToast(controller: UIViewController, message : String, seconds: Double) {
        if MY_BUILD_TYPE == "debug" {
            let alert = UIAlertController(title: nil, message: message, preferredStyle: .alert)
            alert.view.backgroundColor = UIColor.black
            alert.view.alpha = 0.6
            alert.view.layer.cornerRadius = 15
            
            controller.present(alert, animated: true)
            
            DispatchQueue.main.asyncAfter(deadline: DispatchTime.now() + seconds) {
                alert.dismiss(animated: true)
            }
        }
        
    }

}


extension ViewController: WKUIDelegate, WKNavigationDelegate, WKScriptMessageHandler {
    
    func detectBridges() {
        let controller = WKUserContentController()
        mainWebView.configuration.userContentController = controller
        
    }

    func sendUserData(){
        if let myVersion = myVersion{
            print("@@@ myVersion: \(myVersion)")
        }
        let bundleID = Bundle.main.bundleIdentifier
        print("@@@ bundleID: \(bundleID!)")
        let uuidStr = UserDefaults.standard.value(forKey: "uuid") as! String
        let myJsFuncName = "callDeviceInfo('\(myVersion!)','ios','\(iosVersion)','\(uuidStr)','\(bundleID!)')"
        print("@@@ myJsFuncName=\(myJsFuncName)")
        showToast(controller: self, message :myJsFuncName, seconds: 2.0)
        
        // native call -> js (이건 언제든지 가능)
        mainWebView.evaluateJavaScript(myJsFuncName) { (result, error) in
            if let result = result{
                print(result)
            }
        }
    }
    
    func sendTokenData(){
        if let _myToken = Messaging.messaging().fcmToken{
            myToken = _myToken
            print("@@@ my playhome token : \(myToken)")
        }
        let myJsFuncName = "callTokenInfo('\(myToken)')"
        print("@@@ myJsFuncName=\(myJsFuncName)")
        showToast(controller: self, message :myJsFuncName, seconds: 2.0)
        
        // native call -> js (이건 언제든지 가능)
        mainWebView.evaluateJavaScript(myJsFuncName) { (result, error) in
            if let result = result{
                print(result)
            }
        }
    }
    
    // javascript -> native 호출,
    @available(iOS 8.0, *)
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        print("@@@ message.name:\(message.name)")
        if(message.name == "getDeviceInfo"){    // @@@@ 웹 메인에서 @@@@
            print("@@@ getDeviceInfo:\(message.body)")
            sendUserData()  // 앱 정보 전송
        }else if(message.name == "getTokenInfo"){    // token 요청
            print("@@@ getTokenInfo:\(message.body)")
            sendTokenData()  // token 정보 전송
        }else if(message.name == "goWebUrl"){   // 기본 브라우져 호출
            if let requestURL = URL(string: message.body as! String){
                if UIApplication.shared.canOpenURL(requestURL) {
                    UIApplication.shared.open(requestURL, options: convertToUIApplicationOpenExternalURLOptionsKeyDictionary([:]), completionHandler: nil)
                }
            }

        }else if(message.name == "goAppUpdate"){    // 앱 업데이트 요청
            print("@@@ goAppUpdate:\(message.body)")
            
            let myAppId = ""  //   앱 아이디
            if let url = URL(string: "itms-apps://itunes.apple.com/app/id\(myAppId)"), UIApplication.shared.canOpenURL(url) { // 유효한 URL인지 검사합니다. 에뮬은 안됨.
//                if #available(iOS 10.0, *) { //iOS 10.0부터 URL를 오픈하는 방법이 변경 되었습니다.
                    UIApplication.shared.open(url, options: [:], completionHandler: nil)
//                } else {
//                    UIApplication.shared.openURL(url)
//                }
            }
            
        }else if(message.name == "goAppMarket"){    // 앱 마켓 이동
            print("@@@ goAppMarket:\(message.body)")
            
            let myAppId = message.body  //   앱 아이디
            if let url = URL(string: "itms-apps://itunes.apple.com/app/id\(myAppId)"), UIApplication.shared.canOpenURL(url) { // 유효한 URL인지 검사합니다. 에뮬은 안됨.
                UIApplication.shared.open(url, options: [:], completionHandler: nil)

            }
            
        }else if(message.name == "putUserData"){   // 사용자정보 저장
            print("@@@ putUserData:\(message.body)")
            // 인자를 여러개 받을때는 Dictionary로 받는다.
            if let dictionary : [String: String] = message.body as? Dictionary{
                if let userKey = dictionary["userKey"]{
                    if let userData = dictionary["userData"]{
                        UserDefaults.standard.set(userData, forKey: userKey) // Save
                        UserDefaults.standard.synchronize()
                        
                        showToast(controller: self, message :"putUserData: \(userKey), \(userData)", seconds: 2.0)
                    }
                }

            }
        }else if(message.name == "getUserData"){   // 사용자정보 얻기
            if let userKey = message.body as? String{    // TODO
                if let userData = UserDefaults.standard.value(forKey: userKey) as? String{ // Load
                    let myJsFuncName = "callUserData('\(userKey)','\(userData)')"
                    print("@@@ myJsFuncName=\(myJsFuncName)")
                    showToast(controller: self, message :"myJsFuncName=\(myJsFuncName)", seconds: 2.0)
                    
                    // native call -> js (이건 언제든지 가능)
                    mainWebView.evaluateJavaScript(myJsFuncName) { (result, error) in
                        if let result = result{
                            print(result)
                        }
                    }
                }
            }
        }else if(message.name == "sendBadaroEvent"){   // 사용자 이벤트 전달
            if let userEvent = message.body as? String{
                
                showToast(controller: self, message :"sendBadaroEvent: \(userEvent)", seconds: 2.0)
                
                if(userEvent == "menu"){    // 햄버거 메뉴 버튼을 누를때
                    //blueMode()
                }
            }
        }
    }
    
    
    // window.open() 이 호출 될 때 사용
    func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
        print("@@@  createWebViewWith")
        
        guard let requestURL = navigationAction.request.url else {return nil}
        //        let url2 = requestURL.absoluteString
        let hostAddress = navigationAction.request.url?.host
        if hostAddress == "m.kkkkkkkk.com" {
            bt_dismiss.isHidden = true
            
            let request : URLRequest = URLRequest(url: requestURL)
            mainWebView.load(request)
            //mainWebView.load(requestURL)
            return nil
        }

        bt_dismiss.isHidden = true

        popupWebView = WKWebView(frame:view.bounds, configuration: configuration)
        popupWebView!.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        popupWebView!.navigationDelegate = self
        popupWebView!.uiDelegate = self
        view.addSubview(popupWebView!)
        view.addSubview(bt_dismiss)
        
        return popupWebView!
    }
    
    //iOS9.0 이상, window.close()
    func webViewDidClose(_ webView: WKWebView) {
        print("@@@ webViewDidClose")
        if webView == popupWebView {
            popupWebView?.removeFromSuperview()
            popupWebView = nil
        }
    }
    
    func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) {
        print("@@@  runJavaScriptConfirmPanelWithMessage")
        let alertController = UIAlertController(title: nil, message: message, preferredStyle: .alert)
        let yes = UIAlertAction(title: "네", style: .default) {
            action in completionHandler(true)
            
            
        }
        let no = UIAlertAction(title: "취소", style: .default) {
            action in completionHandler(true)
            
        }
        alertController.addAction(no)
        alertController.addAction(yes)
        present(alertController, animated: true, completion: nil)
        
    }
    
    func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) {
        print("@@@  runJavaScriptAlertPanelWithMessage")
        let alertController = UIAlertController(title: nil, message: message, preferredStyle: .alert)
        let otherAction = UIAlertAction(title: "확인", style: .default) {
            action in completionHandler()
            self.navigationController?.popViewController(animated: true)
        }
        alertController.addAction(otherAction)
        present(alertController, animated: true, completion: nil)
        
    }
    
    
    func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
        
    }
    
    func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Swift.Void) {
        print("@@@  decidePolicyFor navigationAction")
        guard let requestURL = navigationAction.request.url else {return}
        let url = requestURL.absoluteString
        let hostAddress = navigationAction.request.url?.host
        // To connnect app store
        if hostAddress == "itunes.apple.com" {
            if UIApplication.shared.canOpenURL(requestURL) {
                UIApplication.shared.open(requestURL, options: convertToUIApplicationOpenExternalURLOptionsKeyDictionary([:]), completionHandler: nil)
                decisionHandler(.cancel)
                return
            }
        }
        
        #if DEBUG
        print("url = \(url), host = \(hostAddress?.description ?? "")")
        #endif
        
        let url_elements = url.components(separatedBy: ":")
        if url_elements[0].contains("http") == false &&
            url_elements[0].contains("https") == false {
            
            if UIApplication.shared.canOpenURL(requestURL) {
                UIApplication.shared.open(requestURL, options: convertToUIApplicationOpenExternalURLOptionsKeyDictionary([:]), completionHandler: nil)
            } else {    // 신용카드 결제화면등을 위해 필요, 해당 결제앱 스키마 호출
                // https://g-y-e-o-m.tistory.com/33
                if url.contains("about:blank") == true {
                    print("@@@ Browser can't be opened, about:blank !! @@@")
                }else{
                    print("@@@ Browser can't be opened, but Scheme try to call !! @@@")
                    bt_dismiss.isHidden = true
                    UIApplication.shared.open(requestURL, options: [:], completionHandler: nil)
                }
                
            }
            
            decisionHandler(.cancel)
            return
            
        }
        decisionHandler(.allow)
        
    }
    
}

// 기본 브라우져 호출
protocol OpenableCustomURL {
    func openCustomApp(urlScheme:String, additional_info:String)
}

extension OpenableCustomURL {
    func openCustomApp(urlScheme:String, additional_info:String) {
        if let requestUrl = URL(string:"\(urlScheme)"+"\(additional_info)") {
            let application:UIApplication = UIApplication.shared
            if application.canOpenURL(requestUrl) {
                application.open(requestUrl, options: convertToUIApplicationOpenExternalURLOptionsKeyDictionary([:]), completionHandler: nil)
            } else {
                print("cant Open2")
            }
        }
    }
}

// Helper function inserted by Swift 4.2 migrator.
fileprivate func convertToUIApplicationOpenExternalURLOptionsKeyDictionary(_ input: [String: Any]) -> [UIApplication.OpenExternalURLOptionsKey: Any] {
    return Dictionary(uniqueKeysWithValues: input.map { key, value in (UIApplication.OpenExternalURLOptionsKey(rawValue: key), value)})
}

 

앱과 서버와 연동을 위한 javascript 파일

푸시 전송을 위한 토큰을 서버로 전달할때, javascript를 이용해서 하든지, json을 이용한 서버 연동을 해댜하는데,

여기서는 javascript를 이용했다.

 

/**
 * 화면명 : 앱 인터페이스
 * 화면HTML : xxx.html
 * 작성자 :
 * 작성일 :  2024.3.30
 */


$(document).ready(function(){

    //goDeviceInfo();  // 접속한 휴대폰기기정보 요청
    //goWebUrl("https://m.naver.com"); // 기본 브라우져 호출 (새 브라우져 창 띄우기)
    goTokenInfo();   // 알림(푸시메시지)전송을 위한 정보 요청

});

function goDeviceInfo(){
    var agent = navigator.userAgent.toLowerCase();
    var isAppIOS      = (agent.match('iosplayhome') != null);
    var isAppAndroid  = (agent.match('androidplayhome') != null);
    
    // 앱으로 정보 요청
    if(isAppAndroid)   //  안드로이드 APP 인 경우
    {
        window.HybridApp.getDeviceInfo("start"); // (Front --> 앱)
    }else if(isAppIOS){    // 아이폰 APP 인 경우
        window.webkit.messageHandlers.getDeviceInfo.postMessage("start"); // (Front --> 앱)
    }else{    // web
    }
}

/*
 * Native App에서 호출, 접속한 스마트폰의 고유값을 전달  (앱 --> Front)
 */
function callDeviceInfo(appVersion, osType, osVersion, key, packagename){
    var temp = "appVersion:"+appVersion+", osType:"+osType+", osVersion:"+osVersion+", Mobile key:"+key+", pkg:"+packagename;
    console.log(temp);

}

function goTokenInfo(){
    var agent = navigator.userAgent.toLowerCase();
    var isAppIOS      = (agent.match('iosplayhome') != null);
    var isAppAndroid  = (agent.match('androidplayhome') != null);
    
    // 앱으로 정보 요청
    if(isAppAndroid)   //  안드로이드 APP 인 경우
    {
        window.HybridApp.getTokenInfo("start"); // (Front --> 앱)
    }else if(isAppIOS){    // 아이폰 APP 인 경우
        window.webkit.messageHandlers.getTokenInfo.postMessage("start"); // (Front --> 앱)
    }else{    // web
    }
}

/*
 * Native App에서 호출, 접속한 스마트폰의 token 값을 전달  (앱 --> Front)
 */
function callTokenInfo(token){
    var temp = "알림(푸시메시지)전송을 위한 token:"+token;
    console.log(temp);
}


function goWebUrl(myurl){
    // 기본 브라우져 호출 (새 브라우져 창 띄우기)
    var agent = navigator.userAgent.toLowerCase();
    var isAppIOS      = (agent.match('iosplayhome') != null);
    var isAppAndroid  = (agent.match('androidplayhome') != null);

    if(isAppAndroid)   //  안드로이드 APP 인 경우
    {
        window.HybridApp.goWebUrl(myurl); // (Front --> 앱)
    }else if(isAppIOS){    // 아이폰 APP 인 경우
        window.webkit.messageHandlers.goWebUrl.postMessage(myurl); // (Front --> 앱)
    }else{    // web
    }
}

+ Recent posts