투케이2K

42. (TWOK/UTIL) [Ios/Swift] A_CookieWebview - 쿠키 설정 및 웹뷰 로드 수행 실시 클래스 본문

투케이2K 유틸파일

42. (TWOK/UTIL) [Ios/Swift] A_CookieWebview - 쿠키 설정 및 웹뷰 로드 수행 실시 클래스

투케이2K 2022. 5. 9. 16:45

[설 명]

프로그램 : Ios / Swift

설 명 : 쿠키 설정 및 웹뷰 로드 수행 실시 클래스

 

[소스 코드]

import UIKit
import SafariServices
import WebKit
import AVFoundation
import Photos

class A_CookieWebview: UIViewController, WKNavigationDelegate, WKScriptMessageHandler, WKUIDelegate {
    
    
    
    // MARK: - [클래스 설명]
    /*
    // -----------------------------------------
    1. 사용하는 스토리보드 : Main
    2. 쿠키 삽입 및 웹뷰 화면 호출 액티비티 화면
    // -----------------------------------------
    */
    
    
    
    
    
    // MARK: [쿠키 웹뷰 로직 설명 실시]
    /*
     * // -----------------------------------------
     * 1. A_Main 클래스 에서 setCookieSettings 자바스크립트 브릿지 명령 받으면 쿠키 삽입 및 웹뷰 호출 수행 실시
     * 2. 자바스크립트에서 내려받은 데이터에서 헤더 값이 있는 경우 헤더 값을 추가하는 로직 / 없으면 일반 웹뷰 호출 로직 수행
     * 3. 쿠키 설정 로직 설정
     *   - [1]. 웹뷰 설정 및 최초 로드 (init_WebView) 시 헤더 쪽에 쿠키 추가 실시 [URLRequest.addValue("데이터", forHTTPHeaderField: "Cookie")]
     *   - [2]. addJavaScriptBridge() 자바스크립트 통신 브릿지 설정 부분 에서 WKUserScript / addUserScript 사용해 웹뷰 로드 될 때 [사전] 쿠키 값 설정
     *   - [3]. 실시간 주소 변경 부분 decidePolicyFor() 에서 decisionHandler(.allow) 브라우저 내 로직 허용 실시
     * // -----------------------------------------
     * */
    
    
    
    
    
    // MARK: - [빠른 로직 찾기 : 주석 로직 찾기]
    // -----------------------------------------
    // [SEARCH FAST] : [웹뷰 설정]
    // [SEARCH FAST] : [자바스크립트 통신]
    // [SEARCH FAST] : [동적 헤더 추가 실시]
    // [SEARCH FAST] : [쿠키 설정 실시]
    // [SEARCH FAST] : [실시간 쿠키 값 확인 실시]
    // -----------------------------------------
    
    
    
    
    
    // MARK: - [자바스크립트에서 전달 받은 쿠키 설정 데이터 형식]
    /*
    // -----------------------------------------
    [쿠키 설정 방식]
    {
        "header" : [
              {"Cookie" : "JSESSIONID=61f0aa76ad66d12300000000; domain=m.test.ac.kr; path=/;"}, // [쿠키 스트링 값]
              {"Autholization" : "1234"} // [헤더 권한 설정]
        ],
        "url" : "https://m.test.ac.kr/v3/app", // [웹뷰 주소 로드 수행]
        "domain" : "https://m.test.ac.kr" // [쿠키 설정 도메인]
    }
    // -----------------------------------------
    [일반 주소 호출]
    {
        "url" : "https://www.naver.com"
    }
    // -----------------------------------------
    */
    
    
    
    
    
    // MARK: - [웹뷰 전역 변수 선언 부분]
    private var main_webview: WKWebView? = nil // [동적으로 웹뷰 생성]

    // [뷰 컨트롤러 종료 시 호출되는 함수]
    deinit {
        // [WKWebView Progress 퍼센트 가져오기 이벤트 제거]
        self.main_webview?.removeObserver(
            self,
            forKeyPath: #keyPath(WKWebView.estimatedProgress)
        )
    }
    
    let javascriptController = WKUserContentController() // [자바스크립트 통신 사용]
    let javascriptConfig = WKWebViewConfiguration() // [자바스크립트 통신 사용]
    let kwebviewPreference = WKPreferences() // [자바스크립트 통신 사용]
    
    
    
    
    
    // MARK: - [전역 변수 선언 실시]
    let ACTIVITY_NAME = "A_CookieWebview" // [액티비티 명칭 지정]
    
    var saveData = "" // [프리퍼런스에 저장된 데이터]
    var loadUrl = "" // [웹뷰 로드 주소]
    
    var cookieDomain = "" // [쿠키 설정 도메인 주소]
    var cookieString = "" // [쿠키 스트링 문자열 저장 값]
    var sharedCookies: Array<HTTPCookie> = [] // [쿠키 값 저장 배열]
    
    var headerExist = false // [자바스크립트로 넘어온 헤더 데이터 있는지 체크 실시]
    var headerArray : Array<Dictionary<String, Any>>? = nil // [헤더 정보를 담는 배열]
    
