WKWebView ne charge pas les fichiers locaux sous iOS 8

pour les versions précédentes de iOS 8 betas, chargez une application web locale (en paquet) et cela fonctionne très bien pour UIWebView et WKWebView , et j'ai même porté un jeu web en utilisant la nouvelle API WKWebView .

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

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

view.addSubview(webView)

mais en beta 4, je viens d'avoir un écran blanc ( UIWebView fonctionne toujours), On dirait que rien n'est chargé ou exécuté. J'ai vu une erreur dans le journal:

ne pouvait pas créer une extension de bac à sable pour /

Any aide pour me guider dans la bonne direction? Merci!

108
demandé sur Peter Lapisu 2014-07-22 12:50:26

14 réponses

ils ont enfin résolu le bug! Maintenant, nous pouvons utiliser -[WKWebView loadFileURL:allowingReadAccessToURL:] . Apparemment, le correctif valait quelques secondes dans WWDC 2015 video 504 Introducing Safari View Controller

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

pour iOS8 ~ iOS10 (Swift 3)

Comme Dan Fabulish la réponse de etats c'est un bug de WKWebView qui, apparemment, n'est pas résolu de sitôt et comme il a dit il y a un work-around:)

je réponds juste parce que je voulais montrer le travail-ici. Code OMI indiqué dans https://github.com/shazron/WKWebViewFIleUrlTest est plein de détails sans rapport que la plupart des gens ne sont probablement pas intéressés.

Le travail est de 20 lignes de code, la gestion des erreurs et des commentaires, pas besoin d'un serveur :)

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
}

Et peut être utilisé as:

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)
        }
    }
}
91
répondu nacho4d 2017-05-23 12:26:38

WKWebView ne peut pas charger le contenu à partir des URLs file: via sa méthode loadRequest: . http://www.openradar.me/18039024

vous pouvez charger du contenu via loadHTMLString: , mais si votre baseURL est un fichier: URL, alors il ne fonctionnera toujours pas.

iOS 9 a une nouvelle API qui fera ce que vous voulez, [WKWebView loadFileURL:allowingReadAccessToURL:] .

il y a une solution pour iOS 8 , démontré par shazron dans L'Objectif-C ici https://github.com/shazron/WKWebViewFIleUrlTest à copiez les fichiers dans /tmp/www et chargez-les à partir de là .

si vous travaillez dans Swift, vous pouvez essayer l'échantillon de nachos4d à la place. (C'est aussi beaucoup plus court que l'échantillon de shazron, donc si vous avez des problèmes avec le code de shazron, essayez plutôt.)

79
répondu Dan Fabulich 2017-05-23 12:26:38

exemple d'utilisation [WKWebView loadFileURL:allowingReadAccessToURL:] sur iOS 9 .

lorsque vous déplacez le dossier web vers un projet, sélectionnez "Créer des références de dossier"

enter image description here

alors utilisez le code qui est quelque chose comme ceci(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)
  }
}

dans le fichier html utilisez des chemins de fichiers comme celui-ci

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

pas comme ceci

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

Un exemple de répertoire qui est déplacé d'un projet xcode.

enter image description here

7
répondu Markus T. 2016-03-07 19:07:47

solution temporaire: j'utilise GCDWebServer , comme suggéré par GuidoMB.

je trouve d'abord le chemin de mon dossier" www/ "(qui contient un " index.html"):

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

... alors démarrez comme ceci:

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

Pour l'arrêter:

[_webServer stop];
_webServer = nil;

la Performance apparaît très bien, même sur un iPad 2.


je j'ai remarqué un crash après que l'application soit passée en arrière plan, donc je l'arrête sur applicationDidEnterBackground: et applicationWillTerminate: ; je la démarre/redémarre sur application:didFinishLaunching... et applicationWillEnterForeground: .

5
répondu EthanB 2014-10-20 22:13:20

outre les solutions mentionnées par Dan Fabulich, XWebView est une autre solution. [WKWebView loadFileURL:allowingReadAccessToURL:] is implemented through extension .

4
répondu soflare 2015-04-28 17:10:58
[configuration.preferences setValue:@"TRUE" forKey:@"allowFileAccessFromFileURLs"];

