Reputation: 19879
It appears that the backForwardList
of a WKWebView is readonly, but I've seen people so some pretty magical things to get around this. I need to figure out some way of clearing the history of a WKWebView. Any ideas how I might so this? So far I've tries a few tricks that have failed:
->
didnt work.I've seen people talk about synthesizing the property and extending the class but I don't really know how that works and couldn't figure it out. Any other ideas?
Upvotes: 9
Views: 15783
Reputation: 1003
Because Apple keeps the interface for WKBackForwardList
pretty tightly locked down and is notoriously strict with developers who go outside of the published public interfaces, you have two options:
The easiest way (and surely what Apple intends) is to create a new WKWebView
and replace your current one with it. This isn't that hard of a solution to implement since there are a pretty finite number of configurations you could have to copy over from your old instance to your new one. You could even create a wrapper view that did this for you, so that you only ever had to call clearHistory()
and the wrapper view would do the underlying swap for you.
If, for some reason, you can't replace the instance of WKWebView
, you can take advantage of the 1D nature of history to clear the history down to two items. You cannot clear it farther than that, I'm afraid. To clear the URL down to two URLs, let's say they're stored in the values urlA and urlB, you would do:
// At initialization, ensure that urlA is the first item in the history
let webview = WKWebView()
// ... whatever other init you need
webview.load(URLRequest(url: urlA))
and then to "clear" the history:
func clearHistory() {
// First we make sure the webview is on the earliest item in the history
if webview.canGoBack {
webview.go(to: webview.backForwardList.backList.first)
}
// Then we navigate to our urlB so that we destroy the old "forward" stack
webview.load(URLRequest(url: urlB))
}
Upvotes: 3
Reputation: 2351
Reading in the documentation you can get WKBackForwardList for the WKWebView.
After that you can find the first item in the backList of WKBackForwardList and after that to use:
func go(to: WKBackForwardListItem) -> WKNavigation?
And with this you can clean the history up to the first url.
Upvotes: 0
Reputation: 483
Worded on iOS8 ~ iOS11.
Objective-C
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
[wek.backForwardList performSelector:NSSelectorFromString(@"_removeAllItems")];
#pragma clang diagnostic pop
Swift 4
webView.backForwardList.perform(Selector(("_removeAllItems")))
!!!!NOTE!!!! THIS METHOD IS DECLARED IN WebKit Open Resource, not a public method。
Upvotes: 7
Reputation: 2552
This code compiles, but I have not tested it...
First I subclass WKWebView
to override backForwardList
with my own subclass of WKBackForwardList
.
Then, in my WKBackForwardList
subclass, I can either override backItem
& forwardItem
to make them return nil, instead of having them look into their respective list (which is most probably the default implementation).
Or I can override backList
& forwardList
in the same way I did in WKWebView
with backForwardList
. I do this to add a setter, which will allow me remove items from the lists.
import Foundation
import WebKit
class WebViewHistory: WKBackForwardList {
/* Solution 1: return nil, discarding what is in backList & forwardList */
override var backItem: WKBackForwardListItem? {
return nil
}
override var forwardItem: WKBackForwardListItem? {
return nil
}
/* Solution 2: override backList and forwardList to add a setter */
var myBackList = [WKBackForwardListItem]()
override var backList: [WKBackForwardListItem] {
get {
return myBackList
}
set(list) {
myBackList = list
}
}
func clearBackList() {
backList.removeAll()
}
}
class WebView: WKWebView {
var history: WebViewHistory
override var backForwardList: WebViewHistory {
return history
}
init(frame: CGRect, configuration: WKWebViewConfiguration, history: WebViewHistory) {
self.history = history
super.init(frame: frame, configuration: configuration)
}
/* Not sure about the best way to handle this part, it was just required for the code to compile... */
required init?(coder: NSCoder) {
if let history = coder.decodeObject(forKey: "history") as? WebViewHistory {
self.history = history
}
else {
history = WebViewHistory()
}
super.init(coder: coder)
}
override func encode(with aCoder: NSCoder) {
super.encode(with: aCoder)
aCoder.encode(history, forKey: "history")
}
}
Upvotes: 2