8000 [PM-18223] Add share error details button to error alerts without a message by matt-livefront · Pull Request #1438 · bitwarden/ios · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

[PM-18223] Add share error details button to error alerts without a message #1438

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

Merged
merged 6 commits into from
Mar 25, 2025

Conversation

matt-livefront
Copy link
Collaborator
@matt-livefront matt-livefront commented Mar 17, 2025

🎟️ Tracking

PM-18381 - Add Feature Flag for Error Reporting
PM-18223 - Implement 'Share error details' Button in Error Dialogs

📔 Objective

This adds the mobile error reporting feature flag and implements the foundational pieces to support showing the 'Share error details' button in error alerts.

The 'Share error details' button will show for any default alerts which don't already show a message. Because 'Share error details' will need access to some services and the coordinator to show a share sheet, I've changed how displaying default error alerts works.

Before: coordinator.showAlert(.networkResponseError(error))
After: await coordinator.showErrorAlert(error: error, services: services)

This was the best compromise to keep this to a one-liner in a processor when presenting this alert, while supporting this new functionality. I tried writing an extension on Coordinator that would allow access to services without needing to pass those in, but ultimately couldn't get it to work with the protocol definition that we have. I think we would instead need a Coordinator base class, which we could explore in the future if needed.

To keep this PR small and focused on the setup pieces, I only updated VaultListProcessor to use this new pattern and will follow up with another PR updating the rest of the app.

Note

This introduces a few SwiftLint warnings, which will be resolved in the next PR:

Backward matching of the unlabeled trailing closure is deprecated; label the argument with 'tryAgain' to suppress this warning.

📸 Screenshots

Before After
Screenshot 2025-03-17 at 10 41 52 AM Screenshot 2025-03-17 at 10 42 06 AM

⏰ Reminders before review

  • Contributor guidelines followed
  • All formatters and local linters executed and passed
  • Written new unit and / or integration tests where applicable
  • Protected functional changes with optionality (feature flags)
  • Used internationalization (i18n) for all UI strings
  • CI builds passed
  • Communicated to DevOps any deployment requirements
  • Updated any necessary documentation (Confluence, contributing docs) or informed the documentation team

🦮 Reviewer guidelines

  • 👍 (:+1:) or similar for great changes
  • 📝 (:memo:) or ℹ️ (:information_source:) for notes or general info
  • ❓ (:question:) for questions
  • 🤔 (:thinking:) or 💭 (:thought_balloon:) for more open inquiry that's not quite a confirmed issue and could potentially benefit from discussion
  • 🎨 (:art:) for suggestions / improvements
  • ❌ (:x:) or ⚠️ (:warning:) for more significant problems or concerns needing attention
  • 🌱 (:seedling:) or ♻️ (:recycle:) for future improvements or indications of technical debt
  • ⛏ (:pick:) for minor or nitpick changes

Copy link
codecov bot commented Mar 17, 2025

Codecov Report

Attention: Patch coverage is 57.81250% with 27 lines in your changes missing coverage. Please review.

Project coverage is 85.78%. Comparing base (1461a65) to head (63c9977).
Report is 11 commits behind head on main.

Files with missing lines Patch % Lines
...wardenShared/UI/Platform/Tabs/TabCoordinator.swift 0.00% 6 Missing ⚠️
BitwardenShared/UI/Auth/AuthCoordinator.swift 0.00% 1 Missing ⚠️
...gin/LoginWithDevice/LoginWithDeviceProcessor.swift 0.00% 1 Missing ⚠️
...h/TwoFactorNotice/TwoFactorNoticeCoordinator.swift 0.00% 1 Missing ⚠️
...hared/UI/Platform/Application/AppCoordinator.swift 0.00% 1 Missing ⚠️
< 8000 a href="https://app.codecov.io/gh/bitwarden/ios/pull/1438?src=pr&el=tree&filepath=BitwardenShared%2FUI%2FPlatform%2FDebugMenu%2FDebugMenuCoordinator.swift&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=bitwarden#diff-Qml0d2FyZGVuU2hhcmVkL1VJL1BsYXRmb3JtL0RlYnVnTWVudS9EZWJ1Z01lbnVDb29yZGluYXRvci5zd2lmdA==" rel="nofollow">...d/UI/Platform/DebugMenu/DebugMenuCoordinator.swift 0.00% 1 Missing ⚠️
...orm/ExtensionSetup/ExtensionSetupCoordinator.swift 0.00% 1 Missing ⚠️
...tform/FileSelection/FileSelectionCoordinator.swift 0.00% 1 Missing ⚠️
...latform/LoginRequest/LoginRequestCoordinator.swift 0.00% 1 Missing ⚠️
...PasswordAutoFill/PasswordAutoFillCoordinator.swift 0.00% 1 Missing ⚠️
... and 12 more
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1438      +/-   ##
==========================================
- Coverage   89.58%   85.78%   -3.81%     
==========================================
  Files         767      984     +217     
  Lines       48194    58509   +10315     
