iOS 하이브리드앱(기본 브라우져 사용)에서시 서버의 Front에서 javaScript를 이용하여 연동하는 방법

(Swift에서 WKWebView를 이용하는 경우)

 

 

 

 

 

 

 

 

 

1. Native앱 ---> Front의 javaScript로 데이터 전송시 (swift)

1) [[Native앱]] 웹브라우져에서 자바스크립트를 호출한다.


A. HTML로딩시 처음 호출하는 경우 자바스크립트 호출 (설정등 사용시)

        // native call -> js (이건  html 로드시에만 가능)
        let mySource = "mergeAppUserInfo('\(myUserid)','\(myVersion!)','ios','\(iosVersion)')"
        let userScript = WKUserScript(source: mySource, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
        contentController.addUserScript(userScript)

B. 필요할때마다 자바스크립트 호출, WKWebView의 evaluateJavaScript 함수를 이용한다.

        let myJsFuncName = "mergeAppUserInfo('\(myUserid)','\(myVersion!)','ios','\(iosVersion)')"
        // native call -> js (이건 언제든지 가능)
        mainWebView.evaluateJavaScript(myJsFuncName) { (result, error) in
            if let result = result{
                print(result)
            }
        }

2) [[JavaScript]] 서버의 jsp,asp,php등의 파일에서 자바스크립트 설정을 한다.

<script type="text/javascript">
// Native App에서 호출, 접속한 사용자 및 버전 정보 (앱 --> Front)
function mergeAppUserInfo(userid, app_version, os_type, os_version){
   alert("AppInfo:"+userid+", "+app_version+","+os_type+", "+os_version);
}
</script>

 

2. Front의 JavaScript ---> Native앱으로 데이터 전송시 (swift)

1) [[Front의 JavaScript]] 서버의 jsp,asp,php등의 파일에서 자바스크립트로 데이터 전송 요청을 한다.

<script type="text/javascript">
//하이브리드앱 연동, 앱에 데이터를 전송한다.
$(document).ready(function(){
   var sendData = "javascript에서 native로 데이터를 전송한다.";
   window.webkit.messageHandlers.sendUserIdVersion.postMessage(sendData); // (JavaScript --> 앱)
});
</script>

2) [[Native앱]] 웹브라우져에서 자바스크립트를 호출한다. 

// js -> native call 위해 설정, 이용하는 자바스크립트 함수명 등록한다.
    ...
    contentController.add(self, name: "sendUserIdVersion")
    configuration.userContentController = contentController
    ...

//WKScriptMessageHandler에서 제공하는 함수이용하여 데이터 수신
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
    if(message.name == "sendUserIdVersion"){    // 사용자id, 앱의 최신버전, 업데이트 팝업 보여줄지 여부
            if let myBody = message.body as? String {
                print("@@@ body=\(myBody)")
            }
    }
}

[참고]안드로이드 하이브리드앱(기본 브라우져 사용)시 서버의 Front에서 javascript를 이용하여 연동하는 방법

https://androi.tistory.com/352

 

안드로이드 하이브리드앱(기본 브라우져 사용)에서 서버의 Front에서 javascript를 이용하여 연동하는 방법

안드로이드 하이브리드앱(기본 브라우져 사용)시 서버의 Front에서 javascript를 이용하여 연동하는 방법 1. Native앱 ---> Front의 JavaScript로 데이터 전송시 1) [[Native앱]] 웹브라우져에서 url 호출하듯이..

androi.tistory.com

3. Front의 JavaScript의 전체 소스

