class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
App()
}
}
}
Slide 11
Slide 11 text
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
App()
}
}
}
Slide 12
Slide 12 text
No content
Slide 13
Slide 13 text
What about iOS?
Slide 14
Slide 14 text
fun MainViewController() : UIViewController =
ComposeUIViewController {
App()
}
Slide 15
Slide 15 text
Skiko
Slide 16
Slide 16 text
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
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 }
}
}
Slide 42
Slide 42 text
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 }
}
}
Slide 43
Slide 43 text
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 }
}
}
Slide 44
Slide 44 text
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 }
}
}
Slide 45
Slide 45 text
override fun accessibilityHint(): String? =
getOrElse(CachedAccessibilityPropertyKeys.accessibilityHint) {
cachedConfig.getOrNull(SemanticsActions.OnClick)?.label
}
Slide 46
Slide 46 text
override fun accessibilityHint(): String? =
getOrElse(CachedAccessibilityPropertyKeys.accessibilityHint) {
cachedConfig.getOrNull(SemanticsActions.OnClick)?.label
}
Slide 47
Slide 47 text
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
}
}
Slide 48
Slide 48 text
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
}
}
Slide 49
Slide 49 text
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
}
}
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
Slide 56
Slide 56 text
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
Slide 57
Slide 57 text
• Espresso accessibility checks
• Paparazzi’s
AccessibilityRenderExtension
• Can integrate into your CI setup
Automated Testing
Slide 58
Slide 58 text
iOS Accessibility Testing
Slide 59
Slide 59 text
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
Slide 60
Slide 60 text
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
Slide 61
Slide 61 text
• 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
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