UIWebView 和 NSURLCache 的关系很麻烦
我正在尝试解决 UIWebView
中的两件事:
- 在适当的条件下加载本地资源代替远程资源
- 如果没有本地资源或者它们已过时,则正常加载远程资源
- 允许 webview “返回”而不需要从服务器重新加载它的 html
因此为了处理 #1 和 #2,我实现了一个自定义子类 NSURLCache
。当 Web 视图请求资产时它会被调用,我可以发送本地资产,从而中止对资产的服务器调用。我什至有一个小的时间戳系统,它知道本地版本是否旧,从而确定使用哪个版本。
但#3 让我陷入了困境。似乎无论缓存控制标头如何,UIWebView
在返回时总是会重新加载 HTML。
因此,我尝试在自定义 NSURLCache
类中强制缓存 HTML。它似乎可以工作,并返回带有我想要的 HTML 的 NSCachedURLResponse
实例,但是 UIWebView
无法注意到并从服务器加载它。经过一番搜索后,我发现 UIWebView 和 url 缓存存在一些“不一致”,我想这就是其中之一。对于资产来说效果很好,但对于构成它试图显示的页面的 HTML 来说效果不太好。
于是我决定,既然这可能行不通,那就手动完成吧。我创建了一堆 url 及其数据,我手动获取并使用 loadData:MIMEType:textEncodingName:baseURL:
加载它。你瞧,它似乎效果很好!我现在可以返回,Web 视图委托将拦截负载,并告诉它加载我手动获取的数据,并且服务器不会受到攻击。
然而,后来我注意到 NSURLCache 不再用于我的资产。如果您使用 loadRequest:
加载内容,而不是通过正确设置 baseUrl
加载数据或 html 字符串,那么它似乎只要求缓存资源。
因此,尽管我付出了所有努力,我还是无法使网络视图有条件地加载本地资源并在不访问服务器的情况下导航回来。看来我的 SDK 中有一些错误。
UIWebView
忽略 HTML 内容的服务器Cache-Control
标头UIWebView
要求为主 HTML 提供NSCachedURLResponse
请求,但随后被忽略并丢弃。- 如果您通过请求加载 UIWebView ,它会向
NSURLCache
请求资源,但是当使用 HTML 或数据加载时,它会完全绕过缓存并直接从远程服务器请求。
首先,有人看到这些问题的解决方案吗?其次,我有什么理由愚蠢而这些实际上并不是 SDK 错误?
I am trying to solve 2 things in my UIWebView
:
- Load local assets in place of remote ones, under the right conditions
- Load remote assets as normal if there are no local ones or they are outdated
- Allow the webview to go "back" without needing to reload it's html form the server
So to handle #1 and #2, I implemented a custom subclass NSURLCache
. It gets called when the webview requests assets, and I can send along the local ones instead, aborting the server call for the asset. I even have a little timestamping system that knows if the local version is old or not to figure out which one to use.
But #3 is throwing me for a loop. It seems that regardless of cache-control headers, UIWebView
will ALWAYS reload the HTML when going back.
So I tried to force caching of the HTML in my custom NSURLCache
class. It seems to work, and returns the instance of NSCachedURLResponse
with the HTML I want, but the UIWebView
fails to notice and loads it from the server anyway. After some searching I see there is some "inconsistencies" with UIWebView
and the url cache, and I guess this is one. Works great for assets, but not so great for the HTML that makes up the page it's trying to display.
So then I decided that since this may not work, do it manually. I created a stack of urls, and their data that I fetch manually and load it with loadData:MIMEType:textEncodingName:baseURL:
. Lo and behold it seemed to work great! I could now go back, the web view delegate would intercept the load, and tell it to load my manually fetched data instead, and the server would not be hit.
However, then I noticed that the NSURLCache was no longer being used for my assets. It seems it only asks for cached assets if you load content with loadRequest:
and not by loading data or an html string with the baseUrl
set properly.
So despite all my efforts I cannot make a web view conditionally load local assets AND navigate back without hitting the server. It seems like I have a few bugs here in the SDK.
UIWebView
ignores the serversCache-Control
header for HTML contentUIWebView
asks for aNSCachedURLResponse
's for the main HTML request but is then ignored and discarded.UIWebView
asks theNSURLCache
for assets if you load it up with a request, but when loading with HTML or data it bypasses the cache completely and requests it directly from the remote server.
So first up, anyone see a solution to these things? And second, any reason that I am being stupid and these are not actually SDK bugs?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
解决你的第一个#3:
虽然它可能看起来有点不正统,但如何将下载的 HTML 作为平面字符串持久保存到 NSUserDefaults 对象,并在以后需要时(有条件地)恢复它?
To tackle your first #3:
While it may seem a little unorthodox, how about persisting the downloaded HTML to an NSUserDefaults object as a flat string, and restoring it (conditionally) later when you need it?
我已经放弃这个了有太多的陷阱。相反,我只是通过 ajax 加载新页面。而不是
[webview goBack]
我有一些自定义的 javascript 可以在[webview stringByEvaluatingJavascriptFromString:@"goBack()"]
上为我做一些很酷的事情I've abandoned this. There were just too many gotchas. Instead I just load new page in via ajax. And instead of
[webview goBack]
I have some custom javascript to do cool stuff for me on[webview stringByEvaluatingJavascriptFromString:@"goBack()"]