web-dev-qa-db-ja.com

WKWebViewを使用してファイルを取得するにはどうすればよいですか?

ダウンロードしたいファイル(CSV)があります。 Webサイトのログイン画面の背後にあります。ユーザーがログインできるようにWKWebViewを表示し、ログイン後にアプリにファイルをダウンロードさせたいと思いました。

ユーザーがWebサイトにログインした後にWKWebViewの外部にファイルをダウンロードしようとしましたが、目的のファイルの代わりにログインフォームでhtmlドキュメントをダウンロードするため、セッションデータはサンドボックス化されているようです。

WKUserScriptWKUserContentControllerオブジェクトに追加しようとしましたが、HTML以外のファイルがロードされたときにスクリプトが実行されません。

ユーザーがWKWebView経由でログインできるようにして、このファイルにアクセスする方法はありますか?

35
Devin McKaskle

現在、WKWebViewインスタンスは、デフォルトのネットワークストレージ(NSURLCache、NSHTTPCookieStorage、NSCredentialStorage)、およびネットワーク要求のカスタマイズに使用できる標準のネットワーククラス(NSURLProtocolなど)を無視します。

したがって、WKWebViewインスタンスのCookieはアプリの標準のCookieストレージに保存されないため、標準のCookieストレージのみを使用するNSURLSession/NSURLConnectionはWKWebViewのCookieにアクセスできません(これはおそらくあなたが抱えている問題です: 「ログインステータス」はほとんどの場合Cookieに保存されますが、NSURLSession/NSURLConnectionはCookieを認識しません。

キャッシュ、クレデンシャルなどの場合も同様です。WKWebViewには独自のプライベートストレージがあり、したがって標準のCocoaネットワーククラスではうまく機能しません。

NSURLProtocolもWKWebViewでサポートされていないため、リクエストをカスタマイズ(独自のカスタムHTTPヘッダーの追加、既存のヘッダーの変更など)、独自のカスタムURLスキームの使用などもできません。

したがって、WKWebViewはCocoaの標準のネットワークAPIに参加していないため、多くのアプリにとってはほとんど役に立たないのです。

私はまだAppleがiOS 8がリリースされるまでこれを変更することを望んでいます。

バグレポートをAppleに送信します。したがって、Appleはこれらの問題が深刻であり、修正が必要であることを認識します。

95
Alex

リクエストから返されるレスポンスCookieを確認しましたか。このようなデリゲートメソッドを使用できます。

- (void)webView:(WKWebView *)webView decidePolicyForNavigationResponse:(WKNavigationResponse *)navigationResponse decisionHandler:(void (^)(WKNavigationResponsePolicy))decisionHandler{
    NSHTTPURLResponse *response = (NSHTTPURLResponse *)navigationResponse.response;
    NSArray *cookies =[NSHTTPCookie cookiesWithResponseHeaderFields:[response allHeaderFields] forURL:response.URL];

    for (NSHTTPCookie *cookie in cookies) {
        [[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookie];
    }

    decisionHandler(WKNavigationResponsePolicyAllow);
}
15
Nick Anger

Cookieを取得できます Javascript経由

次に、取得したCookieを使用して、ファイルを手動でダウンロードできます。

webView.evaluateJavaScript("(function() { return document.cookie })()", completionHandler: { (response, error) -> Void in
  let cookie = response as! String
  let request = NSMutableURLRequest(URL: docURL)
  request.setValue(cookie, forHTTPHeaderField:  "Cookie")

  NSURLSession.sharedSession().dataTaskWithRequest(request, completionHandler: { (data, response, error) in
    // Your CSV file will be in the response object
  }).resume()
})
1

あなたがやろうとしていることに似た何かを達成しました:

  • webビューを使用して、すでに実行しているようにログインします
  • webビューでnavigationDelegateを設定します
  • デリゲートに-webView:decidePolicyForNavigationResponse:decisionHandlerを実装します
  • (ユーザーがログインした後)そのデリゲートメソッドが呼び出されると、navigationResponse.responseNSHTTPURLResponse*にキャスト)を調べて、セッション情報を含むSet-Cookieヘッダーを検索できます。認証済みセッションが必要です。
  • その後、リクエストでCookieヘッダーをレスポンスで指定されたCookieで手動で指定することにより、CSVをダウンロードできます。

デリゲートメソッドは、「メインフレーム」リクエストに対してのみ呼び出されることに注意してください。つまり、AJAXリクエストまたは内部フレームはそれをトリガーしません。これが機能するには、ページ全体を更新する必要があります。

AJAXリクエスト、iframeなどの動作をトリガーする必要がある場合は、JavaScriptを挿入する必要があります。

1

UserContentController didReceiveScriptMessageからリクエストをしようとすると、セッションCookieがないようです。

ただし、decidePolicyForNavigationActionから呼び出されると、次のコードはログインしていることを検出します。

     let urlPath: String = "<api endpoint at url at which you are logged in>"

    let url = NSURL(string: urlPath)
    let session = NSURLSession.sharedSession()
    let task = session.dataTaskWithURL(url!, completionHandler: {data, response, error -> Void in

        let string1 = NSString(data: data, encoding: NSUTF8StringEncoding)
        println(string1)
        println(data)
        if(error != nil) {
            println("Error sending token to server")
            // Print any error to the console
            println(error.localizedDescription)
        }
        var err: NSError?

    })

    task.resume()
0
moonhead