Jiwift

[iOS/Swift] WebKit, window.open을 통해서 새로운 탭 열기 feat.nice 본인 인증. window.close 본문

iOS Dev/Swift

[iOS/Swift] WebKit, window.open을 통해서 새로운 탭 열기 feat.nice 본인 인증. window.close

지위프트 2023. 6. 21. 22:14
반응형

정답이 아닐 수 있습니다. 각자 상황에 맞는 방법을 찾아서 사용하시면 됩니다. 

 

 

 

 

인터넷을 하다보면 새로운 창이 필요할 때가 있습니다. Nice 본인인증과 같은 상황이조.

 

근데 우리가 사용하는 앱들은 창이 여러개가 보여지지 않습니다. 

 

대부분 사용하는 앱들은 회원가입을 진행하거나 물건을 구매할때 순차적으로 진행되는 화면을 보게됩니다. 

PC 환경 처럼 새로운 창을 띄우는 경우는 드물다고 봅니다.(기획에 따라 다름)

 

근데.. 역시 예외 상황은 꼭 생기고 PC 환경 처럼 새로운 탭을 열어서 작업이 필요한 경우가 있습니다. 이유는 모릅니다.

 

한가지 상황을 생각해봅시다. 어딘가에 회원가입을 웹뷰로 만드는 서비스가 있다고 생각합니다. 

(회원가입 부분은 프론트엔드 개발자가, 네이티브 WebKit과 브릿지 확인은 iOS 개발자가 하는 상황입니다. )

 

근데 어찌저찌 하다보니 Nice 본인 인증을 위해서는 새로운 창을 띄어야합니다. PC 환경 처럼 말이지요. 근데 iOS만 맨날 개발하던 개발자는 당황합니다. WebKit은 하나인데 어떻게 화면을 더 띄우지?? 라는 고민을 하게되조. 다행이 구글은 많은 것을 알고 있고 해결법도 있었습니다. 

 

iOS 개발자는 웹을 모릅니다. 그저 vue로 만들어진 회원가입 화면에서 'window.open'라는 명령어를 보내면 새로운 창이 뜬다는 것만 알고있습니다. 

 

다행이 iOS는 그 신호를 캐치할 수 있습니다. 

 

동작이 안된다는 글도있지만 정책 관련된거라 잘 모르겠습니다. 제가 구현했을 땐 동작하니깐요.

 

 

요약

1. 코드를 통해 웹뷰 두개를 옵셔널로 생성

2. 회원가입 웹뷰 먼저 설정하고 add 

3. window.open하면 웹에서 받은 데이터로 nice 웹뷰 설정 및 add

4. 완료되면 닫기 (window.close 혹은 브릿지를 통해서)

 

우선 저는 WebKit을 코드로 생성할 것입니다. 

WebKit이 담기는 View를 하나 Storyboard로 생성하고 login 화면을 위한 WebKit과 Nice 화면을 위한 WebKit을 생성합니다. 
(변수명은 마음대로)

// 로그인 웹킷
private func initLoginWk(url: String) {
    // 로그인 WebKit 초기화
    self.loginWKView = nil

    // 웹뷰에 필요한 설정 및 설정값을 추가로 적용합니다.
    let webConfiguration = WKWebViewConfiguration()

    // 화면 열기
    webConfiguration.preferences.javaScriptCanOpenWindowsAutomatically = true

    // 로그인 웹뷰 브릿지 추가
    webConfiguration.userContentController.add(self, name: "bridge")

    // 자바 스크립트 허용
    if #available(iOS 14.0, *) {
        webConfiguration.defaultWebpagePreferences.allowsContentJavaScript = true
    } else {
        // Fallback on earlier versions
        webConfiguration.preferences.javaScriptEnabled = true
    }

    // 로그인 WebKit을 생성해서 변수에 넣음
    self.loginWKView = WKWebView(frame: self.webKitContainer.bounds, configuration: webConfiguration)
    // 웹뷰 화면 플렉서블하게 적용
    self.loginWKView?.autoresizingMask = [.flexibleWidth, .flexibleHeight]

    // Delegate 등록
    self.loginWKView?.navigationDelegate = self
    self.loginWKView?.uiDelegate = self
    // 링크 미리보기 차단
    self.loginWKView?.allowsLinkPreview = false

    // Web 띄우기
    self.initUrl(url: url)
    // Container에 WebKit 추가
    self.webKitContainer.addSubview(self.loginWKView!)
}

그리고 위와 같이 코드를 작성합니다.  여기서 많은 것들을 설정했지만 WebKit 메소드 설명은 따로 검색해서 보시고 ..

