WKWebViewがiOS 8でローカルファイルをロードしない


123

以前のiOS 8ベータ版では、ローカルWebアプリ(バンドル内)をロードするUIWebViewWKWebView、およびの両方で正常に動作し、新しいWKWebViewAPI を使用してWebゲームを移植することもできました。

var url = NSURL(fileURLWithPath:NSBundle.mainBundle().pathForResource("car", ofType:"html"))

webView = WKWebView(frame:view.frame)
webView!.loadRequest(NSURLRequest(URL:url))

view.addSubview(webView)

しかし、ベータ4では、UIWebView何も読み込まれていない、または実行されていないように見えます(まだ機能します)。ログにエラーが見つかりました:

のサンドボックス拡張を作成できませんでした /

私を正しい方向に導くための助けはありますか?ありがとう!


また、viewDidLoadでビュー階層にwebViewを追加して、viewWillAppearでリクエストをロードしてみてください。私のWKWebViewはまだ機能していますが、それが私のやり方です。おそらく、WebViewには、ビューの階層にない場合にリクエストを読み込まないように最適化されていますか?
rvijay007 14

完了(viewDidLoadのview.addViewおよびviewWillAppearのloadRequest)すると、同じ白い画面と同じエラーメッセージが表示されました。
Lim Thye Chean 14

9
これはデバイス上でのみ発生するようです。シミュレータ上では問題なく動作するためです。ちなみに私はObjcを使用しています。
GuidoMB 2014

1
これは、XCode 6-ベータ7の問題であり続けます。私の一時的な解決策は、github.com / swisspol / GCDWebServerを使用してローカルファイルを提供することでした。
GuidoMB 2014

2
誰かがiOS 8.0.1でそれをテストしましたか?
Lim Thye Chean 2014年

回答:


106

彼らはついにバグを解決しました!これでを使用できます-[WKWebView loadFileURL:allowingReadAccessToURL:]。どうやら、修正はWWDC 2015ビデオ504で数秒の価値があったSafari View Controllerの紹介

https://developer.apple.com/videos/wwdc/2015/?id=504

iOS8〜iOS10(Swift 3)の場合

ダンFabulishの答えは、これはWKWebViewのバグであると述べ明らかにいつでもすぐには解決されていないと彼は言ったように回避策がある:)

ここで回避策を示したかったからといって答えています。https://github.com/shazron/WKWebViewFIleUrlTestに示されているIMOコードは、ほとんどの人がおそらく興味を持たない無関係な詳細でいっぱいです。

回避策は、20行のコード、エラー処理、コメントが含まれ、サーバーは必要ありません:)

func fileURLForBuggyWKWebView8(fileURL: URL) throws -> URL {
    // Some safety checks
    if !fileURL.isFileURL {
        throw NSError(
            domain: "BuggyWKWebViewDomain",
            code: 1001,
            userInfo: [NSLocalizedDescriptionKey: NSLocalizedString("URL must be a file URL.", comment:"")])
    }
    try! fileURL.checkResourceIsReachable()

    // Create "/temp/www" directory
    let fm = FileManager.default
    let tmpDirURL = URL(fileURLWithPath: NSTemporaryDirectory()).appendingPathComponent("www")
    try! fm.createDirectory(at: tmpDirURL, withIntermediateDirectories: true, attributes: nil)

    // Now copy given file to the temp directory
    let dstURL = tmpDirURL.appendingPathComponent(fileURL.lastPathComponent)
    let _ = try? fm.removeItem(at: dstURL)
    try! fm.copyItem(at: fileURL, to: dstURL)

    // Files in "/temp/www" load flawlesly :)
    return dstURL
}

そして、次のように使用できます。

override func viewDidLoad() {
    super.viewDidLoad()
    var fileURL = URL(fileURLWithPath: Bundle.main.path(forResource:"file", ofType: "pdf")!)

    if #available(iOS 9.0, *) {
        // iOS9 and above. One year later things are OK.
        webView.loadFileURL(fileURL, allowingReadAccessTo: fileURL)
    } else {
        // iOS8. Things can (sometimes) be workaround-ed
        //   Brave people can do just this
        //   fileURL = try! pathForBuggyWKWebView8(fileURL: fileURL)
        //   webView.load(URLRequest(url: fileURL))
        do {
            fileURL = try fileURLForBuggyWKWebView8(fileURL: fileURL)
            webView.load(URLRequest(url: fileURL))
        } catch let error as NSError {
            print("Error: " + error.debugDescription)
        }
    }
}

