하이브리드 앱을 개발하던 도중 웹뷰와 네이티브 간 통신이 필요한 상황에 직면하였다.

처음에는 삽질을 많이 했지만 돌이켜보면 storyBoard를 이용했기 때문에 코드가 실행되는 시점이 엇갈려서 그렇다는 것을 깨달았다. 

아무튼 여러분들은 그런 시행착오를 겪지 않길 바라며 설명을 시작하겠다.

 

iOS -> 웹뷰(JavaScript)

 

먼저 viewDidLoad 시점에 아래와 같은 코드를 적어주어 webView를 초기화한다. 

그리고 우리가 원하는 url을 load하여 화면에 표시해보자.

    override func viewDidLoad() {
        super.viewDidLoad()
        
        let config = WKWebViewConfiguration()
        config.preferences.javaScriptEnabled = true


        webView = WKWebView(frame: CGRect(x: 0, y: 100, width: view.bounds.width, height: view.bounds.height), configuration: config)


        self.view.addSubview(webView)
        guard let url = URL(string: "https://yourhomepage.com") else { return }
        let request = URLRequest(url: url)
        webView.load(request)
   
    }

 

그리고 아래처럼 send라는 버튼 하나를 생성해주자.

 

우리는 iOS 네이티브 버튼 send를 누르면 웹뷰에서 어떤 함수가 호출되어 뷰에 변화를 일으켜볼 것이다.

이를 위해 먼저 send를 @IBAction 으로 연결시켜준다.

    @IBAction func touchUpTestButton(_ sender: Any) {
        let scriptToBeExecuted: String = "mobileToJavascript()"
        
        webView.evaluateJavaScript(scriptToBeExecuted) { result, error in
            if let result = result {
                print(result)
            }
            if let error = error {
                print(error)
            }
            
        }
        
    }

 

위의 mobileToJavascript() 라는 텍스트는 실제 자바스크립트에서 구현해줄 녀석이다.

우리가 웹뷰로 띄울 html/javascript 코드는 아래와 같이 적어주어보자.

<body>
   <input id="test_btn" type="button" value="Say hello"
      onClick="showAndroidToast('Hello Android!')" />
      
   <input id="test_iOS_btn" type="button" value="iOS"
      onClick="showIOSAlert('Hello iOS!')" />



</body>
<script type="text/javascript">

   function mobileToJavascript() {
      document.querySelector("#test_btn").value = "mobile->Javascript"
   }

   ...
</script>

 

여기까지 해두고 실행해보면, 아래 코드에 의해

document.querySelector("#test_btn").value = "mobile->Javascript"

send 버튼을 눌렀을 때 버튼의 내용이 바뀌게 된다. 

 

 

웹뷰(JavaScript) -> iOS native

그렇다면 이번에는 자바스크립트를 통해 iOS 네이티브로 메시지를 전달해보자. 

이를 위해 아래와 같이 Delegate 선언 및 메서드를 구현해주고,

"callbackHandler"라는 이름의 메시지를 받으면 "in" 이라는 로그를 찍도록 만들어보자.

extension ViewController: WKScriptMessageHandler, WKUIDelegate, WKNavigationDelegate {
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {

        print(message.name)
        if message.name == "callbackHandler" {
            print("in")
            print(message.body)
        }

    }
}

 

 

그리고 viewDidLoad시에 특정 configuration 을 추가해줄 것이다. 

해당 config를 적용하고나서, webview를 로드하면 우리가 원하는 것처럼 javascript에서 callbackHandler 라는 이름으로 던진 메시지를 받아올 수 있다.

// 아래처럼 수정
	override func viewDidLoad() {
        super.viewDidLoad()
        
        
        let contentController = WKUserContentController()
        let config = WKWebViewConfiguration()
        config.preferences = WKPreferences()
        if #available(iOS 14.0, *) {
            config.defaultWebpagePreferences.allowsContentJavaScript = true
        } else {
//             이전 버전은 아래를 적용
            config.preferences.javaScriptEnabled = true
        }
        config.userContentController = contentController
        contentController.add(self, name: "callbackHandler")
        webView = WKWebView(frame: CGRect(x: 0, y: 100, width: view.bounds.width, height: view.bounds.height), configuration: config)
        
        webView.uiDelegate = self
        webView.navigationDelegate = self
        self.view.addSubview(webView)
        
        guard let url = URL(string: "https://yourhomepage.com") else { return }
        let request = URLRequest(url: url)
        webView.load(request)

  
    }

 

자바스크립트 코드는 아래와 같이 구성하였다:

<body>
	...
    
    <input type="button" value="iOS" onClick="showIOSAlert('Hello iOS!!!')"/>

	...

<script>

	...

function showIOSAlert(message) {
// 아래 코드의 callbackHandler의 이름을 일치시켜주었다.
	window.webkit.messageHandlers.callbackHandler.postMessage(message);
}

	...
</script>

 

이렇게 하고 실행해보면 iOS 버튼을 눌렀을 때 로그가 잘 나오는 것을 확인할 수 있다.

 

 

전체코드

//
//  ViewController.swift
//  iosWebviewTest
//
//  Created by 최강훈 on 2021/10/05.
//

import UIKit
import WebKit

class ViewController: UIViewController {

    var webView: WKWebView!
     
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        
        setWebViewConfiguration()
        setWebViewDelegate()
        self.view.addSubview(webView)
        loadWebView()
        
        
  
    }
    
    private func setWebViewConfiguration() {
        let contentController = WKUserContentController()
        let config = WKWebViewConfiguration()
        config.preferences = WKPreferences()
        if #available(iOS 14.0, *) {
            config.defaultWebpagePreferences.allowsContentJavaScript = true
        } else {
            config.preferences.javaScriptEnabled = true
        }
        config.userContentController = contentController
        contentController.add(self, name: "callbackHandler")
        webView = WKWebView(frame: CGRect(x: 0, y: 100, width: view.bounds.width, height: view.bounds.height), configuration: config)
    }
    
    private func setWebViewDelegate() {
        webView.uiDelegate = self
        webView.navigationDelegate = self
    }
    
    private func loadWebView() {
        guard let url = URL(string: "http://192.168.0.105:8080/") else { return }
        let request = URLRequest(url: url)
        webView.load(request)

    }
    
    @IBAction func touchUpTestButton(_ sender: Any) {
        let scriptToBeExecuted: String = "mobileToJavascript()"
        
        webView.evaluateJavaScript(scriptToBeExecuted) { result, error in
            if let result = result {
                print(result)
            }
            if let error = error {
                print(error)
            }
            
        }
        
    }
}


extension ViewController: WKScriptMessageHandler, WKUIDelegate, WKNavigationDelegate {
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {

        print(message.name)
        if message.name == "callbackHandler" {
            print("in")
            print(message.body)
        }

    }
}
  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기
// custom