웰코발
웰코's iOS
웰코발
전체 방문자
오늘
어제
  • 분류 전체보기 (63)
    • Swift (26)
    • rxSwift (13)
    • SwiftUI (3)
    • iOS (12)
    • 기타 (1)
    • 개발관련 용어정리 (6)
    • 면접준비 (0)
    • 공공데이터 (1)

블로그 메뉴

  • 홈
  • 태그
  • 방명록

공지사항

인기 글

태그

  • 측정소정보
  • 주제구독
  • Scroll
  • content_available
  • collectionview
  • cell
  • UI
  • SWIFT
  • rxswift
  • 대기오염통계 현황
  • Coordinator
  • ReactorKit
  • alamofire
  • delay
  • ios
  • WKWebView
  • Observable
  • 디자인
  • swiftUI
  • uitableview

최근 댓글

최근 글

티스토리

hELLO · Designed By 정상우.
웰코발

웰코's iOS

Swift

[Swift] WKWebView와 Javascript 사이 통신을 만들어보자

2024. 1. 22. 20:58

웹 뷰를 만들고 웹 뷰에서 넘어오는 값을 받거나 웹 뷰에게 값을 넘기는 방법을 알아보자.

 

웹 뷰 초기 세팅

import Foundation
import UIKit
import SnapKit

import WebKit

class MainViewController: UIViewController, ReactorKit.View {

    ...

    // safe area view
    let safetyAreaView = UIView()

    let progressView: UIProgressView = {
       let view = UIProgressView()
        view.trackTintColor = .lightGray
        view.progressTintColor = .systemRed
        return view
    }()
    
    // 웹 뷰 담을 뷰
    let containerView = UIView()
    
    // WebView
    var webView = WKWebView()
    
    // 로딩 뷰
    let indicator: UIActivityIndicatorView = {
       let activityIndicator = UIActivityIndicatorView()
        activityIndicator.frame = CGRect(x: 0, y: 0, width: 50, height: 50)
        activityIndicator.color = .black
        activityIndicator.hidesWhenStopped = true
        activityIndicator.style = UIActivityIndicatorView.Style.medium
        activityIndicator.stopAnimating()

        return activityIndicator
    }()
    
    ...
    
    override func viewDidLoad() {
        super.viewDidLoad()
        initView()
        initConstraint()
    }
    
    private func initView() {
        self.view.backgroundColor = .white

        safetyAreaView.backgroundColor = .clear
        self.view.addSubview(safetyAreaView)

        containerView.backgroundColor = .white
        safetyAreaView.addSubview(containerView)
        
        containerView.addSubview(progressView)
        
        setAttributes()
        
        view.addSubview(indicator)
        
    }
    
    func initConstraint() {
        
        safetyAreaView.snp.makeConstraints { make in
            make.top.equalTo(view.safeAreaLayoutGuide)
            make.leading.trailing.equalToSuperview()
            make.bottom.equalToSuperview()
        }
        
        containerView.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }
        
        progressView.snp.makeConstraints { make in
            make.top.equalToSuperview()
            make.leading.trailing.equalToSuperview()
        }
        
        webView.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }

        indicator.snp.makeConstraints { make in
            make.center.equalToSuperview()
        }
        
    }
    
    
    private func setAttributes() {
        let contentController = WKUserContentController()
        
        // 중요!!! 일단 초기 세팅으로 두자
        contentController.add(self, name: "customHandlerCallBack1")
        contentController.add(self, name: "customHandlerCallBack2")
        
        let configuration = WKWebViewConfiguration()
        configuration.suppressesIncrementalRendering = false
        configuration.userContentController = contentController
        configuration.defaultWebpagePreferences.allowsContentJavaScript = true
        
        let pref = WKWebpagePreferences.init()
        pref.preferredContentMode = .mobile
        configuration.defaultWebpagePreferences = pref

        webView = WKWebView(frame: .zero, configuration: configuration)
        webView.backgroundColor = .white
        webView.scrollView.alwaysBounceVertical = true
        webView.scrollView.bounces = false
        webView.scrollView.isScrollEnabled = true
        webView.scrollView.showsVerticalScrollIndicator = true
        webView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
        webView.scrollView.contentInsetAdjustmentBehavior = .always
        webView.navigationDelegate = self
        webView.uiDelegate = self
        webView.scrollView.backgroundColor = .white
        webView.addObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress), options: .new, context: nil)
//        webView.navigationDelegate = self
        containerView.addSubview(webView)
        
        loadWebView()
        
    }
    
    private func loadWebView() {
        let urlRequest: URLRequest = URLRequest(url: URL(string: "http://웹뷰에 띄울 웹 URL")!)
    
        webView.load(urlRequest)
        
        // 앞뒤 스와이핑 허용 여부 (default: false)
//        webView.allowsBackForwardNavigationGestures = true

    }
    
    // 웹뷰의 모든 캐시나 쿠키를 지우고 싶을 때 사용 (현재 코드엔 사용안함)
    func deleteWebCash() {
        let websiteDataTypes = NSSet(array: [
        WKWebsiteDataTypeDiskCache,
        WKWebsiteDataTypeMemoryCache,
        WKWebsiteDataTypeCookies,
        WKWebsiteDataTypeOfflineWebApplicationCache,
        WKWebsiteDataTypeWebSQLDatabases])
        
        let date = Date(timeIntervalSince1970: 0)
        
        WKWebsiteDataStore.default().removeData(ofTypes: websiteDataTypes as! Set<String>, modifiedSince: date, completionHandler: { })
        
    }
    
    // 웹뷰의 로딩 진행률 출력
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        print("webView processing: \(webView.estimatedProgress)")
        containerView.bringSubviewToFront(progressView)
        if webView.estimatedProgress == 1.0 {
            progressView.setProgress(0, animated: false)
            progressView.isHidden = true
        } else {
            progressView.setProgress(Float(webView.estimatedProgress), animated: true)
            progressView.isHidden = false
        }
        
    }
    
    .....
    
    
}


