WKWebView가 iOS 8에서 로컬 파일을로드하지 않음


123

이전 iOS 8 베타의 경우 로컬 웹 앱 (번들)을로드하면 UIWebViewWKWebView에서 모두 잘 작동 하며 새로운WKWebView API를 .

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 2014-07-22

완료 (viewDidLoad의 view.addView 및 viewWillAppear의 loadRequest), 동일한 흰색 화면과 동일한 오류 메시지가 나타납니다.
Lim Thye Chean

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")!))
Piwaf

1
Piwaf의 솔루션은하지만 위의 예 주어진 "self.webView"가 아니라 "self.loadingWebView"인 것을, 나를 위해 약간 더 참고 일
패트릭 Fabrizius

2
감사합니다 @ nacho4d. tldr; / tmp 폴더 솔루션은 8.0에서는 작동하지 않지만 8.0.2에서는 작동합니다. 이 솔루션을 내 테스트 장치에서 작동하는 데 문제가 있었고 결국 shazron의 repo를 복제하여 시도해 보았습니다. 그것도 작동하지 않았습니다. shazron의 솔루션이 iPhone 6 Plus에서 8.0 (12A366)을 실행중인 iOS 버전에서 작동하지 않는 것으로 나타났습니다. iOS 8.0.2를 실행하는 기기 (iPad Mini)에서 사용해 보았는데 제대로 작동합니다.
jvoll

1
_ = 시도해야합니까? let _ = try 대신 fm.removeItemAtURL (dstURL)? fileMgr.removeItemAtURL (dstURL)
DàChún

3
단순한 웹 사이트에만 해당됩니다. ajax를 사용하거나 angular를 통해 로컬 뷰를로드하는 경우 "Cross origin requests are only supported for HTTP"를 예상하십시오. 유일한 대안은 로컬 네트워크에서 볼 수 있기 때문에 내가 좋아하지 않는 로컬 웹 서버 접근 방식입니다. 이것은 게시물에 주목할 필요가 있으며, 몇 시간을 절약 할 수 있습니다.
젠슨 버튼 이벤트

83

WKWebView는 해당 loadRequest:메서드 를 통해 file : 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의 샘플을 사용해 볼 수 있습니다. (또한 shazron의 샘플보다 훨씬 짧으므로 shazron의 코드에 문제가있는 경우 대신 시도해보세요.)


1
한 가지 해결 방법 (여기에 언급 : devforums.apple.com/message/1051027 )은 콘텐츠를 tmp로 이동하고 거기에서 액세스하는 것입니다. 내 빠른 테스트에서 작동하는 것으로 나타났습니다 ...
Mike M

6
8.1에서 수정되지 않았습니다.
matt

1
파일을 / tmp로 이동하면 저에게 효과적입니다. 하지만 ... 세상에,이게 어떻게 테스트를 통과 했나요?
Greg Maletic

1
샘플 은 데모 용으로 WAY TOO MUCH 코드입니다. 읽을 파일은 내부에 있어야합니다 /tmp/www/. 사용 NSTemporaryDirectory()NSFileManager생성 www(그런 디렉토리가 기본적으로이 없기 때문에) 디렉토리. 그런 다음 거기에 파일을 복사하고 이제이 파일을 읽으십시오 :)
nacho4d

2
8.3 아직 수정되지 않았습니다 ...?
ninjaneer 2015

8

iOS 9 에서 [WKWebView loadFileURL : allowingReadAccessToURL :]을 사용하는 방법의 예입니다 .

웹 폴더를 프로젝트로 이동할 때 "폴더 참조 생성"을 선택하십시오.

여기에 이미지 설명 입력

그런 다음 다음과 같은 코드를 사용하십시오 (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

임시 해결 방법 : GuidoMB 에서 제안한대로 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가 작동하도록했습니다. 앱에 자바 스크립트가 많으면 서버는 그만한 가치가 있습니다. 하지만 지금 당장 WKWebView를 사용하지 못하게 하는 다른 문제 가 있으므로 iOS 9에서 개선되기를 바랍니다.
Tom Hamming

WebView에 표시하는 방법은 무엇입니까? GCDWebServer가 무엇인지 정말 이해가 안 되나요?
chipbk10 2015 년

1
webview를 "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

이것은 훌륭한 발견입니다. 왜 더 높은 투표가되지 않았는지 모르겠습니다.
plivesey

이 게시물에는 더 많은 정보가 있습니다 (웹킷 소스에 대한 링크 포함) : stackoverflow.com/questions/36013645/…
plivesey

configuration.preferences의 setValue는 아이폰 OS 9.3에 충돌을 줄 것이다
인 Vignesh 쿠마에게

iOS 13 SDK를 사용하고 있지만 iOS 8과의 호환성을 유지하려고하는데 allowFileAccessFromFileURLs접근 방식이 NSUnknownKeyException.
arlomedia

4

Dan Fabulich가 언급 한 솔루션 외에도 XWebView 는 또 다른 해결 방법입니다. [WKWebView loadFileURL : allowingReadAccessToURL :]확장을 통해 구현됩니다 .


1
이 문제에 대한 다른 해결 방법을 살펴본 후 XWebView를 사용하기로 결정했습니다. XWebView는 Swift로 구현 된 프레임 워크이지만 iOS 8 Objective-C 앱에서 사용하는 데 문제가 없었습니다.
chmaynard

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-08-04

정확히 내가 필요로하는 것이었다. 감사!
Tinkerbell

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

목표 -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];

빠른 버전에서는 base64 앞에 세미콜론을 추가하십시오. result += "data:image/" + attachmentType + ";base64," + base64String
shahil

덕분에,이 내가 장치의 임시 폴더에 .gif 파일을 다운로드 한 다음에 그 파일을로드하기 때문에, 나를 위해 일한 유일한 일이 WKWebViewint로서 <img>HTMLstring 내.
Mr. 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 2014-07-25

1

iOS8에서이 문제를 해결해야하는 사람 :

페이지가 복잡하지 않은 경우 페이지를 단일 페이지 응용 프로그램으로 만들도록 선택할 수 있습니다.

즉, 모든 리소스를 html 파일에 포함합니다.

해야 할 일 : 1. js / css 파일의 내용을 각각 html 파일의 / 태그에 복사합니다. 2. 이미지 파일을 svg로 변환하여 그에 따라 바꿉니다. 3. 예를 들어 [webView loadHTMLString : baseURL :]을 사용하여 이전과 같이 페이지를로드합니다.

svg 이미지를 스타일링하는 것과는 조금 달랐지만 그렇게 많이 방해해서는 안됩니다.

페이지 렌더링 성능이 약간 저하 된 것처럼 보였지만 iOS8 / 9 / 10에서 이러한 간단한 해결 방법을 사용할 수있는 가치가있었습니다.



0

OS X에서 PHP의 웹 서버를 사용했습니다. 임시 / 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 2014-07-25

3
이것은 리소스가 아닌 HTML 파일 자체에서만 작동합니다.
Lim Thye Chean 2014-07-25

1
아쉽게도 예, HTML 파일 만로드되는 것 같습니다. 로컬 파일을로드하는 새로운 제한이 아니라 버그 일 뿐이기를 바랍니다. WebKit 소스에서이 버그를 찾으려고합니다.
Oleksii

1
동일한 문제가 발생했습니다. HTML 파일은로드되지만 로컬 파일 시스템에있는 이미지 및 기타 리소스는로드되지 않습니다. 콘솔에서 WebKit은 "로컬 리소스를로드 할 수 없음"오류를 표시합니다. 나는 이것에 대한 버그를 제출했다, radar # 17835098
mszaro
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.