==========================================
+ Hits        43176    50191    +7015     
- Misses       5018     8318    +3300     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Contributor
github-actions bot commented Mar 17, 2025

Logo
Checkmarx One – Scan Summary & Detailsd54e7b8b-456a-4079-bdb9-eaf5c5a6b668

Great job, no security vulnerabilities found in this Pull Request

Copy link
Member
@fedemkr fedemkr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 Even though this approach makes sense, I'm not very fond of passing the services through a function to the Coordinator, although I know it's tricky given that it's a protocol.
Do we only need the services for the feature flag check? Or are you planning to use more services in the Coordinator -> showErrorAlert function? Or well anywhere in the Coordinator?
🤔 I was thinking about having some sort of proxy or composed/wrapped Coordinator so you can add any additional logic without disrupting the original implementation. Therefore you can pass any coordinator to be wrapped in the composed one to add this feature flag logic change.

// MARK: Types

typealias ErrorAlertServices = HasConfigService & HasErrorReporter
typealias ErrorAlertTryAgain = (() async -> Void)?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 Why is this typealias needed? Couldn't the closure be used in-situ or you wanted to be flexible to be changed in all places in the future just by changing it here?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not strictly necessary, but it was used in enough places where it seemed nice to have. At one point it wasn't async and I made it async and that triggered a trickle down of errors so I liked the convenience of just defining it once. I'm not particularly tied to it if you prefer to define it in place though.

// MARK: Types

typealias ErrorAlertServices = HasConfigService & HasErrorReporter
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 Is errorReporter service going to be used in a future PR? Because I'm not seeing that used here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My plan was to put the logic of building the error details to share within that service. So once you tap 'Share error details' it would assemble the error details and app info which could then be shared.

@matt-livefront
Copy link
Collaborator Author

🤔 Even though this approach makes sense, I'm not very fond of passing the services through a function to the Coordinator, although I know it's tricky given that it's a protocol. Do we only need the services for the feature flag check? Or are you planning to use more services in the Coordinator -> showErrorAlert function? Or well anywhere in the Coordinator?

We need the services for the feature flag check and possibly some other service that builds the error details + app info that is ultimately shared.

We also have an isOfficialBitwardenServer parameter in Alert.networkResponseError() to determine if the server is an official one or not, but because you have to manually pass this in from the processor, we've only implemented that on a single screen. But this is another case where we could replace that with a shared solution if we had access to services when building the alert.

For the flight recording feature, we talked about possibly logging breadcrumbs. The easiest place to put this could be in the coordinator, or a coordinator base class? For this reason, I kind of had in my head that we might need to create a coordinator base class (or similar solution) which has access to the coordinator's services. And if we did that we could stop passing the services around here.

I have similar concerns around passing services, but putting this logic in the coordinator seemed like the best spot and it feels like we might be headed in a direction that we could get the services directly from the coordinator and avoid this passing.

🤔 I was thinking about having some sort of proxy or composed/wrapped Coordinator so you can add any additional logic without disrupting the original implementation. Therefore you can pass any coordinator to be wrapped in the composed one to add this feature flag logic change.

This kind of sounds like what AnyCoordinator. Is that kind of what you had in mind?

@matt-livefront
Copy link
Collaborator Author

@fedemkr 6d260df updates this to use the services from the coordinator, let me know what you think of that.

@matt-livefront matt-livefront requested a review from fedemkr March 19, 2025 15:30
Copy link
Member
@fedemkr fedemkr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 I think this is is better than the previous solution @matt-livefront and then as you mentioned before we can explore having a base one but I think this lays the grounds for that.
The only other thing I was thinking is: if a coordinator doesn't conform to the new protocol then it wouldn't display the new alert but it would have to have a custom implementation for showErrorAlert, right? So I believe it's a "safe" way to know we're missing that protocol in a new Coordinator.
What do you think @KatherineInCode ?

Comment on lines +115 to +120
func showErrorAlert(error: any Error, tryAgain: (() async -> Void)?) async {
errorReporter.log(error: BitwardenError.generalError(
type: "TabCoordinator: `showErrorAlert` Not Supported",
message: "`showErrorAlert(error:tryAgain:)` is not supported from TabCoordinator."
))
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 Is this not supported here because the TabCoordinator has only other internal coordinators which would handle errors?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, it doesn't build any processors that would ultimately call this on this coordinator. It also doesn't have access to the service container currently, which was the main reason I didn't just implement the protocol.

Comment on lines 284 to 286
await coordinator.showErrorAlert(error: error)
}
services.errorReporter.log(error: error)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤔 Now that I see this, I think one think to keep in mind would be to almost always log to the error reporter first before showing any error alert; so in here services.errorReporter.log(error: error) should be at the beginning of the catch instead of at the end. Therefore if the user/OS kills the app while the alert is being shown the error gets reported nonetheless to Crashlytics.
If I'm thinking this correctly if the above happens now we're going to miss the error report because the catch clause will await for the error alert to finish in order to log in the ErrorReporter.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can move up the error reporter call. Even though showErrorAlert is async though, it will continue on after the coordinator calls present on the alert controller as we don't continue waiting for the alert to be dismissed. So probably minimal risk to the app being killed before the log call.