<script type="text/javascript">
//하이브리드앱 연동
$(document).ready(function(){
	var appInfo = "<c:out value='${appInfo}'/>"; 
	var androidVer =  "<c:out value='${appInfo[0].APP_VERSION}'/>";
	var iosVer =  "<c:out value='${appInfo[1].APP_VERSION}'/>";
	var androidMustYn =  "<c:out value='${appInfo[0].MUST_YN}'/>";
	var iosMustYn =  "<c:out value='${appInfo[1].MUST_YN}'/>";	
// 	console.log("appInfo="+appInfo);
	console.log("androidVer="+androidVer);
	console.log("iosVer="+iosVer);
	
	var agent = navigator.userAgent.toLowerCase();
	console.log("agent="+agent);
	var isAppIOS      = (agent.match('iosapp') != null);
	var isAppAndroid  = (agent.match('androidapp') != null);
	
	//alert("<%=sMemberId%>");
	console.log("sMemberId=<%=sMemberId%>"); // 로그인이 안된 상태이면 "" 값이 넘어감.
	if(isAppAndroid)   //  안드로이드 APP 인 경우
	{    		
		window.HybridApp.sendUserIdVersion("<%=sMemberId%>", androidVer, androidMustYn); // (Front의 JavaScript--> 앱)
	}else if(isAppIOS){	// 아이폰 APP 인 경우
		var sendData = {
			'userid': "<%=sMemberId%>",
			'iosVer': iosVer,
			'iosMustYn': iosMustYn
			};
		window.webkit.messageHandlers.sendUserIdVersion.postMessage(sendData); // (Front의 JavaScript --> 앱)
	}

});
// Native App에서 호출, 접속한 사용자 및 버전 정보 (앱 --> Front)
function mergeAppUserInfo(userid, app_version, os_type, os_version){
	//alert("AppInfo:"+userid+", "+app_version+","+os_type+", "+os_version);
	$.ajax({
		type: "POST",
		cache:false,
		url: '<c:url value="/main/ajaxMergeAppUserInfo.json"/>',
	    dataType: "json",
	    data: {userid : userid, app_version : app_version ,os_type : os_type, os_version : os_version},
		async:false,
		success: function(obj) {
			console.log("접속한 사용자 정보가 갱신되었습니다.");
		},
		error: function(obj) {
			console.log("접속한 사용자 정보가 갱신실패 되었습니다.");
		}
	}); 

}
</script>

iOS 앱을 호출시 전달하는 데이터가 없을때도 임의의 값을 넣어야 동작하네요

window.webkit.messageHandlers.sendUserIdVersion.postMessage(); // 앱에서 호출 안됨 ㅠㅠ

window.webkit.messageHandlers.sendUserIdVersion.postMessage("abc"); // 앱에서 호출됨 ^^

 

4. Native 앱의 전체 소스

//
//  ViewController.swift

import UIKit
import WebKit
import SafariServices

class ViewController: UIViewController, OpenableCustomURL {
    
    let url = URL(string: "http://m.mymobilesite.com/")   
    
    let myVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String
    var myUserid : String = ""
    let iosVersion = UIDevice.current.systemVersion
    let contentController = WKUserContentController()
    var mainWebView = WKWebView()
    var cookie: HTTPCookie?
    let processPool: WKProcessPool = WKProcessPool()

    let statusView: UIView = {
        let view = UIView()
        view.backgroundColor = .white
        view.translatesAutoresizingMaskIntoConstraints = false
        return view
    }()
    
    let bt_dismiss: UIButton = {
        let button = UIButton()
        button.setImage(UIImage(named: "ico_gohome.png"), for: UIControl.State.normal)
        button.layer.opacity = 1.0
        button.addTarget(self, action: #selector(handleDismiss), for: .touchUpInside)
        button.layer.cornerRadius = 20
        button.translatesAutoresizingMaskIntoConstraints = false
        return button
    }()
    
    @objc func handleDismiss() {    
        mainWebView.load(url!)
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        view.backgroundColor = .white
        bt_dismiss.isHidden = true
        setupWebView()
        setupLayout()
        mainWebView.load(url!)
    }
    
    func pushLoad(pushurl : String){
        guard let linkUrl = URL(string: pushurl) else { return }
        print("ViewController linkUrl: \(String(describing: linkUrl))")
        mainWebView.load(linkUrl)
    }
    
    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        
        webView.evaluateJavaScript("navigator.userAgent") { [weak webView] (result, error) in
            if let webView = webView, let userAgent = result as? String {
                webView.customUserAgent = userAgent + " iosapp"  // iOS앱 구분위해 useragent에 추가하고, Front의 javascript에서 이를 사용한다.
            }
        }
    }
    
