{"id":20646566,"url":"https://github.com/tienne/wkwebview-javascript-bridge-sample","last_synced_at":"2026-05-28T01:02:15.304Z","repository":{"id":98494751,"uuid":"480184312","full_name":"tienne/wkwebview-javascript-bridge-sample","owner":"tienne","description":null,"archived":false,"fork":false,"pushed_at":"2022-04-11T11:49:35.000Z","size":138,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-09T16:58:49.115Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tienne.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2022-04-11T01:14:55.000Z","updated_at":"2022-04-11T01:15:29.000Z","dependencies_parsed_at":"2023-06-28T08:31:01.762Z","dependency_job_id":null,"html_url":"https://github.com/tienne/wkwebview-javascript-bridge-sample","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/tienne/wkwebview-javascript-bridge-sample","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tienne%2Fwkwebview-javascript-bridge-sample","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tienne%2Fwkwebview-javascript-bridge-sample/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tienne%2Fwkwebview-javascript-bridge-sample/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tienne%2Fwkwebview-javascript-bridge-sample/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tienne","download_url":"https://codeload.github.com/tienne/wkwebview-javascript-bridge-sample/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tienne%2Fwkwebview-javascript-bridge-sample/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33589684,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-27T02:00:06.184Z","response_time":53,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-16T16:26:18.085Z","updated_at":"2026-05-28T01:02:15.280Z","avatar_url":"https://github.com/tienne.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# sample swift wkwebview javascript bridge \n\n```\nWKWebview로 javascirpt bridge 하는 방식을 셈플링했습니다.\n\n★ 웹(프론트) → 네이티브\n예전에는 UIWebView에서 연동시 기존에는 URL에 스키마를 정의해서 내려받은 스킴정보를 이용해서 파싱해서 처리했었습니다.\nWKWebView에서는 추가적으로 javascript bridge를 사용해서 편하게 로직 처리를 할 수 있는 방법이 있습니다. \n\n★ 네이티브 → 웹(프론트)\n웹(프론트) → 네이티브 로 전달방식은 추가된 방식이 있지만, \n네이티브 → 웹(프론트) 로 전달/처리 방식은 기존에 자바스크립트 함수를 호출하던 1가지(evaluatejavascript) 그대로 입니다.\n\n아래는 그 기능들에 대한 셈플링을 해봤습니다. \n```\n\n# 설명\n\n## WKWebView 셋팅\n\n`wadInterface` 이름으로 WKUserContentController 를 셋팅합니다.\n\n```swift\n// Sample/Bridge/WebviewBridgeViewController.swift\n\nfinal class WebViewBridgeViewController : UIViewController {\n    private var processor: WebViewMessageProcessor!\n    private var webView: WKWebView!\n\n    private struct Constants {\n        static let callBackHandlerKey = \"wadInterface\"\n    }\n    \n    override func viewDidLoad() {\n        super.viewDidLoad()\n        setupView()\n    }\n    \n    func setupProcessor() {\n        self.processor = WebViewMessageProcessor(target: self)\n    }\n    //...\n}\n\n\nprivate extension WebViewBridgeViewController {\n    func setupView() {\n        // Bridge Setting\n        let userController: WKUserContentController = WKUserContentController()\n        \n        userController.add(self, name: Constants.callBackHandlerKey)\n        let configuration = WKWebViewConfiguration()\n        configuration.userContentController = userController\n        \n        // Default WebView Setting\n        self.webView = WKWebView(frame:self.safeAreaContainerView.bounds, configuration: configuration)\n        // ...\n    }\n    \n    //...\n}\n```\n\n## 웹뷰 -\u003e 네이티브 호출\n\n아래와 같이 웹뷰에서 네이티브로 호출시 메세지를 핸들링하는 처리를 합니다.\n\n```swift\n// Sample/Bridge/WebviewBridgeViewController.swift \nextension WebViewBridgeViewController : WKScriptMessageHandler {\n    // MARK: - 웹뷰 -\u003e 네이티브 받는 영역\n    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {\n        print(\"toNative:\\(message.body)\")\n\n        guard let body = message.body as? [String: Any] else { return }\n        let json = JSON(body)\n        let message = WebViewMessage(json: json)\n        self.processor.postMessage(message: message)\n    }\n}\n```\n\n샘플 코드에서 주고받는 메세지 포멧은 아래와 같습니다.\n```json5\n{\n  \"callbackId\": \"액션의 결과를 넘겨줄 callbackId\",\n  \"action\": \"액션이름\",\n  \"actionArgs\": {} // 액션 호출시 필요한 파라미터(액션마다 스펙이 달라집니다.)\n}\n```\n\n호출에 대한 결과값을 넘겨줘야하는 경우 아래와 같이 다시 웹뷰로 전달합니다.\n웹뷰로 다시 전달할때는 위에서 셋팅했던 WKUserContentController 이름(`wadInterface`)으로 `fromNative` 함수를 실행하면 됩니다.\n\n```swift\n// Sample/Bridge/WebviewBridgeViewController.swift\n\nfinal class WebViewBridgeViewController : UIViewController {\n    //...\n    \n    func executeJavaScript(javascriptString: String?) {\n      guard let javascriptString = javascriptString else { return }\n        self.webView.evaluateJavaScript(javascriptString, completionHandler: nil)\n    }\n    \n    //...\n}\n\n// Sample/Bridge/WebViewMessageProcessor.swift\nimport SwiftyJSON\n\nclass WebViewMessageProcessor: NSObject {\n    // MARK: - 웹뷰 -\u003e 네이티브 -\u003e 웹뷰 처리하는 콜백 실행\n    func executeCallback(callbackId: String?, callback: WebViewCallback) {\n      guard let callbackId = callbackId else { return }\n      let argsString = callback.args.rawString(options: []) ?? \"\"\n      let callbackFunction = \"wadInterface.fromNative(\\(callbackId), \\(callback.isSuccessful), \\(argsString));\"\n\n      self.target.executeJavaScript(javascriptString: callbackFunction)\n    }\n}\n```\n\n### fromNative 함수 스펙\n\n```js\nvar wadInterface = {\n  //...\n\n  /**\n   * 네이티브에서 커맨드를 실행한 후, 네이티브 코드가 호출한다.\n   * @param {number} callbackId - 실행할 때 네이티브에 전송했던 콜백 아이디\n   * @param {boolean} isSuccess - 커맨드가 성공적으로 실행되었는지 여부\n   * @param {Object} args - 네이티브에서 전송하는 JSON 객체\n   */\n  fromNative: function(callbackId, isSuccess, args) {}\n  //...\n}\n```\n\n샘플 코드에서는 두가지 케이스를 작성했습니다.\n\n- 앱의 버전정보를 요청하는경우\n- 현재 떠있는 웹뷰를 종료해달라는 요청\n\n```swift\n// Sample/Bridge/WebViewMessageProcessor.swift\n\nclass WebViewMessageProcessor: NSObject {\n    //...\n\n    func postMessage(message: WebViewMessage) {\n        let completion: (WebViewCallback) -\u003e Void = { callback in\n          self.executeCallback(callbackId: message.callbackId, callback: callback)\n        }\n        guard let action = message.webviewAction else {\n          return\n        }\n\n        self.execute(action: action, completion: completion)\n    }\n    \n    private func execute(action: WebViewAction, completion: ((WebViewCallback) -\u003e Void)? = nil) {\n        switch action {\n        case .appVersion:\n            self.appVersion(completion: completion)\n        case .navigationPop:\n            self.popView(completion: completion)\n        }\n    }\n\n    private func popView(completion: ((WebViewCallback) -\u003e Void)? = nil) {\n        self.target.popView()\n        completion?(WebViewCallback(isSuccessful: true))\n    }\n\n    private func appVersion(completion: ((WebViewCallback) -\u003e Void)? = nil) {\n        var callback: WebViewCallback {\n            var args = JSON()\n            args[\"version\"].string = \"1.0.0\";\n            return .init(args: args, isSuccessful: true)\n        }\n\n        completion?(callback)\n    }\n    //...\n}\n```\n\n## 네이티브 -\u003e 웹뷰\n\n반대로 네이티브에서 시작해야하는 케이스가 있습니다.\n\n예시\n- 앱이 백그라운드로 전환\n- 백그라운드에서 다시 앱 활성화\n- 네트워크가 끊킴\n- 끊켰던 네트워크가 활성화\n\n이러한 케이스들은 콜백을 넘겨주는거와 비슷하게 아래처럼 `fromNativeEvent` 함수를 호출하여 처리합니다.\n```swift\n// Sample/Bridge/WebViewMessageProcessor.swift\n\nclass WebViewMessageProcessor: NSObject {\n    // MARK: - 네이티브에서 이벤트로 호출하는 영역\n    func triggerEvent(eventName: String, args: JSON) {\n        let argsString = args.rawString(options: []) ?? \"\"\n        let eventFunction = \"wadInterface.fromNativeEvent(\\\"\\(eventName)\\\", \\(argsString));\"\n\n        self.target.executeJavaScript(javascriptString: eventFunction)\n    }\n}\n```\n\n### fromNativeEvent 함수 스펙\n```javascript\n\nvar wadInterface = {\n  // ...\n  \n  /**\n   * 네이티브에서 이벤트가 발생시 호출할 함수\n   * @param { string } eventName 이벤트명\n   * @param { Object? } args 이벤트에 넘겨줄 파라미터\n   */\n  fromNativeEvent: function (eventName, args) {}\n  \n  // ...\n}\n```\n\n샘플 코드에서는 앱의 상태가 변경되었을때 (백그라운드, 활성화) 시 appStateChange 라는 이벤트를 호출하도록 작성하였습니다.\n\n```swift\n// Sample/Bridge/WebViewMessageProcessor.swift\n \nclass WebViewMessageProcessor: NSObject {\n    func load() {\n        // 앱이 다시 활성화 되었을때\n        NotificationCenter.default.addObserver(forName: UIApplication.didBecomeActiveNotification, object: nil, queue: OperationQueue.main) { [weak self] (_) in\n            self?.triggerEvent(eventName: \"appStateChange\", args: JSON([ \"isActive\": true]))\n        }\n        \n        // 앱이 백그라운드로 전환되었을때\n        NotificationCenter.default.addObserver(forName: UIApplication.willResignActiveNotification, object: nil, queue: OperationQueue.main) { [weak self] (_) in\n            self?.triggerEvent(eventName: \"appStateChange\", args: JSON([ \"isActive\": false]))\n        }\n    }\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftienne%2Fwkwebview-javascript-bridge-sample","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftienne%2Fwkwebview-javascript-bridge-sample","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftienne%2Fwkwebview-javascript-bridge-sample/lists"}