iOS/swift

16. [iOS/Swift] 웹뷰(WKWebView)와 JavaScript 연동(상세) (Native <-> JavaScript)

drizzle0925 2021. 7. 6. 15:29
728x90

앱을 개발하다보면 Web에서 iOS로 iOS에서 Web으로 함수를 호출하고 싶을때가 있습니다.

그러기 위해서 준비된 기능이 Bridge 기능입니다.

Bridge 기능을 사용하는 방법에 대해서 알아보겠습니다.

 

JavaScript -> iOS 호출

Bridge 호출은 위한 JavaScript 작성

1. JavaScript 코드 작성

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
<h1>iOS JavaScript 연동</h1>
<button type="button" onClick="outputMessage('Hello World')">outputMessage</button>
<button type="button" onClick="buttonClick()">buttonClick</button>
<script>
    // 현재 호출한 디바이스가 어떤 것인지 체크합니다.
var isMobile = {
    Android: function () {
        return navigator.userAgent.match(/Android/i) == null ? false : true;
    },
    iOS: function () {
        return navigator.userAgent.match(/iPhone|iPad|iPod/i) == null ? false : true;
    },
    any: function () {
        return (isMobile.Android() || isMobile.iOS());
    }
};

// 입력 된 메세지를 전달하는 Bridge 함수
function outputMessage(message) {
    if (isMobile.any()) {
        if (isMobile.Android()) {
            android.bridge.outLink(message);
        } else if (isMobile.iOS()) {
            webkit.messageHandlers.outputMessage.postMessage(message);
        }
    }
}	

// Button 클릭 Bridge 함수
function buttonClick() {
    if (isMobile.any()) {
        if (isMobile.Android()) {
            android.bridge.back(true);
        } else if (isMobile.iOS()) {
            webkit.messageHandlers.buttonClick.postMessage(true);
        }
    }
}
</script>
</body>
</html>

 

 

Bridge를 호출하는 부분을 잘봅니다

webkit.messageHandlers.outPutMessage.postMessage(message);

webkit.messageHandlers.buttonClick.postMessage(true);

굵게 표시한 부분이 앱에서 호출되었을때 키가 되는 부분입니다. 이 부분의 명칭이 서로 맞아야 정상적으로 함수를 호출할 수 있습니다.

 

2. WebView 초기화 및 Bridge 등록

Xcode를 켜서 앱 쪽에서 작업을 하도록 합니다.

iOS 11 이상부터는 스토리보드에서도 WKWebView를 직접 추가할 수 있게 되었지만, Bridge를 설정하기 위해서는 WKWebViewConfiguration을 WKWebView 초기화 시에 넣어주어야 되는데 해당 기능을 스토리보드에서는 지원하지 않아 코드로 웹뷰를 생성합니다.

 

Bridge 등록하기

let contentController = WKUserContentController()
        
// Bridge 등록
contentController.add(self, name: "outputMessage")
contentController.add(self, name: "buttonClick")
        
let configuration = WKWebViewConfiguration()
configuration.userContentController = contentController

 

WKWebView 초기화

webView = WKWebView(frame: self.view.frame, configuration: configuration)

 

 

3. 호출된 Bridge 처리하기

    // 호출된 Bridge 처리하기
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        switch message.name {
        case "outputMessage":
            print("outputMessage \(message.body)")
            let message = message.body as? String
            let alert = UIAlertController(title: nil, message: message, preferredStyle: .alert)
            let action = UIAlertAction(title: "확인", style: .default, handler: nil)
            alert.addAction(action)
            self.present(alert, animated: true, completion: nil)
        case "buttonClick":
            print("buttonClick \(message.body)")
            let alert = UIAlertController(title: nil, message: " button 버튼 클릭", preferredStyle: .alert)
            let action = UIAlertAction(title: "확인", style: .default, handler: nil)
            alert.addAction(action)
            self.present(alert, animated: true, completion: nil)
        default:
            break;
        }
    }

 

 

최종완성 코드 (ViewController.swift)

//
//  ViewController.swift
//  webview
//
//  Created by pples on 2021/07/06.
//

import UIKit
import WebKit

class ViewController: UIViewController, WKUIDelegate, WKNavigationDelegate, WKScriptMessageHandler {
    
    @IBOutlet var webView: WKWebView!
    
