Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/wix/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
用于脱机查看的Swift iOS缓存WKWebView内容_Ios_Swift_Caching_Offline_Wkwebview - Fatal编程技术网

用于脱机查看的Swift iOS缓存WKWebView内容

用于脱机查看的Swift iOS缓存WKWebView内容,ios,swift,caching,offline,wkwebview,Ios,Swift,Caching,Offline,Wkwebview,我们正在尝试将WKWebView的内容(HTML)保存在持久性存储(NSUserDefaults、CoreData或磁盘文件)中。当用户在没有互联网连接的情况下重新进入应用程序时,可以看到相同的内容。WKWebView不像UIWebView那样使用NSURL协议(参见文章) 虽然我看到过“WKWebView中未启用脱机应用程序缓存”(Apple dev论坛)的帖子,但我知道存在一种解决方案 我知道了两种可能性,但我无法让它们发挥作用: 1) 如果我在Safari for Mac中打开一个网站并选

我们正在尝试将WKWebView的内容(HTML)保存在持久性存储(NSUserDefaults、CoreData或磁盘文件)中。当用户在没有互联网连接的情况下重新进入应用程序时,可以看到相同的内容。WKWebView不像UIWebView那样使用NSURL协议(参见文章)

虽然我看到过“WKWebView中未启用脱机应用程序缓存”(Apple dev论坛)的帖子,但我知道存在一种解决方案

我知道了两种可能性,但我无法让它们发挥作用:

1) 如果我在Safari for Mac中打开一个网站并选择文件>>另存为,它将在下图中显示以下选项。对于Mac应用程序,存在[[[webView大型机]数据源]webArchive],但在UIWebView或WKWebView上没有此类API。但是如果我在WKWebView的Xcode中加载了一个.webarchive文件(就像我从Mac Safari中获得的文件),那么如果没有internet连接,内容就会正确显示(html、外部图像、视频预览)。.webarchive文件实际上是一个plist(属性列表)。我尝试使用mac框架创建.webarchive文件,但它不完整

2) 我在webView:didFinishNavigation中获得了HTML,但它不保存外部图像、css和javascript

 func webView(webView: WKWebView, didFinishNavigation navigation: WKNavigation!) {

    webView.evaluateJavaScript("document.documentElement.outerHTML.toString()",
        completionHandler: { (html: AnyObject?, error: NSError?) in
            print(html)
    })
}
我们挣扎了一个多星期,这是我们的一个主要特点。 任何想法都值得赞赏


谢谢大家!

我建议调查使用App Cache的可行性,从iOS 10开始,
WKWebView
现在支持App Cache:

我不确定您是否只想缓存已经访问过的页面,或者是否有特定的请求要缓存。我目前正在研究后者。所以我要说的是。我的URL是根据api请求动态生成的。从这个响应中,我使用非图像URL设置
requestpath
,然后对每个URL发出请求并缓存响应。对于图像URL,我使用翠鸟库来缓存图像。我已经在AppDelegate中设置了共享缓存
urlCache=urlCache.shared
。并分配了我需要的内存:
urlCache=urlCache(memoryCapacity:,diskCapacity:,diskPath:“urlCache”)
然后只需调用
startRequest(:)
中的每个URL。(如果不需要,可以在后台完成)

我正在使用Alamofire处理网络请求,并使用配置了相应头的cachingSessionManager。因此,在我的WebService课程中,我有:

typealias URLResponseHandler = ((DataResponse<Data>) -> Void)

static let cachingSessionManager: SessionManager = {

        let configuration = URLSessionConfiguration.default
        configuration.httpAdditionalHeaders = cachingHeader
        configuration.urlCache = urlCache

        let cachingSessionManager = SessionManager(configuration: configuration)
        return cachingSessionManager
    }()

    private static let cachingHeader: HTTPHeaders = {

        var headers = SessionManager.defaultHTTPHeaders
        headers["Accept"] = "text/html" 
        headers["Authorization"] = <token>
        return headers
    }()

