How to Control WebView

kuruma-42·2021년 12월 2일
0

swift

목록 보기
11/19
post-thumbnail

WebView Control

  • 내가 담당한 업무 중에 구글 검색내역과 유튜브 시청내역을 스크래핑 해야했다.
  • 유저가 로그인만 하면 뷰를 숨기고 프로그램적으로 사이트를 이동시켜야 했고,
  • 앱내의 유저 로그인 데이터가 사라졌을 경우에 대한 예외 처리 까지 해야했다.
  • 프로그램적으로 이동한 방법을 공유한다.

Where Shoud I Start To Contorl WebView

    func webView(_ webView: WKWebView,
                 didFinish: WKNavigation!) {
                 }
  • 상기의 펑션은 웹뷰의 랜더링이 끝나면 불리는 펑션이다.
  • 상기의 펑션에서 URL을 확인한 후 그 다음 URL을 요청하는 식으로 이동을 진행했다.
   // Move to search history page if url match with login finish page
            if url.absoluteString.contains( GOOGLE_MYACCOUNT_PAGE) {
                // Youtube
                    let myURL = URL(string: YOUTUBE_SEARCH_HISTORY )
                    let myRequest = URLRequest(url: myURL!)
                    webView.load(myRequest)
            }
  • 상기의 코드와 같이 구글 어카운트페이지 URL이라면
  • 다음 목적지인 유튜브 검색 기록 페이지를 로드해줬다.

func webView(_ webView: WKWebView,
          decidePolicyFor navigationAction: WKNavigationAction,
              preferences: WKWebpagePreferences,
                          decisionHandler: @escaping (WKNavigationActionPolicy, WKWebpagePreferences) -> Void){
        return decisionHandler(.allow, preferences)
    }
  • 상기의 펑션은 URL을 로드할 때 권한을 요청할 때 불린다.
  • 따라서 이곳에서 URL에 따라 decisionHandler의 값을 주면 특정 URL의 로드를 막을 수 있다.
  • 이 곳은 아무래도 웹뷰가 랜더링 되는 곳 보다 펑션이 빨리 불린다.
   if let url = webView.url {
            if url.absoluteString.contains( GOOGLE_MYACCOUNT_PAGE) {
                webView.frame(forAlignmentRect: CGRect(x: 0, y: 0, width: 0, height: 0))
                webView.isHidden = true
            }
        }
  • 따라서 상기의 코드처럼 프로그램적으로 UI의 크기를 줄이거나 뷰를 숨기기 좋다
  • 유저들이 사이트가 자동으로 이동하는 모습을 보면 아무래도 거부감을 느끼기 때문이다.
import UIKit
import WebKit
class YoutubeScrapingViewController: UIViewController, WKNavigationDelegate {
    
    var youtubeScrapingModel: YoutubeScrapingModel = YoutubeScrapingModel()
    
    fileprivate let GOOGLE_LOGIN_ADDRESS = "https://accounts.google.com/ServiceLogin"
    fileprivate let GOOGLE_MYACCOUNT_PAGE = "https://myaccount.google.com/"
    fileprivate let GOOGLE_SEARCH_HISTORY = "https://myactivity.google.com/activitycontrols/webandapp?view=item&product=19"
    fileprivate let YOUTUBE_SEARCH_HISTORY = "https://myactivity.google.com/activitycontrols/youtube"
    fileprivate let GOOGLE_LOGIN_DATA_EXIST = "https://myaccount.google.com/?utm_source=sign_in_no_continue"
    
    var webView: WKWebView!
    var loginDetectedAction : () -> Void = {}
    
    override func loadView() {
        let preference = WKPreferences()
        let webConfiguration = WKWebViewConfiguration()
        webConfiguration.preferences = preference
        webView = WKWebView(frame: .zero, configuration: webConfiguration)
        webView.navigationDelegate = self
        view = webView
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let myURL = URL(string: GOOGLE_LOGIN_ADDRESS )
        let myRequest = URLRequest(url: myURL!)
        webView.load(myRequest)
    }
    