2
フォルダ全体、画像などすべてをコピーするためのいくつかの変更。 let orgFolder = NSBundle.mainBundle().resourcePath! + "/www"; var newFilePath = pathForBuggyWKWebView(orgFolder) self.loadingWebView!.loadRequest(NSURLRequest(URL: NSURL.fileURLWithPath(newFilePath!+"/Loading.html")!))
ピワフ2015

1
Piwafのソリューションは少しうまく機能しました。上記の例では、「self.loadingWebView」ではなく「self.webView」である必要があります
Patrick

2
@ nacho4dに感謝します。tldr; / tmpフォルダーソリューションは8.0では機能しませんが、8.0.2では機能します。このソリューションをテストデバイスで機能させるのに苦労し、最終的にはシャズロンのリポジトリを複製して試してみました。それもうまくいきませんでした。私のデバイスがiPhone 6 Plusで8.0(12A366)を実行していたiOSのバージョンでは、shazronのソリューションが機能しないことがわかりました。iOS 8.0.2を実行しているデバイス(iPad Mini)で試してみましたが、正常に動作しました。
jvoll

1
それは_ =試してみるべきですか?let _ = tryの代わりにfm.removeItemAtURL(dstURL)fileMgr.removeItemAtURL(dstURL)
ダチョン

3
シンプルなウェブサイトのみ。ajaxを使用している場合、またはAngularを介してローカルビューをロードしている場合は、「クロスオリジンリクエストはHTTPでのみサポートされている」と想定してください。あなたの唯一のフォールバックは、ローカルネットワーク上に表示されるため、私が好きではないローカルWebサーバーアプローチです。これは投稿でに注意する必要があります。フォークを数時間節約してください。
jenson-button-event 2016

83

WKWebViewは、loadRequest:メソッドからファイル:URLからコンテンツをロードできません。http://www.openradar.me/18039024

を介してコンテンツをロードできますloadHTMLString:が、baseURLがfile:URLの場合は、機能しません。

iOS 9には、あなたが望むことをする新しいAPIがあります [WKWebView loadFileURL:allowingReadAccessToURL:]

iOS 8には回避策があり、shazronがObjective-Cのhttps://github.com/shazron/WKWebViewFIleUrlTestでデモを行い、そこにファイル/tmp/wwwコピーしてロードします

Swiftで作業している場合は、代わりにnachos4dのサンプルを試すことができます。(シャズロンのサンプルよりもはるかに短いので、シャズロンのコードに問題がある場合は、代わりに試してみてください。)


1
回避策の1つ(ここでの説明:devforums.apple.com/message/1051027)は、コンテンツをtmpに移動し、そこからアクセスすることです。私の簡単なテストは、それが機能することを示しているようです...
Mike M

6
8.1では修正されていません。
2014年

1
ファイルを/ tmpに移動すると、うまくいきます。しかし...私の神、これはどのようにテストを通過したのですか?
Greg Maletic、2015年

1
そのサンプルは、デモ用のWAY TOO MUCHコードです。読み込むファイルは内にある必要があり/tmp/www/ます。NSTemporaryDirectory()および NSFileManagerを使用してwwwディレクトリを作成します(デフォルトではそのようなディレクトリはないため)。次に、そこにファイルをコピーして、このファイルを読み取ります:)
nacho4d

2
8.3まだ修正されていません...?
ninjaneer 2015

8

iOS 9で [WKWebView loadFileURL:allowingReadAccessToURL:]を使用する方法の例。

Webフォルダーをプロジェクトに移動する場合は、[フォルダー参照の作成]を選択します

ここに画像の説明を入力してください

次に、次のようなコードを使用します(Swift 2)。

if let filePath = NSBundle.mainBundle().resourcePath?.stringByAppendingString("/WebApp/index.html"){
  let url = NSURL(fileURLWithPath: filePath)
  if let webAppPath = NSBundle.mainBundle().resourcePath?.stringByAppendingString("/WebApp") {
    let webAppUrl = NSURL(fileURLWithPath: webAppPath, isDirectory: true)
    webView.loadFileURL(url, allowingReadAccessToURL: webAppUrl)
  }
}

htmlファイルで次のようなファイルパスを使用します

<link href="bootstrap/css/bootstrap.min.css" rel="stylesheet">

このようではない

<link href="/bootstrap/css/bootstrap.min.css" rel="stylesheet">

xcodeプロジェクトに移動されたディレクトリの例。

ここに画像の説明を入力してください


6

一時的な回避策:GCDWebServerを使用しています提案、しています。

最初に、バンドルされている「www /」フォルダ(「index.html」が含まれています)のパスを見つけます。

NSString *docRoot = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"html" inDirectory:@"www"].stringByDeletingLastPathComponent;

...次に、次のように起動します。

_webServer = [[GCDWebServer alloc] init];
[_webServer addGETHandlerForBasePath:@"/" directoryPath:docRoot indexFilename:@"index.html" cacheAge:3600 allowRangeRequests:YES];
[_webServer startWithPort:port bonjourName:nil];

それを止めるには:

[_webServer stop];
_webServer = nil;

iPad 2でもパフォーマンスは問題なく表示されます。


アプリがバックグラウンドになった後、私は予告にクラッシュをしたので、私は上でそれを停止applicationDidEnterBackground:してapplicationWillTerminate:、私が上でそれを再起動/起動application:didFinishLaunching...applicationWillEnterForeground:


1
「サーバー」を使用すると、WKWebViewを超えるパフォーマンス上のメリットが失われる可能性がありますUIWebView。これが修正されるまで、古いAPIをそのまま使用することもできます。
ray

@rayはシングルページアプリでは使用できません。
EthanB 2014年

アプリの内部ビルドでGCDWebServerも動作するようになりました。そして、あなたのアプリにたくさんのJavaScriptがあるなら、サーバーはそれだけの価値があります。しかし、そこにある の問題私はiOSの9の改善のために願っていますので、今WKWebViewを使用してから私を防止するため、
トム・ハミング

そして、それをWebViewに表示する方法は?GCDWebServerの目的が本当にわかりませんか?
chipbk10

1
「file:// .....」を指すのではなく、「http:// localhost:<port> / ...」を指すようにします。
EthanB 2015年

5
[configuration.preferences setValue:@"TRUE" forKey:@"allowFileAccessFromFileURLs"];

これは私のための問題を解決しましたiOS 8.0 + dev.apple.com

これもうまく機能しているようです...

NSString* FILE_PATH = [[[NSBundle mainBundle] resourcePath]
                       stringByAppendingPathComponent:@"htmlapp/FILE"];
[self.webView
    loadFileURL: [NSURL fileURLWithPath:FILE_PATH]
    allowingReadAccessToURL: [NSURL fileURLWithPath:FILE_PATH]
];

FILEの代わりに、DIRを置くこともできます。
nullqube 2017

これは素晴らしい発見です。なぜそれが高位投票されないのかわかりません。
plivesey 2017年

:この投稿は、(WebKitのソースへのリンクを含む)の詳細情報があるstackoverflow.com/questions/36013645/...
plivesey

configuration.preferences setValueがiOS 9.3でクラッシュする
Vignesh Kumar

iOS 13 SDKを使用していますが、iOS 8との互換性を維持しようとしていますが、allowFileAccessFromFileURLsアプローチがでクラッシュしNSUnknownKeyExceptionます。
arlomedia

4

Dan Fabulichによって言及されたソリューションに加えて、XWebViewは別の回避策です。[WKWebView loadFileURL:allowingReadAccessToURL:]拡張機能を介して実装されます。


1
この問題の他の回避策を検討した後、XWebViewを使用することにしました。XWebViewはSwiftに実装されたフレームワークですが、iOS 8 Objective-Cアプリで使用しても問題ありませんでした。
2015年

4

まだコメントできないので、別の回答として投稿します。

これはnacho4dのソリューションの目的Cバージョンです。これまでに見た中で最高の回避策。

- (NSString *)pathForWKWebViewSandboxBugWithOriginalPath:(NSString *)filePath
{
    NSFileManager *manager = [NSFileManager defaultManager];
    NSString *tempPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"www"];
    NSError *error = nil;

    if (![manager createDirectoryAtPath:tempPath withIntermediateDirectories:YES attributes:nil error:&error]) {
        NSLog(@"Could not create www directory. Error: %@", error);

        return nil;
    }

    NSString *destPath = [tempPath stringByAppendingPathComponent:filePath.lastPathComponent];

    if (![manager fileExistsAtPath:destPath]) {
        if (![manager copyItemAtPath:filePath toPath:destPath error:&error]) {
            NSLog(@"Couldn't copy file to /tmp/www. Error: %@", error);

            return nil;
        }
    }

    return destPath;
}

