[iOS] WKNavigationDelegate 메서드 알아보기
들어가기 전에
개-하! 거두절미하고 웹 페이지의 로딩 과정부터 알아봅시다.
- 사용자의 링크 클릭 또는 URL 입력과 같은 유저의 Action 으로 시작됩니다. 이를 initial request, 혹은 navigation start 라고 부릅니다.
- 이후 서버에서 처리를 마치고 네트워크를 통해 reponse 를 사용자의 브라우저로 다시 전송합니다. 이를 response start 혹은 first byte 라고 부릅니다.
- 사용자의 브라우저가 response 를 수신하기 시작하고, Document Object Model 또는 DOM 처리를 시작합니다.
- DOM 로드가 완료되는 시점을 DOM ready 라고 하며, DOM을 사용하여 사용자의 브라우저가 페이지를 렌더링하기 시작합니다.
흠.. DOM 이 뭔데 ???? 라고 한다면 저도 잘 모르니까 그냥 여기서는 웹 페이지 로딩을 위해
- Action / Request — 사용자의 URL 입력 혹은 링크 클릭 등으로 발생하는 웹 페이지에 대한 Client 의 요청
- Response — Server의 응답
의 과정이 일어난다고 간략하게 생각해볼게여
이 내용을 한번 짚고 넘어가야 아래 코드를 볼때
decidePolicyFor navigationAction: WKNavigationAction
decidePolicyFor navigationResponse: WKNavigationResponse
- 첫번째는 navigationAction 에 대한 policy 를 정한다고? 음.. 처음 요청할때에 대한 policy 를 설정하는거겠군?
- 두번째는 navigationResponse 에 대한 policy 를 정한다고? 음… 서버에서 응답을 받고나서 policy 를 설정하는거겠군?
이라고 대충 때려맞출 수 있기 때문이져
iOS 에서는 이런 웹 페이지 요청이나, 진행률 추적, 오류 처리 등을 위해 WKNavigationDelegate
메서드를 사용할 수 있는데여, 저는 이 메서드들이 어떤 순서로 호출되나 체크하려고 이 글을 시작한거거든여?
제가 내용을 다 쓰고 추측해 봤을때 일반적인 성공 상황이라면 아래와 같은 순서로 실행될 것 같았슴다. 그리고 실제로도 이 순서로 실행 되었구여 ㅇㅇ
func webView(WKWebView, decidePolicyFor: WKNavigationAction, preferences: WKWebpagePreferences, decisionHandler: (WKNavigationActionPolicy, WKWebpagePreferences) -> Void)
func webView(WKWebView, didStartProvisionalNavigation: WKNavigation!)
func webView(WKWebView, decidePolicyFor: WKNavigationResponse, decisionHandler: (WKNavigationResponsePolicy) -> Void)
func webView(WKWebView, didCommit: WKNavigation!)
func webView(WKWebView, didFinish: WKNavigation!)
그럼 이제 WKNavigationDelegate
문서에 나와있는 Definition 이랑 Discussion 을 박박 긁어와봅시다.
1. navigation request 를 허용 / 거절하기
webView(_:decidePolicyFor:decisionHandler:)
optional func webView(
_ webView: WKWebView,
decidePolicyFor navigationAction: WKNavigationAction,
decisionHandler: @escaping (WKNavigationActionPolicy) -> Void
)
지정된 action 정보를 기반으로, 새 콘텐츠로 이동할 수 있는 권한을 요청합니다. 이 메서드를 통해 navigation request 를 허용하거나 거부할 수 있습니다.
웹뷰는 인터렉션 발생 후 내용을 로드하기 전에 이 메서드 먼저 호출하며, 구현부에서는 항상 아래와 같은 decisionHandler
블록을 실행합니다.
decisionHandler(.allow)
decisionHandler(.cancel)
decisionHandler(.download)
webView(_:decidePolicyFor:preferences:decisionHandler:)
optional func webView(
_ webView: WKWebView,
decidePolicyFor navigationAction: WKNavigationAction,
preferences: WKWebpagePreferences,
decisionHandler: @escaping (WKNavigationActionPolicy, WKWebpagePreferences) -> Void
)
지정된 환경설정(preferences) 및 action 정보를 기반으로, 새 콘텐츠로 이동할 수 있는 권한을 요청합니다.
바로 위의 webView(_:decidePolicyFor:decisionHandler:)
메서드와 비슷하지만, 만약 이 메서드를 구현하면 웹뷰에서 webView(_:decidePolicyFor:decisionHandler:)
를 호출하지 않습니다.
webView(_:decidePolicyFor:decisionHandler:)
optional func webView(
_ webView: WKWebView,
decidePolicyFor navigationResponse: WKNavigationResponse,
decisionHandler: @escaping (WKNavigationResponsePolicy) -> Void
)
navigation request 에 대한 응답을 받고 난 후, 새 콘텐츠로 이동할 수 있는 권한을 요청합니다.
2. request 의 로딩 진행률 추적하기
webView(_:didStartProvisionalNavigation:)
optional func webView(
_ webView: WKWebView,
didStartProvisionalNavigation navigation: WKNavigation!
)
메인 프레임에서 navigation 이 시작되었음을 알립니다.
웹 뷰는 navigation request 를 처리하기 위한 임시(provisional) 허가를 받은 후, 해당 요청에 대한 응답을 받기 전에 이 메서드를 호출합니다.
webView(_:didReceiveServerRedirectForProvisionalNavigation:)
optional func webView(
_ webView: WKWebView,
didReceiveServerRedirectForProvisionalNavigation navigation: WKNavigation!
)
웹 뷰가 request 에 대한 서버 redirection 을 수신했음을 알립니다.
webView(_:didCommit:)
optional func webView(
_ webView: WKWebView,
didCommit navigation: WKNavigation!
)
웹 뷰가 메인 프레임에 대한 내용을 수신하기 시작했음을 알립니다.
webView(_:decidePolicyFor:decisionHandler:)
method 가 navigation response 를 허용하기로 결정 한 후, 웹뷰는 해당 응답을 처리하기 시작합니다. 변경 사항이 준비되면 웹 뷰는 main frame 업데이트를 시작하기 직전에 이 메서드를 호출합니다.
webView(_:didFinish:)
optional func webView(
_ webView: WKWebView,
didFinish navigation: WKNavigation!
)
navigation 이 완료되었음을 알립니다.
3. 인증(authentication) 문제 대응하기
webView(_:didReceive:completionHandler:)
optional func webView(
_ webView: WKWebView,
didReceive challenge: URLAuthenticationChallenge,
completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void
)
인증 확인에 응답하도록 요청합니다. 만약 해당 메서드를 구현하지 않으면 웹뷰는 인증 문제에 대해 URLSession.AuthChallengeDisposition.rejectProtectionSpace
로 응답합니다.
webView(_:authenticationChallenge:shouldAllowDeprecatedTLS:)
optional func webView(
_ webView: WKWebView,
authenticationChallenge challenge: URLAuthenticationChallenge,
shouldAllowDeprecatedTLS decisionHandler: @escaping (Bool) -> Void
)
더 이상 사용되지 않는 버전의 TLS를 사용하는 연결을 계속할 것인지 묻습니다.
이 방법을 구현하지 않으면 웹 뷰는 시스템 설정을 사용하여 더 이상 사용되지 않는 버전의 TLS 사용을 허용할지 여부를 결정합니다.
4. navigation 에러 대응하기
webView(_:didFailProvisionalNavigation:withError:)
optional func webView(
_ webView: WKWebView,
didFail navigation: WKNavigation!,
withError error: Error
)
초기 navigation 프로세스 중에 오류가 발생했음을 알립니다. (웹페이지 로딩되었을 때 호출되는 것이 아니라 URL이 잘못되었거나 네트워크 오류가 발생해 웹 페이지 자체를 아예 불러오지 못했을 때 호출되는 메서드라고 함)
webView(_:didFail:withError:)
optional func webView(
_ webView: WKWebView,
didFailProvisionalNavigation navigation: WKNavigation!,
withError error: Error
)
navigation 중에 오류가 발생했음을 알립니다. (didCommit 이후에 발생하는 에러에 대해 호출된다고 함)
webViewWebContentProcessDidTerminate(_:)
optional func webViewWebContentProcessDidTerminate(_ webView: WKWebView)
웹 뷰의 content 프로세스가 종료되었음을 알립니다.
웹 뷰는 별도의 프로세스를 사용하여 웹 콘텐츠를 렌더링하고 관리합니다. 웹킷은 지정된 웹 뷰에 대한 프로세스가 어떤 이유로든 종료될 때 이 메서드를 호출합니다.
5. 다운로드 진행률 처리하기
webView(_:navigationAction:didBecome:)
optional func webView(
_ webView: WKWebView,
navigationAction: WKNavigationAction,
didBecome download: WKDownload
)
navigation action 이 다운로드 되었음을 알립니다 (became a download. 아마 decisionHandler
에서 .download
로 설정되었다는 뜻인듯).
다운로드 진행률 추적을 시작하려면 이 메서드를 구현합니다.
webView(_:navigationResponse:didBecome:)
optional func webView(
_ webView: WKWebView,
navigationResponse: WKNavigationResponse,
didBecome download: WKDownload
)
navigation response 가 다운로드 되었음을 알립니다 (became a download. 아마 decisionHandler
에서 .download
로 설정되었다는 뜻인듯).
다운로드 진행률 추적을 시작하려면 이 메서드를 구현합니다.
마무리
그러니까 처음에 제가 추측한 순서는 아래 내용에 기반했던 것입니당
func webView(WKWebView, decidePolicyFor: WKNavigationAction, preferences: WKWebpagePreferences, decisionHandler: (WKNavigationActionPolicy, WKWebpagePreferences) -> Void)
👉🏻 처음에 Action 으로 요청할때 해당 navigation request 를 허용하거나 거부
2. func webView(WKWebView, didStartProvisionalNavigation: WKNavigation!)
👉🏻 1번에서 decisionHandler(.allow)
로 허가 났으면 navigation 시작
3. func webView(WKWebView, decidePolicyFor: WKNavigationResponse, decisionHandler: (WKNavigationResponsePolicy) -> Void)
👉🏻 navigation request 에 대한 응답을 받고 난 후, 이어서 새 콘텐츠로 이동을 허용하거나 거부
4. func webView(WKWebView, didCommit: WKNavigation!)
👉🏻 3번에서 decisionHandler(.allow)
로 허가 났으면 메인 프레임 내용 수신 시작
5. func webView(WKWebView, didFinish: WKNavigation!)
👉🏻 navigation 완료