iOS WKWebView에서 Local/Remote 환경에 있는 Javascript와 인터페이스 하는 방식에 대해 간략히 살펴보겠습니다.
유사한 내용을 정리한 글은 찾아보면 많이 있지만 대부분 한 쪽 측면(웹 또는 네이티브)으로 작성되어 있다보니 웹 프론트 사이드에서 해야 할 일, 네이티브 사이드에서 해야 할 일을 분명하게 명시해서 어떤 지점에서 인터페이스 규약을 서로 맞춰야 하는지에 대한 관점을 우선으로 정리해봤습니다.
iOS > Javascript
iOS에서 Javascript 소스를 호출하는 방법은 3가지 방식이 있습니다. 웹프론트가 어떤 방식으로 개발됐는지에 따라 적용 가능한 방법이 다를 수 있으니 유의해주세요.
방법1
React.js, Vue.js와 같이 컴포넌트 기반의 SPA 방식으로 개발된 웹프론트에서 컴포넌트 라이프 사이클에 맞춰 이벤트 수신 리스너를 추가/삭제 하는 방식
1. (front side) 네이티브에서 호출하기 위한 함수를 CustomEvent로 만들고 window.함수명으로 할당합니다.
useEffect(() => {
window.sayHello = new CustomEvent("NativeEvent");
const nativeEventCallback = (event) => {
alert(`event receive from Native`);
};
window.addEventListener("NativeEvent", nativeEventCallback);
// event listener clean up
return () => {
window.removeEventListener("NativeEvent", nativeEventCallback);
};
}, []);
2. (native side) 네이티브에서는 webView가 제공하는 evaluateJavascript 함수로 아래와 같이 window.dispatchEvent로 웹에 생성한 함수를 호출할 수 있습니다.
webView.evaluateJavascript("window.dispatchEvent(sayHello)") { (result, error) in }
방법2
전역 scope로 생성된 함수 호출
1. (front side) 네이티브에서 호출하기 위한 모듈을 정의하고, 전역 scope에 해당하는 모듈 또는 파일에 import 시킵니다.
window.NativeInterface = {
helloWorld: () => {
// your javascript code
},
...
}
2. (native side) 네이티브에서는 webView가 제공하는 evaluateJavascript 함수를 이용하여 웹에 생성된 함수를 호출할 수 있습니다.
webView.evaluateJavascript("javascript:window.NativeInterface.helloWorld()") { (result, error) in }
방법3
HTML 문서 로딩 시작과 종료 시점에만 가능하다. 이미 선언된 스크립트 내 함수를 호출하거나 웹소스에 대한 직접 컨트롤이 불가한 경우 CSS 초기화 등 앱에서 추가하고 싶은 스크립트를 삽입하는 용도로 사용할 수 있다.
let controller = WKUserContentController()
let userScript = WKUserScript(
source: "function plus(a, b) { return a + b }",
injectionTime: .atDocumentEnd,
forMainFrameOnly: true
}
contoller.addUserScript(userScript)
let config = WKWebViewConfiguration()
config.userContentController = controller
webView = WKWebView(frame: view.bounds, configuration: config)
Javascript > iOS
1. (native side) 웹에서 호출하기 위한 인터페이스명을 WKUserContentController로 정의합니다.
let controller = WKUserContentController()
controller.add(self, name: "interfaceName1")
controller.add(self, name: "interfaceName2")
2. (native side) 웹뷰 설정에 1번에서 만든 Controller를 연결합니다.
let config = WKWebViewConfiguration()
config.userContentController = controller
webView = WKWebView(frame: view.bounds, configuration: config)
3. (native side) WKUserContentController의 구현체는 ViewController 내에 delegate를 다음과 같이 구현합니다.
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if (message.name == "interfaceName1") {
// your native code
} else if ...
}
4. (front side) 웹에서는 1번에서 정의한 interfaceName을 이용해 다음과 같이 postMessage를 이용해 호출할 수 있습니다.
window.webkit.messageHandlers.interfaceName1.postMessage("params")
window.webkit.messageHandlers.interfaceName2.postMessage([
key1: 'value1',
key2: 'value2'
])
이상으로 iOS <-> Javascript 간 인터페이스 하는 방법에 대해 알아봤습니다.
잘못된 점이나 추가로 궁금하신 내용이 있다면 댓글로 남겨주세요.
Android에서 Javascript와 인터페이스 하는 방법은 iOS와 유사한 면도 있지만 조금 다른 지점도 있습니다.
웹 프론트 개발자는 플랫폼 OS별로 다르게 호출하는 부분을 모두 알고 있어야 하기 때문에 해당 글을 참고하시기 바랍니다.
'Trouble Shooting > iOS' 카테고리의 다른 글
ITMS-91053: Missing API declaration 해결 (1) | 2024.04.02 |
---|---|
pb_check_proto3_default_value + 342 (0) | 2024.03.20 |
Invalid MinimumOSVersion. Apps that only support 64-bit devices must specify a deployment target of 8.0 or later. (2) | 2024.03.15 |
엔터프라이즈 배포 파일(ipa) 설치 후 실행시 종료되는 케이스 (0) | 2019.06.25 |