From a63f658b47e447782b8526aa5b10f7e71ad7290a Mon Sep 17 00:00:00 2001 From: Mattias Pfeiffer Date: Fri, 23 Feb 2024 15:02:26 +0100 Subject: [PATCH 1/7] Snapshot and deactivate Visitable during its lifecycle, eg. when dismissing a modal --- Source/Session/Session.swift | 8 ++++++++ Source/Visit/Visit.swift | 4 ++++ Source/Visitable/Visitable.swift | 2 ++ Source/Visitable/VisitableViewController.swift | 10 ++++++++++ Source/WebView/WebViewBridge.swift | 4 ++++ Source/WebView/turbo.js | 6 ++++++ 6 files changed, 34 insertions(+) diff --git a/Source/Session/Session.swift b/Source/Session/Session.swift index bcfdab3..f948350 100644 --- a/Source/Session/Session.swift +++ b/Source/Session/Session.swift @@ -244,6 +244,14 @@ extension Session: VisitableDelegate { } } + public func visitableViewWillDisappear(_ visitable: Visitable) { + topmostVisit?.cacheSnapshot() + } + + public func visitableViewDidDisappear(_ visitable: Visitable) { + deactivateVisitable(visitable) + } + public func visitableDidRequestReload(_ visitable: Visitable) { guard visitable === topmostVisitable else { return } reload() diff --git a/Source/Visit/Visit.swift b/Source/Visit/Visit.swift index 15ed413..7385bc0 100644 --- a/Source/Visit/Visit.swift +++ b/Source/Visit/Visit.swift @@ -68,6 +68,10 @@ class Visit: NSObject { delegate?.visitDidFinish(self) } + func cacheSnapshot() { + bridge.cacheSnapshot() + } + func startVisit() {} func cancelVisit() {} func completeVisit() {} diff --git a/Source/Visitable/Visitable.swift b/Source/Visitable/Visitable.swift index 6f51247..079c48b 100644 --- a/Source/Visitable/Visitable.swift +++ b/Source/Visitable/Visitable.swift @@ -4,6 +4,8 @@ import WebKit public protocol VisitableDelegate: AnyObject { func visitableViewWillAppear(_ visitable: Visitable) func visitableViewDidAppear(_ visitable: Visitable) + func visitableViewWillDisappear(_ visitable: Visitable) + func visitableViewDidDisappear(_ visitable: Visitable) func visitableDidRequestReload(_ visitable: Visitable) func visitableDidRequestRefresh(_ visitable: Visitable) } diff --git a/Source/Visitable/VisitableViewController.swift b/Source/Visitable/VisitableViewController.swift index b245952..47433ab 100644 --- a/Source/Visitable/VisitableViewController.swift +++ b/Source/Visitable/VisitableViewController.swift @@ -28,6 +28,16 @@ open class VisitableViewController: UIViewController, Visitable { visitableDelegate?.visitableViewDidAppear(self) } + open override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + visitableDelegate?.visitableViewWillDisappear(self) + } + + open override func viewDidDisappear(_ animated: Bool) { + super.viewDidDisappear(animated) + visitableDelegate?.visitableViewDidDisappear(self) + } + // MARK: Visitable open func visitableDidRender() { diff --git a/Source/WebView/WebViewBridge.swift b/Source/WebView/WebViewBridge.swift index a7bdc73..74c23fb 100644 --- a/Source/WebView/WebViewBridge.swift +++ b/Source/WebView/WebViewBridge.swift @@ -76,6 +76,10 @@ final class WebViewBridge { callJavaScript(function: "window.turboNative.clearSnapshotCache") } + func cacheSnapshot() { + callJavaScript(function: "window.turboNative.cacheSnapshot") + } + func cancelVisit(withIdentifier identifier: String) { callJavaScript(function: "window.turboNative.cancelVisitWithIdentifier", arguments: [identifier]) } diff --git a/Source/WebView/turbo.js b/Source/WebView/turbo.js index 329c90c..b9299bd 100644 --- a/Source/WebView/turbo.js +++ b/Source/WebView/turbo.js @@ -64,6 +64,12 @@ } } + cacheSnapshot() { + if (window.Turbo) { + Turbo.session.view.cacheSnapshot() + } + } + // Current visit issueRequestForVisitWithIdentifier(identifier) { From 1390d047f37221976307258a4fcd8963120e317f Mon Sep 17 00:00:00 2001 From: Mattias Pfeiffer Date: Tue, 27 Feb 2024 06:25:05 +0100 Subject: [PATCH 2/7] Session: Only cache snapshot from native side if no other Visitable will appear to avoid double snapshots --- Source/Session/Session.swift | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Source/Session/Session.swift b/Source/Session/Session.swift index f948350..c0a26c5 100644 --- a/Source/Session/Session.swift +++ b/Source/Session/Session.swift @@ -35,6 +35,7 @@ public class Session: NSObject { private var currentVisit: Visit? private var topmostVisit: Visit? + private var disappearingVisitForSnapshotting: Visit? /// The topmost visitable is the visitable that has most recently completed a visit public var topmostVisitable: Visitable? { @@ -213,6 +214,8 @@ extension Session: VisitDelegate { extension Session: VisitableDelegate { public func visitableViewWillAppear(_ visitable: Visitable) { + self.disappearingVisitForSnapshotting = nil + guard let topmostVisit = self.topmostVisit, let currentVisit = self.currentVisit else { return } if visitable === topmostVisit.visitable && visitable.visitableViewController.isMovingToParent { @@ -245,10 +248,11 @@ extension Session: VisitableDelegate { } public func visitableViewWillDisappear(_ visitable: Visitable) { - topmostVisit?.cacheSnapshot() + self.disappearingVisitForSnapshotting = topmostVisit } public func visitableViewDidDisappear(_ visitable: Visitable) { + disappearingVisitForSnapshotting?.cacheSnapshot() deactivateVisitable(visitable) } From 661b3a92c4bb2b59bea46f853959d743793b52b9 Mon Sep 17 00:00:00 2001 From: Mattias Pfeiffer Date: Tue, 27 Feb 2024 06:46:06 +0100 Subject: [PATCH 3/7] Detect restore visit in viewWillAppear when navigating from a native screen --- Source/Session/Session.swift | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Source/Session/Session.swift b/Source/Session/Session.swift index c0a26c5..a1a0991 100644 --- a/Source/Session/Session.swift +++ b/Source/Session/Session.swift @@ -214,6 +214,7 @@ extension Session: VisitDelegate { extension Session: VisitableDelegate { public func visitableViewWillAppear(_ visitable: Visitable) { + let lastDisappearingVisit = self.disappearingVisitForSnapshotting self.disappearingVisitForSnapshotting = nil guard let topmostVisit = self.topmostVisit, let currentVisit = self.currentVisit else { return } @@ -228,7 +229,7 @@ extension Session: VisitableDelegate { } else if visitable === currentVisit.visitable && currentVisit.state == .started { // Navigating forward - complete navigation early completeNavigationForCurrentVisit() - } else if visitable !== topmostVisit.visitable { + } else if visitable !== topmostVisit.visitable || visitable === lastDisappearingVisit?.visitable { // Navigating backward visit(visitable, action: .restore) } From ef3b9fa5b1fa2ffee3824dbf7c64b82166f767a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Denis=20=C5=A0vara?= Date: Mon, 4 Mar 2024 17:00:48 +0100 Subject: [PATCH 4/7] Rename `disappearingVisitForSnapshotting` to `previousVisit`. --- Source/Session/Session.swift | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Source/Session/Session.swift b/Source/Session/Session.swift index a1a0991..7fbcd67 100644 --- a/Source/Session/Session.swift +++ b/Source/Session/Session.swift @@ -35,7 +35,7 @@ public class Session: NSObject { private var currentVisit: Visit? private var topmostVisit: Visit? - private var disappearingVisitForSnapshotting: Visit? + private var previosVisit: Visit? /// The topmost visitable is the visitable that has most recently completed a visit public var topmostVisitable: Visitable? { @@ -214,8 +214,8 @@ extension Session: VisitDelegate { extension Session: VisitableDelegate { public func visitableViewWillAppear(_ visitable: Visitable) { - let lastDisappearingVisit = self.disappearingVisitForSnapshotting - self.disappearingVisitForSnapshotting = nil + let lastDisappearingVisit = self.previosVisit + self.previosVisit = nil guard let topmostVisit = self.topmostVisit, let currentVisit = self.currentVisit else { return } @@ -229,7 +229,7 @@ extension Session: VisitableDelegate { } else if visitable === currentVisit.visitable && currentVisit.state == .started { // Navigating forward - complete navigation early completeNavigationForCurrentVisit() - } else if visitable !== topmostVisit.visitable || visitable === lastDisappearingVisit?.visitable { + } else if visitable !== topmostVisit.visitable || visitable === previosVisit?.visitable { // Navigating backward visit(visitable, action: .restore) } @@ -249,11 +249,11 @@ extension Session: VisitableDelegate { } public func visitableViewWillDisappear(_ visitable: Visitable) { - self.disappearingVisitForSnapshotting = topmostVisit + previosVisit = topmostVisit } public func visitableViewDidDisappear(_ visitable: Visitable) { - disappearingVisitForSnapshotting?.cacheSnapshot() + previosVisit?.cacheSnapshot() deactivateVisitable(visitable) } From 54b1cd86a1e4059142708ab443e734338e109603 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Denis=20=C5=A0vara?= Date: Mon, 4 Mar 2024 17:17:02 +0100 Subject: [PATCH 5/7] Use defer to nil out the previous visit. --- Source/Session/Session.swift | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/Source/Session/Session.swift b/Source/Session/Session.swift index 7fbcd67..ea9664e 100644 --- a/Source/Session/Session.swift +++ b/Source/Session/Session.swift @@ -214,8 +214,9 @@ extension Session: VisitDelegate { extension Session: VisitableDelegate { public func visitableViewWillAppear(_ visitable: Visitable) { - let lastDisappearingVisit = self.previosVisit - self.previosVisit = nil + defer { + previosVisit = nil + } guard let topmostVisit = self.topmostVisit, let currentVisit = self.currentVisit else { return } @@ -229,8 +230,11 @@ extension Session: VisitableDelegate { } else if visitable === currentVisit.visitable && currentVisit.state == .started { // Navigating forward - complete navigation early completeNavigationForCurrentVisit() - } else if visitable !== topmostVisit.visitable || visitable === previosVisit?.visitable { - // Navigating backward + } else if visitable !== topmostVisit.visitable { + // Navigating backward from a web view screen to a web view screen. + visit(visitable, action: .restore) + } else if visitable === previosVisit?.visitable { + // Navigating backward from a native to a web view screen. visit(visitable, action: .restore) } } From 56b26eb6609b8117873a988face8b8a533db7b42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Denis=20=C5=A0vara?= Date: Mon, 4 Mar 2024 17:29:37 +0100 Subject: [PATCH 6/7] Document double-snapshotting. --- Source/Session/Session.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Source/Session/Session.swift b/Source/Session/Session.swift index ea9664e..34fcdda 100644 --- a/Source/Session/Session.swift +++ b/Source/Session/Session.swift @@ -215,6 +215,7 @@ extension Session: VisitDelegate { extension Session: VisitableDelegate { public func visitableViewWillAppear(_ visitable: Visitable) { defer { + /// Nilling out the previous visit here prevents `double-snapshotting` for web -> web visits. previosVisit = nil } From fc05e387fcd49aea3be7b87c636a6dc30eece899 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Denis=20=C5=A0vara?= Date: Mon, 4 Mar 2024 17:59:26 +0100 Subject: [PATCH 7/7] Fix typo. --- Source/Session/Session.swift | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Source/Session/Session.swift b/Source/Session/Session.swift index 34fcdda..a464d0a 100644 --- a/Source/Session/Session.swift +++ b/Source/Session/Session.swift @@ -35,7 +35,7 @@ public class Session: NSObject { private var currentVisit: Visit? private var topmostVisit: Visit? - private var previosVisit: Visit? + private var previousVisit: Visit? /// The topmost visitable is the visitable that has most recently completed a visit public var topmostVisitable: Visitable? { @@ -216,7 +216,7 @@ extension Session: VisitableDelegate { public func visitableViewWillAppear(_ visitable: Visitable) { defer { /// Nilling out the previous visit here prevents `double-snapshotting` for web -> web visits. - previosVisit = nil + previousVisit = nil } guard let topmostVisit = self.topmostVisit, let currentVisit = self.currentVisit else { return } @@ -234,7 +234,7 @@ extension Session: VisitableDelegate { } else if visitable !== topmostVisit.visitable { // Navigating backward from a web view screen to a web view screen. visit(visitable, action: .restore) - } else if visitable === previosVisit?.visitable { + } else if visitable === previousVisit?.visitable { // Navigating backward from a native to a web view screen. visit(visitable, action: .restore) } @@ -254,11 +254,11 @@ extension Session: VisitableDelegate { } public func visitableViewWillDisappear(_ visitable: Visitable) { - previosVisit = topmostVisit + previousVisit = topmostVisit } public func visitableViewDidDisappear(_ visitable: Visitable) { - previosVisit?.cacheSnapshot() + previousVisit?.cacheSnapshot() deactivateVisitable(visitable) }