Skip to content

[Woo POS] Improve VoiceOver accessibility for barcode scanning errors #15793

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

Open
wants to merge 10 commits into
base: trunk
Choose a base branch
from

Conversation

staskus
Copy link
Contributor

@staskus staskus commented Jun 20, 2025

WOOMOB-652

Description

This PR improves VoiceOver accessibility for barcode scanning errors in Woo POS. When barcode scanning fails, users will now receive audio feedback with accessibility focusing on a cart item row.

The implementation addresses several accessibility gaps:

  1. Error announcement timing: VoiceOver announcements are timed after failure sounds complete
  2. Proper accessibility focus: Error items automatically receive VoiceOver focus using native SwiftUI patterns

Additionally

  1. Bullet point accessibility: Information modal bullet points are read as sequential steps rather than literal bullet characters

Steps to reproduce

  1. Launch the app and open POS
  2. Enable VoiceOver
  3. Attempt to scan invalid barcodes or trigger scan errors
  4. Verify VoiceOver announces errors after the failure sound completes
  5. Confirm that focus moves to error items automatically
  6. Test barcode information modal for proper bullet point reading

Testing information

Tested on iPad Air M2 18.3 with VoiceOver enabled.


  • I have considered if this change warrants user-facing release notes and have added them to RELEASE-NOTES.txt if necessary.

staskus added 6 commits June 20, 2025 17:11
- Add playSound method with completion parameter
- Implement AVAudioPlayerDelegate for precise sound finish detection
- Maintain backward compatibility with existing playSound calls
- Enable VoiceOver announcements after sound playback completes
- Add optional accessibilityLabel property to Cart.PurchasableItem
- Generate accessibility labels for barcode scan errors with format "Error description. Barcode"
- Update ItemRowView to use custom accessibility labels for VoiceOver
- Maintain default behavior for normal cart items
- Import UIKit for UIAccessibility support
- Use sound player completion callback to trigger VoiceOver announcements
- Announce accessibility labels after barcode scan failure sounds complete
- Improve accessibility feedback for scanning errors
- Add accessibilityFocusedItemID property to Cart model
- Implement @AccessibilityFocusState in CartView for native VoiceOver focus
- Replace UIAccessibility.post with accessibility focus on error items after sound
- Remove UIKit dependency from PointOfSaleAggregateModel
- Use proper SwiftUI accessibility patterns for better user experience
- Add accessibility labels to replace bullet point characters with sequential indicators
- Create screen reader-friendly versions using "First:", "Second:", etc. instead of "•"
- Preserve visual bullet points for sighted users while improving screen reader experience
- Add proper localization for accessible text alternatives
@dangermattic
Copy link
Collaborator

dangermattic commented Jun 20, 2025

1 Warning
⚠️ View files have been modified, but no screenshot or video is included in the pull request. Consider adding some for clarity.

Generated by 🚫 Danger

@wpmobilebot
Copy link
Collaborator

wpmobilebot commented Jun 20, 2025

App Icon📲 You can test the changes from this Pull Request in WooCommerce iOS Prototype by scanning the QR code below to install the corresponding build.

App NameWooCommerce iOS Prototype
Build Number30669
VersionPR #15793
Bundle IDcom.automattic.alpha.woocommerce
Commitca5a868
Installation URL7o2cmrmjgrfqo
Automatticians: You can use our internal self-serve MC tool to give yourself access to those builds if needed.

@staskus staskus added type: task An internally driven task. feature: POS labels Jun 20, 2025
@staskus staskus added this to the 22.7 milestone Jun 20, 2025
@staskus staskus requested a review from joshheald June 20, 2025 15:40
Copy link
Contributor

@joshheald joshheald left a comment

Choose a reason for hiding this comment

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

I didn't notice this one before, sorry... but the first bullet point reads as

First, set up barcodes in the G-T-I-N, U-P-C, ean, I-S-B-N field in Products greater than Product Details greater than Inventory. More details.

So a few things we could improve

  1. Announce that More details is a link/button
  2. Announce the > as "right arrow", or use a better symbol to indicate the screen transitions.
  3. Spell out E-A-N.

Similarly, in the second bullet point, we could spell out H-I-D

The error announcing is much better. I think we should probably move the focus to any new item that's added to the cart (via scanning only)... that would also help with an issue we have now where the error is only announced the first time it happens – even if there are other successful scans in the meantime. It only announces it again if the message is different (e.g. a different erroring item is scanned.)

error.announcements.mp4

This is a good improvement though, and I don't want us to delve too deep at the expense of other things. See what you think about further improvements.

// Only play a sound and track analytics if the item still exists in the cart.
await soundPlayer.playSound(.barcodeScanFailure)
await soundPlayer.playSound(.barcodeScanFailure, completion: { [weak self] in
self?.cart.accessibilityFocusedItemID = errorItem.id
Copy link
Contributor

Choose a reason for hiding this comment

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

It's nice that we set this inside the scanned method – it means we can easily do it for non-errors as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, I'll do it. Looks simple enough and makes sense for the UX.

@@ -90,9 +91,15 @@ struct CartView: View {
})
.id(cartItem.id)
.transition(.opacity)
.accessibilityFocused($accessibilityFocusedItem, equals: cartItem.id)
Copy link
Contributor

Choose a reason for hiding this comment

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

It seems strange that this doesn't result in errors being read out after it changes from error to success back to the same error again. There must be some string diffing happening inside VoiceOver to prevent it reading the same thing over and over.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good observation. I'm taking a look how to solve this 🤔

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I cannot find a working solution. I tried resetting the focus and introducing timeouts, but it doesn't help. Although it's not documented, it does seem that the same accessibility label is not read twice with this @AccessibilityFocusState.

@staskus
Copy link
Contributor Author

staskus commented Jun 23, 2025

@joshheald thanks for testing!

  • Improved the accessibility labels for the information modal
  • Put accessibility focus on a successfully scanned item as well

However, I wasn't able to find a clean solution for reading out the same accessibility label twice in a row 🤔 If I put any new prefix on accessibility label, then it's read fine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature: POS type: task An internally driven task.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants