import UIKit import WebKit import SafariServices import Turbo import Strada final class SceneDelegate: UIResponder { private static var sharedProcessPool = WKProcessPool() var window: UIWindow? private let baseURL = <%= name %>.baseURL private let rootURL1 = <%= name %>.homeURL1 private let rootURL2 = <%= name %>.homeURL2 private var tabBarController: UITabBarController! private lazy var navigationController1 = { tabBarController.viewControllers![0] as! TurboNavigationController }() private lazy var navigationController2 = { tabBarController.viewControllers![1] as! TurboNavigationController }() private func navigationController() -> TurboNavigationController { tabBarController.selectedViewController as! TurboNavigationController } // MARK: - Setup private func configureRootViewController() { guard let window = window else { fatalError() } tabBarController = window.rootViewController as? UITabBarController navigationController1.session = session1 navigationController1.modalSession = modalSession navigationController2.session = session2 navigationController2.modalSession = modalSession } // MARK: - Sessions private lazy var session1 = makeSession() private lazy var session2 = makeSession() private lazy var modalSession = makeSession() private func makeSession() -> Session { let webView = WKWebView(frame: .zero, configuration: .appConfiguration) webView.uiDelegate = self webView.allowsLinkPreview = false Bridge.initialize(webView) // Initialize Strada bridge. let session = Session(webView: webView) session.delegate = self session.pathConfiguration = pathConfiguration return session } private func session() -> Session { navigationController().session } // MARK: - Path Configuration private lazy var pathConfiguration = PathConfiguration(sources: [ .file(Bundle.main.url(forResource: "path-configuration", withExtension: "json")!), ]) } extension SceneDelegate: UIWindowSceneDelegate { func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { guard let _ = scene as? UIWindowScene else { return } configureRootViewController() navigationController1.route(url: rootURL1, options: VisitOptions(action: .replace), properties: [:]) navigationController2.route(url: rootURL2, options: VisitOptions(action: .replace), properties: [:]) } } extension SceneDelegate: SessionDelegate { func session(_ session: Session, didProposeVisit proposal: VisitProposal) { navigationController().route(url: proposal.url, options: proposal.options, properties: proposal.properties) } func session(_ session: Session, didFailRequestForVisitable visitable: Visitable, error: Error) { if let errorPresenter = visitable as? ErrorPresenter { errorPresenter.presentError(error) { session.reload() } } else { fatalError("Visit failed!") } } // When a form submission completes in the modal session, we need to // manually clear the snapshot cache in the default session, since we // don't want potentially stale cached snapshots to be used func sessionDidFinishFormSubmission(_ session: Session) { if (session == modalSession) { self.session().clearSnapshotCache() } } func sessionDidLoadWebView(_ session: Session) { session.webView.navigationDelegate = self } func sessionWebViewProcessDidTerminate(_ session: Session) { session.reload() } } extension SceneDelegate: WKNavigationDelegate { func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { if navigationAction.navigationType == .linkActivated { // Any link that's not on the same domain as the Turbo root url will go through here // Other links on the domain, but that have an extension that is non-html will also go here // You can decide how to handle those, by default if you're not the navigationDelegate // the Session will open them in the default browser let url = navigationAction.request.url! // For this demo, we'll load files from our domain in a SafariViewController so you // don't need to leave the app. You might expand this in your app // to open all audio/video/images in a native media viewer if url.host == baseURL.host, !url.pathExtension.isEmpty { let safariViewController = SFSafariViewController(url: url) navigationController().present(safariViewController, animated: true) } else { UIApplication.shared.open(url) } decisionHandler(.cancel) } else { decisionHandler(.allow) } } } extension SceneDelegate: WKUIDelegate { func webView(_ webView: WKWebView, runJavaScriptConfirmPanelWithMessage message: String, initiatedByFrame frame: WKFrameInfo, completionHandler: @escaping (Bool) -> Void) { let confirm = UIAlertController(title: nil, message: message, preferredStyle: .alert) confirm.addAction(UIAlertAction(title: "Cancel", style: .cancel) { _ in completionHandler(false) }) confirm.addAction(UIAlertAction(title: "OK", style: .default) { _ in completionHandler(true) }) // JavaScript alerts in Turbo Native navigationController().present(confirm, animated: true) } }