    var errorExist = false // [에러 발생 여부값]
    
    
    

        
    // MARK: - [뷰 로드 실시]
    override func viewDidLoad() {
        super.viewDidLoad()
        print("")
        print("====================================")
        print("[\(self.ACTIVITY_NAME) >> viewDidLoad() :: 뷰 로드 실시]")
        print("====================================")
        print("")
        
        
        // -----------------------------------------
        // [웹뷰 리로드 플래그 값 초기화 실시]
        // S_Preference().setString(_sKey: S_FinalData.PRE_WV_COOKIE_RELOAD_FLAG, _sValue: "")
        // -----------------------------------------
        
        
        // -----------------------------------------
        // [뷰 컨트롤러 배경 색상 지정]
        self.view.backgroundColor = UIColor.init(rgb: 0xffffff).withAlphaComponent(1.0)
        // -----------------------------------------
        
        
        // -----------------------------------------
        // [자바스크립트에서 전달 받은 웹뷰 초기 로드 데이터 확인 실시]
        self.saveData = S_Preference().getString(_sKey: S_FinalData.PRE_WV_COOKIE_DATA)
        
        if C_Util().stringNotNull(str: self.saveData) == true { // [널 데이터가 아닌 경우]
            print("")
            print("====================================")
            print("[\(self.ACTIVITY_NAME) >> viewDidLoad() :: 웹뷰 로드 정보 확인 실시]")
            print("-------------------------------")
            print("saveData :: \(self.saveData)")
            print("====================================")
            print("")
            
            // [프리퍼런스 데이터 초기화 실시]
            S_Preference().setString(_sKey: S_FinalData.PRE_WV_COOKIE_DATA, _sValue: "")
            
            // [사전 json 데이터로 파싱 가능한지 체크 실시]
            if C_Util().stringJsonObjectEnable(str: self.saveData) == true { // [json 데이터로 변경 가능한 경우]
                
                
                // [딕셔너리 객체 생성 실시 및 전체 json 데이터 파싱]
                var jsonDic : Dictionary<String, Any> = C_Util().jsonObject_To_Dic(jsonString: self.saveData)
                
                
                // [세부 데이터 파싱 수행 실시]
                do {
                    // MARK: [1]. url 데이터가 포함되어 있는지 확인 실시 (웹뷰 로드 주소)
                    if jsonDic.keys.contains("url") {
                        self.loadUrl = String(describing: jsonDic["url"] ?? "")
                    }
                    
                    
                    // MARK: [2]. 도메인 주소가 포함되어 있는지 확인 실시 (쿠키 설정 도메인 주소)
                    if jsonDic.keys.contains("domain") {
                        self.cookieDomain = String(describing: jsonDic["domain"] ?? "")
                        self.cookieDomain = self.cookieDomain.replaceAll(_string: "https://", _replace: "")
                        self.cookieDomain = self.cookieDomain.replaceAll(_string: "http://", _replace: "")
                    }
                    
                    
                    // MARK: [3]. 웹뷰 로드에 필요한 헤더 값이 포함되어있는지 확인 실시 (헤더 , 쿠키 값)
                    if jsonDic.keys.contains("header") {
                        self.headerArray = jsonDic["header"] as! Array<Dictionary<String, Any>>
                    }
                    
                    
                    // MARK: [4]. 파싱한 데이터 검증 수행 실시
                    if C_Util().stringNotNull(str: self.loadUrl) == true { // [웹뷰 로드 주소가 널이 아닌 경우]
                        
                        // [도메인 주소와 헤더 값이 널이 아닌 지 확인]
                        if C_Util().stringNotNull(str: self.cookieDomain) == true
                            && self.headerArray != nil && self.headerArray!.count>0 && self.headerArray?.isEmpty == false {
                            
                            // [헤더 배열을 돌면서 세부적인 설정 값 파싱 실시]
                            for i in stride(from: 0, through: self.headerArray!.count-1, by: 1) {
                                var jsonObj : Dictionary<String, Any> = self.headerArray![i] // [배열 각 번지 지정]
                                
                                // MARK: [개별 딕셔너리 데이터 확인 실시]
                                for (key, value) in jsonObj {
                                    /*
                                    print("")
                                    print("====================================")
                                    print("[\(self.ACTIVITY_NAME) >> viewDidLoad() :: [헤더] 배열 값 파싱 수행 실시]")
                                    print("-------------------------------")
                                    print("array :: headerArray")
                                    print("-------------------------------")
                                    print("key :: ", "\(key)")
                                    print("-------------------------------")
                                    print("value :: ", jsonObj["\(key)"] ?? "")
                                    print("====================================")
                                    print("")
                                    // */
                                    
                                    // [실제 헤더가 포함된 경우 플래그 값 설정]
                                    self.headerExist = true
                                    
                                    
                                    // MARK: [쿠키 스트링 값 저장]
                                    if "\(key)".lowercased().trim().contains("cookie") { // [key 값이 쿠키 인 경우]
                                        
                                        // [쿠키 스트링 값 포맷 수행 실시]
                                        ///*
                                        self.cookieString = jsonObj["\(key)"] as? String ?? ""
                                        if self.cookieString.hasSuffix(";") == false { // [쿠키 스트링 값 문자열 마지막이 ; 세미 콜론으로 종료하는 지 확인]
                                            self.cookieString += ";"
                                        }
                                        self.cookieString = self.cookieString.replaceAll(_string: " ", _replace: "") // [공백 제거 실시]
                                        // */
                                        
                                        
                                        /*
                                        // -----------------------------------------
                                        MARK: [쿠키 스트링 형태 설명]
                                        // -----------------------------------------
                                        1. 쿠키 스트링 형태 : JSESSIONID=61f0aa76ad66d12308000000;domain=m.test.ac.kr;path=/;
                                        // -----------------------------------------
                                        2. 주요 사항 : 쿠키 스트링은 각 데이터 별로 [;] 세미콜론 구분이 필요하다
                                        // -----------------------------------------
                                         */
                                        
                                        
                                        // [for 문을 돌면서 = [등호] 와 [;] 세미 콜론 개수 카운트 실시 >> 쿠키 스트링 형식 문자열 검사]
                                        var check_1 = 0 // 특수문자 [=] 개수
                                        var check_2 = 0 // 특수문자 [;] 개수
                                        for c in self.cookieString { // [charAt 확인 실시]
                                            if "\(c)" == "=" {
                                                check_1 += 1
                                            }
                                            if "\(c)" == ";" {
                                                check_2 += 1
                                            }
                                        }
                                        
                                        
                                        // [정상적인 쿠키 스트링 값이 아닌 경우]
                                        if check_1 != check_2 {
                                            print("")
                                            print("====================================")
                                            print("[\(self.ACTIVITY_NAME) >> viewDidLoad() :: 쿠키 설정 데이터 포맷 양식 확인 에러]")
                                            print("-------------------------------")
                                            print("error :: 쿠키 설정에 필요한 데이터 확인 중 문제가 발생했습니다. [cookie data format check error]")
                                            print("-------------------------------")
                                            print("cookieString :: \(self.cookieString)")
                                            print("====================================")
                                            print("")
                                            
                                            // [쿠키 스트링 값 초기화]
                                            self.cookieString = ""
                                            
                                            // [액티비티 종료 팝업창 호출 실시]
                                            self.showAlertFinish(
                                                tittle: "[알림]",
                                                content: "쿠키 설정에 필요한 데이터 확인 중 문제가 발생했습니다. [cookie data format check error]",
                                                okBtb: "확인",
                                                noBtn: ""
                                            )
                                        }
                                        else {
                                            // [정상적으로 포맷을 수행한 쿠키 값을 배열에 삽입 실시]
                                            jsonObj["\(key)"] = self.cookieString
                                            self.headerArray![i] = jsonObj
                                        }
                                    }
                                }
                            }
                            ///*
                            print("")
                            print("====================================")
                            print("[\(self.ACTIVITY_NAME) >> viewDidLoad() :: 웹뷰 로드 [쿠키] 설정 데이터 [검증] 수행]")
                            print("-------------------------------")
                            print("url :: ", "\(self.loadUrl)")
                            print("-------------------------------")
                            print("domain :: ", "\(self.cookieDomain)")
                            print("-------------------------------")
                            print("header :: ", "\(self.headerArray?.description)")
                            print("-------------------------------")
                            print("cookieString :: ", "\(self.cookieString)")
                            print("====================================")
                            print("")
                            // */
                            
                            // [SEARCH FAST] : [웹뷰 설정]
                            self.init_WebView(_loadType: 1, _loadUrl: self.loadUrl)
                        }
                        else { // [일반 주소 로드 실시]
                            print("")
                            print("====================================")
                            print("[\(self.ACTIVITY_NAME) >> viewDidLoad() :: 웹뷰 로드 [일반] 설정 데이터 [검증] 수행]")
                            print("-------------------------------")
                            print("url :: ", "\(self.loadUrl)")
                            print("====================================")
                            print("")
                            
                            // [SEARCH FAST] : [웹뷰 설정]
                            self.init_WebView(_loadType: 0, _loadUrl: self.loadUrl)
                        }
                    }
                    else { // [웹뷰 로드 주소가 널 인 경우]
                        print("")
                        print("====================================")
                        print("[\(self.ACTIVITY_NAME) >> viewDidLoad() :: 웹뷰 로드 주소 확인 에러]")
                        print("-------------------------------")
                        print("error :: 웹뷰 로드에 필요한 주소 데이터를 다시 확인해주세요. [url data is null]")
                        print("-------------------------------")
                        print("saveData :: \(self.saveData)")
                        print("====================================")
                        print("")
                        
                        // [액티비티 종료 팝업창 호출 실시]
                        self.showAlertFinish(
                            tittle: "[알림]",
                            content: "웹뷰 로드에 필요한 주소 데이터를 다시 확인해주세요. [url data is null]",
                            okBtb: "확인",
                            noBtn: ""
                        )
                    }
                }
                catch {
                    print("")
                    print("====================================")
                    print("[\(self.ACTIVITY_NAME) >> viewDidLoad() :: 웹뷰 로드 정보 데이터 파싱 에러]")
                    print("-------------------------------")
                    print("catch :: ", error.localizedDescription)
                    print("====================================")
                    print("")
                }
            }
            else { // [json 데이터로 변경 못하는 경우]
                print("")
                print("====================================")
                print("[\(self.ACTIVITY_NAME) >> viewDidLoad() :: 웹뷰 로드 정보 확인 에러]")
                print("-------------------------------")
                print("error :: 데이터 json 형식 여부를 다시 확인해주세요. [data parsing error]")
                print("-------------------------------")
                print("saveData :: \(self.saveData)")
                print("====================================")
                print("")
                
                // [액티비티 종료 팝업창 호출 실시]
                self.showAlertFinish(
                    tittle: "[알림]",
                    content: "데이터 json 형식 여부를 다시 확인해주세요. [data parsing error]",
                    okBtb: "확인",
                    noBtn: ""
                )
            }
            
        }
        else { // [널 데이터 인 경우]
            print("")
            print("====================================")
            print("[\(self.ACTIVITY_NAME) >> viewDidLoad() :: 웹뷰 로드 정보 확인 실패]")
            print("-------------------------------")
            print("error :: 웹뷰 로드에 필요한 데이터를 다시 확인해주세요. [data is null]")
            print("-------------------------------")
            print("saveData :: \(self.saveData)")
            print("====================================")
            print("")
            
            // [프리퍼런스 데이터 초기화 실시]
            S_Preference().setString(_sKey: S_FinalData.PRE_WV_COOKIE_DATA, _sValue: "")
            
            // [액티비티 종료 팝업창 호출 실시]
            self.showAlertFinish(
                tittle: "[알림]",
                content: "웹뷰 로드에 필요한 데이터를 다시 확인해주세요. [data is null]",
                okBtb: "확인",
                noBtn: ""
            )
        }
        // -----------------------------------------
    }
    
    
    
    
    
