-
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
Alessandro/address bookmarks feedback bookmark logic #2231
Alessandro/address bookmarks feedback bookmark logic #2231
Conversation
🚫 The Asana task linked in the PR description is not added to macOS App Board project.
|
…popover and dialog
d2a954d
to
00b1f99
Compare
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.
Works as expected from what I see. In general looks good but I feels like architecture is a little bit too complicated for a simple feature.
It feels like there are too many layers but this is probably more of a personal choice.
I have left a few comments we can discuss some although in general I am happy to approve it.
|
||
func cancel() | ||
func addOrSave() | ||
protocol AddEditBookmarkFolderDialogViewModelProtocol: BookmarksDialogViewModel { |
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 don’t like the name protocol to a protocol… Maybe we can chose a name that describes what it is useful for...
Maybe could be BookmarkFolderEditing?
Also I would apply BookmarksDialogViewModel protocol to the actual view model directly so that this can be separate although I suppose it complicates the coordinator
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.
No problem for the name, not a fan of the word protocol either.
About "Also I would apply BookmarksDialogViewModel protocol to the actual view model directly so that this can be separate although I suppose it complicates the coordinator”, the Coordinator still needs to accept a AddEditBookmarkFolderDialogViewModelProtocol
so if I don’t use this approach I cannot inject the VM.
I could use composition via typealias
and have something like typalias BookmarkFolderDialogViewModel = <protocol_a> & <protocol_b>
but there are no scenarios where I would inject only protocol_a
or only protocol_b
.
Happy to have a chat about this if I misunderstood what you meant.
import Combine | ||
|
||
@MainActor | ||
protocol AddEditBookmarkDialogViewModelProtocol: BookmarksDialogViewModel { |
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.
Again we could find a protocol name based on what it does maybe BookmarkDialogModeling?
Also I would apply BookmarksDialogViewModel protocol to the actual view model directly so that this can be separate although I suppose it complicates the coordinator
if bookmark.url != url.absoluteString { | ||
bookmark = bookmarkManager.updateUrl(of: bookmark, to: url) ?? bookmark | ||
} | ||
// |
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 guess this can be deleted
import SwiftUI | ||
import Combine | ||
|
||
final class AddEditBookmarkDialogCoordinatorViewModel<BookmarkViewModel: AddEditBookmarkDialogViewModelProtocol, AddFolderViewModel: AddEditBookmarkFolderDialogViewModelProtocol>: ObservableObject { |
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.
It feels like we have to fight the system doesn’t it?!
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 think this approach is ok, I was thinking another approach that might be more streamlined could be to have a unique view model for edit folders and bookmarks since they are closely related day they are very related and have the same dependency.
Just thinking out loud do not need to change.
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 thought about that, my worry was that the VM would increase in complexity to handle all the different cases/operations.
return UserText.save | ||
} | ||
private var addFolderView: some View { | ||
AddEditBookmarkFolderView( |
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.
For both isn’t it better to pass the view model directly? I assume you will want to use the view from all the other places and don’t want to do the list every time
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 get your point, the reason I’m not currently passing the VM is because the “Popover” we show from the address bar re-use the same views but have a different VM due to the live updates. Having said, that I should be able (🤞) to re-use the protocols I made for the new VMs and inject that one to the view. The thing is as soon as I pass the protocol I have to make the Views generic over the ViewModel due to any <viewModelProtocol>
and not being able to use property wrappers.
c8c6737
into
alessandro/address-bookmarks-feedback
Task/Issue URL: https://app.asana.com/0/0/1206590790808718/f **Description**: 1. Implementation of add/edit bookmark dialog logic + tests. 2. Extracted view to be reused by AddBookmarkPopoverView & AddEditBookmarkDialogView
Task/Issue URL: https://app.asana.com/0/0/1206590790808718/f **Description**: 1. Implementation of add/edit bookmark dialog logic + tests. 2. Extracted view to be reused by AddBookmarkPopoverView & AddEditBookmarkDialogView
Task/Issue URL: https://app.asana.com/0/0/1206590790808718/f **Description**: 1. Implementation of add/edit bookmark dialog logic + tests. 2. Extracted view to be reused by AddBookmarkPopoverView & AddEditBookmarkDialogView
Task/Issue URL: https://app.asana.com/0/0/1206590790808718/f **Description**: 1. Implementation of add/edit bookmark dialog logic + tests. 2. Extracted view to be reused by AddBookmarkPopoverView & AddEditBookmarkDialogView
Task/Issue URL: https://app.asana.com/0/0/1206590790808718/f **Description**: 1. Implementation of add/edit bookmark dialog logic + tests. 2. Extracted view to be reused by AddBookmarkPopoverView & AddEditBookmarkDialogView
Task/Issue URL: https://app.asana.com/0/0/1206590790808718/f
CC: @samsymons
Description:
In this PR I address the following:
NOTE
I tried different approaches to present the “Add Folder” dialog when the user taps on the “Add Folder” button in the “Add/Edit Bookmark” dialog. The main issue encountered was to "sync” the folder selected by the user via the picker in the “Add Bookmar” dialog to the one in the “Add Folder” dialog and viceversa. Scenarios explained below:
Scenario 1:
Expected result: The picker in the “Add Folder” dialog should have selected the folder previously selected by the user.
Scenario 2:
Expected result: On landing back to the “Add/Edit Bookmark” dialog the picker should have selected the new folder created.
Approach 1.
Use of the @EnvironmentObject as previously discussed.
This approach was simple altough I found that to synchronise the two folders I had to do some logic in the view and I could not test that.
For scenario 1 I would assign the value from one VM to the other one in
onAppear
as per commit 45185fb#diff-c0a84739fdc5e3437d79a0df01b5ffa4c9464e048036422022d72a6d1cfb2a0bFor scenario 2 I had a callback that fired when the folder was created. The issue with that was that the VM was created beforehand and injected so the closure behaviour would be de at the caller site. That means that this logic would be duplicated at every call site. While this could be fixed via a factory I opted not to have logic in the factory.
Approach 2.
Shared mutable state.
The title says all 🗡️ . I extracted the logic to store the folders and the selected one in its own class. This was then injected in the VMs for both dialogs. It worked fine but shared mutable state is difficult to reason about and always introduce bugs in the long term.
Approach 3.
A wrapper view model (called CoordinatorViewModel) that orchestrates the events between the view model for
AddEdit BookmarkDialogView
& the view model forAddEdit BookmarkFolderDialogView
.I don’t think it’s perfect. It’s not really a coordinator….It hijacks the addFolderAction which is called when the user taps on the “Add Folder” button. This approach let me test every path.
I’m happy to hear your thoughts and I’m available ti have a sync conversation Tomorrow to walk through the PR.
—
Internal references:
Pull Request Review Checklist
Software Engineering Expectations
Technical Design Template
Pull Request Documentation