하이브리드 앱을 개발하던 도중 웹뷰와 네이티브 간 통신이 필요한 상황에 직면하였다.
처음에는 삽질을 많이 했지만 돌이켜보면 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)
}
}
}
'iOS::스위프트(swift)' 카테고리의 다른 글
[iOS][Swift] objc 함수 인자로 넘기기 (0) | 2021.10.14 |
---|---|
[iOS][Swift] 하나의 뷰에서 세 가지 뷰 컨트롤러 스위치하기 (1) | 2021.10.11 |
프로그래머스 - 순위검색 Swift (0) | 2021.09.09 |
Node.js로 푸시 알람 구현하여 iOS, Android에서 받아보기(feat. FCM) (0) | 2021.09.01 |
swift 키보드 화면 가림 방지하기! (0) | 2021.07.27 |
최근댓글