extension MainViewController: WKNavigationDelegate {
    // 로드 시작
    func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
        Log.network("webView didStartProvisionalNavigation")
        Log.network("WebView didstart url : \(webView.url?.absoluteString)")

    }
    
    
    // 로드 종료
    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        Log.network("webView didFinish")
        Log.network("WebView didFinish url : \(webView.url?.absoluteString)")
        webView.configuration.suppressesIncrementalRendering = true

    }
    
    // 로드 오류
    func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
        Log.network("webView didFail fail : \(error)")
        Log.network("WebView didFail fail url : \(webView.url?.absoluteString)")
        
        let alert = UIAlertController(title: "webView didFail fail", message: error.localizedDescription, preferredStyle: UIAlertController.Style.alert)
        let okAction = UIAlertAction(title: "OK", style: .default, handler : nil )
        alert.addAction(okAction)
        
        present(alert, animated: true, completion: nil)
        
        
    }
    
    // 서버 끊겨있을 시
    func webView(_ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error) {
        Log.network("webView didFailProvisionalNavigation fail : \(error)")
        Log.network("WebView didFailProvisionalNavigation fail url : \(webView.url?.absoluteString)")
        
        let alert = UIAlertController(title: "webView didFailProvisionalNavigation fail", message: error.localizedDescription, preferredStyle: UIAlertController.Style.alert)
        let okAction = UIAlertAction(title: "OK", style: .default, handler : nil )
        alert.addAction(okAction)
        
        present(alert, animated: true, completion: nil)

    }
    
}

웹 뷰 -> 네이티브 받을 때 코드

우선 네이티브 쪽

/*
반드시 위에 코드 중
	contentController.add(self, name: "customHandlerCallBack1")
    contentController.add(self, name: "customHandlerCallBack2")
을 작성하여 핸들러를 추가해주자
*/

extension MainViewController: WKScriptMessageHandler {
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        Log.debug("message: \(message.debugDescription)")
        Log.debug("userContentController, didReceive : \(message.name), \(message.body)")
        
        switch message.name {
        case "customHandlerCallBack1":
            if let data = message.body as? [String: Any] {

                Log.debug("웹 뷰에서 넘어온 data: \(data)")
                
                Log.debug(data["key값"]) // value값
                
                ....

            }
            
        case "customHandlerCallBack2":
            if let data = message.body as? [String: Any] {

                Log.debug("웹 뷰에서 넘어온 data: \(data)")
                
                Log.debug(data["key값"]) // value값
				
                ....
            }
        default:
             break
        }
        
    }
}

자바스크립트 쪽

function callNative() {
	
    // 네이티브에 customHandlerCallBack1 이란 name으로 전송
	webkit.messageHandlers.customHandlerCallBack1.postMessage(data);
    
    // 네이티브에 customHandlerCallBack2 란 name으로 전송
  	webkit.messageHandlers.customHandlerCallBack2.postMessage(data);

}

네이티브 -> 웹 뷰 넘길 때 코드

class MainViewController: UIViewController, ReactorKit.View {

...



    func bindAction(_ reactor: MainViewReactor) {
        //action
        testBtn.rx.tap
            .subscribe {[weak self] _ in
                guard let self = self else { return }

				// show라는 javascript 함수 호출 (파라미터에 'a' 인자 넣어서 전송)
                self.webView.evaluateJavaScript("show('a')") { (result, error) in
                    
                    print(result)
                    
                }

            }
            .disposed(by: disposeBag)
    }


...

}

 

자바스크립트 코드

function show(data) {
	console.log(data)
}

 

 

 

저작자표시 동일조건 (새창열림)

'Swift' 카테고리의 다른 글

[Swift] FCM 주제 구독 및 백그라운드 처리  (0) 2024.07.01
[Swift] 의존성 주입, DIContainer(IOC Container)만들기  (0) 2024.02.15
[Swift] 좌우 무한 collectionView 를 만들어 보자  (2) 2024.01.05
[Swift] 상단 탭바 페이지 뷰컨트롤러 만들기 (Upper Tab Page View)  (0) 2024.01.02
[Swift] UICollectionView 내부 내용에 따른 Cell 동적 높이 설정법  (0) 2023.11.10
    'Swift' 카테고리의 다른 글
    • [Swift] FCM 주제 구독 및 백그라운드 처리
    • [Swift] 의존성 주입, DIContainer(IOC Container)만들기
    • [Swift] 좌우 무한 collectionView 를 만들어 보자
    • [Swift] 상단 탭바 페이지 뷰컨트롤러 만들기 (Upper Tab Page View)
    웰코발
    웰코발
    나의 개발 일지

    티스토리툴바