    // 권한 요청
    func webView(_ webView: WKWebView,
          decidePolicyFor navigationAction: WKNavigationAction,
              preferences: WKWebpagePreferences,
                          decisionHandler: @escaping (WKNavigationActionPolicy, WKWebpagePreferences) -> Void){
        print( "권한요청" )
        // If login result is success, hide webview when system request auth to access new page.
        if let url = webView.url {
            if url.absoluteString.contains( GOOGLE_MYACCOUNT_PAGE) {
                webView.frame(forAlignmentRect: CGRect(x: 0, y: 0, width: 0, height: 0))
                webView.isHidden = true
            }
        }
        return decisionHandler(.allow, preferences)
    }
    
    // 수신시작
    func webView(_ : WKWebView, didCommit: WKNavigation!){
        print("수신 시작")
    }
    
    // 탐색이 완료 되었음
    func webView(_ webView: WKWebView,
                 didFinish: WKNavigation!) {
        print("탐색이 완료")
        print("WebView URL : \(String(describing: webView.url))")
        
        if let url = webView.url {
            
            // Move to search history page if url match with login finish page
            if url.absoluteString.contains( GOOGLE_MYACCOUNT_PAGE) {
                // Youtube
                    let myURL = URL(string: YOUTUBE_SEARCH_HISTORY )
                    let myRequest = URLRequest(url: myURL!)
                    webView.load(myRequest)
            }
            
            
            //Scarping Youtube history data if url match with search Youtube history page
            if url.absoluteString.contains( YOUTUBE_SEARCH_HISTORY ) {
                
                let scrollPoint = CGPoint(x: 0, y: webView.scrollView.contentSize.height * 300)
                webView.scrollView.setContentOffset(scrollPoint, animated: false)//Set false if you doesn't want animation
                DispatchQueue.main.asyncAfter(deadline: .now() + 4.0) {
                    // @Escaping 예정
                    self.youtubeScrapingModel.insertYoutubeKeywordData(url: url)
                    self.dismiss(animated: false, completion: nil)
                }
            }
            
            // Login Page
            if url.absoluteString.contains( GOOGLE_LOGIN_ADDRESS ) {
                print("LOGIN DETECTED ")
                loginDetectedAction()
            }
            
            // When User have ID
            if url.absoluteString.contains( GOOGLE_LOGIN_DATA_EXIST ) {
                print("Google Login Data Exist")
            }
        }
    }
    
    // 초기 탐색 프로세스 중에 오류가 발생했음 - Error Handler
    func webView(_ webView: WKWebView,
                 didFailProvisionalNavigation: WKNavigation!,
                 withError: Error) {
        print("초기 탐색 프로세스 중에 오류가 발생했음")
    }
    
    // 탐색 중에 오류가 발생했음 - Error Handler
    func webView(_ webView: WKWebView,
                 didFail navigation: WKNavigation!,
                 withError error: Error) {
        print("탐색 중에 오류가 발생했음")
    }
    
}

Counclusion

  • 스크래핑은 웬만하면 서버에서 했으면 좋겠다.
  • 앱에서 직접 스크래핑을 하니 스크래핑 과정을 숨기거나 예외처리를 하거나 동작제어를 하는 게 힘들다.
  • SwiftUI와 UIKit 두 곳에서 진행했는데 숨기는 과정은 SwiftUI가 제약사항이 훨씬 많았다.
  • Safari의 방어로직이 있어서 스크래핑도 원활이 진행되지 않는다.
  • AHIG에 반하는 것들을 가끔 구현하면 내 생물학적 수명이 깎인다.
  • 그래도 안정적인 것은 UIKit다 SwiftUI는 AHIG에 반하는 것들을 구현하려고 하면 버그가 난다.

0개의 댓글