아리의 iOS 탐구생활

[iOS] 웹뷰를 사용할 때 자바스크립트로 양방향 통신하기 본문

Swift/iOS

[iOS] 웹뷰를 사용할 때 자바스크립트로 양방향 통신하기

Ari Lee 2023. 9. 24. 23:16
반응형
최근에 웹뷰 관련된 작업을 진행했는데,
웹뷰에서 보내는 메시지를 전달받아 화면을 이동하거나 띄우는 동작을 구현하였다.
사실 뭣도 모르고 기존 프로젝트에 작성되어있던 코드를 참고하여
작업을 완료하였기 때문에...
그냥 넘어가기 굉장히 찝찝하고 이해가 부족하다고 느껴서
정확히 이해하고 넘어가고 싶어서 정리하게 되었다.

 

내가 알아볼 키워드는 다음과 같다.

  • WKScriptMessageHandler
  • WKWebView
  • evaluateJavaScript

 

iOS에서 웹뷰를 사용할 때 양방향 통신을 하는 방법

 

WKScriptMessageHandler | Apple Developer Documentation

An interface for receiving messages from JavaScript code running in a webpage.

developer.apple.com

WKScriptMessageHandler는 iOS의 WebKit 프레임워크에서 제공하는 프로토콜이다.
이 프로토콜은 WKWebView 내부의 JavaScript에서 웹뷰의 호스트 애플리케이션으로 메시지를 보낼 수 있도록 도와준다.

간단히 말해 WKScriptMessageHandler는 웹 페이지 내의 JavaScript 코드와 네이티브 iOS 코드 사이에서 상호작용(메시지 전달)을 할 수 있다.

 

JavaScript → iOS

웹페이지 내의 버튼을 클릭하면 JavaScript가 WKWebView의 네이티브 애플리케이션으로 어떤 정보나 명령을 보낼 수 있다.

 

 

iOS → JavaScript

 

evaluateJavaScript(_:completionHandler:) | Apple Developer Documentation

Evaluates the specified JavaScript string.

developer.apple.com

iOS 앱에서 사용자의 액션에 대응하여 JavaScript 함수를 호출할 수 있다. 이 경우에는 WKWebView의 evaluateJavaScript 메소드를 사용하면 된다.

 

 


사용 예시 (JavaScript → iOS)

import UIKit
import WebKit

final class WebViewController: UIViewController, WKScriptMessageHandler {
    
    private weak var webView: WKWebView!

    override func viewDidLoad() {
        super.viewDidLoad()

        let contentController = WKUserContentController()
        contentController.add(self, name: "messageHandler") // 해당 이름으로 메시지를 식별하거나 보낼 수 있게 된다.

        let config = WKWebViewConfiguration()
        config.userContentController = contentController
        
        webView = WKWebView(frame: .zero, configuration: config)
        view.addSubview(webView)
        // ... (웹 뷰 레이아웃 및 URL 로딩 코드)
    }

    // WKScriptMessageHandler 프로토콜의 메서드. JavaScript로 부터 전달받은 메시지를 전달 받는다.
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        if message.name == "messageHandler", let messageBody = message.body as? [String: Any] {
            print("Received message from JavaScript: \(messageBody)") // 웹으로부터 받은 메시지를 파싱하여 print를 호출하는 코드.
        }
    }
}

위 예제에서는 userContentController 메소드에서 JavaScript로부터 내려온 메시지를 받아서 간단하게 print를 실행하는 모습을 확인할 수 있다.

 

userContentController(_:didReceive:) | Apple Developer Documentation

Tells the handler that a webpage sent a script message.

developer.apple.com

 

 

 

 

위 예제를 참고하여 JavaScript에서는 다음과 같은 코드로 iOS에 메시지를 보낼 수 있다.

// iOS에게 메시지를 보내는 코드.
window.webkit.messageHandlers.messageHandler.postMessage("Hello from JavaScript!");

위 코드를 보면 iOS에서 contentController에 add 함수로 등록한 name과 일치하는 이름(window.webkit.messageHandlers.messageHandler.postMessage)으로 메시지를 보내는 모습을 확인할 수 있다.

 

어떤 메시지를 주고 받을지에 대한 정의는 프론트와 iOS가 같이 인터페이스를 별도로 정의해야한다.
위 코드는 간단한 예시라는 것을 명심하자…!

 

 

 

WKScriptMessage | Apple Developer Documentation

An object that encapsulates a message sent by JavaScript code from a webpage.

developer.apple.com

  • message.name
    • iOS 측에서 WKUserContentController 객체를 사용하여 메시지 핸들러를 추가할 때 지정한 이름이다.
    • 이 이름을 통해 웹 페이지 내의 JavaScript에서 해당 핸들러를 식별하고 메시지를 보낼 수 있다. 
따라서, 앱 개발자가 미리 정의하고 웹 개발자에게 해당 이름을 알려줘야 한다.
  • message.body
    • JavaScript에서 postMessage 메서드를 호출할 때 전달하는 데이터다.
    • 이 데이터의 형태와 내용은 웹 개발자와 앱 개발자 사이의 약속에 따라 다를 수 있다. 
예를 들어, 문자열을 보낼 수도 있고, JSON 형태의 데이터를 보낼 수도 있다.

 

 

 

 

 

사용 예시 (iOS JavaScript)

extension WebViewController {

    /// JavaScript 메소드를 호출하는 메소드
    func callJavaScript(script: String, completionHandler: @escaping (Any?, Error?) -> Void) {
        // evaluateJavaScript를 사용하여 웹 페이지 내의 JavaScript 함수를 호출할 수 있다.
        webView.evaluateJavaScript(script) { (result, error) in
            if let error = error {
                print("Error calling JS function: \(error)")
                completionHandler(nil, error)
            } else if let result = result {
                print("Received result from JS function: \(result)")
                completionHandler(result, nil)
            }
        }
    }
}

위 예제에서는 callJavaScript라는 메소드를 별도로 구현하였다.
어떤 특정 상황에서 웹뷰의 기능을 수행해야한다면 위 웹뷰의 evaluateJavaScript 메소드를 통해 JavaScript 함수를 호출하도록 해볼 수 있다.

 

 

evaluateJavaScript(_:completionHandler:) | Apple Developer Documentation

Evaluates the specified JavaScript string.

developer.apple.com

 

 

 

 

 

 

 

결론적으로 message.name과 message.body의 정확한 정의와 사용 방식은 웹 개발자와 앱 개발자 사이의 협업과 의사소통을 통해 정의되어야 한다. 두 개발자는 서로 어떤 메시지 이름을 사용하며, 어떤 형태의 데이터를 주고받을 것인지 사전에 명확히 약속하고 이해해야 한다.

 

 

 

 

제 글에 틀린 내용이 있거나 혹은 도움이 되셨다면, 공감💛 ㆍ 구독✅ ㆍ 공유🔗 ㆍ댓글✍🏻  부탁드립니다. 🙏🏻
반응형
Comments