JavaScript에서 iOS Native로 데이터를 보내려면 webkit.messageHandlers.${custom_name}.postMessage,
iOS Native에서 JavaScript로 데이터를 보내려면 WKWebView의 evaluateJavaScript를 사용합니다
WKUserContentController의 add 메서드를 통해 자바스크립트에서 호출할 수 있는 메시지 핸들러를 설치(Install) 해줍니다
let contentController = WKUserContentController()
contentController.add(self, name: "MyJavaScriptInterfaces")
생성한 WKUserContentController 객체를 사용하는 WKWebViewConfiguration를 생성하고, WKWebViewConfiguration를 기반으로 WKWebView를 생성합니다
let configuration = WKWebViewConfiguration()
configuration.userContentController = contentController
let view = WKWebView(frame: .zero, configuration: configuration)
JavaScript에서 webkit.messageHandlers.${custom_name}.postMessage를 통해 메시지 핸들러를 호출합니다
${custom_name}는 WKUserContentController의 add 메서드 호출시 name 파라미터에 넘겨준 값과 동일해야 합니다 (이 글에서는 MyJavaScriptInterfaces 입니다)
여러 데이터를 넘겨야 하는 경우, JSON과 같은 형식으로 넘겨주면 됩니다
webkit.messageHandlers.MyJavaScriptInterfaces.postMessage({
message: "Hello Native!",
time: Date().toString()
});
메시지 핸들러가 호출될 경우, WKScriptMessageHandler 프로토콜의 userContentControlle:didReceive: 메서드에 메시지가 전달됩니다
WKScriptMessage의 body에 JavaScript가 전달한 데이터가 담겨 있습니다
extension ViewController: WKScriptMessageHandler {
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
guard let body = message.body as? [String : String] else { return }
let messageFromJavaScript: String = {
return body.map { (key: String, value: String) in
return "\(key): \(value)"
}.joined(separator: "\n")
}()
let alertController = UIAlertController(title: "Alert", message: messageFromJavaScript, preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default)
alertController.addAction(okAction)
present(alertController, animated: true)
}
}
WKWebView의 evaluateJavaScript:completionHandler: 메서드를 통해 JavaScript의 함수를 호출합니다
큰따옴표 3개를 사용해서 여러줄을 표현할 수 있습니다.
자바스크립트의 함수가 여러 파라미터를 요구하거나 문자열을 요구하는 경우 해당 방식을 사용하는 것이 편리합니다 (큰따옴표를 이스케이프 처리하거나 작은 따옴표를 사용하지 않아도 됩니다)
문법 링크
self.webView.evaluateJavaScript("""
receiveHelloFromNative("Hello Javascript!", Date())
""") { (result, error) in
if let error = error {
print(error)
return
}
print(result ?? "no result")
}
import UIKit
import WebKit
final class ViewController: UIViewController {
private lazy var webView: WKWebView = {
let contentController = WKUserContentController()
contentController.add(self, name: "MyJavaScriptInterfaces")
let configuration = WKWebViewConfiguration()
configuration.userContentController = contentController
let view = WKWebView(frame: .zero, configuration: configuration)
view.loadHTMLString("""
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=100%, initial-scale=1.0">
<title>Hello, World!</title>
<script>
function sendHelloToNative() {
webkit.messageHandlers.MyJavaScriptInterfaces.postMessage({
message: "Hello Native!",
time: Date().toString()
});
}
function receiveHelloFromNative(message, time) {
document.querySelector("#result").innerHTML = (message + " at " + time.toString());
}
</script>
</head>
<body>
<button onclick="javascript:sendHelloToNative()">
Tap
</button>
<p id="result"></p>
</body>
</html>
""", baseURL: nil)
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
layout()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
DispatchQueue.global().async {
sleep(3)
DispatchQueue.main.async {
self.webView.evaluateJavaScript("""
receiveHelloFromNative("Hello Javascript!", Date())
""") { (result, error) in
if let error = error {
print(error)
return
}
print(result ?? "no result")
}
}
}
}
private func layout() {
view.backgroundColor = .white
webView.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(webView)
NSLayoutConstraint.activate([
webView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
webView.leadingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.leadingAnchor),
webView.trailingAnchor.constraint(equalTo: view.safeAreaLayoutGuide.trailingAnchor),
webView.bottomAnchor.constraint(equalTo: view.safeAreaLayoutGuide.bottomAnchor),
])
}
}
extension ViewController: WKScriptMessageHandler {
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
guard let body = message.body as? [String : String] else { return }
let messageFromJavaScript: String = {
return body.map { (key: String, value: String) in
return "\(key): \(value)"
}.joined(separator: "\n")
}()
let alertController = UIAlertController(title: "Alert", message: messageFromJavaScript, preferredStyle: .alert)
let okAction = UIAlertAction(title: "OK", style: .default)
alertController.addAction(okAction)
present(alertController, animated: true)
}
}
https://developer.apple.com/documentation/webkit/wkusercontentcontroller
https://developer.apple.com/documentation/webkit/wkuserscript
https://developer.apple.com/documentation/webkit/wkwebviewconfiguration
https://oingbong.tistory.com/225
https://g-y-e-o-m.tistory.com/13
https://ios-development.tistory.com/752
https://ios-development.tistory.com/750
https://ios-development.tistory.com/700
요즘은 정말 웹뷰가 많이 자연스럽고 많이사용되는것같더라고요...
이번에 진행하고있는 프로젝트에도 웹뷰를사용해서 데이터를 웹뷰와 연동해야햐는데
도움이 많이됐습니다!!
iOS개발자로써 웹뷰를 어떻게 생각하시는지 개인적으로 궁금하네요!