코드 흐름을 보면 configuration을 생성해 옵션과 함께 처음에 생성한 webkit에 넣어줍니다. 

 

그리고 initUrl함수를 통해서 웹페이지 로드를 하고 WebKit이 담길 webKitContainer에 addSubview합니다.

(initUrl은 제가 만든 것이니 굳이 따라하실 필요없습니다. 그냥 웹킷 load 함수에요) 

// 화면 열기
webConfiguration.preferences.javaScriptCanOpenWindowsAutomatically = true

// Delegate 등록
self.loginWKView?.uiDelegate = self

이것만 빼먹지 않으면 됩니다. 

 

자바스크립트 코드로 화면을 여는 것을 허용해주는 것과 delegate인데 이 둘이 다합니다. 

 

ViewController에 WKUIDelegate도 상속합니다. 

 

 

// window.open
func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView? {
    // 기존 웹뷰 브릿지 제거
    self.loginWKView?.configuration.userContentController.removeScriptMessageHandler(forName: "bridge")

    // WebView Size
    let frame = UIScreen.main.bounds
    // 파라미터로 받은 configuration
    self.niceWebView = WKWebView(frame: frame, configuration: configuration)
    // 오토레이아웃 처리
    self.niceWebView?.autoresizingMask = [.flexibleWidth, .flexibleHeight]
    // 링크 미리보기 차단
    self.niceWebView?.allowsLinkPreview = false
    // Nice 웹뷰 브릿지 추가
    self.niceWebView?.configuration.userContentController.add(self, name: "bridge")

    // 화면에 추가
    self.webKitContainer.addSubview(self.niceWebView!)
    return self.niceWebView!
}

이번에는 window.open이 발생했을 때 WebKit이 수행하는 Delegate 함수 중 하나입니다. 

 

// 파라미터로 받은 configuration
self.niceWebView = WKWebView(frame: frame, configuration: configuration)

configuration은 delegate에서 받고 그외 넣고 싶은 옵션을 넣어줍니다. 그리고 위에서 생성한 niceWebView에 넣어줍니다. 

그러고 마찬가지로 WebKit이 담길 webKitContainer에 addSubview합니다. 

 

우리는 총 두개의 웹킷을 사용해서 window.open을 수행했습니다. 

 

 


func webViewDidClose(_ webView: WKWebView) {
    niceWebView?.removeFromSuperview()
    niceWebView = nil
}

해당 코드를 작성하면 웹에서 window.close를 수행했을때 네이티브에서 동작하고 싶은 코드를 실행할 수 있습니다. 

 

nice화면을 닫고싶어서 SuperView에서 지우고 nil처리를 했습니다.

 

 

위와 같이 구성하면 브릿지를 따로 주어야합니다. 아니면 하나의 브릿지만 사용해서 add하고 remove해도됩니다. 

 

[iOS/Swift] 웹뷰 브릿지 등록, WebKit과 브릿지, userContentController, 데이터 전송 (tistory.com)

 

[iOS/Swift] 웹뷰 브릿지 등록, WebKit과 브릿지, userContentController, 데이터 전송

세상에 많은 앱들은 네이티브 + 웹으로 구성되어있다. 코어한 부분은 네이티브로.. 수정이 많지만 배포를 줄이고 싶은 경우, 이미 웹으로 만들어서 두번 작업하기 싫은 경우. iOS에서는 WebKit을 사

jiwift.tistory.com

[iOS/Swift] 웹뷰 브릿지 삭제, userContentController 제거 | removeScriptMessageHandler... (두개의 WebKit과 브릿지,) (tistory.com)

 

[iOS/Swift] 웹뷰 브릿지 삭제, userContentController 제거 | removeScriptMessageHandler... (두개의 WebKit과 브릿지

우선 브릿지 삭제 방법은 forName자리에 삭제하고 싶은 userContentController 입력 self.loginWKView.configuration.userContentController.removeScriptMessageHandler(forName: "bridge") 사실 이건 나만이 겪을 것 같은 상황이다.

jiwift.tistory.com

 

 

 

WKWebView에서 자바스크립트로 window.open(), window.close() 하는 경우 처리 (github.com)

 

WKWebView에서 자바스크립트로 window.open(), window.close() 하는 경우 처리

WKWebView에서 자바스크립트로 window.open(), window.close() 하는 경우 처리 - createWebViewWith.swift

gist.github.com

iOS 팝업 정책과 window.open (velog.io)

 

iOS 팝업 정책과 window.open

iOS의 팝업 정책과 window.open

velog.io

 

반응형