on
26. (ios/swift) 웹뷰 호출 및 자바스크립트 통신 수행 실시 - WKWebView
26. (ios/swift) 웹뷰 호출 및 자바스크립트 통신 수행 실시 - WKWebView
728x90
반응형
[개발 환경 설정]
개발 툴 : XCODE
개발 언어 : SWIFT
[필요 설정]
[소스 코드 : 웹뷰 메인]
import UIKit // MARK: [웹뷰를 사용하기 위한 import] import WebKit class ViewController: UIViewController , WKNavigationDelegate, WKScriptMessageHandler , WKUIDelegate { // MARK: [클래스 상속 설명] /* 1. WKNavigationDelegate : 웹뷰 실시간 로드 상태 감지 2. WKScriptMessageHandler : 자바스크립트 통신 사용 3. WKUIDelegate : alert 팝업창 이벤트 감지 */ // MARK: [액티비티 메모리 로드 수행 실시] override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. print("") print("===============================") print("[ViewController >> viewDidLoad() : 액티비티 메모리 로드 실시]") print("===============================") print("") // [웹뷰 호출 실시] webviewInit(_loadUrl: "https://google.com") // get 방식 //webviewInit(_loadUrl: "https://jsonplaceholder.typicode.com/posts?userId=1&id;=1") // post 쿼리 파람 방식 } // MARK: [웹뷰 변수 선언 실시 = 스토리보드 없이 동적으로 생성] private var mainWebView: WKWebView? = nil // [ViewController 종료 시 호출되는 함수] deinit { // WKWebView Progress 퍼센트 가져오기 이벤트 제거 self.mainWebView?.removeObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress)) } // MARK: [웹뷰 초기 설정 값 정의 실시 및 웹뷰 로드 수행] func webviewInit(_loadUrl:String){ print("") print("===============================") print("[ViewController >> webviewInit() : 웹뷰 초기 설정 값 정의 실시 및 웹뷰 로드 수행]") print("url : \(_loadUrl)") print("===============================") print("") // [자바스크립트 통신 경로 지정 실시 : 모두 정의] self.addJavaScriptBridgeOpen() self.addJavaScriptBridgeClose() self.addJavaScriptBridgeTest() // [웹뷰 전체 화면 설정 실시] // self.mainWebView = WKWebView.init(frame: CGRect.init(x: 0, y: 0, width: self.view.frame.size.width, height: self.view.frame.size.height)) self.mainWebView = WKWebView.init(frame: CGRect.init(x: 0, y: 0, width: self.view.frame.size.width, height: self.view.frame.size.height), configuration: self.javascriptConfig) // [웹뷰 캐시 삭제 실시] WKWebsiteDataStore.default().removeData(ofTypes: [WKWebsiteDataTypeDiskCache, WKWebsiteDataTypeMemoryCache], modifiedSince: Date(timeIntervalSince1970: 0), completionHandler:{ }) // [웹뷰 여백 및 배경 부분 색 투명하게 변경] //self.mainWebView?.backgroundColor = UIColor.clear //self.mainWebView?.isOpaque = false //self.mainWebView?.loadHTMLString("", baseURL: nil) // [웹뷰 옵션값 지정] self.mainWebView?.configuration.preferences.javaScriptCanOpenWindowsAutomatically = true // 자바스크립트 활성화 self.mainWebView?.navigationDelegate = self // 웹뷰 변경 상태 감지 위함 self.mainWebView?.allowsBackForwardNavigationGestures = true // 웹뷰 뒤로가기, 앞으로 가기 제스처 사용 self.mainWebView?.addObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress), options: .new, context: nil) // 웹뷰 로드 상태 퍼센트 확인 self.mainWebView?.uiDelegate = self // alert 팝업창 이벤트 받기 위함 // [웹뷰 화면 비율 설정 및 초기 웹뷰 로드 실시 : get url 주소] self.view.addSubview(self.mainWebView!) let url = URL (string: _loadUrl) // 웹뷰 로드 주소 let request = URLRequest(url: url! as URL) self.mainWebView!.load(request) // [웹뷰 화면 비율 설정 및 초기 웹뷰 로드 실시 : post query parameters url 주소] /*self.view.addSubview(self.mainWebView!) let url = URL (string: _loadUrl) // 웹뷰 로드 주소 var request = URLRequest(url: url! as URL) request.addValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type") request.httpMethod = "POST" self.mainWebView!.load(request)*/ // [웹뷰 화면 비율 설정 및 초기 웹뷰 로드 실시 : 로컬 html 파일] /*self.view.addSubview(self.mainWebView!) guard let localFilePath = Bundle.main.path(forResource: "javaScriptTest", ofType: "html") else { print("") print("===============================") print("[ViewController >> webviewInit() : 웹뷰 로드 수행 시작]") print("error : file path is nil") print("===============================") print("") return } let urlFile = URL(fileURLWithPath: localFilePath) let request = URLRequest(url: urlFile) self.mainWebView!.load(request as URLRequest)*/ } // MARK: [자바스크립트 통신을 위한 초기화 부분] let javascriptController = WKUserContentController() let javascriptConfig = WKWebViewConfiguration() func addJavaScriptBridgeOpen(){ print("") print("===============================") print("[ViewController >> addJavaScriptBridgeOpen() : 자바스크립트 통신 브릿지 추가]") print("Bridge : open") print("===============================") print("") // [open 브릿지 경로 추가] self.javascriptController.add(self, name: "open") self.javascriptConfig.userContentController = self.javascriptController //self.mainWebView = WKWebView(frame: self.view.bounds, configuration: javascriptConfig) } func addJavaScriptBridgeClose(){ print("") print("===============================") print("[ViewController >> addJavaScriptBridgeClose() : 자바스크립트 통신 브릿지 추가]") print("Bridge : close") print("===============================") print("") // [close 브릿지 경로 추가] self.javascriptController.add(self, name: "close") self.javascriptConfig.userContentController = self.javascriptController //self.mainWebView = WKWebView(frame: self.view.bounds, configuration: javascriptConfig) } func addJavaScriptBridgeTest(){ print("") print("===============================") print("[ViewController >> addJavaScriptBridgeTest() : 자바스크립트 통신 브릿지 추가]") print("Bridge : test") print("===============================") print("") // [test 브릿지 경로 추가] self.javascriptController.add(self, name: "test") self.javascriptConfig.userContentController = self.javascriptController //self.mainWebView = WKWebView(frame: self.view.bounds, configuration: javascriptConfig) } // MARK: [자바스크립트 >> IOS 통신 부분] @available(iOS 8.0, *) func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) { // MARK: [웹 코드] window.webkit.messageHandlers.open.postMessage("[open] 자바스크립트 >> IOS 호출"); if message.name == "open" { // 브릿지 경로 지정 let receiveData = message.body // 전달 받은 메시지 확인 print("") print("===============================") print("[ViewController >> userContentController() : 자바스크립트 >> IOS]") print("Bridge : open") print("receiveData : ", receiveData) print("===============================") print("") // MARK: [웹 코드] function receive_Open() {} : IOS >> 자바스크립트 데이터 전송 실시 self.sendFunctionOpen(_send: "") // 널 데이터 } // MARK: [웹 코드] window.webkit.messageHandlers.close.postMessage("[close] 자바스크립트 >> IOS 호출"); if message.name == "close" { // 브릿지 경로 지정 let receiveData = message.body // 전달 받은 메시지 확인 print("") print("===============================") print("[ViewController >> userContentController() : 자바스크립트 >> IOS]") print("Bridge : close") print("receiveData : ", receiveData) print("===============================") print("") // MARK: [웹 코드] function receive_Close(value) {} : IOS >> 자바스크립트 데이터 전송 실시 self.sendFunctionClose(_send: "IOS >> 자바스크립트") // 데이터 } // MARK: [웹 코드] window.webkit.messageHandlers.test.postMessage("[test] 자바스크립트 >> IOS 호출"); if message.name == "test" { // 브릿지 경로 지정 let receiveData = message.body // 전달 받은 메시지 확인 print("") print("===============================") print("[ViewController >> userContentController() : 자바스크립트 >> IOS]") print("Bridge : test") print("receiveData : ", receiveData) print("===============================") print("") // MARK: [웹 코드] function receive_Close(value) {} : IOS >> 자바스크립트 데이터 전송 실시 self.sendFunctionTest(_send: "") // 널 데이터 } } // MARK: [IOS >> 자바스크립트 통신 부분] func sendFunctionOpen(_send:String){ print("") print("===============================") print("[ViewController >> sendFunctionOpen() : IOS >> 자바스크립트]") print("_send : ", _send) print("===============================") print("") self.mainWebView!.evaluateJavaScript("receive_Open('\(_send)')", completionHandler: nil) /*self.mainWebView!.evaluateJavaScript("receive_Open('')", completionHandler: { (any, err) -> Void in print(err ?? "[receive_Open] IOS >> 자바스크립트 : SUCCESS") })*/ } func sendFunctionClose(_send:String){ print("") print("===============================") print("[ViewController >> sendFunctionClose() : IOS >> 자바스크립트]") print("_send : ", _send) print("===============================") print("") self.mainWebView!.evaluateJavaScript("receive_Close('\(_send)')", completionHandler: nil) /*self.mainWebView!.evaluateJavaScript("receive_Close('')", completionHandler: { (any, err) -> Void in print(err ?? "[receive_Close] IOS >> 자바스크립트 : SUCCESS") })*/ } func sendFunctionTest(_send:String){ print("") print("===============================") print("[ViewController >> sendFunctionClose() : IOS >> 자바스크립트]") print("_send : ", _send) print("===============================") print("") self.mainWebView!.evaluateJavaScript("receive_Test('\(_send)')", completionHandler: nil) /*self.mainWebView!.evaluateJavaScript("receive_Test('')", completionHandler: { (any, err) -> Void in print(err ?? "[receive_Test] IOS >> 자바스크립트 : SUCCESS") })*/ } // [웹뷰 로드 수행 시작 부분] func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) { let _startUrl = String(describing: webView.url?.description ?? "") print("") print("===============================") print("[ViewController >> didStartProvisionalNavigation() : 웹뷰 로드 수행 시작]") print("url : \(_startUrl)") print("===============================") print("") } // [웹뷰 로드 상태 퍼센트 확인 부분] override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { // 0 ~ 1 사이의 실수형으로 결과값이 출력된다 [0 : 로딩 시작, 1 : 로딩 완료] print("") print("===============================") print("[ViewController >> observeValue() : 웹뷰 로드 상태 확인]") print("loading : \(Float((self.mainWebView?.estimatedProgress)!)*100)") print("===============================") print("") } // [웹뷰 로드 수행 완료 부분] func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) { let _endUrl = String(describing: webView.url?.description ?? "") print("") print("===============================") print("[ViewController >> didFinish() : 웹뷰 로드 수행 완료]") print("url : \(_endUrl)") print("===============================") print("") } // [웹뷰 로드 수행 에러 확인] func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) { let _nsError = error as NSError let _errorUrl = String(describing: webView.url?.description ?? "") print("") print("===============================") print("[ViewController >> didFail() : 웹뷰 로드 수행 에러]") print("_errorUrl : \(_errorUrl)") print("_errorCode : \(_nsError)") print("_errorMsg : \(S_WebViewErrorCode().checkError(_errorCode: 1019))") print("===============================") print("") } // [웹뷰 실시간 url 변경 감지 실시] func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { let _shouldUrl = String(describing: webView.url?.description ?? "") var action: WKNavigationActionPolicy? defer { decisionHandler(action ?? .allow) } guard let url = navigationAction.request.url else { return } print("") print("===============================") print("[ViewController >> decidePolicyFor() : 웹뷰 실시간 url 변경 감지]") print("_shouldUrl : \(_shouldUrl)") print("requestUrl : \(url)") print("===============================") print("") } // [웹뷰 모달창 닫힐때 앱 종료현상 방지] override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() } // [alert 팝업창 처리] func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void){ print("") print("===============================") print("[ViewController >> runJavaScriptAlertPanelWithMessage() : alert 팝업창 처리]") print("message : ", message) print("===============================") print("") let alertController = UIAlertController(title: "", message: message, preferredStyle: .alert) alertController.addAction(UIAlertAction(title: "확인", style: .default, handler: { (action) in completionHandler() })) self.present(alertController, animated: true, completion: nil) } // [confirm 팝업창 처리] func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) { print("") print("===============================") print("[ViewController >> runJavaScriptConfirmPanelWithMessage() : confirm 팝업창 처리]") print("message : ", message) print("===============================") print("") let alertController = UIAlertController(title: "", message: message, preferredStyle: .alert) alertController.addAction(UIAlertAction(title: "취소", style: .default, handler: { (action) in completionHandler(false) })) alertController.addAction(UIAlertAction(title: "확인", style: .default, handler: { (action) in completionHandler(true) })) self.present(alertController, animated: true, completion: nil) } // [href="_blank" 링크 이동 처리] /*func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? { print("") print("===============================") print("[ViewController >> createWebViewWith() : href=_blank 링크 이동]") print("===============================") print("") if navigationAction.targetFrame == nil { webView.load(navigationAction.request) } return nil }*/ }
[소스 코드 : 웹뷰 에러 코드]
// // S_WebViewErrorCode.swift // testCode // // Created by lotecs on 2021/10/24. // import Foundation class S_WebViewErrorCode { /* [클래스 설명] 1. 웹뷰 호출 시 발생하는 에러 코드 관리 클래스 2. 사용 방법 : S_WebViewErrorCode().checkError(_errorCode: 1019) */ func checkError(_errorCode:Int) -> String { // ========== [일반 에러 정의] ========== if _errorCode == NSURLErrorCancelled { return "NSURLErrorCancelled [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorUnknown{ return "NSURLErrorUnknown [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorBadURL{ return "NSURLErrorBadURL [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorTimedOut{ return "NSURLErrorTimedOut [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorUnsupportedURL{ return "NSURLErrorUnsupportedURL [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorCannotFindHost{ return "NSURLErrorCannotFindHost [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorCannotConnectToHost{ return "NSURLErrorCannotConnectToHost [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorNetworkConnectionLost{ return "NSURLErrorNetworkConnectionLost [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorDNSLookupFailed{ return "NSURLErrorDNSLookupFailed [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorHTTPTooManyRedirects{ return "NSURLErrorHTTPTooManyRedirects [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorResourceUnavailable{ return "NSURLErrorResourceUnavailable [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorNotConnectedToInternet{ return "NSURLErrorNotConnectedToInternet [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorRedirectToNonExistentLocation{ return "NSURLErrorRedirectToNonExistentLocation [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorBadServerResponse{ return "NSURLErrorBadServerResponse [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorUserCancelledAuthentication{ return "NSURLErrorUserCancelledAuthentication [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorUserAuthenticationRequired{ return "NSURLErrorUserAuthenticationRequired [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorZeroByteResource{ return "NSURLErrorZeroByteResource [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorCannotDecodeRawData{ return "NSURLErrorCannotDecodeRawData [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorCannotDecodeContentData{ return "NSURLErrorCannotDecodeContentData [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorCannotParseResponse{ return "NSURLErrorCannotParseResponse [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorAppTransportSecurityRequiresSecureConnection{ return "NSURLErrorAppTransportSecurityRequiresSecureConnection [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorFileDoesNotExist{ return "NSURLErrorFileDoesNotExist [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorFileIsDirectory{ return "NSURLErrorFileIsDirectory [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorNoPermissionsToReadFile{ return "NSURLErrorNoPermissionsToReadFile [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorDataLengthExceedsMaximum{ return "NSURLErrorDataLengthExceedsMaximum [ \(String(_errorCode)) ]" } // ========== [ssl 에러 발생] ========== else if _errorCode == NSURLErrorSecureConnectionFailed{ return "NSURLErrorSecureConnectionFailed [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorServerCertificateHasBadDate{ return "NSURLErrorServerCertificateHasBadDate [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorServerCertificateUntrusted{ return "NSURLErrorServerCertificateUntrusted [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorServerCertificateHasUnknownRoot{ return "NSURLErrorServerCertificateHasUnknownRoot [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorServerCertificateNotYetValid{ return "NSURLErrorServerCertificateNotYetValid [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorClientCertificateRejected{ return "NSURLErrorClientCertificateRejected [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorClientCertificateRequired{ return "NSURLErrorClientCertificateRequired [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorCannotLoadFromNetwork{ return "NSURLErrorCannotLoadFromNetwork [ \(String(_errorCode)) ]" } // ========== [파일 관련 에러] ========== else if _errorCode == NSURLErrorCannotCreateFile{ return "NSURLErrorCannotCreateFile [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorCannotOpenFile{ return "NSURLErrorCannotOpenFile [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorCannotCloseFile{ return "NSURLErrorCannotCloseFile [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorCannotWriteToFile{ return "NSURLErrorCannotWriteToFile [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorCannotRemoveFile{ return "NSURLErrorCannotRemoveFile [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorCannotMoveFile{ return "NSURLErrorCannotMoveFile [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorDownloadDecodingFailedMidStream{ return "NSURLErrorDownloadDecodingFailedMidStream [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorDownloadDecodingFailedToComplete{ return "NSURLErrorDownloadDecodingFailedToComplete [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorInternationalRoamingOff{ return "NSURLErrorInternationalRoamingOff [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorCallIsActive{ return "NSURLErrorCallIsActive [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorDataNotAllowed{ return "NSURLErrorDataNotAllowed [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorRequestBodyStreamExhausted{ return "NSURLErrorRequestBodyStreamExhausted [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorBackgroundSessionRequiresSharedContainer{ return "NSURLErrorBackgroundSessionRequiresSharedContainer [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorBackgroundSessionInUseByAnotherProcess{ return "NSURLErrorBackgroundSessionInUseByAnotherProcess [ \(String(_errorCode)) ]" } else if _errorCode == NSURLErrorBackgroundSessionWasDisconnected{ return "NSURLErrorBackgroundSessionWasDisconnected [ \(String(_errorCode)) ]" } // ========== [else 처리] ========== else { return "else [ \(String(_errorCode)) ]" } } }
[결과 출력]
728x90
반응형
from http://kkh0977.tistory.com/1348 by ccl(A) rewrite - 2021-10-24 21:01:44