ios - before - wkwebview navigation swift
WKWebView no carga archivos locales en iOS 8 (14)
¡Finalmente resolvieron el error! Ahora podemos usar -[WKWebView loadFileURL:allowingReadAccessToURL:]
. Aparentemente la solución valió algunos segundos en el video WWDC 2015 504 Presentamos Safari View Controller
Para iOS8 ~ iOS10 (Swift 3)
Como la respuesta de Dan Fabulish dice que esto es un error de WKWebView que aparentemente no se está solucionando en el corto plazo y como él dijo, hay una solución :)
Estoy respondiendo solo porque quería mostrar el trabajo aquí. El código de IMO que se muestra en https://github.com/shazron/WKWebViewFIleUrlTest está lleno de detalles no relacionados que la mayoría de las personas probablemente no estén interesadas.
El work-around es de 20 líneas de código, manejo de errores y comentarios incluidos, sin necesidad de un servidor :)
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
}
Y puede usarse como:
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)
}
}
}
Para versiones anteriores de iOS 8, cargue una aplicación web local (en Bundle) y funciona bien para UIWebView
y WKWebView
, e incluso WKWebView
un juego web utilizando la nueva API de WKWebView
.
var url = NSURL(fileURLWithPath:NSBundle.mainBundle().pathForResource("car", ofType:"html"))
webView = WKWebView(frame:view.frame)
webView!.loadRequest(NSURLRequest(URL:url))
view.addSubview(webView)
Pero en la versión beta 4, acabo de obtener una pantalla blanca en blanco ( UIWebView
aún funciona), parece que no se cargó ni ejecutó nada. Vi un error en el registro:
No se pudo crear una extensión de sandbox para /
¿Alguna ayuda para guiarme en la dirección correcta? ¡Gracias!
Además de las soluciones mencionadas por Dan Fabulich, XWebView es otra solución alternativa. [WKWebView loadFileURL: allowReadAccessToURL:] se implementa a través de la extensión .
En el caso de que intente mostrar una imagen local en el medio de una cadena HTML más grande como: <img src="file://...">
, todavía no aparece en el dispositivo, así que cargué el archivo de imagen en NSData y fue capaz de mostrarlo reemplazando la cadena src con los datos en sí. Código de ejemplo para ayudar a compilar la cadena HTML para cargar en WKWebView, donde el resultado es lo que reemplazará lo que está dentro de las comillas de src = "":
Rápido:
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 objetivo:
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];
En la misma línea de GCDWebServer, estoy usando SImpleHttpServer ( http://www.andyjamesdavies.com/blog/javascript/simple-http-server-on-mac-os-x-in-seconds ) y luego loadRequest con el localhost url. Con este enfoque, no tiene que agregar ninguna biblioteca, pero los archivos del sitio web no estarán en el paquete, por lo que no será entregable. Por eso, esto sería más apropiado para los casos de depuración.
Estoy usando el de abajo Tengo algunas cosas adicionales en las que estoy trabajando pero puedes ver dónde he comentado loadRequest y estoy sustituyendo loadHTMLString. Espero que esto ayude hasta que arreglen el error.
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)")
}
}
Intenta usar
[webView loadHTMLString:htmlFileContent baseURL:baseURL];
Parece que todavía está funcionando. Todavía.
La solución de @nacho4d es buena. Quiero cambiarlo un poco, pero no sé cómo cambiarlo en tu publicación. Así que lo puse aquí, espero que no te importe. Gracias.
En caso de que tenga una carpeta www, hay muchos otros archivos, como png, css, js, etc. Luego, debe copiar todos los archivos a la carpeta tmp / www. por ejemplo, tiene una carpeta www como esta:
luego en 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 función fileURLForBuggyWKWebView8 se copia 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
}
Logré usar el servidor web de PHP en OS X. Copiar al directorio temporal / www no funcionó para mí. Python SimpleHTTPServer se quejó de querer leer tipos MIME, probablemente un problema de sandboxing.
Aquí hay un servidor que usa 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()
No puedo comentar todavía, así que estoy publicando esto como una respuesta separada.
Esta es una versión objetivo-c de la solución de nacho4d . La mejor solución que he visto hasta ahora.
- (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;
}
Para quién debe solucionar este problema en iOS8:
Si su página no es complicada, puede optar por hacer que la página sea una aplicación de una sola página.
En otras palabras, para insertar todos los recursos en el archivo html.
Para hacerlo: 1. copie el contenido de su archivo js / css en / etiquetas en el archivo html, respectivamente; 2. convierte tus archivos de imagen en svg para reemplazar el correspondiente. 3. cargue la página como antes, usando [webView loadHTMLString: baseURL:], por ejemplo
Era un poco diferente al estilo de una imagen svg, pero no debería bloquearlo demasiado.
Parecía que el rendimiento del rendimiento de la página disminuía un poco, pero era digno de tener una solución tan simple que funcionó en iOS8 / 9/10.
Un ejemplo de cómo usar [WKWebView loadFileURL: allowReadAccessToURL:] en iOS 9 .
Cuando mueva la carpeta web a un proyecto, seleccione "Crear referencias de carpeta"
Luego use un código que sea similar a esto (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)
}
}
En el archivo html use filepaths como este
<link href="bootstrap/css/bootstrap.min.css" rel="stylesheet">
así no
<link href="/bootstrap/css/bootstrap.min.css" rel="stylesheet">
Un ejemplo de directorio que se mueve a un proyecto xcode.
WKWebView no puede cargar contenido de archivo: URL a través de su método loadRequest:
. http://www.openradar.me/18039024
Puede cargar contenido a través de loadHTMLString:
, pero si su baseURL es un archivo: URL, entonces todavía no funcionará.
iOS 9 tiene una nueva API que hará lo que quieras, [WKWebView loadFileURL:allowingReadAccessToURL:]
.
Hay una solución para iOS 8 , demostrada por shazron en Objective-C aquí https://github.com/shazron/WKWebViewFIleUrlTest para copiar archivos en /tmp/www
y cargarlos desde allí .
Si trabajas en Swift, puedes probar la muestra de nachos4d . (También es mucho más corto que la muestra de Shazron, por lo que si tienes problemas con el código de Shazron, intentalo).
github.com/swisspol/GCDWebServer temporal: estoy usando github.com/swisspol/GCDWebServer , como lo sugiere GuidoMB.
Primero encuentro la ruta de mi carpeta incluida "www /" (que contiene un "index.html"):
NSString *docRoot = [[NSBundle mainBundle] pathForResource:@"index" ofType:@"html" inDirectory:@"www"].stringByDeletingLastPathComponent;
... entonces comienza de esta manera:
_webServer = [[GCDWebServer alloc] init];
[_webServer addGETHandlerForBasePath:@"/" directoryPath:docRoot indexFilename:@"index.html" cacheAge:3600 allowRangeRequests:YES];
[_webServer startWithPort:port bonjourName:nil];
Para detenerlo:
[_webServer stop];
_webServer = nil;
El rendimiento parece estar bien, incluso en un iPad 2.
Noté un bloqueo después de que la aplicación pasa a segundo plano, así que lo detengo en applicationDidEnterBackground:
y applicationWillTerminate:
:; Comienzo / reinicio en la application:didFinishLaunching...
y applicationWillEnterForeground:
[configuration.preferences setValue:@"TRUE" forKey:@"allowFileAccessFromFileURLs"];
Esto solucionó el problema para mí iOS 8.0+ dev.apple.com
también esto parece funcionar bien también ...
NSString* FILE_PATH = [[[NSBundle mainBundle] resourcePath]
stringByAppendingPathComponent:@"htmlapp/FILE"];
[self.webView
loadFileURL: [NSURL fileURLWithPath:productURL]
allowingReadAccessToURL: [NSURL fileURLWithPath:FILE_PATH]
];