    // MARK: - [뷰 컨트롤러 파일에서 설정 : 상태바 콘텐츠 색상 변경]
    override var preferredStatusBarStyle: UIStatusBarStyle {
        if #available(iOS 13, *) {
            //return .lightContent // [흰 색상 콘텐츠 표시]
            return .darkContent // [검은 색상 콘텐츠 표시]
            //return .default // [검은 색상 콘텐츠 표시]
        } else {
            return .default
        }
    }
    
    
    
    
    
    // MARK: - [뷰 로드 완료]
    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        print("")
        print("====================================")
        print("[\(self.ACTIVITY_NAME) >> viewWillAppear() :: 뷰 로드 완료]")
        print("====================================")
        print("")
    }
        
    
    
    
    
    // MARK: - [뷰 화면 표시]
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        print("")
        print("====================================")
        print("[\(self.ACTIVITY_NAME) >> viewDidAppear() :: 뷰 화면 표시]")
        print("====================================")
        print("")
        
        // -----------------------------------------
        // [SEARCH FAST] : [포그라운드 및 백그라운드 상태 확인]
        NotificationCenter.default.addObserver( // [포그라운드]
            self,
            selector: #selector(self.checkForeground),
            name: UIApplication.willEnterForegroundNotification,
            object: nil
        )
        NotificationCenter.default.addObserver( // [백그라운드]
            self,
            selector: #selector(self.checkBackground),
            name: UIApplication.didEnterBackgroundNotification,
            object: nil
        )
        // -----------------------------------------
    
        
        // -----------------------------------------
        // [포그라운드 처리 실시]
        self.checkForeground()
        // -----------------------------------------
    }
    
    
    
    
    
    // MARK: - [뷰 정지 상태]
    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        print("")
        print("====================================")
        print("[\(self.ACTIVITY_NAME) >> viewWillDisappear() :: 뷰 정지 상태]")
        print("====================================")
        print("")
    }
        
    
    
    
    
    // MARK: - [뷰 종료 상태]
    override func viewDidDisappear(_ animated: Bool) {
        print("")
        print("====================================")
        print("[\(self.ACTIVITY_NAME) >> viewDidDisappear() :: 뷰 종료 상태]")
        print("====================================")
        print("")
        
        // -----------------------------------------
        // [SEARCH FAST] : [포그라운드 및 백그라운드 상태 확인]
        NotificationCenter.default.removeObserver( // [포그라운드]
            self,
            name: UIApplication.willEnterForegroundNotification,
            object: nil
        )
        NotificationCenter.default.removeObserver( // [백그라운드]
            self,
            name: UIApplication.didEnterBackgroundNotification,
            object: nil
        )
        // -----------------------------------------
    }

    
    
    
    
    // MARK: - [포그라운드 상태 처리 메소드 작성]
    @objc func checkForeground() {
        print("")
        print("====================================")
        print("[\(self.ACTIVITY_NAME) >> checkForeground() :: 뷰 컨트롤러 포그라운드]")
        print("====================================")
        print("")
    }
    
    
    
    
    
    // MARK: - [백그라운드 상태 처리 메소드 작성]
    @objc func checkBackground() {
        print("")
        print("====================================")
        print("[\(self.ACTIVITY_NAME) >> checkBackground() :: 뷰 컨트롤러 백그라운드]")
        print("====================================")
        print("")
    }
    
    
    
    
    
    // MARK: - [SEARCH FAST] : [웹뷰 설정]
    func init_WebView(_loadType:Int, _loadUrl:String){
        let statusBarHeight = UIApplication.shared.statusBarFrame.height
        print("")
        print("====================================")
        print("[\(self.ACTIVITY_NAME) >> init_WebView() :: 웹뷰 초기 설정 값 정의 및 웹뷰 로드 수행 실시]")
        print("-------------------------------")
        print("_loadType [로드 타입] :: \(_loadType)")
        print("-------------------------------")
        print("_loadUrl [로드 주소] :: \(_loadUrl)")
        print("-------------------------------")
        print("statusBarHeight [상태 바 높이] :: ", statusBarHeight)
        print("====================================")
        print("")
        
        /*
        // -----------------------------------------
        // [원하는 캐시 데이터만 골라서 삭제]
        let websiteDataTypes = NSSet(array:
                                            [WKWebsiteDataTypeDiskCache, // 디스크 캐시
                                             WKWebsiteDataTypeMemoryCache, // 메모리 캐시
                                             WKWebsiteDataTypeCookies, // 웹 쿠키,
                                             
                                             WKWebsiteDataTypeOfflineWebApplicationCache, // 앱 캐시
                                             WKWebsiteDataTypeWebSQLDatabases, // 웹 SQL 데이터 베이스
                                             WKWebsiteDataTypeIndexedDBDatabases // 데이터 베이스 정보
                                             
                                             //WKWebsiteDataTypeLocalStorage // 로컬 스토리지
                                             //WKWebsiteDataTypeSessionStorage // 세션 스토리지
                                            ])
        let date = NSDate(timeIntervalSince1970: 0)
        WKWebsiteDataStore.default().removeData(ofTypes: websiteDataTypes as! Set, modifiedSince: date as Date, completionHandler:{
            print("")
            print("====================================")
            print("[\(self.ACTIVITY_NAME) >> init_WebView() :: 사전 캐시 및 세션 데이터 삭제 수행]")
            print("-------------------------------")
            print("type :: removeData")
            print("====================================")
            print("")
        })
        // -----------------------------------------
        
        
        // -----------------------------------------
        // [URL 요청 후 잔여 캐시 데이터 삭제]
        URLCache.shared.removeAllCachedResponses()
        // -----------------------------------------
        
        
        // -----------------------------------------
        // [설치된 날짜 가져오는 코드]
        let urlToDocumentsFolder: URL? = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).last
        let installDate = try? FileManager.default.attributesOfItem(atPath: (urlToDocumentsFolder?.path)!)[.creationDate] as! Date
        
        // [설치된 날짜부터 지금까지의 cookie all clear]
        URLSession.shared.configuration.httpCookieStorage?.removeCookies(since: installDate!)
        
        // [로그 출력 실시]
        print("")
        print("====================================")
        print("[\(self.ACTIVITY_NAME) >> init_WebView() :: 사전 캐시 및 세션 데이터 삭제 수행]")
        print("-------------------------------")
        print("type :: URLSession removeCookies")
        print("====================================")
        print("")
        // -----------------------------------------
        // */
        
        
        // -----------------------------------------
        // [웹 보기에 대한 쿠키, 디스크 및 메모리 캐시, 기타 유형의 데이터를 관리하는 개체]
        //self.javascriptConfig.websiteDataStore = WKWebsiteDataStore.default() // [디폴트]
        self.javascriptConfig.websiteDataStore = WKWebsiteDataStore.nonPersistent() // ㅡㅁㄱ[쿠키 셋팅]
        // -----------------------------------------
        
        
        // -----------------------------------------
        // [SEARCH FAST] : [자바스크립트 통신]
        // [웹뷰 로드 (전) 쿠키 값 설정 실시]
        self.addJavaScriptBridge()
        // -----------------------------------------
        
        
        // -----------------------------------------
        // [웹뷰 전체 화면 설정 실시]
        self.main_webview = WKWebView.init(
            frame: CGRect.init(
                x: 0,
                y: statusBarHeight,
                width: self.view.frame.width, // [웹뷰에 맞게 화면 맞춤]
                height: self.view.frame.height - statusBarHeight // [웹뷰에 맞게 화면 맞춤 길이 맞춤]
            ),
            configuration: self.javascriptConfig // MARK: [자바스크립트 저장 공간 지정]
        )
        // -----------------------------------------
        
        
        // -----------------------------------------
        // [웹뷰 옵션값 지정]
        self.main_webview?.configuration.preferences.javaScriptCanOpenWindowsAutomatically = true  // [자바스크립트 활성화]
        self.main_webview?.navigationDelegate = self // [웹뷰 변경 상태 감지 위함]
        //self.main_webview?.allowsBackForwardNavigationGestures = true // [웹뷰 뒤로가기, 앞으로 가기 제스처 사용]
        self.main_webview?.addObserver(self, forKeyPath: #keyPath(WKWebView.estimatedProgress), options: .new, context: nil) // [웹뷰 로드 상태 퍼센트 확인]
        self.main_webview?.uiDelegate = self // [alert 팝업창 이벤트 받기 위함]
        
        self.main_webview?.translatesAutoresizingMaskIntoConstraints = false
        //self.main_webview?.scrollView.bounces = true
        self.main_webview?.scrollView.showsHorizontalScrollIndicator = false
        self.main_webview?.scrollView.scrollsToTop = true
        // -----------------------------------------
        
        
        // -----------------------------------------
        // [웹뷰 스크롤 바운스 방지]
        self.main_webview?.scrollView.alwaysBounceVertical = false
        self.main_webview?.scrollView.bounces = false
        // -----------------------------------------
        
        
        // -----------------------------------------
        // [액티비티 화면에 웹뷰 화면 추가 실시]
        self.view.addSubview(self.main_webview!)
        // -----------------------------------------
        
        
        // -----------------------------------------
        // [웹뷰 로드 헤더 값 설정 및 웹뷰 로드 수행 실시]
        let url = URL (string: _loadUrl) // [웹뷰 로드 주소]
        var request = URLRequest(url: url! as URL) // [request 지정]
        
        if _loadType == 1 && self.headerExist == true { // [웹뷰 쿠키 설정 값으로 로드 수행]
            print("")
            print("====================================")
            print("[\(self.ACTIVITY_NAME) >> init_WebView() :: [쿠키] 웹뷰 설정 및 로드 수행 실시]")
            print("====================================")
            print("")
            
            // MARK: [for 문을 돌면서 헤더 배열 값을 request.addValue 추가 실시]
            // [SEARCH FAST] : [동적 헤더 추가 실시]
            for i in stride(from: 0, through: self.headerArray!.count-1, by: 1) {
                let jsonObj : Dictionary<String, Any> = self.headerArray![i] // [배열 각 번지 지정]
                
                // MARK: [개별 딕셔너리 데이터 확인 실시]
                for (key, value) in jsonObj {
                    let keyData:String = "\(key)" as? String ?? "" // [key]
                    let valueData:String = jsonObj["\(key)"] as? String ?? "" // [value]
                    print("")
                    print("====================================")
                    print("[\(self.ACTIVITY_NAME) >> init_WebView() :: 웹뷰 로드 동적 [헤더] 값 추가 실시]")
                    print("-------------------------------")
                    print("key :: ", "\(keyData)")
                    print("-------------------------------")
                    print("value :: ", "\(valueData)")
                    print("====================================")
                    
                    
                    /*
                    // -----------------------------------------
                    // MARK: [쿠키 추가 로직 설명]
                    // -----------------------------------------
                    1. URLRequest addValue 를 통해서 웹뷰 로드 시 헤더 쪽에 쿠키 추가
                    // -----------------------------------------
                    2. 자바스크립트 통신 브릿지 추가 부분에서 addUserScript 를 통해서 쿠키 값 설정
                    // -----------------------------------------
                    3. 쿠키 스트링 형태 : JSESSIONID=61f0aa76ad66d12308000000;domain=m.test.ac.kr;path=/;
                    // -----------------------------------------
                    */
                    
                    
                    // [헤더 필드에 추가 실시]
                    request.addValue(valueData, forHTTPHeaderField: keyData)
                }
            }
            
            // MARK: [쿠키 설정 헤더 값 지정 웹뷰 주소 로드 실시]
            self.main_webview!.load(request)
        }
        else { // [일반 웹뷰 설정으로 로드 수행]
            print("")
            print("====================================")
            print("[\(self.ACTIVITY_NAME) >> init_WebView() :: [일반] 웹뷰 설정 및 로드 수행 실시]")
            print("====================================")
            print("")
            
            // MARK: [일반 주소 호출 실시]
            self.main_webview!.load(request)
        }
        // -----------------------------------------
        
        
        // -----------------------------------------
        // [네이티브 버튼 활성 및 비활성 처리]
        ///*
        let button = UIButton(
            frame: CGRect(
                x: self.view.frame.size.width - 55,
                y: statusBarHeight + 0,
                width: 40,
                height: 40
            )
        ) // 마진 및 사이즈 지정
        button.backgroundColor = .blue // [배경 색상 지정]
        button.setTitle("", for: .normal) // [타이틀 지정]
        //button.setImage(UIImage(named: "closecirclebtn.png")! as UIImage, for: .normal) // [버튼 이미지]
        button.addTarget(self, action: #selector(self.buttonAction), for: .touchUpInside) // [클릭 이벤트 지정]

        self.view.addSubview(button) // [뷰에 추가 실시]
        // */
        // -----------------------------------------
    }
    
    
    
    
    
    // MARK: - [자바스크립트 통신을 위한 초기화 부분]
    // [SEARCH FAST] : [자바스크립트 통신]
    func addJavaScriptBridge(){
        print("")
        print("====================================")
        print("[\(self.ACTIVITY_NAME) >> addJavaScriptBridge() :: 자바스크립트 통신 브릿지 추가]")
        print("-------------------------------")
        print("Bridge :: setMarket")
        print("====================================")
        print("")
        
        // -----------------------------------------
        // [브릿지 경로 추가 : 데이터 받을 경로 : 자바스크립트 >> IOS]
        self.javascriptController.add(self, name: "setMarket") // [마켓 이동]
        // -----------------------------------------
        
        
        // -----------------------------------------
        // MARK: [쿠키 값 널 여부 체크 실시 >> 웹뷰 로드 시 사전 쿠키 값 설정 실시]
        // [SEARCH FAST] : [쿠키 설정 실시]
        if C_Util().stringNotNull(str: self.cookieString) == true {
            
            // [쿠키 스트링 값 포맷 실시]
            var cookies = "document.cookie='"
            cookies += self.cookieString
            cookies += "'"
            
            let cookieScript = WKUserScript(source: cookies, injectionTime: .atDocumentStart, forMainFrameOnly: false)
            self.javascriptController.addUserScript(cookieScript)
            print("")
            print("====================================")
            print("[\(self.ACTIVITY_NAME) >> addJavaScriptBridge() :: [사전] 쿠키 설정 값 추가 실시]")
            print("-------------------------------")
            print("cookieString [원본] :: \(self.cookieString)")
            print("-------------------------------")
            print("cookies [포맷] :: \(cookies)")
            print("====================================")
            print("")
        }
        // -----------------------------------------
        
        
        // -----------------------------------------
        self.javascriptConfig.userContentController = self.javascriptController
        //self.main_webview = WKWebView(frame: self.view.bounds, configuration: javascriptConfig)
        // -----------------------------------------
    }
    
    
    
    
    
    // MARK: - [자바스크립트 >> IOS 통신 부분]
    // [SEARCH FAST] : [자바스크립트 통신]
    @available(iOS 8.0, *)
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        // -----------------------------------------
        // [SEARCH FAST] : [자바스크립트 통신 수행]
        // -----------------------------------------
        
        
        // -----------------------------------------
        // [웹 코드] window.webkit.messageHandlers.setMarket.postMessage(jsonString);
        // -----------------------------------------
        if message.name == "setMarket" {
            guard let checkType = message.body as? String // [전달 받은 메시지 확인]
            else {
                print("")
                print("====================================")
                print("[\(self.ACTIVITY_NAME) >> userContentController :: setMarket() :: JS >> IOS]")
                print("-------------------------------")
                print("error :: [type] :: ", type(of: message.body))
                print("====================================")
                print("")
                
                // [에러 팝업창 알림 표시]
                self.showAlert(
                    type: 0,
                    tittle: S_FinalData.AL_TITLE,
                    content: S_FinalData.ERROR_DATA_NULL + " [setMarket]",
                    okBtb: S_FinalData.AL_OK,
                    noBtn: ""
                )
                
                // [리턴 종료]
                return
            }
            
            
            // -----------------------------------------
            
            // [자바스크립트에서 전달 받은 데이터 저장]
            let receiveData = message.body as! String
            
            // -----------------------------------------
            
            // [자바스크립트 데이터 방어 로직 추가 실시 : 데이터 널 체크]
            if C_Util().stringNotNull(str: receiveData) == true { // [널 값이 아닌 경우]
            }
            else { // [널 값인 경우]
                print("")
                print("====================================")
                print("[\(self.ACTIVITY_NAME) >> userContentController :: setMarket() :: JS >> IOS]")
                print("-------------------------------")
                print("전달받은 데이터 :: ", receiveData)
                print("-------------------------------")
                print("error :: [msg] :: ", S_FinalData.ERROR_DATA_NULL)
                print("====================================")
                print("")
                
                // [에러 팝업창 알림 표시]
                self.showAlert(
                    type: 0,
                    tittle: S_FinalData.AL_TITLE,
                    content: S_FinalData.ERROR_DATA_NULL + " [setMarket]",
                    okBtb: S_FinalData.AL_OK,
                    noBtn: ""
                )
                
                // [리턴 종료]
                return
            }
            
            // -----------------------------------------
            
            // [자바스크립트 데이터 방어 로직 추가 실시 : key 값 체크]
            if C_Util().stringMultiContains(str: receiveData.lowercased(), array: ["ios", "id"]) == true {
            }
            else {
                print("")
                print("====================================")
                print("[\(self.ACTIVITY_NAME) >> userContentController :: setMarket() :: JS >> IOS]")
                print("-------------------------------")
                print("전달받은 데이터 :: ", receiveData)
                print("-------------------------------")
                print("error :: [msg] :: ", S_FinalData.ERROR_PARSING_KEY)
                print("====================================")
                print("")
                
                // [에러 팝업창 알림 표시]
                self.showAlert(
                    type: 0,
                    tittle: S_FinalData.AL_TITLE,
                    content: S_FinalData.ERROR_PARSING_KEY + " [setMarket]",
                    okBtb: S_FinalData.AL_OK,
                    noBtn: ""
                )
                
                // [리턴 종료]
                return
            }
            
            // -----------------------------------------
            
            // [자바스크립트 데이터 방어 로직 추가 실시 : json 데이터 형식 체크 실시]
            if C_Util().stringJsonObjectEnable(str: receiveData) == true {
                print("")
                print("====================================")
                print("[\(self.ACTIVITY_NAME) >> userContentController :: setMarket() :: JS >> IOS]")
                print("-------------------------------")
                print("전달받은 데이터 :: ", receiveData)
                print("-------------------------------")
                print("설 명 :: ", "마켓 이동 정보 전달 받음")
                print("-------------------------------")
                print("로 직 :: ", "마켓 수행 실시")
                print("====================================")
                print("")
                
                // [json 형식 문자열을 딕셔너리로 변환 실시]
                let jsonDic = C_Util().jsonObject_To_Dic(jsonString: receiveData)
                
                // [세부 json 데이터 받기 실시]
                if jsonDic.keys.contains("ios") == true { // [키값이 존재할 경우]
                    do {
                        // -----------------------------------------
                        // [딕셔너리에 세부 데이터 저장 실시]
                        var dicData : Dictionary<String, Any> = jsonDic["ios"] as! Dictionary<String, Any>
                        // -----------------------------------------
                        // [패키지명 및 앱 아이디 확인 실시]
                        let appId = dicData["id"] as! String // [앱 스토어 이동 아이디]
                        // -----------------------------------------
                        // [자바스크립트 전달 받은 데이터 확인 방어 로직 추가 : 에러 체크]
                        if C_Util().stringNotNull(str: appId) == true
                            && appId.hasPrefix("id") == true { // [널 데이터 아님]
                            
                            // [마켓 이동 수행 실시]
                            self.goMarketRun(_id: appId)
                        }
                        // -----------------------------------------
                        else { // [널 데이터]
                            print("")
                            print("====================================")
                            print("[\(self.ACTIVITY_NAME) >> userContentController :: setMarket() :: JS >> IOS]")
                            print("-------------------------------")
                            print("전달받은 데이터 :: ", receiveData)
                            print("-------------------------------")
                            print("error :: [msg] :: ", S_FinalData.ERROR_PARSING_DATA)
                            print("====================================")
                            print("")
                            
                            // [에러 팝업창 알림 표시]
                            self.showAlert(
                                type: 0,
                                tittle: S_FinalData.AL_TITLE,
                                content: S_FinalData.ERROR_PARSING_DATA + " [setMarket]",
                                okBtb: S_FinalData.AL_OK,
                                noBtn: ""
                            )
                            
                            // [리턴 종료]
                            return
                        }
                        // -----------------------------------------
                    }
                    catch {
                        print("")
                        print("====================================")
                        print("[\(self.ACTIVITY_NAME) >> userContentController :: setMarket() :: JS >> IOS]")
                        print("-------------------------------")
                        print("전달받은 데이터 :: ", receiveData)
                        print("-------------------------------")
                        print("catch :: ", error.localizedDescription)
                        print("====================================")
                        print("")
                    }
                }
            }
            else {
                print("")
                print("====================================")
                print("[\(self.ACTIVITY_NAME) >> userContentController :: setMarket() :: JS >> IOS]")
                print("-------------------------------")
                print("전달받은 데이터 :: ", receiveData)
                print("-------------------------------")
                print("error :: [msg] :: ", S_FinalData.ERROR_JSON_PARSING)
                print("====================================")
                print("")
                
                // [에러 팝업창 알림 표시]
                self.showAlert(
                    type: 0,
                    tittle: S_FinalData.AL_TITLE,
                    content: S_FinalData.ERROR_JSON_PARSING + " [setMarket]",
                    okBtb: S_FinalData.AL_OK,
                    noBtn: ""
                )
                
                // [리턴 종료]
                return
            }
        }
        // -----------------------------------------
    }
    
    
    
    
    
    // MARK: - [IOS >> 자바스크립트 통신 부분]
    // -----------------------------------------
    // [SEARCH FAST] : [자바스크립트 통신]
    // -----------------------------------------
    func iosToJs_shakeResult(){
        print("")
        print("====================================")
        print("[\(self.ACTIVITY_NAME) >> iosToJs_shakeResult() :: IOS >> JS]")
        print("-------------------------------")
        print("JS 함수 :: shakeResult")
        print("-------------------------------")
        print("설 명 :: ", "디바이스 흔들기 이벤트 발생 전달")
        print("====================================")
        print("")
        
        // MARK: [함수 호출]
        self.main_webview!.evaluateJavaScript("shakeResult('\("")')") { (success, error) in
            if error != nil {
                print("")
                print("====================================")
                print("[\(self.ACTIVITY_NAME) >> iosToJs_shakeResult() :: IOS >> JS]")
                print("-------------------------------")
                print("JS 함수 :: shakeResult")
                print("-------------------------------")
                print("설 명 :: ", "디바이스 흔들기 이벤트 발생 전달")
                print("-------------------------------")
                print("전송 결과 [error] :: ", error)
                print("====================================")
                print("")
            }
            else {
                print("")
                print("====================================")
                print("[\(self.ACTIVITY_NAME) >> iosToJs_shakeResult() :: IOS >> JS]")
                print("-------------------------------")
                print("JS 함수 :: shakeResult")
                print("-------------------------------")
                print("설 명 :: ", "디바이스 흔들기 이벤트 발생 전달")
                print("-------------------------------")
                print("전송 결과 [success] :: ", "OK")
                print("====================================")
                print("")
            }
        }
    }
    
    
    
    
    
    // MARK: - [웹뷰 로드 수행 시작 부분]
    func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
        let _startUrl = String(describing: webView.url?.description ?? "")
        print("")
        print("====================================")
        print("[\(self.ACTIVITY_NAME) >> didStartProvisionalNavigation() :: 웹뷰 로드 수행 시작]")
        print("-------------------------------")
        print("주 소 :: \(_startUrl ?? "")")
        print("====================================")
        print("")
    }
    
    
    
    
    
    // MARK: - [실시간 웹뷰 로드 상태 퍼센트 확인 부분 : 0.1 ~ 1.0]
    override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        // [0 ~ 1 사이의 실수형으로 결과값이 출력된다 [0 : 로딩 시작, 1 : 로딩 완료]]
        print("")
        print("====================================")
        print("[\(self.ACTIVITY_NAME) >> observeValue() :: 실시간 웹뷰 로드 상태 확인]")
        print("-------------------------------")
        print("원 본 :: \(Float((self.main_webview?.estimatedProgress)!))")
        print("-------------------------------")
        print("퍼센트 :: \(Float((self.main_webview?.estimatedProgress)!)*100)")
        print("====================================")
        print("")
        
        if Float((self.main_webview?.estimatedProgress)!) >= 1 {
            // -----------------------------------------
            DispatchQueue.main.async {
                /*
                print("")
                print("====================================")
                print("[\(self.ACTIVITY_NAME) >> observeValue() :: 웹뷰 로드 완료 상태]")
                print("-------------------------------")
                print("퍼센트 :: \(Float((self.main_webview?.estimatedProgress)!)*100)")
                print("====================================")
                print("")
                // */
                
                
                // -----------------------------------------
                // MARK: [SEARCH FAST] : [실시간 쿠키 값 확인 실시]
                // -----------------------------------------
                /*
                 1. 쿠키 설정 웹뷰 로드 경우 로직
                   - [1]. 웹뷰 최초 로드 시 헤더 쪽에 쿠키 추가 실시
                   - [2]. addJavaScriptBridge() 에서 WKUserScript / addUserScript 사용해 웹뷰 로드 전 사전 쿠키 값 설정
                 */
                // -----------------------------------------
                if self.main_webview != nil {
                    
                    // [웹뷰 정상 표시 실시]
                    self.main_webview!.isHidden = false
                    
                    // [웹뷰에 저장된 쿠키 값 확인 실시]
                    if #available(iOS 11.0, *) {
                        self.main_webview!.configuration.websiteDataStore.httpCookieStore.getAllCookies { cookies in
                            print("")
                            print("====================================")
                            print("[\(self.ACTIVITY_NAME) >> observeValue() :: 웹뷰 로드 완료 상태]")
                            print("-------------------------------")
                            print("퍼센트 :: \(Float((self.main_webview?.estimatedProgress)!)*100)")
                            print("-------------------------------")
                            print("웹뷰 저장된 쿠키 :: ", cookies.description)
                            print("====================================")
                            print("")
                        }
                    }
                    else {
                        print("")
                        print("====================================")
                        print("[\(self.ACTIVITY_NAME) >> observeValue() :: 웹뷰 로드 완료 상태]")
                        print("-------------------------------")
                        print("퍼센트 :: \(Float((self.main_webview?.estimatedProgress)!)*100)")
                        print("-------------------------------")
                        print("웹뷰 저장된 쿠키 :: iOS 11.0 미만 디바이스")
                        print("====================================")
                        print("")
                    }
                }
                // -----------------------------------------
                
                
                // -----------------------------------------
                // MARK: [쿠키 설정 : evaluateJavaScript 자바스크립트 데이터 전송 방법]
                // -----------------------------------------
                /*
                 1. 웹뷰 최초 설정 및 로드 시 헤더 쪽에 쿠키 값 추가 및 로드 실시
                 2. 실시간 웹뷰 주소 완료 부분 확인 observeValue 에서 웹뷰 로드 100프로가 되면 evaluateJavaScript 자바스크립트로 쿠키 심는다
                 3. 실시간 웹뷰 주소 완료 부분 확인 observeValue 에서 쿠키가 정상적으로 심어지면 웹뷰 reload 를 통해서 다시 로드를 수행한다
                 4. 최종 웹뷰 reload 까지 완료 된 후에는 브라우저는 allow 이동 처리 실시
                 */
                // -----------------------------------------
                // MARK: [자바스크립트 사용해서 쿠키 셋팅 실시]
                /*
                if  C_Util().stringNotNull(str: self.cookieString) == true { // [쿠키 스트링 값이 널이 아닌 경우]
                    
                    // MARK: [웹뷰 리로드 처리 플래그]
                    let load_flag = S_Preference().getString(_sKey: S_FinalData.PRE_WV_COOKIE_RELOAD_FLAG)
                    if C_Util().stringNotNull(str: load_flag) == true { // [널 아닌 경우]
                        
                        // [쿠키 스트링 값 초기화]
                        self.cookieString = ""
                        
                        // [웹뷰 정상 표시 실시]
                        self.main_webview!.isHidden = false
                    }
                    else { // MARK: [널인 경우 >> 웹뷰 쿠키 심기 >> 리로드 처리]
                 
                        // [자바스크립트 사용해 쿠키를 셋팅하기 위해 형식 정의 실시]
                        var cookies = "document.cookie='"
                        cookies += self.cookieString
                        cookies += "'"
                 
                        print("")
                        print("====================================")
                        print("[\(self.ACTIVITY_NAME) >> observeValue() :: 웹뷰 로드 완료 상태]")
                        print("-------------------------------")
                        print("쿠키 값 자바스크립트 셋팅 실시 :: \(cookies)")
                        print("====================================")
                        print("")
                        
                        // MARK: [자바스크립트에 쿠키 셋팅 실시 >> evaluateJavaScript]
                        self.main_webview!.evaluateJavaScript("\(cookies)") { (success, error) in
                            if error != nil {
                                print("")
                                print("====================================")
                                print("[\(self.ACTIVITY_NAME) >> observeValue() :: 쿠키 값 설정 수행 실시]")
                                print("-------------------------------")
                                print("설 명 :: ", "[IOS >> 자바스크립트] 쿠키 값 자바스크립트 셋팅")
                                print("-------------------------------")
                                print("cookies :: ", cookies)
                                print("-------------------------------")
                                print("전송 결과 [error] :: ", error)
                                print("====================================")
                                print("")
                            }
                            else {
                                print("")
                                print("====================================")
                                print("[\(self.ACTIVITY_NAME) >> observeValue() :: 쿠키 값 설정 수행 실시]")
                                print("-------------------------------")
                                print("설 명 :: ", "[IOS >> 자바스크립트] 쿠키 값 자바스크립트 셋팅")
                                print("-------------------------------")
                                print("cookies :: ", cookies)
                                print("-------------------------------")
                                print("전송 결과 [success] :: ", "OK")
                                print("====================================")
                                print("")
                                
                                // MARK: [웹뷰 리로드 플래그 값 확인 실시]
                                if C_Util().stringNotNull(str: load_flag) == true { // [널 아닌 경우]
                                    
                                    // [웹뷰 정상 표시 실시]
                                    self.main_webview!.isHidden = false
                                }
                                else { // [널 인 경우]
                                    
                                    // [웹뷰 리로드 플래그 값 지정 실시]
                                    S_Preference().setString(_sKey: S_FinalData.PRE_WV_COOKIE_RELOAD_FLAG, _sValue: "TRUE")
                                    
                                    // [쿠키 생성 후 쿠키 정보가 반영된 웹페이지를 불러 올 수 있도록 새로 고침을 해줍니다]
                                    self.main_webview!.reload() // MARK: [중요 부분]
                                }
                            }
                        }
                    }
                }
                // -----------------------------------------
                else { // MARK: [일반 웹 접속 인 경우]
                    
                    // [웹뷰 정상 표시 실시]
                    self.main_webview!.isHidden = false
                }
                // -----------------------------------------
                // */
            }
        }
    }
    
    
    
    
    
    // MARK: - [웹뷰 로드 수행 완료 부분]
    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        let _endUrl = String(describing: webView.url?.description ?? "")
        print("")
        print("====================================")
        print("[\(self.ACTIVITY_NAME) >> didFinish() :: 웹뷰 로드 수행 완료]")
        print("-------------------------------")
        print("주 소 :: \(_endUrl)")
        print("====================================")
        print("")
    }
    
    
    
    
    
    // MARK: - [웹뷰 로드 수행 에러 확인]
    func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
        // -----------------------------------------
        let _nsError = (error as NSError).code
        let _errorUrl = String(describing: webView.url?.description ?? "")
        // -----------------------------------------
        
        
        // -----------------------------------------
        print("")
        print("====================================")
        print("[\(self.ACTIVITY_NAME) >> didFail() :: 웹뷰 로드 수행 에러]")
        print("-------------------------------")
        print("에러 주소 :: \(_errorUrl)")
        print("-------------------------------")
        print("에러 코드 :: \(_nsError)")
        print("-------------------------------")
        print("에러 메시지 :: \(S_WebViewErrorCode().checkError(_errorCode: _nsError))")
        print("====================================")
        print("")
        // -----------------------------------------
    }
    
    
    
    
    
    // MARK: - [웹뷰 window open 새창 열기 및 닫기 이벤트 감지 실시]
    func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
        // [url 주소 확인 실시]
        let _urlString = navigationAction.request.url!.absoluteString
        print("")
        print("====================================")
        print("[\(self.ACTIVITY_NAME) >> createWebViewWith() :: 웹뷰 [window open] 새창 이벤트 확인]")
        print("====================================")
        print("")
        return nil
    }
    func webViewDidClose(_ webView: WKWebView) {
        print("")
        print("====================================")
        print("[\(self.ACTIVITY_NAME) >> webViewDidClose() :: 웹뷰 [window open] 닫기 이벤트 확인]")
        print("====================================")
        print("")
    }

    
    
    
    
    // MARK: - [웹뷰 실시간 url 변경 감지 실시]
    private let websiteDataStore = WKWebsiteDataStore.default()
    func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
        
        // -----------------------------------------
        // [실시간 웹뷰 주소 변경 감지]
        let _shouldUrl = String(describing: webView.url?.description ?? "")
        var action: WKNavigationActionPolicy?
        guard let url = navigationAction.request.url else { return }
        print("")
        print("====================================")
        print("[\(self.ACTIVITY_NAME) >> decidePolicyFor() :: 웹뷰 실시간 url 변경 감지]")
        print("-------------------------------")
        print("주 소 :: \(url)")
        print("====================================")
        print("")
        // -----------------------------------------
        
        
        // -----------------------------------------
        // [전화 이동 실시]
        if "\(url)".hasPrefix("tel:") {
            print("")
            print("====================================")
            print("[\(self.ACTIVITY_NAME) >> decidePolicyFor() :: 웹뷰 실시간 url 변경 감지]")
            print("-------------------------------")
            print("주 소 :: \(url)")
            print("-------------------------------")
            print("로 직 :: 전화 걸기 이동 실시")
            print("====================================")
            print("")
            
            // [전화 이동 실시]
            self.goTelIntent(_url: "\(url)")
            
            // [브라우저 내 로직 캔슬]
            decisionHandler(.cancel)
            return
        }
        // -----------------------------------------
        
        
        // -----------------------------------------
        // [이메일 이동 실시]
        if "\(url)".hasPrefix("mailto:") {
            print("")
            print("====================================")
            print("[\(self.ACTIVITY_NAME) >> decidePolicyFor() :: 웹뷰 실시간 url 변경 감지]")
            print("-------------------------------")
            print("주 소 :: \(url)")
            print("-------------------------------")
            print("로 직 :: 메일 보내기 이동 실시")
            print("====================================")
            print("")
            
            // [메일 이동 실시]
            self.goMailIntent(_url: "\(url)")
            
            // [브라우저 내 로직 캔슬]
            decisionHandler(.cancel)
            return
        }
        // -----------------------------------------
        
        
        // -----------------------------------------
        // [문자 이동 실시]
        if "\(url)".hasPrefix("sms:") {
            print("")
            print("====================================")
            print("[\(self.ACTIVITY_NAME) >> decidePolicyFor() :: 웹뷰 실시간 url 변경 감지]")
            print("-------------------------------")
            print("주 소 :: \(url)")
            print("-------------------------------")
            print("로 직 :: 문자 보내기 실시")
            print("====================================")
            print("")
            
            // [문자 이동 실시]
            self.goSmsIntent(_url: "\(url)")
            
            // [브라우저 내 로직 캔슬]
            decisionHandler(.cancel)
            return
        }
        // -----------------------------------------
        
        
        // -----------------------------------------
        // [하이퍼링크 이동 실시]
        if "\(url)".hasPrefix("l:") {
            print("")
            print("====================================")
            print("[\(self.ACTIVITY_NAME) >> decidePolicyFor() :: 웹뷰 실시간 url 변경 감지]")
            print("-------------------------------")
            print("주 소 :: \(url)")
            print("-------------------------------")
            print("로 직 :: 하이퍼링크 이동 실시")
            print("====================================")
            print("")
            
            // [링크 이동 실시]
            self.goLinkIntent(_url: "\(url)")
            
            // [브라우저 내 로직 캔슬]
            decisionHandler(.cancel)
            return
        }
        // -----------------------------------------
        
        
        // -----------------------------------------
        if self.main_webview != nil {
            if #available(iOS 11.0, *) {
                self.main_webview!.configuration.websiteDataStore.httpCookieStore.getAllCookies { cookies in
                    print("")
                    print("====================================")
                    print("[\(self.ACTIVITY_NAME) >> decidePolicyFor() :: 웹뷰 실시간 url 변경 감지]")
                    print("-------------------------------")
                    print("설 명 :: ", "저장된 쿠키값 확인 실시")
                    print("-------------------------------")
                    print("cookie [쿠키] :: ", cookies)
                    print("====================================")
                    print("")
                }
            } else {
                print("")
                print("====================================")
                print("[\(self.ACTIVITY_NAME) >> decidePolicyFor() :: 웹뷰 실시간 url 변경 감지]")
                print("-------------------------------")
                print("설 명 :: ", "저장된 쿠키값 확인 실시")
                print("-------------------------------")
                print("cookie [쿠키] :: iOS 버전 11 미만 디바이스")
                print("====================================")
                print("")
            }
        }
        // -----------------------------------------
        // [브라우저 로직 허용 실시]
        decisionHandler(.allow)
        return
        // -----------------------------------------
    }
    
    
    
    
    
    // MARK: - [웹뷰 실시간 url status 상태 코드 감지 실시]
    /*
    func webView(_ webView: WKWebView, decidePolicyFor navigationResponse: WKNavigationResponse,
                 decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void) {
        
        // -----------------------------------------
        // [웹뷰 에러 코드 감지]
        // -----------------------------------------
        // [핸들러 처리 실시]
        defer {
            decisionHandler(.allow)
        }
        var _url = ""
        // -----------------------------------------
        if let url = navigationResponse.response.url {
            _url = url.description
        }
        else {
            _url = ""
        }
        // -----------------------------------------
        // [응답 상태 값 확인 실시]
        if let response = navigationResponse.response as? HTTPURLResponse {
            print("")
            print("====================================")
            print("[\(self.ACTIVITY_NAME) >> WKNavigationResponse() :: 웹뷰 실시간 url status 상태 코드 감지]")
            print("-------------------------------")
            print("주 소 :: \(_url)")
            print("-------------------------------")
            print("상태 코드 :: \(response.statusCode)")
            print("====================================")
            print("")
            
            
            // MARK: [에러 응답 코드 인 경우]
            // -----------------------------------------
            if response.statusCode >= 400 {
                
                // [무한 루프 방지 위해 기존에 저장된 에러 값 체크 실시]
                let errorCheck = String(describing:S_Preference().getString(_sKey: S_FinalData.PRE_WEBVIEW_RELOAD))
                print("")
                print("====================================")
                print("[\(self.ACTIVITY_NAME) >> WKNavigationResponse() :: 웹뷰 실시간 url status 상태 코드 감지]")
                print("-------------------------------")
                print("로 직 :: 에러 발생 로직 처리 실시")
                print("-------------------------------")
                print("[기존] 저장된 에러 발생 여부 :: \(errorCheck)")
                print("====================================")
                print("")
                
                // -----------------------------------------
                // MARK: [에러가 중복 실행된 경우 >> 에러 팝업창 표시]
                if errorCheck != nil
                    && errorCheck.count>0
                    && errorCheck.trim().equals(_string: "") == false
                    && errorCheck.trim().equals(_string: "1") == true { // MARK: [에러 팝업창]
                    print("")
                    print("====================================")
                    print("[\(self.ACTIVITY_NAME) >> WKNavigationResponse() :: 웹뷰 실시간 url status 상태 코드 감지]")
                    print("-------------------------------")
                    print("로 직 :: 재시도 최종 실패 >> [에러 팝업창] 웹뷰 로드 에러 표시 실시")
                    print("====================================")
                    print("")
                    
                    
                    // MARK: [실시간으로 웹뷰 에러가 발생한 경우 >> 에러 팝업창 표시]
                    // MARK: [사용자가 다시 접속 시 에러 발생한 경우 >> SceneDelegate >> sceneWillEnterForeground >> 앱 재기동]
                    
                    
                    // [에러 팝업창 표시]
                    self.showAlertWebview(
                        tittle:S_FinalData.AL_TITLE,
                        
                        content:"URL : " + "\(_url)" + "\n" + "\n" +
                                "Error : " + S_FinalData.ERROR_WEBVIEW_LOAD + "\n" + "\n" +
                                "HTTPURLResponse : " + "\(response.statusCode)" + "\n" + "\n" +
                                "TIME : " + String(describing: C_Util().getNowDateTime24()) + "",
                        
                        //_url:_url, // [에러 주소]
                        _url: S_FinalData.WV_LOAD_MAIN_ENZ_URL, // [메인 주소]
                        
                        okBtb:S_FinalData.AL_OK,
                        noBtn:""
                    )
                }
                // -----------------------------------------
                // MARK: [처음으로 에러가 발생한 경우 >> 프리퍼런스 데이터 널 인 경우 :: 재시도 기회 제공]
                else {
                    print("")
                    print("====================================")
                    print("[\(self.ACTIVITY_NAME) >> WKNavigationResponse() :: 웹뷰 실시간 url status 상태 코드 감지]")
                    print("-------------------------------")
                    print("로 직 :: 에러 최초 발생 >> [재시도] 웹 페이지 다시 로드 수행 실시")
                    print("====================================")
                    print("")
                    
                    
                    // -----------------------------------------
                    // MARK: [웹뷰 재호출 프리퍼런스 값 지정 실시]
                    S_Preference().setString(_sKey: S_FinalData.PRE_WEBVIEW_RELOAD, _sValue: "1")
                    // -----------------------------------------
                    
                    
                    // -----------------------------------------
                    // [SEARCH FAST] : [웹뷰 세션 및 캐시 삭제 수행]
                    self.clearWebViewChahe()
                    // -----------------------------------------
                    
                    
                    // -----------------------------------------
                    // [웹뷰 객체 지우기 실시]
                    if self.main_webview != nil {
                        // [웹뷰 제거 실시]
                        self.main_webview?.removeFromSuperview()
                        self.main_webview = nil
                        
                        // [자바스크립트 통신 사용 초기화]
                        self.javascriptController = WKUserContentController()
                    }
                    // -----------------------------------------
                    
                    
                    // -----------------------------------------
                    // [SEARCH FAST] : [인트로 화면 처리]
                    self.introBg.isHidden = false // [로딩 이미지 활성]
                    // -----------------------------------------
                    
                    
                    // -----------------------------------------
                    // [SEARCH FAST] : [웹뷰 로드 수행 실시]
                    self.init_WebView(_loadUrl: S_FinalData.WV_LOAD_MAIN_ENZ_URL)
                    // -----------------------------------------
                }
            }
            // -----------------------------------------
            else { // MARK: [정상 응답 코드 인 경우]
                print("")
                print("====================================")
                print("[\(self.ACTIVITY_NAME) >> WKNavigationResponse() :: 웹뷰 실시간 url status 상태 코드 감지]")
                print("-------------------------------")
                print("로 직 :: [정상] 웹뷰 이동 로직 처리 실시")
                print("====================================")
                print("")
                
                // MARK: [웹뷰 재호출 프리퍼런스 값 지정 실시]
                S_Preference().setString(_sKey: S_FinalData.PRE_WEBVIEW_RELOAD, _sValue: "")
            }
            // -----------------------------------------
        }
    }
    // */
    
    
    
    
    
    // MARK: - [웹뷰 모달창 닫힐때 앱 종료현상 방지]
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
    
    
    
    
    
    // MARK: - [웹뷰 alert 팝업창 처리]
    func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String,
                 initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void){
        print("")
        print("====================================")
        print("[\(self.ACTIVITY_NAME) >> runJavaScriptAlertPanelWithMessage() :: alert 팝업창 처리]")
        print("-------------------------------")
        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)
    }


    
    
    
    // MARK: - [웹뷰 confirm 팝업창 처리]
    func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) {
        print("")
        print("====================================")
        print("[\(self.ACTIVITY_NAME) >> runJavaScriptConfirmPanelWithMessage() :: confirm 팝업창 처리]")
        print("-------------------------------")
        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)
    }
    
    
    
    
    
    // MARK: - [웹뷰 상단 버튼 닫기 클릭 이벤트]
    @objc func buttonAction(sender: UIButton!) {
        print("")
        print("====================================")
        print("[\(self.ACTIVITY_NAME) >> buttonAction() :: 웹뷰 닫기 버튼 클릭 이벤트 발생]")
        print("====================================")
        print("")
        // -----------------------------------------
        // [현재 액티비티 닫기 수행 실시]
        self.dismiss(animated: false, completion: nil)
        // -----------------------------------------
    }
    
    
    
    
    
    // MARK: [액티비티 종료 팝업창 메소드]
    func showAlertFinish(tittle:String, content:String, okBtb:String, noBtn:String) {
        // [메인 큐에서 비동기 방식 실행 : UI 동작 실시]
        DispatchQueue.main.async {
            print("")
            print("====================================")
            print("[\(self.ACTIVITY_NAME) >> showAlertFinish() :: 액티비티 종료 팝업창 호출 실시]")
            print("tittle :: \(tittle)")
            print("content :: \(content)")
            print("====================================")
            print("")
            
            // [UIAlertController 객체 정의 실시]
            let alert = UIAlertController(title: tittle + "\n", message: content + "\n", preferredStyle: UIAlertController.Style.alert)
            
            // [인풋으로 들어온 확인 버튼이 nil 아닌 경우]
            if(okBtb != "" && okBtb.count>0){
                let okAction = UIAlertAction(title: okBtb, style: .default) { (action) in
                    // -------------------------------
                    // [확인 버튼 클릭 이벤트 내용 정의 실시]
                    // -------------------------------
                    // [현재 액티비티 닫기 수행 실시]
                    self.dismiss(animated: false, completion: nil)
                    // -------------------------------
                    return
                    // -------------------------------
                }
                alert.addAction(okAction) // [버튼 클릭 이벤트 객체 연결]
            }
            
            // [인풋으로 들어온 취소 버튼이 nil 아닌 경우]
            if(noBtn != "" && noBtn.count>0){
                let noAction = UIAlertAction(title: noBtn, style: .default) { (action) in
                    // -------------------------------
                    // [취소 버튼 클릭 이벤트 내용 정의 실시]
                    // -------------------------------
                    return
                    // -------------------------------
                }
                alert.addAction(noAction) // [버튼 클릭 이벤트 객체 연결]
            }
            
            // [alert 팝업창 활성 실시]
            self.present(alert, animated: false, completion: nil)
        }
    }
    
    
} // MARK: [클래스 종료]

 

반응형
Comments