diff --git a/packages/turbo/ios/RNSession.swift b/packages/turbo/ios/RNSession.swift index 789c8a98..b61fa691 100644 --- a/packages/turbo/ios/RNSession.swift +++ b/packages/turbo/ios/RNSession.swift @@ -116,6 +116,15 @@ extension RNSession: SessionDelegate { func sessionDidFinishFormSubmission(_ session: Session) { visitableView?.didFinishFormSubmission() } + + func session(_ session: Session, decidePolicyFor navigationAction: WKNavigationAction) -> WebViewPolicyManager.Decision { + guard let url = navigationAction.request.url else { + return .allow + } + // regardless of the return value here nothing happens, so we have to manually open external URL + visitableView?.didOpenExternalUrl(url: url) + return .allow + } } extension RNSession: WKScriptMessageHandler { diff --git a/packages/turbo/ios/RNVisitableView.swift b/packages/turbo/ios/RNVisitableView.swift index ed57ec1e..56af3ced 100644 --- a/packages/turbo/ios/RNVisitableView.swift +++ b/packages/turbo/ios/RNVisitableView.swift @@ -175,10 +175,10 @@ class RNVisitableView: UIView, RNSessionSubscriber { } private func visit() { - if (controller?.visitableURL?.absoluteString == url as String) { + if (controller?.currentVisitableURL.absoluteString == url as String) { return } - controller!.visitableURL = URL(string: String(url)) + controller!.initializeVisit(url: URL(string: String(url))!) session?.visit(controller!) } @@ -218,7 +218,7 @@ class RNVisitableView: UIView, RNSessionSubscriber { public func didFailRequestForVisitable(visitable: Visitable, error: Error){ let event: [AnyHashable: Any] = [ - "url": visitable.visitableURL.absoluteString, + "url": visitable.currentVisitableURL.absoluteString, "description": error.localizedDescription, "statusCode": getStatusCodeFromError(error: error as? TurboError) ] diff --git a/packages/turbo/ios/RNVisitableViewController.swift b/packages/turbo/ios/RNVisitableViewController.swift index bd5302af..2662b2bc 100644 --- a/packages/turbo/ios/RNVisitableViewController.swift +++ b/packages/turbo/ios/RNVisitableViewController.swift @@ -6,6 +6,7 @@ // import Foundation +import WebKit public protocol RNVisitableViewControllerDelegate { @@ -29,14 +30,28 @@ class RNVisitableViewController: UIViewController, Visitable { public var delegate: RNVisitableViewControllerDelegate? open weak var visitableDelegate: VisitableDelegate? - open var visitableURL: URL! + public var initialVisitableURL: URL + public var currentVisitableURL: URL { + resolveVisitableLocation() + } private var reactViewController: UIViewController? = nil - - public convenience init(reactViewController: UIViewController?, delegate: RNVisitableViewControllerDelegate?) { - self.init() + + public init(reactViewController: UIViewController?, delegate: RNVisitableViewControllerDelegate?) { + self.initialVisitableURL = URL(string: "about:blank")! + self.visitableLocationState = .initialized(self.initialVisitableURL) self.reactViewController = reactViewController self.delegate = delegate + super.init(nibName: nil, bundle: nil) + } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + public func initializeVisit(url: URL) { + initialVisitableURL = url + visitableLocationState = .initialized(url) } // MARK: View Lifecycle @@ -71,6 +86,7 @@ class RNVisitableViewController: UIViewController, Visitable { func visitableDidRender() { delegate?.visitableDidRender(visitable: self) + visitableLocationState = .resolved } func showVisitableActivityIndicator() { @@ -81,9 +97,21 @@ class RNVisitableViewController: UIViewController, Visitable { delegate?.hideVisitableActivityIndicator() } + func visitableDidActivateWebView(_ webView: WKWebView) { + // No-op + } + + func visitableWillDeactivateWebView() { + visitableLocationState = .deactivated(visitableView.webView?.url ?? initialVisitableURL) + } + + open func visitableDidDeactivateWebView() { + // No-op + } + // MARK: Visitable View - open private(set) lazy var visitableView: VisitableView! = { + open private(set) lazy var visitableView: VisitableView = { let view = VisitableView(frame: CGRect.zero) view.translatesAutoresizingMaskIntoConstraints = false @@ -103,5 +131,23 @@ class RNVisitableViewController: UIViewController, Visitable { public var visitableViewController: UIViewController { self.reactViewController?.parent ?? self } - + + enum VisitableLocationState { + case resolved + case initialized(URL) + case deactivated(URL) + } + + private var visitableLocationState: VisitableLocationState + + private func resolveVisitableLocation() -> URL { + switch visitableLocationState { + case .resolved: + return visitableView.webView?.url ?? initialVisitableURL + case .initialized(let url): + return url + case .deactivated(let url): + return url + } + } } diff --git a/packages/turbo/package.json b/packages/turbo/package.json index 011bf1b1..2181ecb7 100644 --- a/packages/turbo/package.json +++ b/packages/turbo/package.json @@ -22,7 +22,7 @@ "types": "lib/typescript/src/index.d.ts", "react-native": "src/index.tsx", "hotwireNative": { - "ios": "1.1.3", + "ios": "1.2.0", "android": "1.1.1" }, "scripts": {