Notice
Recent Posts
Recent Comments
Link
투케이2K
26. (ios/swift) 웹뷰 호출 및 자바스크립트 통신 수행 실시 - WKWebView 본문
[개발 환경 설정]
개발 툴 : 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("<body style=\"background-color: transparent\">", 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)) ]"
}
}
}
[결과 출력]
반응형
'IOS' 카테고리의 다른 글
Comments