    func setupWebView() {
        
        let preferences = WKPreferences()
        preferences.javaScriptEnabled = true
        preferences.javaScriptCanOpenWindowsAutomatically = true
        
        let configuration = WKWebViewConfiguration()
        configuration.preferences = preferences
        configuration.processPool = WKProcessPool()
        configuration.allowsInlineMediaPlayback = true
        configuration.allowsPictureInPictureMediaPlayback = true
        configuration.processPool = processPool
        
        // js -> native call
        contentController.add(self, name: "sendUserIdVersion")
        configuration.userContentController = contentController
        
        // native call -> js (이건  html 로드시에만 가능)
//        let mySource = "mergeAppUserInfo('\(myUserid)','\(myVersion!)','ios','\(iosVersion)')"
//        let userScript = WKUserScript(source: mySource, injectionTime: .atDocumentEnd, forMainFrameOnly: true)
//        contentController.addUserScript(userScript)
        
        mainWebView.scrollView.bounces = false
        mainWebView = WKWebView(frame: .zero, configuration: configuration)
        mainWebView.scrollView.isScrollEnabled = true
        mainWebView.scrollView.bounces = false
        mainWebView.uiDelegate = self
        mainWebView.navigationDelegate = self
        mainWebView.allowsLinkPreview = true
        mainWebView.allowsBackForwardNavigationGestures = true
        mainWebView.translatesAutoresizingMaskIntoConstraints = false
    }
    
    func setupLayout() {
        
        view.addSubview(statusView)
        view.addSubview(mainWebView)
        view.addSubview(bt_dismiss)
        
        NSLayoutConstraint.activate([

            statusView.topAnchor.constraint(equalTo: view.topAnchor),
            statusView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            statusView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            statusView.heightAnchor.constraint(equalToConstant: 20),

            mainWebView.topAnchor.constraint(equalTo: statusView.bottomAnchor),
            mainWebView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            mainWebView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            mainWebView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
            
            bt_dismiss.bottomAnchor.constraint(equalTo: view.bottomAnchor, constant: -30),
            bt_dismiss.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20),
            bt_dismiss.widthAnchor.constraint(equalToConstant: 40),
            bt_dismiss.heightAnchor.constraint(equalToConstant: 40)
            ])
    }
    
    
}


extension ViewController: WKUIDelegate, WKNavigationDelegate, WKScriptMessageHandler {
    
    func detectBridges() {
        let controller = WKUserContentController()
        mainWebView.configuration.userContentController = controller
        
    }
    
    func sendUserData(){
        if let myVersion = myVersion{
            print("@@@ myVersion: \(myVersion)")
        }
        let myJsFuncName = "mergeAppUserInfo('\(myUserid)','\(myVersion!)','ios','\(iosVersion)')"
        print("@@@ myJsFuncName=\(myJsFuncName)")
        // native call -> js (이건 언제든지 가능)
        mainWebView.evaluateJavaScript(myJsFuncName) { (result, error) in
            if let result = result{
                print(result)
            }
        }
    }
    
