ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • iOS 앱 간의 통신을 구현하기
    iOS 2018. 8. 18. 23:22


    통신을 부르는 앱을 A, 실행되는 앱을 B라고 하면

    B에서는 info.plist에 다음과 같이 추가를 해 주어야 합니다.


    <key>CFBundleURLTypes</key>

    <array>

    <dict>

    <key>CFBundleTypeRole</key>

    <string>Editor</string>

    <key>CFBundleURLSchemes</key>

    <array>

    <string>test</string>

    </array>

    </dict>

    </array>


    이제 다른 앱에서

    URL Scheme가 'test' 인 URL을 오픈하면, B 앱이 실행되게 됩니다.


    이제 A 앱에서 B 앱을 부를 차례입니다. 

    스키마를 'test'로 지정해주고 콜하면 일단 B 앱은 부를 수 있지만, 무슨 목적으로 B 앱을 불렀는지도 같이 넘겨 주어야 합니다.

    따라서 호스트나 쿼리 형식으로 기타 정보를 같이 넘겨줍니다.



        var components = URLComponents()

                    components.scheme = panService.serviceCode

                    components.host = "tester_host" //호스트에는 어떤 목적으로 쐈는지 표시해 주었습니다.

                    components.queryItems = [URLQueryItem(name: "sender", value: AppText.app_service_code)] // 누가 보냈는지 같이 쏴 줍니다.

                    guard let schemeURL = components.url else {

                        return

                    }

                    

                    if UIApplication.shared.canOpenURL(schemeURL) {

                        print(schemeURL)

                        UIApplication.shared.open(schemeURL, options: [:]) { (bool) in

                            print(bool)

                        }

                    }else {

                        self.alert("B 앱이이 현재 설치되어 있지 않은 상태이므로, 실행할 수 없습니다.")

                    }


    실행되는 B 앱에서는 A 앱이 B 앱을 불러왔다는 것을 감지하고, 일반적인 실행과 다른 모습을 보여야 합니다.

    AppDelegate에     func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool  함수에 다음과 같은 부분을 추가해 줍니다. 저는 B 앱의  BViewController() 뷰컨트롤러에서 해당 부분을 처리하기로 했으므로, 강제로 BViewController 를 root로 해 주는 방식으로 해 주었습니다.


        @available(iOS 9.0, *)

        func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {

            if (url.scheme == 'test'){

                if (url.host == "tester_auth"){

                    let vc = BViewController()

                    vc.openUrl = url // url을 같이 보내줍니다.

                    self.window?.rootViewController = vc

                    self.window?.makeKeyAndVisible()

                    return true

                }   

            }


    *** 이전 코드들을 이어 줍니다.

            

            if KOSession.isKakaoAccountLoginCallback(url){

                return KOSession.handleOpen(url)

            }

            

            guard let scheme = url.scheme else{

                return false

            }

            if scheme == NaverKey.serviceUrlScheme {

                return NaverThirdPartyLoginConnection.getSharedInstance().application(app, open: url, options: options)

                /*if result == .CANCELBYUSER{

                 

                 }*/

                

            }

            if scheme == FacebookKey.serviceUrlScheme {

                SDKApplicationDelegate.shared.application(app, open:url, options: options)

            }

            return GIDSignIn.sharedInstance().handle(url, sourceApplication: options[UIApplicationOpenURLOptionsKey.sourceApplication] as? String, annotation: [:])

            //return SDKApplicationDelegate.shared.application(app, open:url, options: options)

        }

        



    BViewController에서는 전송받은 url을 해석해 원하는 행동을 해 줍니다.

     


    모든 행동을 다 끝냈으면, B 앱은 정상적으로 앱 사이클을 돌아야 합니다. 따라서 정상적인 스토리보드의 뷰컨트롤러를 탈 수 있도록 다음 함수를 콜 해 줍니다.



     func goToLauncher() {

            let storyboard = UIStoryboard(name: "Main", bundle: nil)

            UIApplication.shared.keyWindow?.rootViewController = storyboard.instantiateInitialViewController()

        }



    다만, B 앱에서 하는 일이 마음에 안 들어 유저가 행동을 취소할 수도 있겠죠.

    이 경우, A 앱으로 돌아가는 액션도 구현이 필요합니다.

     

    취소는 B 앱이 A 앱을 실행시키는 것으로 구현해야 합니다. A 앱에서 B 앱으로 실행하는 것처럼 info.plist에서 등록했던 것을 되짚어 A 앱에도 info.plist에 스키마를 등록합니다.



      

        @IBAction func cancelAuth(_ sender: Any) {

            var components = URLComponents()

            components.scheme = self.sender!

            guard let schemeURL = components.url else {

                return

            }

            

            if UIApplication.shared.canOpenURL(schemeURL) {

                print(schemeURL)

                UIApplication.shared.open(schemeURL, options: [:]) { (bool) in

                    print(bool)

                    self.goToLauncher()

                }

            }else {

                self.goToLauncher()

            }

        }



    (A 앱에서 취소 액션 처리하기)

    A 앱의 델리게이트에 다음과 같이 구현합니다. 그러면 B 앱이 A 앱으로 다시 돌아갔을 때,

    이를 감지하고 'OpenCanceled' 라는 커스텀 노티피케이션을 전송할 수 있을 것입니다. 노티피케이션엔 겸사겸사 url도 넣어 줍니다.

      @available(iOS 9.0, *)

        func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {

            if (url.scheme == 'test_cancel'){

                if (url.host == "cancel"){

                    print("called this")

                    var userinfo = [AnyHashable : Any]()

                    userinfo["url"] = url

                    NotificationCenter.default.post(

                        name: Notification.Name(rawValue: "OpenCanceled"), object: nil, userInfo: userinfo

                    )

                    

                }

     }


    A 앱에서 B 앱으로 접속했던 뷰 컨트롤러에 해당 노티피케이션을 해석하는 부분을 구현합니다.



     override func viewDidLoad() {

            super.viewDidLoad()

            setupViews()

         

            NotificationCenter.default.addObserver(self, selector: #selector(cancelCallBack), name: Notification.Name(rawValue: "OpenCanceled")

                , object: nil)

       }



        @objc func cancelCallBack(notification: Notification){

            print(notification)

            if let userInfo = notification.userInfo {

                if let url = userInfo["url"] as? URL {

         ...  url을 해석해 취소 부분을 해석합니다.

                  }

            }

        }

Designed by Tistory.