이전 글에서 이어집니다.

 

자바스크립트에서 네이티브 앱(Swift)로 데이터 보내기

웹 페이지의 자바스크립트 부분에 다음을 추가합니다.

const is_iOS = navigator.userAgent.match(/iPhone|iPad|iPod/i) == null ? false : true
function sendToNativeApp(event) {
  if(is_iOS) {
      // receiveFromJS - 네이티브 앱에서 사용할 함수 이름
      webkit.messageHandlers.receiveFromJS.postMessage("자바스크립트로부터")
  }
}
  • is_iOS -해당 스크립트를 실행하는 기기가 iOS 계열 기기인지 확인합니다. 동일한 웹 페이지에서 안드로이드와의 통신이 필요할 수도 있기 때문에 구분하여 작성합니다.
  • webkit.messageHandlers.[Swift에서 사용할 이름].postMessage(데이터)
    • 위의 [Swift에서 사용할 이름] 부분에 Swift에서 데이터를 받을 때 사용할 구분자를 작성합니다.
    • webkit.messageHandlers.receiveFromJS 의 경우 receiveFromJS 입니다.
  • 데이터 부분에는 네이티브 앱에서 받을 데이터를 지정합니다. 자료형은 상관없습니다. 입력값이 없더라도 boolean 값을 보내야 오류를 피할 수 있습니다.

 

Swift 네이티브 앱의 뷰 컨트롤러의 클래스 선언부분 또는 extension 선언 부분에 WKScriptMessageHandler 프로토콜을 추가합니다.

extension ViewController: WKUIDelegate, WKNavigationDelegate, WKScriptMessageHandler {
    
    // ... //

}

이 프로토콜은 다음 protocol stub을 요구합니다. IDE의 지시에 따라 추가해줍니다.

func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
    
    // ... //

}

 

Swift 네이티브 앱의 뷰 컨트롤러에 다음을 추가합니다. (하이라이트 부분)

func loadHelpPage() {
    if #available(iOS 14.0, *) {
        webView.configuration.defaultWebpagePreferences.allowsContentJavaScript = true
    } else {
        // Fallback on earlier versions
        webView.configuration.preferences.javaScriptEnabled = true
    }
    
    // 자바스크립트 -> 네이티브 앱 연결
    // 브리지 등록
    webView.configuration.userContentController.add(self, name: "receiveFromJS")
    
    // 웹 파일 로딩
    webView.uiDelegate = self
    webView.navigationDelegate = self
    
    let pageName = "index"
    guard let url = Bundle.main.url(forResource: pageName, withExtension: "html", subdirectory: "webpage") else {
        return
    }
    webView.loadFileURL(url, allowingReadAccessTo: url)

}
  • loadHelpPage() 함수는 viewDidLoad에서 실행합니다.
  • "receiveFromJS"는 앞의 자바스크립트에서 지정했던 이름으로, 이것을 등록한 뒤 userContentController에서 해당 이름을 if문이나 switch 문으로 찾아 해당 부분에 실행할 작업을 작성합니다.

 

func userContentController(...) 부분에 구분자를 찾고 실행해야 할 작업을 기재합니다.

func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
    
    switch message.name {
    case "receiveFromJS":
        let alert = UIAlertController(title: "자바스크립트로부터 받은 메시지", message: message.body as? String, preferredStyle: .alert)
        let alertAction = UIAlertAction(title: "확인", style: .default, handler: nil)
        alert.addAction(alertAction)
        
        DispatchQueue.main.async {
            self.present(alert, animated: true, completion: nil)
        }
    case "logHandler":
        print("console log:", message.body)
    default:
        print("default")
        break
    }
}
  • 구분자는 message.name에 저장되어 있습니다. 이 구분자는 여러개일 수 있으므로 switch문으로 구분자마다 분기를 만들어줍니다.
  • 네이티브 앱에서 실행할 내용을 작성합니다. 여기서는 메시지를 받아 경고 창을 띄우게 했습니다.
  • message.body as? String – 자바스크립트에서 postMessage(데이터)를 실행했을 때 데이터는 message.body에 담기게 됩니다. 이 데이터는 타입이 Any 이므로 해당하는 자료에 맞게 타입캐스팅을 해야합니다.

 

자바스크립트에서 sendToNativeApp 함수를 실행하면 네이티브 앱에서 경고창이 뜨게 됩니다.

 

네이티브 앱(Swift)에서 자바스크립트로 데이터 보내기

예를 들어 네이티브 앱에서 아래 자바스크립트 함수를 실행하고 싶다고 한다면

function start(msg) {
    document.getElementById("title").textContent = msg
}

 

다음과 같은 코드를 네이티브 앱에 작성하면 됩니다.

// 자바스크립트의 특정 함수 실행하기
let msg = "Swift에서 JS로 값을 넘겼습니다."
let injectionSource = "start('\(msg)')"
let injectionScript = WKUserScript(source: injectionSource, injectionTime: .atDocumentEnd, forMainFrameOnly: false)
webView.configuration.userContentController.addUserScript(injectionScript)
  • injectionSource – 여기에 자바스크립트 코드를 String 형태로 입력합니다.
  • injectionScript – 이 코드를 WKUserScript 타입의 인스턴스에 담아야 합니다. source는 코드, injectionTime은 실행 지점이 어디인지 결정하는 부분으로, 문서의 시작, 끝부분을 지정할 수 있는데 일반적으로 문서의 끝 부분(.atDocumentEnd)에서 실행합니다.
  • webView.configuration.userContentController.addUserScript(injectionScript)
    • 앞의 injectionScript를 userContentController에 추가합니다.

 

실행되면 웹 페이지의 제목이 위와 같이 바뀝니다.

 

추가: console.log 표시하기

위에 나온 두 방법들을 응용해서 웹 페이지 내부 자바스크립트의 console.log를 Xcode의 터미널에서 표시할 수 있습니다. (stackoverflow)

// inject JS to capture console.log output and send to iOS
let source = """
    function captureLog(msg) { window.webkit.messageHandlers.logHandler.postMessage(msg); }
    window.console.log = captureLog;
"""
let script = WKUserScript(source: source, injectionTime: .atDocumentEnd, forMainFrameOnly: false)
webView.configuration.userContentController.addUserScript(script)

// register the bridge script that listens for the output
webView.configuration.userContentController.add(self, name: "logHandler")
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
    
    switch message.name {
    // ... //

    case "logHandler":
        print("console log:", message.body)

    // ... //
    }
}
// 자바스크립트
console.log("success")

 


문의 | 코멘트 또는 yoonbumtae@gmail.com  donaricano-btn

카테고리: Swift

답글 남기기

이메일 주소를 발행하지 않을 것입니다. 필수 항목은 *(으)로 표시합니다