ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Swift WKWebview 구현하기
    🧑‍💻/Swift 2023. 8. 5. 19:32
    반응형

    앱에 WebView를 띄워 postMessage를 전달하고 결과를 받아 봅시다.


    WKWebview 생성하기

    먼저 WebKit을 import 하고 WKWebView 타입 변수를 생성하고 View에 WKWebView를 추가합니다.

    import UIKit
    import WebKit
    
    class WebViewController: UIViewController, WKUIDelegate {
        var webView: WKWebView!
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            createWebView()
        }
        
        /// WKWebView 생성
        func createWebView() {
            let webConfiguration = WKWebViewConfiguration()
            webView = WKWebView(frame: .zero, configuration: webConfiguration)
            webView.uiDelegate = self
            webView.frame = view.bounds
            view.addSubview(webView)
        }
    }

    WKUIDelegate를 채택하여 WebView의 생성/삭제, JavaScript의 이벤트 수신할 수 있습니다.
    https://developer.apple.com/documentation/webkit/wkuidelegate

     

    WKUIDelegate | Apple Developer Documentation

    The methods for presenting native user interface elements on behalf of a webpage.

    developer.apple.com

     

     

    WKWebview 불러오기

    WKWebView의 load API를 호출하면 웹페이지를 불러옵니다.

    /// WebView 불러오기
    func loadWebView() {
        guard let url = URL(string: "https://주소를 입력하세요") else { return }
        let request = URLRequest(url: url)
    
        webView.load(request)
    }

     

     

    PostMessage 보내기

    window.postMessage()와 동일한 역할을 하는 API 이름을 알기 쉽지 않았는데
    구글링도 아니고 삽질을 통해 알아냈습니다..ㅎㅎ
    WKUserScript를 생성하여 Configuration에 추가해 줍니다.

    /// WKWebView 생성
    func createWebView() {
        let webConfiguration = WKWebViewConfiguration()
        
        ////////////////////////////////////
        // postMessage 설정
        let requestData: String = "전송할 데이터"
        let userScript = WKUserScript(source: "postMessage('\(requestData)')",
                               injectionTime: .atDocumentEnd,
                            forMainFrameOnly: true)
        webConfiguration.userContentController.addUserScript(userScript)
        webConfiguration.preferences.javaScriptEnabled = true
        ////////////////////////////////////
        
        webView = WKWebView(frame: .zero, configuration: webConfiguration)
        webView.uiDelegate = self
        view.addSubview(webView)
    }

     

     

    메시지 수신하기

    메시지를 수신하기 위해 WKScriptMessageHandler를 채택하고, userContentController에 응답이름을 등록합니다.
    WKScriptMessageHandler는 userContentController(_:didReceive:) 함수를 필수로 구현해야 합니다.
    message.body로 수신한 메시지를 확인해 봅시다.

    class WebViewController: UIViewController, WKUIDelegate, WKScriptMessageHandler {
        var webView: WKWebView!
        let responseName: String = "응답이름"
        
        override func viewDidLoad() {
            super.viewDidLoad()
            
            createWebView()
        }
    
        /// WKWebView 생성
        func createWebView() {
            let webConfiguration = WKWebViewConfiguration()
    
            // postMessage 설정
            let requestData: String = "전송할 데이터"
            let userScript = WKUserScript(source: "postMessage('\(requestData)')",
                                   injectionTime: .atDocumentEnd,
                                forMainFrameOnly: true)
            webConfiguration.userContentController.addUserScript(userScript)
            webConfiguration.preferences.javaScriptEnabled = true
    
            ////////////////////////////////////
            // 메시지 수신할 핸들러 등록
            webConfiguration.userContentController.add(self, name: responseName)
            ////////////////////////////////////
    
            webView = WKWebView(frame: .zero, configuration: webConfiguration)
            webView.uiDelegate = self
            view.addSubview(webView)
        }
        
        /// WebView 메시지 핸들러
        func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
            guard message.name == responseName, let body = message.body as? String else { return }
            // body를 파싱하거나 결과 메시지를 확인합니다.
            NSLog("수신한 메시지: \(body)")
        }
    }

     

     

    WebView에서 카메라 사용 시 카메라 권한 확인하기

    Configuration에 allowsInlineMediaPlayback을 true로 설정합니다.
    카메라 권한이 있을 경우 이전에 구현한 loadWebView를 호출하도록 했습니다.
    그런데 카메라 권한을 앱에서 줘도 WebView에서 한 번 더 체크하더라구요? 제거하는 방법은 찾지 못했습니다.. 어쩔 수 없는 것인지..

    import AVFoundation
    
    /// WKWebView 생성
    func createWebView() {
        let webConfiguration = WKWebViewConfiguration()
        
        ////////////////////////////////////
        // 카메라 권한 허용
        webConfiguration.allowsInlineMediaPlayback = true
        ////////////////////////////////////
    
        // postMessage 설정
        let requestData: String = "전송할 데이터"
        let userScript = WKUserScript(source: "postMessage('\(requestData)')",
                               injectionTime: .atDocumentEnd,
                            forMainFrameOnly: true)
        webConfiguration.userContentController.addUserScript(userScript)
        webConfiguration.preferences.javaScriptEnabled = true
    
        // 메시지 수신할 핸들러 등록
        webConfiguration.userContentController.add(self, name: responseName)
    
        webView = WKWebView(frame: .zero, configuration: webConfiguration)
        webView.uiDelegate = self
        view.addSubview(webView)
    }
    
    /// Camera 권한 체크
    func checkCameraPermission() {
        let permission = AVCaptureDevice.authorizationStatus(for: .video)
        switch permission {
        case .authorized:
            DispatchQueue.main.async { self.loadWebView() }
        case .denied:
            let alert = UIAlertController(title: "카메라 권한 필요",
                                          message: "설정 > 개인 정보 보호 > 카메라에서 권한을 변경하실 수 있습니다.",
                                          preferredStyle: .alert)
            let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
            alert.addAction(okAction)
            present(alert, animated: false, completion: nil)
            return
        case .notDetermined:
            // 권한 요청
            AVCaptureDevice.requestAccess(for: .video) { granted in
                if granted {
                    DispatchQueue.main.async { self.loadWebView() }
                } else {
                    NSLog("권한이 거부되었습니다.")
                }
            }
        default:
            NSLog("Permission = \(permission.rawValue)")
        }
    }

    카메라 권한을 얻기 위해서는 Info.plist에 Privacy - Camera Usage Description을 설정해야 합니다.
    안할 경우 crash 발생합니다.

     

     

    JSON Encoding/Decoding

    /// JSON Encoding
    func encodedString() -> String? {
        var jsonData: [String: Any] = []
        
        do {
            // JSON
            let jsonData = try JSONSerialization.data(withJSONObject: jsonData, options: .prettyPrinted)
            return encodedString = String(data: jsonData, encoding: .utf8)
        } catch {
            NSLog(error.localizedDescription)
        }
        return nil
    }
    
    /// JSON Decoding
    func parsingJson(_ jsonString: String) -> MyResponse? {
        if let jsonData = jsonString.data(using: .utf8) {
            let decoder = JSONDecoder()
            let response = try? decoder.decode(MyResponse.self, from: jsonData)
            return response
        }
        return nil
    }
    
    struct MyResponse: Codable {
        let result: String
    }
    반응형

    댓글

Designed by Tistory.