用于脱机查看的Swift iOS缓存WKWebView内容
我们正在尝试将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用于脱机查看的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中打开一个网站并选
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)
}
}