Trouble Shooting/iOS

하이브리드앱(iOS-Javascript) 간 인터페이스 방법

Gradler 2022. 6. 20. 14:54

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별로 다르게 호출하는 부분을 모두 알고 있어야 하기 때문에 해당 글을 참고하시기 바랍니다.