Skip to content

Instantly share code, notes, and snippets.

Last active April 12, 2023 14:51
Show Gist options
  • Save unijad/90a88ddb4b576dc4d58fdf394d34aa36 to your computer and use it in GitHub Desktop.
Save unijad/90a88ddb4b576dc4d58fdf394d34aa36 to your computer and use it in GitHub Desktop.
iOS: Stably Ramp with Navigation Controller and WKWebView-SFSafariViewController Integration



This code contains a simple iOS application that intercepts links tapped in a web view and opens them in Safari using SFSafariViewController. The app uses WKWebView to display web content and overrides the default behavior of JavaScript function to listen to URL tapping events.


  • Xcode 12 or higher
  • iOS 13 or higher

How to Use

  1. Clone the repository or download the code as a ZIP file.
  2. Open the project in Xcode.
  3. Ensure that Navigation Controller is embedded in Storyboard. To embed Navigation Controller, go to the Editor menu, choose Embed, then Navigation Controller.
  4. Build and run the app on the simulator or your iOS device.
  5. Tap on a link in the web view to see it open in Safari.

Known Issues

There is no SwiftUI alternative for this specific functionality as it requires WKWebView and SFSafariViewController, which are not currently available in SwiftUI.


The code provides a basic structure for intercepting link taps in a WKWebView using JavaScript and opening them in Safari using SFSafariViewController. The provided code is meant to help you get started, but feel free to modify it to fit your specific application needs. If you have any issues or questions, please feel free to reach out.

import UIKit
import WebKit
import SafariServices
class ViewController: UIViewController, SFSafariViewControllerDelegate {
private var webView: WKWebView!
private var messageHandler: MessageHandler!
override func loadView() {
webView = WKWebView()
messageHandler = MessageHandler { (urlString) in
self.openURLInSafari(url: URL(string: urlString)!)
webView.configuration.userContentController.add(messageHandler, name: "interceptor")
webView.navigationDelegate = self
view = webView
override func viewDidLoad() {
let url = URL(string: "")!
webView.load(URLRequest(url: url))
webView.allowsBackForwardNavigationGestures = false
func createScript() -> WKUserScript {
let jsCode = " = function (url) { " +
"window.webkit.messageHandlers.interceptor.postMessage(url);" +
" }"
let userScript = WKUserScript(source: jsCode, injectionTime: .atDocumentEnd, forMainFrameOnly: false)
return userScript
func openURLInSafari(url: URL) {
let safariVC = SFSafariViewController(url: url)
safariVC.delegate = self
safariVC.preferredControlTintColor = UIColor.clear
navigationController?.pushViewController(safariVC, animated: true)
// Delegate function to detect when the user taps the 'Done' button
func safariViewControllerDidFinish(_ controller: SFSafariViewController) {
navigationController?.popViewController(animated: true)
extension ViewController: WKNavigationDelegate {
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) {
class MessageHandler: NSObject, WKScriptMessageHandler {
var completionHandler: ((String) -> Void)?
init(completionHandler: ((String) -> Void)?) {
self.completionHandler = completionHandler
func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
if let urlString = message.body as? String {
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment