Project/Swift+WebView

iOS/WebView/Project - 웹뷰 JS 양방향 통신#13

건담아빠 2023. 1. 20. 11:14

웹뷰를 이용한 하이브리드앱을 개발할 시 네이티브와 웹의 상호 작용하는 부분들이 생각보다 많습니다.

 

팝업 & 로그인 세션 & JWT 토큰 등 이벤트를 캐치해서 처리하는 경우 인터페이스를 정의해서 일련의 규칙을 정하여 개발하지 않으면 개발이 산으로 갈 수밖에 없고 유지보수도 상당히 어려울 것입니다.

이러한 부분들을 가능하게 해주는 부분들에 대해서 처리해 볼 것입니다.

 

기본적으로 WKUserContentController을 사용하여서 가능하며 코드로서 생성하는 것이 좋다고 합니다.

웹뷰 초기화 함수의 configuration속성을 사용해야 하는데 스토리 보드로는 해당 속성을 사용 못하기 때문입니다.

 

여러 사이트를 참조하여 아래 코드를 완성였습니다.

참조 블로그 님들 감사드립니다.

 

MainViewController.swift

세부적인 속성들은 좀더 살펴보고 실제 작업에 착수해야 하겠습니다.

import Foundation
import UIKit
import WebKit

class MainViewController: BaseViewController {
    @IBOutlet weak var viewMain: UIView!
    @IBOutlet weak var viewMainWeb: UIView!
    
    var wkWebView:WKWebView?
    var cookiePool: WKProcessPool?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
        
        title = "MainViewController"
        
        wkWebView = createWKWebView(viewMainWeb!)
        
        let appInfo = AppInfo.shared
//        let urlString = "\(appInfo.serverUrl)?deviceType=ios&pushToken=\(fcmToken)&versionCode=\(build)"
        let urlString = "\(appInfo.serverUrl)"
        
        if let url = URL(string: urlString) {
            let urlRequest = URLRequest(url: url)
            Utils.Log("urlString : \(url)")
                        
//            self.wkWebView?.load(urlRequest)
            DispatchQueue.main.async {
                self.wkWebView?.load(urlRequest)
            }
        }
    }
    
    func createWKWebView(_ view: UIView) -> WKWebView {
        cookiePool = WKProcessPool()
        
        let jScript = "var meta = document.createElement('meta'); meta.setAttribute('name', 'viewport'); meta.setAttribute('content', 'width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no'); document.getElementsByTagName('head')[0].appendChild(meta);"
        let preferences = WKPreferences()
        // window.open 자바스크립트 함수 호출을 자동으로 할 수 있게??
        preferences.javaScriptCanOpenWindowsAutomatically = true
        
        let wkUScript = WKUserScript(source: jScript, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
        let wkUController = WKUserContentController()
        wkUController.addUserScript(wkUScript)
        
        // Swift에 JavaScript 인터페이스 연결
        wkUController.add(self, name: "IosBridge")
        
        let wkWebConfig = WKWebViewConfiguration()
        wkWebConfig.allowsInlineMediaPlayback = true
        wkWebConfig.mediaTypesRequiringUserActionForPlayback = []
        wkWebConfig.userContentController = wkUController
        wkWebConfig.processPool = cookiePool!
        wkWebConfig.preferences = preferences
        wkWebConfig.ignoresViewportScaleLimits = true

        let webViewWK = WKWebView(frame: .zero, configuration: wkWebConfig)
        view.addSubview(webViewWK)
        webViewWK.translatesAutoresizingMaskIntoConstraints = false
        webViewWK.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true
        webViewWK.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true
        webViewWK.topAnchor.constraint(equalTo: view.topAnchor).isActive = true
        webViewWK.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true

        webViewWK.allowsLinkPreview = true

        return webViewWK;
    }
}

// WebAction을 구분하는데 사용되는 타입
enum WebAction: String {
    case setAppData
    case getAppData
}
extension MainViewController: WKScriptMessageHandler {
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        guard message.name == "IosBridge",
              let messages = message.body as? [String: Any],
              let action = messages["action"] as? String else { return }

        let webAction = WebAction(rawValue: action)
        switch webAction {
            case .setAppData:
                print("setAppData")
            case .getAppData:
                print("getAppData")
            default:
                print("undefined action")
        }
        
    }
}

 

테스트

사파리 디버깅 툴을 활용하여 연결한 IosBridge라는 인터페이스를 통해서 Javascript와 네이티브간 통신하는것을 테스트 하였습니다.

테스트코드

var message = {
	action: "getAppData",
	name: '',
	value: null
};
window.webkit.messageHandlers.IosBridge.postMessage(message);

var message = {
	action: "setAppData",
	name: '',
	value: null
};
window.webkit.messageHandlers.IosBridge.postMessage(message);

 

결과

 

캡쳐된 화면의 콘솔로 테스트 하려면 아래 포스팅 참조 바랍니다.

https://dchkang83.tistory.com/129

 

IOS/Xcode 14X - 웹뷰 사파리 브라우저 디버깅

개발자용 메뉴 켜기 사파리 브라우저 -> 설정 메뉴 막대에서 개발자용 메뉴 보기 활성화 개발자용 메뉴가 활성화 되어있는것을 알수 있다. 개발자용 -> Safari Technology Preview 받기 클릭 또는 아래사

dchkang83.tistory.com

 

 

참조

https://ios-development.tistory.com/752

 

[iOS - swift] 3. WKWebView - 양방향 통신, WKUserScript, WKScriptMessageHandler 델리게이트를 구현하여 JavaScript i

1. WKWebView - UIToolBar 사용하여 뒤로가기, 앞으로가기 구현 (goBack(), goForward()) 2. WKWebView - Header 설정, Cookie 설정, access token 전달, deeplink 수신 방법 3. WKWebView - 양방향 통신, WKUserScript, WKScriptMessageHandle

ios-development.tistory.com

https://zetal.tistory.com/entry/WKUserContentController

 

WKUserContentController 뽀개기

WKUserContentController 뽀개기 이번 포스팅에서는 WKWebView 기능 중에 하나인 WKUserContentController 기능을 살펴보겠습니다. WKWebView기능 중에 좋은 기능이 있습니다. 기존 UIWebView에서는 지원이 안됐던 기

zetal.tistory.com