    // javascript -> native 호출,
    func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
        if(message.name == "sendUserIdVersion"){    // 사용자id, 앱의 최신버전, 업데이트 팝업 보여줄지 여부
            // 인자를 여러개 받을때는 Dictionary로 받는다.
            if let dictionary : [String: String] = message.body as? Dictionary{
                if let userid = dictionary["userid"]{
                    print("@@@ userid=\(userid)")
                    myUserid = userid
                }
                if let iosVer = dictionary["iosVer"]{
                    print("@@@ iosVer=\(iosVer)")
                }
                if let iosMustYn = dictionary["iosMustYn"]{
                    print("@@@ iosMustYn=\(iosMustYn)")
                }
            }
	    // 인자가 1개일때는 String으로 받는다.
//            if let myBody = message.body as? String {
//                print("@@@ body=\(myBody)")
//            }
            
            sendUserData()

        }
    }
    
    // window.open() 이 호출될때 사용
    func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {

        guard let requestURL = navigationAction.request.url else {return nil}
//        let url2 = requestURL.absoluteString
        let hostAddress = navigationAction.request.url?.host
        if hostAddress == "m.mymobilesite.com" {
            bt_dismiss.isHidden = true
            mainWebView.load(requestURL)
            return nil
        }
        
//        let url2 = navigationAction.request.url
        let webVC = PresentedWebViewController()
//        webVC.url = url2!
        webVC.url = requestURL
        webVC.processPool = self.processPool
        present(webVC, animated: true, completion: nil)
        return nil
    }
    
    func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) {
        
        let alertController = UIAlertController(title: nil, message: message, preferredStyle: .alert)
        let yes = UIAlertAction(title: "네", style: .default) {
            action in completionHandler(true)
            
            
        }
        let no = UIAlertAction(title: "취소", style: .default) {
            action in completionHandler(true)
            
        }
        alertController.addAction(no)
        alertController.addAction(yes)
        present(alertController, animated: true, completion: nil)
        
    }
    
    func webView(_ webView: WKWebView, runJavaScriptAlertPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping () -> Void) {
        
        let alertController = UIAlertController(title: nil, message: message, preferredStyle: .alert)
        let otherAction = UIAlertAction(title: "확인", style: .default) {
            action in completionHandler()
            self.navigationController?.popViewController(animated: true)
        }
        alertController.addAction(otherAction)
        present(alertController, animated: true, completion: nil)
        
    }
    
    func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Swift.Void) {
        
        guard let requestURL = navigationAction.request.url else {return}
        let url = requestURL.absoluteString
        let hostAddress = navigationAction.request.url?.host
        // To connnect app store
        if hostAddress == "itunes.apple.com" {
            if UIApplication.shared.canOpenURL(requestURL) {
                UIApplication.shared.open(requestURL, options: convertToUIApplicationOpenExternalURLOptionsKeyDictionary([:]), completionHandler: nil)
                decisionHandler(.cancel)
                return
            }
        }
        
        if url.hasPrefix("mywindowpop://http//") {  // 이 표시가 있으면 기본브라우져 새창을 띄움
            let new_url = url.replacingOccurrences(of: "mywindowpop://http//", with: "http://")
            if let myUrl = URL(string: new_url) {
                if UIApplication.shared.canOpenURL(myUrl) {
                    UIApplication.shared.open(myUrl, options: convertToUIApplicationOpenExternalURLOptionsKeyDictionary([:]), completionHandler: nil)
                    decisionHandler(.cancel)
                    return
                }
            }
        }
        if url.hasPrefix("mywindowpop://https//") {  // 이 표시가 있으면 기본브라우져 새창을 띄움
            let new_url = url.replacingOccurrences(of: "mywindowpop://https//", with: "https://")
            if let myUrl = URL(string: new_url) {
                if UIApplication.shared.canOpenURL(myUrl) {
                    UIApplication.shared.open(myUrl, options: convertToUIApplicationOpenExternalURLOptionsKeyDictionary([:]), completionHandler: nil)
                    decisionHandler(.cancel)
                    return
                }
            }
        }
        if hostAddress == "m.mymobilesite.com" || hostAddress == "" {
            bt_dismiss.isHidden = true
        }else{
            bt_dismiss.isHidden = false
        }
        
        #if DEBUG
        print("url = \(url), host = \(hostAddress?.description ?? "")")
        #endif
        let url_elements = url.components(separatedBy: ":")
        if url_elements[0].contains("http") == false &&
            url_elements[0].contains("https") == false {  // 신용카드 결제 화면등을 위해 필요
            
            if UIApplication.shared.canOpenURL(requestURL) {
                UIApplication.shared.open(requestURL, options: convertToUIApplicationOpenExternalURLOptionsKeyDictionary([:]), completionHandler: nil)
            } else {
                print("@@@ Browser can't be opened, but Scheme try to call !! @@@")
                UIApplication.shared.open(requestURL, options: convertToUIApplicationOpenExternalURLOptionsKeyDictionary([:]), completionHandler: nil)
            }
            
            decisionHandler(.cancel)
            return
            
        }
        decisionHandler(.allow)
        
    }
}



protocol OpenableCustomURL {
    func openCustomApp(urlScheme:String, additional_info:String)
}

extension OpenableCustomURL {
    func openCustomApp(urlScheme:String, additional_info:String) {
        if let requestUrl = URL(string:"\(urlScheme)"+"\(additional_info)") {
            let application:UIApplication = UIApplication.shared
            if application.canOpenURL(requestUrl) {
                application.open(requestUrl, options: convertToUIApplicationOpenExternalURLOptionsKeyDictionary([:]), completionHandler: nil)
            } else {
                print("cant Open2")
            }
        }
    }
}

// Helper function inserted by Swift 4.2 migrator.
fileprivate func convertToUIApplicationOpenExternalURLOptionsKeyDictionary(_ input: [String: Any]) -> [UIApplication.OpenExternalURLOptionsKey: Any] {
	return Dictionary(uniqueKeysWithValues: input.map { key, value in (UIApplication.OpenExternalURLOptionsKey(rawValue: key), value)})
}

 

참고 URL

 https://oingbong.tistory.com/225 

 

WKWebview를 이용한 Javascript, Swift 양방향 통신

아래 작성된 방법은 WebView 가 로드될 때 실행되는 것을 기준으로 하였으며 네이티브 앱에서 버튼을 누르거나 특정 동작을 할 때 Javascript 로 값을 전달하고 싶은 경우에는 아래 블로그에 나온 방법( evaluateJ..

oingbong.tistory.com

 

+ Recent posts