Skip to content

Instantly share code, notes, and snippets.

@LeeKahSeng
Created February 16, 2022 14:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save LeeKahSeng/7f01d9ed7db4925debf24ba6bcf17dd9 to your computer and use it in GitHub Desktop.
Save LeeKahSeng/7f01d9ed7db4925debf24ba6bcf17dd9 to your computer and use it in GitHub Desktop.
Full sample code for article "How to Hijack WKWebView Navigation Actions" (https://swiftsenpai.com/development/hijack-wkwebview-navigation/)
import UIKit
import WebKit
// MARK: - Code related to "How to Hijack WKWebView Navigation Actions" starts here.
// https://swiftsenpai.com/development/hijack-wkwebview-navigation/
class ColorViewController: UIViewController {
var screenColor: UIColor?
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = screenColor
}
}
enum TransitionType: String {
case present
case push
}
enum ActionType: String {
case navigate
}
extension HijackViewController: WKNavigationDelegate {
func webView(_ webView: WKWebView,
decidePolicyFor navigationAction: WKNavigationAction,
decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
if
navigationAction.navigationType == WKNavigationType.linkActivated,
let url = navigationAction.request.url {
// Check is user request to perform custom navigate action
if
let scheme = url.scheme,
let actionType = ActionType(rawValue: scheme),
actionType == .navigate {
// Extract transition type from URL
guard
let host = url.host,
let transitionType = TransitionType(rawValue: host) else {
return
}
// Extract RGB from URL query items
guard
let component = URLComponents(string: url.absoluteString),
let r = component.queryItems?.first(where: { $0.name == "r" })?.value,
let g = component.queryItems?.first(where: { $0.name == "g" })?.value,
let b = component.queryItems?.first(where: { $0.name == "b" })?.value else {
return
}
// Convert rgb String to Double (Only available in Swift 5.5)
guard
let red = Double(r),
let green = Double(g),
let blue = Double(b) else {
return
}
let viewController = ColorViewController()
let screenColor = UIColor(red: red, green: green, blue: blue, alpha: 1.0)
viewController.screenColor = screenColor
// Perform navigation based on transition type
switch transitionType {
case .present:
present(viewController, animated: true)
case .push:
navigationController?.pushViewController(viewController, animated: true)
}
} else {
// Load URL as usual
let req = URLRequest(url: url)
webView.load(req)
}
decisionHandler(.cancel)
return
}
decisionHandler(.allow)
}
}
// MARK: - Following code showcases JavaScript injection technique discussed in "Injecting JavaScript Into Web View In iOS".
// https://swiftsenpai.com/development/web-view-javascript-injection/
class HijackViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
configureWebView()
}
private func configureWebView() {
let css = """
html, body {
overflow-x: hidden;
}
body {
background-color: #333333;
line-height: 1.5;
color: white;
padding: 10;
font-weight: 600;
font-family: -apple-system;
}
a {
color: white;
}
"""
// Make CSS into single liner
let cssString = css.components(separatedBy: .newlines).joined()
// Create JavaScript that loads the CSS
let javaScript = """
var element = document.createElement('style');
element.innerHTML = '\(cssString)';
document.head.appendChild(element);
"""
// Create user script that inject the JavaScript after the HTML finishes loading
let userScript = WKUserScript(source: javaScript,
injectionTime: .atDocumentEnd,
forMainFrameOnly: true)
// Set user script to a configuration object and load it into the webView
let userContentController = WKUserContentController()
userContentController.addUserScript(userScript)
let configuration = WKWebViewConfiguration()
configuration.userContentController = userContentController
let webView = WKWebView(frame: CGRect.zero, configuration: configuration)
// Show web view on screen
view.addSubview(webView)
webView.layer.cornerRadius = 20.0
webView.layer.masksToBounds = true
webView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
webView.topAnchor.constraint(equalTo: view.layoutMarginsGuide.topAnchor, constant: 20.0),
webView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20.0),
webView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20.0),
webView.bottomAnchor.constraint(equalTo: view.layoutMarginsGuide.bottomAnchor, constant: -20.0),
])
webView.navigationDelegate = self
// Load content to webView
let htmlContent =
"""
<h1>Swift Senpai Demo</h1>
<h2>This is a web view</h2>
<ul>
<li><a href="navigate://present?r=255&g=255&b=0" target="_blank">Present Yellow Screen</a></li>
<li><a href="navigate://push?r=255&g=0&b=0" target="_blank">Push Red Screen</a></li>
</ul>
"""
// Prepend `<meta>` viewport element to the HTML so that it will scale correctly in a mobile device
let metaTag = "<meta name=\"viewport\" content=\"user-scalable=no, width=device-width\">"
let html = "\(metaTag)\(htmlContent)"
// Load HTML string to web view in main thread
webView.loadHTMLString(html, baseURL: nil)
}
}
@shravanteegala
Copy link

nice.
But How Can view handle the webView data (cache) when navigation stack(webView navigation). and how we restore and reload the webPage when we poptoViewcontroller

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment