web-dev-qa-db-ja.com

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

以前のiOS 8ベータ版では、ローカルWebアプリ(バンドル内)をロードすると、UIWebViewWKWebViewの両方で正常に動作し、新しいWKWebView APIを使用して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がまだ機能している)が表示されただけで、何もロードまたは実行されていないように見えます。ログにエラーが表示されました:

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

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

117
Lim Thye Chean

彼らはついにバグを解決しました!これで-[WKWebView loadFileURL:allowingReadAccessToURL:]を使用できます。どうやら WWDC 2015ビデオ504 Introducing Safari View Controller

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

IOS8〜iOS10(Swift 3)の場合

ダンファブリッシュの答え は、これが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)
        }
    }
}
97
nacho4d

WKWebViewはloadRequest:メソッドを介してfile:URLからコンテンツをロードできません。 http://www.openradar.me/18039024

loadHTMLString:経由でコンテンツをロードできますが、baseURLがfile:URLの場合、それでも機能しません。

iOS 9には、必要なことを行う新しいAPIがあります[WKWebView loadFileURL:allowingReadAccessToURL:]

iOS 8の回避策があります、Objective-Cのshazronによってここに示されています https://github.com/shazron/WKWebViewFIleUrlTest to ファイルを/tmp/wwwにコピーし、そこからロードします

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

81
Dan Fabulich

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

Webフォルダーをプロジェクトに移動するときに、「フォルダー参照の作成」を選択します

enter image description here

次に、次のようなコードを使用します(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プロジェクトに移動されるディレクトリの例。

enter image description here

8
Markus T.

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

最初に、バンドルされた「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:で開始/再開します。

5
EthanB
[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:productURL]
    allowingReadAccessToURL: [NSURL fileURLWithPath:FILE_PATH]
];
4
nullqube

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

4
soflare

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

これは、 nacho4dのソリューション のObjective-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;
}
3
Faryar

<img src="file://...">のような大きなHTML文字列の途中でローカル画像を表示しようとしている場合、それはまだデバイスに表示されないので、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

目的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];
2
patrickd105

IOS8でこの問題を回避する必要があるユーザーの場合:

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

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

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

Svg画像のスタイリングとは少し異なりましたが、それほどブロックするべきではありません。

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

1
firebear

私は以下を使用しています。私が取り組んでいる余分なものがいくつかありますが、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)")

    }

}
1
Dustin Nielson

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

Wwwフォルダーがある場合、png、css、jsなど、他の多くのファイルがあります。すべてのファイルをtmp/wwwフォルダーにコピーする必要があります。たとえば、次のようなwwwフォルダがあります。 enter image description here

その後、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
}
0
User9527

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

0
toupper

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
Patrick Smith