@discardableResult
static func sendCachingRequest(for request: URLRequest, completion: @escaping URLResponseHandler) -> DataRequest {

    let completionHandler: (DataResponse<Data>) -> Void = { response in
        completion(response)
    }

    let dataRequest = cachingSessionManager.request(request).responseData(completionHandler: completionHandler)

    return dataRequest
}
当然,如果出现加载错误,您也需要处理它

func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {

    DDLogError("Request failed with error \(error.localizedDescription)")

    if let reach = reach, !reach.isReachable() {
        showNetworkUnavailableAlert()
        handlingCacheRequest = true
    }
    webView.stopLoading()
    loadingIndicator.stopAnimating()
}
我希望这有帮助。我唯一还想弄清楚的是,图像资产没有离线加载。我想我需要对这些图片提出单独的请求,并在本地保留对它们的引用。只是一个想法,但我会更新这一点,当我已经解决了

使用以下代码脱机加载图像进行更新 我使用Kanna库解析缓存响应中的html字符串,找到div的
style=background image:
属性中嵌入的url,使用regex获取url(这也是翠鸟缓存图像的键),获取缓存图像,然后修改css以使用图像数据(基于本文:),然后用修改后的html加载webview。(呸!)这是一个相当复杂的过程,也许还有更简单的方法。。但是我没有找到它。我的代码将更新以反映所有这些更改。祝你好运

func modify(_ html: String, completedModification: @escaping (String) -> Void) {

    guard let doc = HTML(html: html, encoding: .utf8) else {
        DDLogInfo("Couldn't parse HTML with Kannan")
        completedModification(html)
        return
    }

    var imageDiv = doc.at_css("div[class='<your_div_class_name>']")

    guard let currentStyle = imageDiv?["style"],
        let currentURL = urlMatch(in: currentStyle)?.first else {

            DDLogDebug("Failed to find URL in div")
            completedModification(html)
            return
    }

    DispatchQueue.main.async {

        self.replaceURLWithCachedImageData(inHTML: html, withURL: currentURL, completedCallback: { modifiedHTML in

            completedModification(modifiedHTML)
        })
    }
}

func urlMatch(in text: String) -> [String]? {

    do {
        let urlPattern = "\\((.*?)\\)"
        let regex = try NSRegularExpression(pattern: urlPattern, options: .caseInsensitive)
        let nsString = NSString(string: text)
        let results = regex.matches(in: text, options: [], range: NSRange(location: 0, length: nsString.length))

        return results.map { nsString.substring(with: $0.range) }
    }
    catch {
        DDLogError("Couldn't match urls: \(error.localizedDescription)")
        return nil
    }
}