1
これをiOS 8のシミュレーターで動作させることはできません。/ tmp / wwwに.pngを配置し、htmlでimgタグを使用したい。imgタグsrcには何を使用すればよいですか?
user3246173

まさに私が必要としたものでした。ありがとう!
ティンカーベル、2015

3

次のような大きなHTML文字列の途中でローカル画像を表示しようとしている場合<img src="file://...">、それはまだデバイスに表示されないため、画像ファイルをNSDataにロードし、src文字列を次のように置き換えることで表示できました。データ自体。WKWebViewにロードするHTML文字列の作成に役立つサンプルコード。結果はsrc = ""の引用符の内側にあるものを置き換えます。

迅速:

let pathURL = NSURL.fileURLWithPath(attachmentFilePath)
guard let path = pathURL.path else {
    return // throw error
}
guard let data = NSFileManager.defaultManager().contentsAtPath(path) else {
    return // throw error
}

let image = UIImage.init(data: data)
let base64String = data.base64EncodedStringWithOptions(.Encoding64CharacterLineLength)
result += "data:image/" + attachmentType + "base64," + base64String

var widthHeightString = "\""
if let image = image {
    widthHeightString += " width=\"\(image.size.width)\" height=\"\(image.size.height)\""
}

result += widthHeightString

Objective-C:

NSURL *pathURL = [NSURL fileURLWithPath:attachmentFilePath];
NSString *path = [pathURL path];
NSData *data = [[NSFileManager defaultManager] contentsAtPath:path];

UIImage *image = [UIImage imageWithData:data];
NSString *base64String = [data base64EncodedStringWithOptions:0];
[result appendString:@"data:image/"];
[result appendString:attachmentType]; // jpg, gif etc.
[result appendString:@";base64,"];
[result appendString:base64String];

NSString *widthHeightString = @"\"";
if (image) {
    widthHeightString = [NSString stringWithFormat:@"\" width=\"%f\" height=\"%f\"", image.size.width, image.size.height];
}
[result appendString:widthHeightString];

Swiftバージョンでは、base64の前にセミコロンを追加してください。 result += "data:image/" + attachmentType + ";base64," + base64String
shahil

おかげで、これは私は、デバイス上の一時フォルダに.gifファイルをダウンロードし、その後にそのファイルをロードするので、私のために働いた唯一のものであるWKWebViewとして<img>HTMLstring内。
Zystem氏

1

以下を使用しています。私が取り組んでいる追加の要素がありますが、loadRequestをコメントアウトしてloadHTMLString呼び出しを置き換えているところを確認できます。彼らがバグを修正するまでこれが役立つことを願っています。

import UIKit
import WebKit

class ViewController: UIViewController, WKScriptMessageHandler {

    var theWebView: WKWebView?

    override func viewDidLoad() {
        super.viewDidLoad()

        var path = NSBundle.mainBundle().pathForResource("index", ofType: "html", inDirectory:"www" )
        var url = NSURL(fileURLWithPath:path)
        var request = NSURLRequest(URL:url)
        var theConfiguration = WKWebViewConfiguration()

        theConfiguration.userContentController.addScriptMessageHandler(self, name: "interOp")

        theWebView = WKWebView(frame:self.view.frame, configuration: theConfiguration)

        let text2 = String.stringWithContentsOfFile(path, encoding: NSUTF8StringEncoding, error: nil)

        theWebView!.loadHTMLString(text2, baseURL: nil)

        //theWebView!.loadRequest(request)

        self.view.addSubview(theWebView)


    }

    func appWillEnterForeground() {

    }