    override func loadView() {
        super.loadView()
        
        let contentController = WKUserContentController()
                
        // Bridge 등록
        contentController.add(self, name: "outputMessage")
        contentController.add(self, name: "buttonClick")
        
        let configuration = WKWebViewConfiguration()
        configuration.userContentController = contentController
        
        webView = WKWebView(frame: self.view.frame, configuration: configuration)
        webView.uiDelegate = self
        webView.navigationDelegate = self
        self.view = self.webView
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let url = URL(string: "https://www.google.com")
        let request = URLRequest(url: url!)
        webView.load(request)
        
        
    }
    
    override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } //모달창 닫힐때 앱 종료현상 방지.
        
    //alert 처리
    func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String,
                 initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void){
        let alertController = UIAlertController(title: "", message: message, preferredStyle: .alert)
        alertController.addAction(UIAlertAction(title: "확인", style: .default, handler: { (action) in completionHandler() }))
        self.present(alertController, animated: true, completion: nil) }

    //confirm 처리
    func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) {
        let alertController = UIAlertController(title: "", message: message, preferredStyle: .alert)
        alertController.addAction(UIAlertAction(title: "취소", style: .default, handler: { (action) in completionHandler(false) }))
        alertController.addAction(UIAlertAction(title: "확인", style: .default, handler: { (action) in completionHandler(true) }))
        self.present(alertController, animated: true, completion: nil) }
    
    // href="_blank" 처리
    func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
        if navigationAction.targetFrame == nil { webView.load(navigationAction.request) }
        return nil }
    
    // 호출된 Bridge 처리하기
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        switch message.name {
        case "outputMessage":
            print("outputMessage \(message.body)")
            let message = message.body as? String
            let alert = UIAlertController(title: nil, message: message, preferredStyle: .alert)
            let action = UIAlertAction(title: "확인", style: .default, handler: nil)
            alert.addAction(action)
            self.present(alert, animated: true, completion: nil)
        case "buttonClick":
            print("buttonClick \(message.body)")
            let alert = UIAlertController(title: nil, message: " button 버튼 클릭", preferredStyle: .alert)
            let action = UIAlertAction(title: "확인", style: .default, handler: nil)
            alert.addAction(action)
            self.present(alert, animated: true, completion: nil)
        default:
            break;
        }
    }
}

 

 

결과화면


iOS -> JavaScript 호출

 

JavaScript 작성

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
<h1>iOS JavaScript 연동</h1>
<script>
// 현재 호출한 디바이스가 어떤 것인지 체크합니다.
var isMobile = {
    Android: function () {
        return navigator.userAgent.match(/Android/i) == null ? false : true;
    },
    iOS: function () {
        return navigator.userAgent.match(/iPhone|iPad|iPod/i) == null ? false : true;
    },
    any: function () {
        return (isMobile.Android() || isMobile.iOS());
    }
};

// iOS에서 Web JavaScript를 호출
function messageAlert(message){
    if(isMobile.any()){
        if(isMobile.iOS()){
            alert(message);
        }
    }
}
</script>
</body>
</html>

 

 

Swift

ViewController.swift

//
//  ViewController.swift
//  webview
//
//  Created by pples on 2021/07/06.
//

import UIKit
import WebKit

class ViewController: UIViewController, WKUIDelegate, WKNavigationDelegate {
    @IBOutlet var webView: WKWebView!
    
    override func loadView() {
        super.loadView()
        
        webView = WKWebView(frame: self.view.frame)
        webView.uiDelegate = self
        webView.navigationDelegate = self
        self.view = self.webView
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let url = URL(string: "https://www.google.kr")
        let request = URLRequest(url: url!)
        webView.load(request)
    }
    
    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        webView.evaluateJavaScript("messageAlert('ios->web')")
    }
    
    override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } //모달창 닫힐때 앱 종료현상 방지.
        
        //alert 처리
        func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String,
                     initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void){
            let alertController = UIAlertController(title: "", message: message, preferredStyle: .alert)
            alertController.addAction(UIAlertAction(title: "확인", style: .default, handler: { (action) in completionHandler() }))
            self.present(alertController, animated: true, completion: nil) }

        //confirm 처리
        func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) {
            let alertController = UIAlertController(title: "", message: message, preferredStyle: .alert)
            alertController.addAction(UIAlertAction(title: "취소", style: .default, handler: { (action) in completionHandler(false) }))
            alertController.addAction(UIAlertAction(title: "확인", style: .default, handler: { (action) in completionHandler(true) }))
            self.present(alertController, animated: true, completion: nil) }
        
        // href="_blank" 처리
        func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
            if navigationAction.targetFrame == nil { webView.load(navigationAction.request) }
            return nil }

}

 

 

결과화면

728x90