How to clear the WKBackForwardList of a WKWebView?

10,196

Solution 1

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")
    }
}

Solution 2

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。

Solution 3

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:

  1. 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.

  2. 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))
} 
Share:
10,196
Chet
Author by

Chet

Updated on July 25, 2022

Comments

  • Chet
    Chet almost 2 years

    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:

    • using keyValue:forKey didn't work.
    • using a C pointer -> 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?

  • Chet
    Chet almost 7 years
    Thanks for the help! Trying to figure out how to convert this into Objective C and integrate into this plugin: github.com/apache/cordova-plugin-wkwebview-engine/blob/maste‌​r/…
  • Chet
    Chet almost 7 years
    Here's my attempt -- doesn't seem to work though. github.com/ccorcos/cordova-plugin-wkwebview-engine/commit/… Again, I'm not an Objective C expert my any means...
  • Chet
    Chet almost 7 years
    I feel like I'm pretty close though. Currently getting this error: -[WKWebView clearHistory]: unrecognized selector sent to instance
  • nyg
    nyg almost 7 years
    @Chet I saw your latest commit which fixed a typo in the name of the method. Did that solved the error?
  • Chet
    Chet almost 7 years
    No, I get a runtime error saying that its not a CVDWKWebView... :/ Never figured it out
  • nyg
    nyg almost 7 years
    @Chet Check in CDVWKWebViewEngine.m, line 138, you're still using [WKWebView alloc]. I think it should be [CVDWKWebView alloc] no?
  • Chet
    Chet almost 7 years
    Ah you're right!! I was only looking at line 108. Thanks for the help -- I'll let you know if this works!
  • Chet
    Chet over 6 years
    What does "declear" mean, and secondly, where did you find out how to do this?
  • xu tong
    xu tong over 6 years
    @Chet It's "declared".Spell error. Find in WebKit Open Resource
  • Oscar Yuandinata
    Oscar Yuandinata over 6 years
    backForwardList never changes during navigation. it remains empty in my app. what's wrong?
  • JDroid
    JDroid about 6 years
    xu tong's solution will accept by apple ? Is there any rejection due to this ?
  • Axy
    Axy over 5 years
    Definetely gonna be rejected for using this method. It's very simple, it's not listed in the WKBackForwardList api doc, so it's private api. A very bad way to solve the problem and to generate crashes when Apple decides to change that signature in the future version of iOS.
  • drewster
    drewster over 5 years
    It means it is working now but it's not a documented method, so you're not supposed to use it because it could go away without warning.
  • Ely
    Ely over 5 years
    Unfortunately, this brilliant idea doesn't work. It seems that backList is simply a derived property, and it's therefore not used internally by WebKit to store the actual visited items.
  • Jonathan K
    Jonathan K about 5 years
    indeed, doesn't work. A workaround is to create a new WKWebView and pass the old configuration.