    func appDidEnterBackground() {

    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    func userContentController(userContentController: WKUserContentController!, didReceiveScriptMessage message: WKScriptMessage!){
        println("got message: \(message.body)")

    }

}

3
これはHTMLファイルでのみ機能し、他のリソースでは機能しないようです。
Lim Thye Chean 14

1

iOS8でこの問題を回避する必要がある人のために:

ページが複雑でない場合は、ページを単一ページアプリケーションとして作成することを選択できます。

つまり、すべてのリソースをhtmlファイルに埋め込むことです。

方法:1. js / cssファイルのコンテンツをそれぞれhtmlファイルの/タグにコピーします。2.画像​​ファイルをsvgに変換して、適宜置き換えます。3.前と同じように、[webView loadHTMLString:baseURL:]を使用してページをロードします。

svgイメージのスタイル設定とは少し異なりましたが、それほどブロックされるべきではありません。

ページのレンダリングパフォーマンスは少し低下したようですが、iOS8 / 9/10でこのような簡単な回避策を実行することは価値がありました。


0

GCDWebServerの同じ行で、SImpleHttpServer(http://www.andyjamesdavies.com/blog/javascript/simple-http-server-on-mac-os-x-in-seconds)を使用してから、ローカルホストでloadRequestを使用していますURL。このアプローチでは、ライブラリを追加する必要はありませんが、Webサイトファイルはバンドルに含まれないため、配信できません。そのため、これはデバッグの場合により適しています。


0

私はなんとかOS XでPHPのWebサーバーを使用できました。temporary/ wwwディレクトリへのコピーは機能しませんでした。Python SimpleHTTPServerは、MIMEタイプを読みたい、おそらくサンドボックス化の問題について不満を述べました。

これが使用するサーバーphp -Sです:

let portNumber = 8080

let task = NSTask()
task.launchPath = "/usr/bin/php"
task.arguments = ["-S", "localhost:\(portNumber)", "-t", directoryURL.path!]
// Hide the output from the PHP server
task.standardOutput = NSPipe()
task.standardError = NSPipe()

task.launch()

0

@ nacho4dソリューションは優れています。少し変更したいのですが、投稿で変更する方法がわかりません。だからここに置いておきますよ。ありがとう。

wwwフォルダーがある場合、png、css、jsなどの他の多くのファイルがあります。次に、すべてのファイルをtmp / wwwフォルダーにコピーする必要があります。たとえば、次のようなwwwフォルダーがあるとします。 ここに画像の説明を入力してください

それからSwift 2.0では:

override func viewDidLoad() {
    super.viewDidLoad()

    let path = NSBundle.mainBundle().resourcePath! + "/www";
    var fileURL = NSURL(fileURLWithPath: path)
    if #available(iOS 9.0, *) {
        let path = NSBundle.mainBundle().pathForResource("index", ofType: "html", inDirectory: "www")
        let url = NSURL(fileURLWithPath: path!)
        self.webView!.loadRequest(NSURLRequest(URL: url))
    } else {
        do {
            fileURL = try fileURLForBuggyWKWebView8(fileURL)
            let url = NSURL(fileURLWithPath: fileURL.path! + "/index.html")
            self.webView!.loadRequest( NSURLRequest(URL: url))
        } catch let error as NSError {
            print("Error: \(error.debugDescription)")
        }
    }
}

関数fileURLForBuggyWKWebView8は@ nacho4dからコピーされます。

func fileURLForBuggyWKWebView8(fileURL: NSURL) throws -> NSURL {
    // Some safety checks
    var error:NSError? = nil;
    if (!fileURL.fileURL || !fileURL.checkResourceIsReachableAndReturnError(&error)) {
        throw error ?? NSError(
            domain: "BuggyWKWebViewDomain",
            code: 1001,
            userInfo: [NSLocalizedDescriptionKey: NSLocalizedString("URL must be a file URL.", comment:"")])
    }

    // Create "/temp/www" directory
    let fm = NSFileManager.defaultManager()
    let tmpDirURL = NSURL.fileURLWithPath(NSTemporaryDirectory())
    try! fm.createDirectoryAtURL(tmpDirURL, withIntermediateDirectories: true, attributes: nil)

    // Now copy given file to the temp directory
    let dstURL = tmpDirURL.URLByAppendingPathComponent(fileURL.lastPathComponent!)
    let _ = try? fm.removeItemAtURL(dstURL)
    try! fm.copyItemAtURL(fileURL, toURL: dstURL)

    // Files in "/temp/www" load flawlesly :)
    return dstURL
}

-1

使ってみてください

[webView loadHTMLString:htmlFileContent baseURL:baseURL];

まだ機能しているようです。まだ。


よろしくお願いします。
Lim Thye Chean 14

3
これはリソースではなくHTMLファイル自体に対してのみ機能しますよね?
Lim Thye Chean 14

1
残念ながら、はい、HTMLファイルのみがロードされているようです。ローカルファイルをロードするための新しい制限ではなく、単なるバグであることを期待しましょう。私はWebKitのソースでこのバグを見つけようとしています。
Oleksii 2014

1
同じ問題が発生しました-HTMLファイルは読み込まれますが、ローカルファイルシステムにある画像やその他のリソースが読み込まれません。コンソールで、WebKitは「ローカルリソースのロードを許可されていません」というエラーをスローします。私はこれについてバグを報告しました、レーダー番号17835098
mszaro
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.