[iOS] WKNavigationDelegate 메서드 알아보기

웹 뷰 로딩.. 어떤 순서로 실행되고 있던걸까요?

naljin
12 min readNov 14, 2022

들어가기 전에

개-하! 거두절미하고 웹 페이지의 로딩 과정부터 알아봅시다.

  1. 사용자의 링크 클릭 또는 URL 입력과 같은 유저의 Action 으로 시작됩니다. 이를 initial request, 혹은 navigation start 라고 부릅니다.
  2. 이후 서버에서 처리를 마치고 네트워크를 통해 reponse 를 사용자의 브라우저로 다시 전송합니다. 이를 response start 혹은 first byte 라고 부릅니다.
  3. 사용자의 브라우저가 response 를 수신하기 시작하고, Document Object Model 또는 DOM 처리를 시작합니다.
  4. DOM 로드가 완료되는 시점을 DOM ready 라고 하며, DOM을 사용하여 사용자의 브라우저가 페이지를 렌더링하기 시작합니다.

흠.. DOM 이 뭔데 ???? 라고 한다면 저도 잘 모르니까 그냥 여기서는 웹 페이지 로딩을 위해

  1. Action / Request — 사용자의 URL 입력 혹은 링크 클릭 등으로 발생하는 웹 페이지에 대한 Client 의 요청
  2. Response — Server의 응답

의 과정이 일어난다고 간략하게 생각해볼게여

이 내용을 한번 짚고 넘어가야 아래 코드를 볼때

decidePolicyFor navigationAction: WKNavigationAction
decidePolicyFor navigationResponse: WKNavigationResponse
  • 첫번째는 navigationAction 에 대한 policy 를 정한다고? 음.. 처음 요청할때에 대한 policy 를 설정하는거겠군?
  • 두번째는 navigationResponse 에 대한 policy 를 정한다고? 음… 서버에서 응답을 받고나서 policy 를 설정하는거겠군?

이라고 대충 때려맞출 수 있기 때문이져

iOS 에서는 이런 웹 페이지 요청이나, 진행률 추적, 오류 처리 등을 위해 WKNavigationDelegate 메서드를 사용할 수 있는데여, 저는 이 메서드들이 어떤 순서로 호출되나 체크하려고 이 글을 시작한거거든여?

제가 내용을 다 쓰고 추측해 봤을때 일반적인 성공 상황이라면 아래와 같은 순서로 실행될 것 같았슴다. 그리고 실제로도 이 순서로 실행 되었구여 ㅇㅇ

  1. func webView(WKWebView, decidePolicyFor: WKNavigationAction, preferences: WKWebpagePreferences, decisionHandler: (WKNavigationActionPolicy, WKWebpagePreferences) -> Void)
  2. func webView(WKWebView, didStartProvisionalNavigation: WKNavigation!)
  3. func webView(WKWebView, decidePolicyFor: WKNavigationResponse, decisionHandler: (WKNavigationResponsePolicy) -> Void)
  4. func webView(WKWebView, didCommit: WKNavigation!)
  5. 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 로 설정되었다는 뜻인듯).

다운로드 진행률 추적을 시작하려면 이 메서드를 구현합니다.

마무리

그러니까 처음에 제가 추측한 순서는 아래 내용에 기반했던 것입니당

  1. 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 완료

--

--

Responses (1)