Cela a résolu le problème pour moi iOS 8.0+ dev.apple.com

aussi cela semble fonctionner très bien aussi...

NSString* FILE_PATH = [[[NSBundle mainBundle] resourcePath]
                       stringByAppendingPathComponent:@"htmlapp/FILE"];
[self.webView
    loadFileURL: [NSURL fileURLWithPath:productURL]
    allowingReadAccessToURL: [NSURL fileURLWithPath:FILE_PATH]
];
4
répondu nullqube 2017-08-06 21:38:57

Je ne peux pas encore commenter, donc je poste ceci comme une réponse séparée.

il s'agit d'une version objective-c de solution de nacho4d . Le meilleur contournement que j'ai vu jusqu'à présent.

- (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
répondu Faryar 2017-05-23 10:31:37

j'utilise ce qui suit. Il y a des trucs supplémentaires sur lesquels je travaille mais vous pouvez voir où j'ai commenté la loadRequest et je remplace l'appel loadHTMLString. J'espère que ça aidera jusqu'à ce qu'ils réparent le bug.

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
répondu Dustin Nielson 2014-07-23 23:17:40

Pour qui doit contourner ce problème sous iOS8:

si votre page n'est pas compliquée, vous pouvez choisir de faire de la page une Application D'une seule Page.

En d'autres termes, pour intégrer toutes les ressources dans le fichier html.

à faire: 1. Copiez le contenu de votre fichier js/css dans / tags dans le fichier html respectivement; 2. convertir vos fichiers images en svg pour remplacer le en conséquence. 3. chargez la page comme avant, en utilisant [webView liaison de charge: baseURL:], par exemple

c'était un peu différent du style d'une image svg, mais cela ne devrait pas vous bloquer autant.

il semblait que la performance du rendu de la page avait un peu diminué, mais il était digne d'avoir une solution aussi simple sous iOS8/9/10.

1
répondu firebear 2016-12-19 15:32:41

dans la même ligne de GCDWebServer, j'utilise SImpleHttpServer ( http://www.andyjamesdavies.com/blog/javascript/simple-http-server-on-mac-os-x-in-seconds ) et ensuite loadRequest avec l'url localhost. Avec cette approche, vous n'avez pas à ajouter de bibliothèque, mais les fichiers du site web ne seront pas dans le paquet de sorte qu'il ne sera pas livrable. Pour cette raison, ce serait plus approprié pour les cas de débogage.

0
répondu toupper 2014-12-12 14:23:22

j'ai réussi à utiliser le serveur web de PHP sur OS X. Copier dans le répertoire temporaire/www n'a pas fonctionné pour moi. Le Python SimpleHTTPServer s'est plaint de vouloir lire des types MIME, probablement un problème de bac à sable.

voici un serveur utilisant 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
répondu Patrick Smith 2015-03-17 10:13:42

@nacho4d solution est bonne. Je veux changer un peu mais je ne sais pas comment le changer dans votre post. Alors je l'ai mis ici, j'espère que ça ne vous dérange pas. grâce.

dans le cas où vous avez un dossier www il y a beaucoup d'autres dossiers tels que png, css, js etc. Ensuite, vous devez copier tous les fichiers tmp/dossier www. par exemple, vous avez un répertoire " www " comme ceci: enter image description here

puis dans 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)")
        }
    }
}

la fonction fileURLForBuggyWKWebView8 est copiée de @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
répondu User9527 2016-02-20 20:14:07

dans le cas où vous essayez d'afficher une image locale au milieu d'une chaîne HTML plus grande comme: <img src="file://..."> , il n'apparaît toujours pas sur le périphérique donc j'ai chargé le fichier image dans NSData et j'ai été en mesure de l'afficher en remplaçant la chaîne src avec les données elles-mêmes. Exemple de code pour aider à construire la chaîne HTML à charger dans WKWebView, où le résultat est ce qui remplacera ce qui est à l'intérieur des guillemets De src="":

Swift:

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

Objectif-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];
0
répondu patrickd105 2016-05-26 21:33:14

essayer d'utiliser

[webView loadHTMLString:htmlFileContent baseURL:baseURL];

semble fonctionner. Encore.

-1
répondu Oleksii 2014-12-29 09:18:55