func replaceURLWithCachedImageData(inHTML html: String, withURL key: String, completedCallback: @escaping (String) -> Void) {

    // Remove parenthesis
    let start = key.index(key.startIndex, offsetBy: 1)
    let end = key.index(key.endIndex, offsetBy: -1)

    let url = key.substring(with: start..<end)

    ImageCache.default.retrieveImage(forKey: url, options: nil) { (cachedImage, _) in

        guard let cachedImage = cachedImage,
            let data = UIImagePNGRepresentation(cachedImage) else {
                DDLogInfo("No cached image found")
                completedCallback(html)
                return
        }

        let base64String = "data:image/png;base64,\(data.base64EncodedString(options: .endLineWithCarriageReturn))"
        let modifiedHTML = html.replacingOccurrences(of: url, with: base64String)

        completedCallback(modifiedHTML)
    }
}
func modify(html:String,completedmodify:@escaping(String)->Void){
guard let doc=HTML(HTML:HTML,编码:.utf8)else{
DDLogInfo(“无法用Kannan解析HTML”)
完成修改(html)
返回
}
var imageDiv=doc.at_css(“div[class=”))
guard let currentStyle=imageDiv?[“样式”],
让currentURL=urlMatch(在:currentStyle中)?。首先{
DDLogDebug(“未能在div中找到URL”)
完成修改(html)
返回
}
DispatchQueue.main.async{
self.replaceURLWithCachedImageData(inHTML:html,withURL:currentURL,completedCallback:{modifiedHTML中
完成的修改(修改的DHTML)
})
}
}
func urlMatch(文本:String)->[String]?{
做{
让urlPattern=“\\(.*?\)”
让regex=try NSRegularExpression(模式:urlPattern,选项:。不区分大小写)
设nsString=nsString(字符串:文本)
让results=regex.matches(in:text,options:[],range:NSRange(位置:0,长度:nsString.length))
返回results.map{nsString.substring(带$0.range)}
}
抓住{
DDLogError(“无法匹配URL:\(错误。本地化描述)”)
归零
}
}
func replaceURLWithCachedImageData(在html:String中,带URL键:String,completedCallback:@escaping(String)->Void){
//删除括号
让start=key.index(key.startIndex,offsetBy:1)
让end=key.index(key.endIndex,offsetBy:-1)

让url=key.substring(带:start..使用缓存网页的最简单方法如下所示,在Swift 4.0中:-

/*其中isCacheLoad=true(脱机加载数据)& isCacheLoad=false(正常负载数据)*/


我知道我迟到了,但我最近一直在寻找一种存储网页以供脱机阅读的方法,但仍然找不到任何可靠的解决方案,该解决方案不依赖于网页本身,也不会使用不推荐的
UIWebView
。许多人认为应该使用现有的HTTP缓存,但WebKit似乎做了很多工作f进程外,使强制执行完全缓存几乎不可能(请参阅或)。然而,这个问题将我引向了正确的方向。通过修补web存档方法,我发现编写自己的web存档导出程序实际上非常容易

如问题中所述,网络档案
func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {

    DDLogError("Request failed with error \(error.localizedDescription)")

    if let reach = reach, !reach.isReachable() {
        showNetworkUnavailableAlert()
        handlingCacheRequest = true
    }
    webView.stopLoading()
    loadingIndicator.stopAnimating()
}
func modify(_ html: String, completedModification: @escaping (String) -> Void) {

    guard let doc = HTML(html: html, encoding: .utf8) else {
        DDLogInfo("Couldn't parse HTML with Kannan")
        completedModification(html)
        return
    }

    var imageDiv = doc.at_css("div[class='<your_div_class_name>']")

    guard let currentStyle = imageDiv?["style"],
        let currentURL = urlMatch(in: currentStyle)?.first else {

            DDLogDebug("Failed to find URL in div")
            completedModification(html)
            return
    }

    DispatchQueue.main.async {

        self.replaceURLWithCachedImageData(inHTML: html, withURL: currentURL, completedCallback: { modifiedHTML in

            completedModification(modifiedHTML)
        })
    }
}

func urlMatch(in text: String) -> [String]? {

    do {
        let urlPattern = "\\((.*?)\\)"
        let regex = try NSRegularExpression(pattern: urlPattern, options: .caseInsensitive)
        let nsString = NSString(string: text)
        let results = regex.matches(in: text, options: [], range: NSRange(location: 0, length: nsString.length))

        return results.map { nsString.substring(with: $0.range) }
    }
    catch {
        DDLogError("Couldn't match urls: \(error.localizedDescription)")
        return nil
    }
}

func replaceURLWithCachedImageData(inHTML html: String, withURL key: String, completedCallback: @escaping (String) -> Void) {

    // Remove parenthesis
    let start = key.index(key.startIndex, offsetBy: 1)
    let end = key.index(key.endIndex, offsetBy: -1)

    let url = key.substring(with: start..<end)

    ImageCache.default.retrieveImage(forKey: url, options: nil) { (cachedImage, _) in

        guard let cachedImage = cachedImage,
            let data = UIImagePNGRepresentation(cachedImage) else {
                DDLogInfo("No cached image found")
                completedCallback(html)
                return
        }

        let base64String = "data:image/png;base64,\(data.base64EncodedString(options: .endLineWithCarriageReturn))"
        let modifiedHTML = html.replacingOccurrences(of: url, with: base64String)

        completedCallback(modifiedHTML)
    }
}
internal func loadWebPage(fromCache isCacheLoad: Bool = false) {

    guard let url =  url else { return }
    let request = URLRequest(url: url, cachePolicy: (isCacheLoad ? .returnCacheDataElseLoad: .reloadRevalidatingCacheData), timeoutInterval: 50)
        //URLRequest(url: url)
    DispatchQueue.main.async { [weak self] in
        self?.webView.load(request)
    }
}