@fedemkr
Copy link
Member
fedemkr commented Mar 19, 2025

Also @matt-livefront sorry I didn't answer the previous comment earlier, thanks so much for the additional detailed information 😄 It has been really helpful to understand everything.
Regarding the wrapper, yes I was thinking something alike the AnyCoordinator that actually could take an AnyCoordinator as a parameter to add any additional logic in top of that. However, maybe your last commit solution is better than that and requires less changes in structure.

@matt-livefront
Copy link
Collaborator Author

The only other thing I was thinking is: if a coordinator doesn't conform to the new protocol then it wouldn't display the new alert but it would have to have a custom implementation for showErrorAlert, right? So I believe it's a "safe" way to know we're missing that protocol in a new Coordinator.

@fedemkr Exactly, you'll get a build error that the coordinator doesn't conform to the Coordinator protocol. And then conforming to HasErrorAlertServices should be easier than implementing a custom showErrorAlert implementation.

628C

@KatherineInCode
Copy link
Contributor

@fedemkr @matt-livefront I do think exploring what a base Coordinator might look like may actually be the best route to go. I'm a little concerned about us making Coordinator things internal as well, because ultimately we'll want to move the Coordinator protocol or base class into BitwardenKit so both PM and BWA can have access to it.

@matt-livefront
Copy link
Collaborator Author

@fedemkr @matt-livefront I do think exploring what a base Coordinator might look like may actually be the best route to go. I'm a little concerned about us making Coordinator things internal as well, because ultimately we'll want to move the Coordinator protocol or base class into BitwardenKit so both PM and BWA can have access to it.

I removed some public access levels on Coordinator in my original approach (dedf18c) because including services in the showErrorAlert call would have required those services (or the protocols at least and any types referenced) to be public, which seemed unnecessary since the coordinator doesn't need to be public today. But that has since changed with the latest changes.

I think it may still be worth exploring a base class, especially if we want breadcrumb logging from the coordinator. But maybe as a separate effort as this approach seemed less invasive. On one hand, the approach I landed on might be a little more flexible depending on where you draw the line for what goes in BitwardenKit vs not. A base class would likely require services in BitwardenKit, whereas something like HasErrorAlertServices allows you to compose protocols at any package level.

@KatherineInCode
Copy link
Contributor

depending on where you draw the line for what goes in BitwardenKit vs not

Which is something we're still hashing out a bit, and will be able to a bit more once I get back to finishing up the first wave of monorepo work.

To a certain extent, I think we might want to pull a lot of those services into BitwardenKit when we're all said and done—especially obviously-common things like error alerting, logging, and such. It is something we can also figure out when we get there, and that includes the public/internal status of various protocols/structs/classes.

I do think exploring a base class might be a good idea. I'm usually about composition over inheritance, but this seems like one of the few places doing inheritance might be better.

@matt-livefront
Copy link
Collaborator Author

depending on where you draw the line for what goes in BitwardenKit vs not

Which is something we're still hashing out a bit, and will be able to a bit more once I get back to finishing up the first wave of monorepo work.

To a certain extent, I think we might want to pull a lot of those services into BitwardenKit when we're all said and done—especially obviously-common things like error alerting, logging, and such. It is something we can also figure out when we get there, and that includes the public/internal status of various protocols/structs/classes.

I do think exploring a base class might be a good idea. I'm usually about composition over inheritance, but this seems like one of the few places doing inheritance might be better.

Yep, that all makes sense.

@matt-livefront matt-livefront requested a review from fedemkr March 20, 2025 16:44
@fedemkr
Copy link
Member
fedemkr commented Mar 20, 2025

In my opinion when we move these classes to the BitwardenKit we'd need to think about it so not to add too much dependency as you were saying. Like the error or config service protocols should only have the minimum that the BitwardenKit needs and not everything else that may be needed for the actual app. I think that goes beyond the scope of the PR so perhaps we could do the refactor in a different one when we have more clarity about what to share; I would think this whole feature will be there but we can always reiterate on this.

@fedemkr
Copy link
Member
fedemkr commented Mar 20, 2025

I believe we can approve as is or would you like any other changes @KatherineInCode done directly in this PR to prepare for the broader refactor/preparation for the Kit?

@KatherineInCode
Copy link
Contributor

I agree that the move to BitwardenKit is outside the scope of this PR, and I'd rather save any real preparations until a moment closer to the last responsible one, because at that point we'll have a much better idea of what we'll need to do.

I have no objections to moving forward as-is.

@matt-livefront matt-livefront merged commit 6fd7a9c into main Mar 25, 2025
12 checks passed
@matt-livefront matt-livefront deleted the matt/PM-18223-share-error-alert branch March 25, 2025 16:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants
0