-
Notifications
You must be signed in to change notification settings - Fork 14
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
Lazy load background tabs at app startup #553
Conversation
Also wait for the current tab to finish loading before initiating lazy loading.
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.
Dominik, this looks good to me! 👍 Very nice code 🎖️ 🏅
Please, I will approve once we discuss suggestions I raised in Asana related to:
- Pausing of the lazy loading if currently selected tab starts loading
- Disabling of title changes while tab is being lazy loaded
@@ -1091,6 +1097,7 @@ extension Tab: WKNavigationDelegate { | |||
// https://app.asana.com/0/1199230911884351/1200381133504356/f | |||
// hasError = true | |||
|
|||
webViewDidFailNavigationPublisher.send() |
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.
Nice! 👍
@@ -59,6 +59,7 @@ final class TabCollectionViewModel: NSObject { | |||
|
|||
// In a special occasion, we want to select the "parent" tab after closing the currently selected tab | |||
private var selectParentOnRemoval = false | |||
private var tabLazyLoader: TabLazyLoader<TabCollectionViewModel>? |
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.
Nice! 👍
import Combine | ||
import os | ||
|
||
final class TabLazyLoader<DataSource: TabLazyLoaderDataSource> { |
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.
Please, just to understand, what is the advantage of data source being generic parameter here?
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.
The main reason for introducing generics to this code was increasing testability. It starts with Tab
class which is hard to work with in unit tests, because it has an assert in deinit
requiring to call tabWillClose()
before deinitializing. It also contains a web view, etc.
But the lazy loader works on tabs via TabCollectionViewModel
, and even if we created a data source protocol that TabCollectionViewModel
would implement, that protocol would still make use of the concrete Tab
type. And after all, the lazy loader only needs a few properties and functions from Tab
, and we definitely don't want to deal with a real webView in unit tests.
Hence the LazyLoadable
protocol and the use of associatedtype
in the data source protocol. And that associated type makes it impossible to use TabLazyLoaderDataSource
as a standalone type, producing the infamous error:
Protocol 'TabLazyLoaderDataSource' can only be used as a generic constraint because it has Self or associated type requirements
So for the lazy loader to define a data source of type TabLazyLoaderDataSource
, it needs to become a generic class.
Bottom line - it was not my intention to make the code look sophisticated through the use of generics, but rather it was the only way to get the code to compile after abstracting the data source :)
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.
Thanks! 🙏 More clear to me now :)
|
||
var url: URL? { content.url } | ||
|
||
var loadingFinishedPublisher: AnyPublisher<Tab, Never> { |
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.
Nice! 👍
… to current Load 10 adjacent tabs, based on index diff vs current tab: 1, -1, 2, -2, 3, etc. After that, load 10 recently selected tabs.
I added loading adjacent tabs when there are more than 20 tabs, but this PR will keep evolving as I'm adding new features requested in the Asana task comments :) marking as draft for now. |
@tomasstrba the PR is ready for review and contains the following changes since last time you've checked it:
I will work on "Reopen Last Closed Window" in a separate pull request as I believe it is fully independent of these changes. |
Awesome! 👏 Will take a look at it soon. (Probably won't be part of the release today, but that is ok) |
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.
LGTM! Nice work! 💯 I only have found last blocker that I would like to discuss. We currently trigger lazy loading for each window. Does it make sense to limit it to the main window?
Hypothetically, someone with 10 windows restored could have really slow start. WDYT?
import Combine | ||
import os | ||
|
||
final class TabLazyLoader<DataSource: TabLazyLoaderDataSource> { |
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.
Thanks! 🙏 More clear to me now :)
a56acba
to
3699edf
Compare
That's a very fair point Tom! I adjusted the code so that lazy loading is requested by Going forward, lazy loading can be expanded to cover multiple windows as a separate project, someday. |
Jumping on this for a final review |
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.
LGTM! 👍
@mallexxx had a pretty good point in Asana, that if there is no connection after the start, tabs show "Oooops" title. I think it is fine to move on with this PR but do you mind scoping a follow-up task solving this issue?
Thank you very much Tom! Yes, I will add a follow-up task according to the comment from @mallexxx. |
# By Alexey Martemyanov (3) and others # Via GitHub * develop: Lazy load background tabs at app startup (#553) Update the Fireproof checkmark in the Save Credentials view controller (#555) Support config v2 (#528) Fullscreen video fixing (#541) Add data import failure pixels (#552) Update BSK to fix autofill on Catalina (#551) fix contrast bug on Catalina / Big Sur (#546) Disable download reload on page tab reactivation/session restoration (#516) Add "New Window" item to App Dock menu (#544) # Conflicts: # DuckDuckGo.xcodeproj/project.pbxproj
Task/Issue URL: https://app.asana.com/0/1177771139624306/1201065943414949/f
Tech Design URL:
CC: @mallexxx @tomasstrba
Description:
I added a mechanism that records selected tabs in chronological order and reloads them in that order at app startup.
The logic is as follows:
Additional details:
TabLazyLoader
is owned byTabCollectionViewModel
which serves as its data sourceOSLog.tabLazyLoading
was added to help debugging lazy loading (disabled by default)Steps to test this PR:
You may find this URL helpful when testing: http://tfc.home.pl/dev/dominik/index-delay.php?delay=4
It's a PHP script that loads a simple website after a delay specified in the parameter.
Also enable logging for better insights into what's happening.
Scenario 1:
Scenario 2:
Scenario 3:
Scenario 4:
Scenario 5:
Scenario 6:
...
Testing checklist:
Internal references:
Software Engineering Expectations
Technical Design Template
When ready for review, remember to post the PR in MM