Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Droidcon NYC 2024: Unpacking Compose Multiplatf...

Colin Marsch
September 21, 2024

Droidcon NYC 2024: Unpacking Compose Multiplatform Accessibility

In this talk, we will provide a comprehensive overview of the current state of accessibility in Compose Multiplatform. As an emerging UI framework in multiplatform development, understanding its accessibility features is critical for developers.

We will delve into the current accessibility capabilities of Compose Multiplatform, discuss its strengths and weaknesses, and provide practical strategies for leveraging its features to ensure the accessibility of your apps.

Colin Marsch

September 21, 2024
Tweet

More Decks by Colin Marsch

Other Decks in Programming

Transcript

  1. var showImage by remember { mutableStateOf(false) } Column { Button(onClick

    = { showImage = !showImage }) { Text("Toggle image") } AnimatedVisibility(showImage) { Image("Compose Multiplatform Logo") } }
  2. Canvas Implementation Pros • Identical UI between platforms • UI

    won’t change during OS version updates • Less platform speci fi c code Cons • Possible performance overhead • Native UI interoperability complexity • Accessibility complexity
  3. Column { Text("How to use UIKitView inside Compose Multiplatform") UIKitView(

    factory = { MKMapView() }, modifier = Modifier.border(2.dp, Color.Blue).size(300.dp), ) }
  4. Column { Text("How to use UIKitView inside Compose Multiplatform") UIKitView(

    factory = { MKMapView() }, modifier = Modifier.border(2.dp, Color.Blue).size(300.dp), ) }
  5. override fun accessibilityLabel(): String? = getOrElse(CachedAccessibilityPropertyKeys.accessibilityLabel) { val contentDescription =

    config.getOrNull(SemanticsProperties.ContentDescription) if (contentDescription != null) { contentDescription } else { val editableText = config.getOrNull(SemanticsProperties.EditableText)?.text editableText ?: config.getOrNull(SemanticsProperties.Text) ?.joinToString("\n") { it.text } } }
  6. override fun accessibilityLabel(): String? = getOrElse(CachedAccessibilityPropertyKeys.accessibilityLabel) { val contentDescription =

    config.getOrNull(SemanticsProperties.ContentDescription) if (contentDescription != null) { contentDescription } else { val editableText = config.getOrNull(SemanticsProperties.EditableText)?.text editableText ?: config.getOrNull(SemanticsProperties.Text) ?.joinToString("\n") { it.text } } }
  7. override fun accessibilityLabel(): String? = getOrElse(CachedAccessibilityPropertyKeys.accessibilityLabel) { val contentDescription =

    config.getOrNull(SemanticsProperties.ContentDescription) if (contentDescription != null) { contentDescription } else { val editableText = config.getOrNull(SemanticsProperties.EditableText)?.text editableText ?: config.getOrNull(SemanticsProperties.Text) ?.joinToString("\n") { it.text } } }
  8. override fun accessibilityLabel(): String? = getOrElse(CachedAccessibilityPropertyKeys.accessibilityLabel) { val contentDescription =

    config.getOrNull(SemanticsProperties.ContentDescription) if (contentDescription != null) { contentDescription } else { val editableText = config.getOrNull(SemanticsProperties.EditableText)?.text editableText ?: config.getOrNull(SemanticsProperties.Text) ?.joinToString("\n") { it.text } } }
  9. override fun accessibilityTraits(): UIAccessibilityTraits = getOrElse(CachedAccessibilityPropertyKeys.accessibilityTraits) { config.getOrNull(SemanticsActions.OnClick)?.let { result

    = result or UIAccessibilityTraitButton } if (config.contains(SemanticsProperties.Heading)) { result = result or UIAccessibilityTraitHeader } }
  10. override fun accessibilityTraits(): UIAccessibilityTraits = getOrElse(CachedAccessibilityPropertyKeys.accessibilityTraits) { config.getOrNull(SemanticsActions.OnClick)?.let { result

    = result or UIAccessibilityTraitButton } if (config.contains(SemanticsProperties.Heading)) { result = result or UIAccessibilityTraitHeader } }
  11. override fun accessibilityTraits(): UIAccessibilityTraits = getOrElse(CachedAccessibilityPropertyKeys.accessibilityTraits) { config.getOrNull(SemanticsActions.OnClick)?.let { result

    = result or UIAccessibilityTraitButton } if (config.contains(SemanticsProperties.Heading)) { result = result or UIAccessibilityTraitHeader } }
  12. UIKitView( factory = { MKMapView() }, modifier = Modifier.semantics {

    contentDescription = "Map of NYC” }, properties = UIKitInteropProperties( // false by default isNativeAccessibilityEnabled = false, ), )
  13. App built by Google • Highlights accessibility issues via an

    overlay on top of the running app • Must install this app on your device or emulator • Performs pass/fail accessibility checks on the UI • Color contrast, touch target size, missing labels, etc. Accessibility Scanner
  14. Manual screen reader testing • Android’s main screen reader technology

    • Requires interpreting the results • Can uncover deeper navigation and interaction issues • Should always be a part of your development work fl ow Talkback
  15. Manual screen reader testing • iOS’ main screen reader technology

    • Requires interpreting the results • Can uncover deeper navigation and interaction issues • Should always be a part of your development work fl ow VoiceOver
  16. XCode developer tool • Examine accessibility properties of UI elements

    • Requires interpreting the results • Supports iOS simulators • Similar to screen readers (i.e. Talkback/VoiceOver) in a written format Accessibility Inspector
  17. • performAccessibilityAudit on XCUIApplication in UI tests • Performs the

    same audit that is able to be manually run from the Accessibility Inspector tool • Performs pass/fail checks similar to Android’s Espresso checks Automated Testing
  18. iOSMain > MainViewController.kt fun MainViewController() = ComposeUIViewController( configure = {

    accessibilitySyncOptions = Always() } ) { App() } iOS Simulator Testing
  19. Want to learn more? • github.com/JetBrains/compose-multiplatform: Examples and documentation •

    github.com/JetBrains/compose-multiplatform-core: All the internals • Compose Multiplatform on iOS: On the Road to Stable - Sebastian Aigner • Tomorrow at 3:25 PM