forem / ForemWebView-ios

Forem core interface SDK
GNU General Public License v3.0
21 stars 7 forks source link

Clear WebView history #12

Open fdocr opened 3 years ago

fdocr commented 3 years ago

Is your feature request related to a problem? Please describe. It would be ideal if we can clear the WKWebView history after a user has signed in (to avoid them going back to an OAuth page).

The problem with this is that I wasn't able to find a working solution from the WKWebView API (it seems to be read only). Looking into StackOverflow I was able to find a solution that overrides the WKWebView history implementation (#10) but it does not seem to be working good enough (swipe gestures still go back in history).

Describe the solution you'd like A workaround in order to make the WKWebView clear it's browsing history

Additional context PoC here: #10

fdocr commented 3 years ago

The PoC has a compiler warning that may be troublesome for anyone using the Framework because it seems to indicate we're subclassing an interface to a private API:

Property type 'ForemWebViewHistory * _Nonnull' is incompatible with type 'WKBackForwardList * _Nonnull' inherited from 'WKWebView'

It's also not 100% effective as mentioned above

larson-carter commented 3 years ago

Here is an idea. Could you possibly "hold" then release the state of the WebKitView? Doing so would logically clear the WebView's history. This is just a theory that I have and I believe that logically it would work.

Secondly we could write an override method which would clear the history of the webkitview. Basically destroying and creating a new instance of the view. Is this the most computational efficient answer, no. I do however believe that it would work also.

For example clearing the cache would reset the top level history memory of teh WebKitView if I recall correctly. This can be done here:

let websiteDataTypes = NSSet(array: [WKWebsiteDataTypeDiskCache, WKWebsiteDataTypeMemoryCache])
let date = Date(timeIntervalSince1970: 0)
WKWebsiteDataStore.default().removeData(ofTypes: websiteDataTypes as! Set<String>, modifiedSince: date, completionHandler:{ })

This can be done as an extension to WebKitView itself so you would just call the webview's integrated method.

For another reference: https://developer.apple.com/documentation/webkit/webhistory It is deprecated and is for macOS. However I was thinking we could emulate this for iOS devices. I think this should be a last option to try out.

Just let me know what you think @fdoxyz I'm looking forward to your input.

fdocr commented 3 years ago

Hey @larson-carter thanks for chiming in!

Could you possibly "hold" then release the state of the WebKitView? Doing so would logically clear the WebView's history.

This could be handled in the apps using the ForemWebView, but that would make the consumers responsible of implementing the logic (which we'd need to test before confirming first).

The end goal of adding this feature here is to provide the easy-to-use interface to clear the history. And also we can call this method after a user has signed in, which will help us avoid the edge case where they go back after a social sign in into an OAuth provider's page.

Secondly we could write an override method which would clear the history of the webkitview. Basically destroying and creating a new instance of the view.

This idea sounds interesting. I think instead of overriding we can add our own method, as I think no public method currently exists in WKWebView to clear the history.

I'll play around with the approach suggested soon, but if you want to send in a PR if you make it work feel free to do so!

larson-carter commented 3 years ago

This could be handled in the apps using the ForemWebView, but that would make the consumers responsible of implementing the logic (which we'd need to test before confirming first).

In this case what would the consumers be?

The end goal of adding this feature here is to provide the easy-to-use interface to clear the history. And also we can call this method after a user has signed in, which will help us avoid the edge case where they go back after a social sign in into an OAuth provider's page.

Ah so possibly another view to directly clear the history? This would basically be a method that would be called after the user has signed in. Would we have to setup JavaScript to detect when they are actually signed in?

This idea sounds interesting. I think instead of overriding we can add our own method, as I think no public method currently exists in WKWebView to clear the history.

I've looked closer into this idea. Right there is no public method that clears the history. I was meaning that we could override the code in the initializer of the WebView.

Thank you for your reply @fdoxyz

fdocr commented 3 years ago

In this case what would the consumers be?

Terminology here is confusing because of all the moving parts, let me try to clarify what I meant:

This means that your suggestion to "hold" then release the state of the WebKitView (assuming you meant WKWebView) would be the consumers responsibility, because they hold on to the ForemWebView instance and present it however they want.

What I meant in my response was: If the ForemWebView had a public function to .clearHistory() that would be ideal, because it makes the consumer's life much easier than to code their own logic to clear the history.

This would basically be a method that would be called after the user has signed in. Would we have to setup JavaScript to detect when they are actually signed in?

There are a couple of patterns I've seen that can help us know when the user has signed in or signed out. For example:

At the moment I think the second option is better (I actually took this approach when running an experiment in #10)

I've looked closer into this idea. Right there is no public method that clears the history. I was meaning that we could override the code in the initializer of the WebView.

Maybe I'm not following the exact way to implement it as you mention, but luckily there's a million and one ways to solve the same problem 😄 . I would experiment creating an open function that resets the cache/history one way or another that can be called from any consumer (you can check out how we call a function/property defined here from the DEV-ios app).

But if you have an idea/experiment that works feel free to send in a PR, it doesn't matter if the approach is different we'll definitely evaluate it and see if it fits our use case. Otherwise no worries, I'm always open for discussing ideas on how to solve these challenges.

larson-carter commented 3 years ago
Terminology here is confusing because of all the moving parts, let me try to clarify what I meant:

ForemWebView is the framework that is exposing a custom WKWebView (ForemWebView)
DEV-ios and other apps are the consumers of the framework. Just like in the past they used WKWebView they are now using ForemWebView (way simpler to use and it encapsulates a bunch of the business logic)

Ah yes, this makes sense. I keep forgetting that this is a frame working and isn't the dev.to app.

This means that your suggestion to "hold" then release the state of the WebKitView (assuming you meant WKWebView) would be the consumers responsibility, because they hold on to the ForemWebView instance and present it however they want.

What I meant in my response was: If the ForemWebView had a public function to .clearHistory() that would be ideal, because it makes the consumer's life much easier than to code their own logic to clear the history.

Ah gotcha, now that I'm understanding completely what the consumer's role is. This method might be ideal.

The userData field represents the user data extracted from the DOM. When it changes from nil to "something" we know the user has just signed in

Hmm. So we could have an method that runs when it is detected?

We're implementing the WKNavigationDelegate here so in this function we can check the URL and if it includes ?signin=true we know the user has just signed in.

I think this option is better personally than messing with nil values. We could just parse the URL from the delegate.

Here: https://stackoverflow.com/questions/26342546/get-the-current-full-url-for-wkwebview Also Here: https://stackoverflow.com/questions/41217721/swift-parse-a-string-which-contains-a-url Similarly, this could be a method. Extract, detect, execute stuff after that.

func detectSignIn() {

     // Get URL save as string
     // Parse it to the ?signin part
     // if signin = true do clears
     // else don't destory

}

At the moment I think the second option is better (I actually took this approach when running an experiment in #10)

Alright, I'll take a look at this in a bit, I might have to wait till after the weekend and such!

Maybe I'm not following the exact way to implement it as you mention, but luckily there's a million and one ways to solve the same problem 😄 . I would experiment creating an open function that resets the cache/history one way or another that can be called from any consumer (you can check out how we call a function/property defined here from the DEV-ios app).

I already did some testing with this when I was making a basic HTML5 web browser (Seen on my github, but not complete) I got stuck on making the cache cleared - it was clearing every button I pressed and every time a new URL was accessed (this was a basic coding error) I'm going to take a look at your test PR, then I'll start looking at possibly integrating a button that you tap to clear the history. If that works then we can expand from there. I'm sure you are implementing it exactly how I was describing it.