-
Notifications
You must be signed in to change notification settings - Fork 88
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Snapshot and deactivate Visitable during it's lifecycle #181
Snapshot and deactivate Visitable during it's lifecycle #181
Conversation
Thanks @pfeiffer! This is definitely a longstanding issue and something we never addressed. The issue presents itself in our 37signals apps when navigating from a native screen -> webview screen -> back to native screen -> revisit webview screen. The webview may not have a snapshot at all or have a stale snapshot before the fresh data is loaded. My one concern is whether the snapshot caching approach isn't quite smart enough and we'll be double-snapshotting in the normal visit flow in a single session. Also, FYI, we'll need to address this in |
@pfeiffer I implemented an initial, very similar solution for It does appear to solve the issue nicely. The only concern I have that remains is now both the native library and core |
@jayohms I think that is a valid concern and I had the same when implementing this. One thing I could think of is if there'd be a potential race condition when eg. manipulating the page before snapshotting, eg. via I suppose what we want is to only snapshot from native side if no other |
Yeah, it's not exactly straightforward to solve this elegantly. Let's think about it a bit more and see if we can come up with an approach that avoids double-caching for normal web -> web visits. |
@jayohms I gave it some thought over the weekend and I have potential, albeit not that elegant solution working, skipping the snapshot caching from native side, when performing a normal JS visit via Turbo with something like: // Session
private var disappearingVisitNeedingSnapshot: Visit?
public func visitableViewWillAppear(_ visitable: Visitable) {
self.disappearingVisitNeedingSnapshot = nil
// ...
}
public func visitableViewWillDisappear(_ visitable: Visitable) {
self.disappearingVisitNeedingSnapshot = topmostVisit
}
public func visitableViewDidDisappear(_ visitable: Visitable) {
disappearingVisitNeedingSnapshot?.cacheSnapshot()
// ..
} This works as expected; it delays the snapshotting until after a potential new visit has been triggered, in which case the What are your thoughts? Is it worth it? |
@pfeiffer interesting, I don't think this would work when going from a web -> native screen, would it? We want to capture the |
It would; in case of navigating to a native screen, the |
…ill appear to avoid double snapshots
@jayohms Pushed an update here with the proposed solution, and we now avoid double-snapshotting when moving from web -> web (handled by Turbo JS), while taking a snapshot from native side when moving from web -> native or between two web sessions (ie. modal) During my testing, I've discovered an unrelated issue with the native -> web navigations, where the Is the double-snapshotting also an issue with hotwired/turbo-android#303? |
In order to address that, I actually needed access to the last disappearing visit, so I've pushed the fix to this PR instead in 661b3a9. An advance -> restore flow between web and native will now snapshot when advancing as well as detect the restore when navigating back, activating the WebView during the transition: Simulator.Screen.Recording.-.iPhone.15.Pro.-.2024-02-27.at.06.44.48.mov |
@@ -225,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 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This covers the case of restoring from native screen back to web, which previously wasn't considered a restore. The web view would both topmost and current visit, even when navigating back.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This works as expected.
Since this is an or operation I'd propose to evaluate the latter in a separate else if
statement.
This will make it clear that we're dealing with two types of backward navigation.
I've addressed it and added comments in my feedback PR #189
Hey @jayohms, is there anything I can help with to land this? |
@pfeiffer I'll wrap up the review today or tomorrow at the latest. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@@ -225,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 { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This works as expected.
Since this is an or operation I'd propose to evaluate the latter in a separate else if
statement.
This will make it clear that we're dealing with two types of backward navigation.
I've addressed it and added comments in my feedback PR #189
@@ -35,6 +35,7 @@ public class Session: NSObject { | |||
|
|||
private var currentVisit: Visit? | |||
private var topmostVisit: Visit? | |||
private var disappearingVisitForSnapshotting: Visit? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've renamed disappearingVisitForSnapshotting
to previousVisit
in the feedback PR #189.
cc @jayohms @joemasilotti What do you think about the name?
This PR fixes #180.
Today, when dismissing eg. a modal, a snapshot is not taken of the WebView before the dismissal. This is different to the conceptual idea of snapshots in Turbo, where a snapshot it taken before any navigation events happen, so that any re-visits could show a snapshot.
This leads to some unexpected behavior, the most simple example being in the Demo app, where opening the modal example multiple times leads to no or stale snapshots being shown, as described in #180.
This PR addresses that by notifying adding two new lifecycle methods to the Session -
visitableViewWillDisappear
andvisitableViewDidDisappear
. When aVisitable
will disappear (eg. if closing a modal or otherwise navigating away from it natively), a snapshot is taken of the page, ensuring that we have a fresh snapshot to show on next re-visit. When theVisitable
did disappear, it's WebView will be deactivated, ensuring that there's no "orphaned", but active WebView.In a normal single-session setup this should have no effect, but it fixes some of the issues we've seen while using a multi-session setup (eg. with tabs, modals, stacks etc.).
The behavior before in the demo app with a timeout on the
/new
page and showing a random number:Simulator.Screen.Recording.-.iPhone.15.Pro.-.2024-02-23.at.14.23.13.mov
The behavior with the changes in this PR:
AfterPR.mov
CC @jayohms @joemasilotti