-
UIWebView 를 WKWebView 로 이전할 때 반드시 알아야 하는 7가지 주의 사항iOS 2020. 2. 28. 03:17반응형
작년 9월경부터, 앱을 테스트플라이트에 올리면 메일로 이런 메일이 오기 시작했습니다.
Dear Developer,
We identified one or more issues with a recent delivery for your app. Your delivery was successful, but you may wish to correct the following issues in your next delivery:
ITMS-90809: Deprecated API Usage - Apple will stop accepting submissions of apps that use UIWebView APIs starting from December 2020 . See https://developer.apple.com/documentation/uikit/uiwebview for more information.
After you’ve corrected the issues, you can upload a new binary to App Store Connect.
Best regards,
The App Store Team
이후 언제 리젝을 할 것인지 소문만 무성하다가, 12월 23일에 이런 공지가 올라왔습니다.
Web View를 사용하는 앱 업데이트 2019년 12월 23일 귀하의 앱이 더 이상 사용되지 않는 UIWebView API를 사용하여 웹 콘텐츠를 포함하는 경우, 보안 및 안전성 향상을 위하여 WKWebView로 신속히 업데이트할 것을 권고해 드립니다. WKWebView는 앱의 웹 처리를 제한하여 문제가 발생한 웹 콘텐츠가 앱의 나머지 부분에 영향을 미치지 않도록 합니다. 또한 WKWebView는 iOS, macOS 및 Mac Catalyst에서 지원됩니다. App Store는 2020년 4월부터 UIWebView를 사용하는 새로운 앱을 수락하지 않을 예정이며, 2020년 12월부터 UIWebView를 사용하는 앱 업데이트를 수락하지 않을 예정입니다.
https://developer.apple.com/kr/news/?id=12232019
App Store는 2020년 4월부터 UIWebView를 사용하는 새로운 앱을 수락하지 않을 예정이며, 2020년 12월부터 UIWebView를 사용하는 앱 업데이트를 수락하지 않을 예정입니다.
사실.. 신규 앱을 이제와서 UIWebView 로 만들 일은 없겠죠.
문제가 있다면 지금 잘 돌아가긴 하는데, 꽤 업력도 오래 된 UIWebView 일 것입니다.
UIWebView 에서 WKWebView 에서 마이그레이션할 때 모르면 피를 크게 보는 7가지 사항이 있는데,
직접 마이그레이션하면서 얻은 요령을 정리해봤습니다.
1. iOS 10에서 post 방식 시 httpbody 가 전송되지 않음
iOS 10 한정으로 post 방식으로 WKWebView를 전송할 때, httpbody 가 전송되지 않는 버그가 있습니다.
OS 분기를 하여, 프로젝트 내부에 javascript 파일을 선로딩하고, 그 자바스크립트 파일에서 포스트 요청을 보내는 방식으로 우회 공략합니다.
자세한 사항은 아래 글에 포스팅해놓았습니다.
https://sesang06.tistory.com/14
2. URLRequest 와 쿠키 공유가 되지 않음
UIWebView 는 URLRequest 와 UIWebview 가 자동적으로 쿠키가 공유되었습니다. 따라서 Alamofire 에서 쿠키 기반 로그인을 하면, 별다른 처리 없이도 UIWebView에서도 로그인이 유지되었는데요.
WKWebView 는 쿠키를 따로 관리하기 때문에, 수동으로 서로 공유하는 것이 필요합니다.
또한 WKWebView 는 각각의 웹뷰간 쿠키를 공유하지 않기 때문에, 별다른 설정이 없으면 앱을 재시작시 모든 쿠키가 날아갑니다.
그렇게 해도, ajax 요청은 쿠키가 완전히 싱크되지 않아서 자주 불편함을 겪습니다.
이를 우회하기 위해서는 모든 전송에 개입하여 쿠키를 집어넣고, 웹뷰 로딩시마다 쿠키 삽입 자바스크립트를 강제 삽입하고, ajax 요청을 위해 자바스크립트를 집어넣고 하는 처리를 해 주어야 합니다.
이런 식으로 좀 처리를 해보았습니다만, 제 경험상 쿠키 연동 코드는 일부 사용자에게서 예기치 못한 버그를 발생시켰습니다.
핵심 서비스가 쿠키를 전혀 사용하지 않는다면... 굳이 쿠키 싱크 코드를 삽입하지 않을 것을 권장합니다.
어쩔수 없다.. 하면, 아래 오픈 소스를 참고합니다.
https://github.com/Kofktu/WKCookieWebView
3. WKWebview 에서 Post 요청을 intercept 할 수 없음
WKWebview는 아래 델리게이션으로 오가는 요청을 취소하거나 허용할 수 있습니다.
optional func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void)
만약 특정한 통신을 변조하고 싶다..?
이 델리게이션을 응용해서... navigationAction 의 url 의 값을 보고,
똑같은 URLRequest를 만들어서 요청을 하고, 이전의 요청은 cancel 하는 설계를 생각할 수 있겠죠.
문제는 그것이 안 된다는 것입니다. 왜냐하면..
https://forums.developer.apple.com/thread/125753
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: ((WKNavigationActionPolicy) -> Void)) { print(navigationAction.request.httpBody) //nil }
post 요청의 경우. 분명히 httpBody 가 있는데도... 적절하게 가져오질 못하기 때문입니다.
그래서 일체의 post 요청을 클라단에서 변조하는게 불가능하고..
- post 요청에 토큰 하나 섞어 넣기
- post 요청에 유저 에이전트 바꿔 넣기
등이 불가능합니다.
4. 네트워크 통신 에러 띄우기
네트워크 통신이 에러가 나면, 뷰를 지운다든가 하는 액션이 필요할 수 있겠죠.
이 때는 아래 코드를 이용하면 네트워크가 끊기는 에러만을 걸러낼 수 있습니다.
func webView( _ webView: WKWebView, didFailProvisionalNavigation navigation: WKNavigation!, withError error: Error ) { let code = (error as NSError).code guard code != NSURLErrorUnsupportedURL, code != NSURLErrorCancelled else { return } self.handleNetworkError() }
5. 특정한 사이트에서만 특정한 user-agent 띄우기
자사 사이트에서 웹뷰에 User-agent를 변조하는 건 좋은데..
다른 사이트로 옮겨졌을 경우, user-agent 변조가 귀찮은 경우가 있죠.
그 때는 아래 코드를 이용하면 수월합니다.
유투브나 앱스토어, 핸들링도 같이 나와 있으니, 참고하시면 좋을 듯.
func webView( _ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void ) { guard let delegate = self.baseWebViewDelegate else { decisionHandler(.allow) return } guard let url = navigationAction.request.url else { decisionHandler(.allow) return } guard let scheme = url.scheme else { decisionHandler(.allow) return } switch scheme { case "http", "https": self.handleHttpURL( url: url, navigationType: navigationAction.navigationType, isMainFrame: navigationAction.targetFrame?.isMainFrame ?? false ) { policy in decisionHandler(policy) } default: UIApplication.shared.defaultOpen(url) decisionHandler(.allow) } } func handleHttpURL( url: URL, navigationType: WKNavigationType, isMainFrame: Bool, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void ) { guard let host = url.host else { decisionHandler(.allow) return } if host.contains("my.site") { /// 유저 에이전트를 내부 url 에서만 처리하도록 변경합니다. self.customUserAgent = "custom" decisionHandler(.allow) return } self.customUserAgent = nil /// 자기 사이트가 아닌 경우, nil 로 두면 다시 WKWebview 고유의 유저 에이전트로 변경됩니다. if host.contains("youtube.com"), navigationType != .other { self.handleYoutubeURL(url: url) { policy in decisionHandler(policy) } return } if host == "itunes.apple.com" { self.handleAppStoreURL(url: url) { policy in decisionHandler(policy) } return } decisionHandler(.allow) } private func handleAppStoreURL( url: URL, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void ) { guard UIApplication.shared.canOpenURL(url) else { decisionHandler(.allow) return } UIApplication.shared.open(url, options: [:], completionHandler: nil) decisionHandler(.cancel) } private func handleYoutubeURL( url: URL, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void ) { guard UIApplication.shared.canOpenURL(url) else { decisionHandler(.allow) return } UIApplication.shared.open(url, options: [:], completionHandler: nil) decisionHandler(.cancel) }
6. UIWebView 서드 파티 라이브러리 업데이트
FacebookSDK, FirebaseSDK 등등... 굵직굵직한 라이브러리들이 모두 2019년 9월 이전에는 UIWebView 를 포함하고 있었습니다.
저 메시지가 오는 기준은.. 저희 프로젝트에서 UIWebView를 쓰는가도 있지만.. 쓰고 있는 서드 파티 라이브러리에서 쓰고 있느냐도 중요합니다.
저 발표가 있고 나서 2019년 9월경에 대부분의 라이브러리는 일제히 UIWebView 사용을 없애는 신규 패치를 발표했습니다.
그러니 이번 기회에 라이브러리를 몽땅 올려버려야 하는데...
RxSwift 라이브러리는 아직도 UIWebView 를 지우지 않음
https://github.com/ReactiveX/RxSwift/issues/2055
RxSwift 또한 UIWebview 익스텐션을 쓰고 있었고, 저 이슈 이후 UIWebView+Rx 를 지워달라는 많은 요구가 있었습니다.
하지만, 당시 RxSwift 5가 따끈따끈하게 릴리즈된 참이었고.. 저것 하나 때문에 메이저를 올리고 싶지 않다는 답변이 왔습니다.
따라서 현재까지도 RxSwift는 UIWebView 가 포함된 상태입니다.
아마 4월 막바지 쯤에 UIWebview 관련 메소드를 지운 메이저 업데이트를 배포하지 않을까 싶습니다.
7. 별도의 구현이 없으면 '팝업 창' 띄우기가 동작하지 않음
자바스크립트의 window.open() 이라는 메소드가 일부 휴대폰 인증에서 사용됩니다.
UIWebview 에서는 적절하게 핸들링을 해 주었는데, WKWebView 에서는 새로운 웹뷰를 만들어서, 인자로 넘겨주는 것을 구현해주지 않으면 팝업창이 뜨지 않습니다.
저 메소드는 제가 개발했을 당시에 LG의 휴대폰 인증쪽에서 쓰고 있었기 때문에,
핸들링하지 않고 배포했으면 휴대폰 인증에 문제가 생길 수 있는 크리티컬한 상황이었죠.해당 구현은 WKUIDelegate 의
optional func webView(_ webView: WKWebView, createWebViewWith configuration: WKWebViewConfiguration, for navigationAction: WKNavigationAction, windowFeatures: WKWindowFeatures) -> WKWebView?
을 오버라이드하여, 새로운 웹뷰를 할당하고, 뭐 현재 뷰컨트롤러에 addSubview 하든 새로운 뷰컨트롤러에 집어넣고 push 하든 한 후에,
그 웹뷰를 리턴하는 방식으로 구현해주어야 합니다.
그 외의 자잘한 테크닉?
잘 돌아가는 서비스에 UIWebView 를 WKWebview 로 완전히 바꾸는 것은 부담스러운 작업이었습니다.
제가 뭘 빠뜨렸는지 좀 확신이 안 섰기 때문에..
저는 최초 마이그레이션 작업 때 UIWebView 와 WKWebview 를 래핑하는 별도 뷰 프로토콜을 만들었습니다.
뷰 컨트롤러는 해당 프로토콜을 의존성 주입받았습니다.
그리고 서버에서 UIWebView 를 써라 ! 라고 내려주면, 뷰 컨트롤러에 UIWebView 를 의존성 주입하고,
WKWebView 를 써라! 라고 내려주면, 뷰 컨트롤러에 WKWebView 를 의존성 주입받게 구현했습니다.
이런 식으로 하면, 문제가 생겨도, 긴급 배포를 할 필요가 없고 서버 핫픽스로만 끝났습니다.
반응형'iOS' 카테고리의 다른 글
iOS 13에서 리뉴얼된 위치 정보 변경 필독 사항 2 - 위치정보 한 번 허용 (0) 2020.03.07 iOS 13에서 리뉴얼된 위치 정보 변경 필독 사항 1 - 위치정보 지연된 항상 허용 (0) 2020.03.07 UICollectionViewDiffableDataSource, UITableViewDiffableDataSource 로 깔끔한 콜렉션 뷰 데이터 관리하기 (0) 2020.02.26 iOS, Android WebView 와 네이티브간의 유용한 통신 방법 - Javascript Interface, Webkit Messaging (0) 2020.02.20 IOS 에서 머터리얼 디자인의 물결 이펙트 버튼 만들기 (0) 2019.12.26