Agent skills for iOS, iPadOS, Swift, SwiftUI, and modern Apple framework development.
71
89%
Does it follow best practices?
Impact
—
No eval scenarios have been run
Advisory
Suggest reviewing before use
Button(action: { }) {
Image(systemName: "heart.fill")
}
.accessibilityLabel("Favorite")
Slider(value: $volume, in: 0...100)
.accessibilityValue("\(Int(volume)) percent")
Button("Submit")
.accessibilityHint("Submits the form and sends your feedback")// Add traits without overwriting defaults
Button("Go") { }
.accessibilityAddTraits(.updatesFrequently)
// Group children into a single accessibility element
HStack {
Image(systemName: "person.circle")
VStack {
Text("John Doe")
Text("Engineer")
}
}
.accessibilityElement(children: .combine)HStack { /* custom star rating UI */ }
.accessibilityElement()
.accessibilityLabel("Rating")
.accessibilityValue("\(rating) out of 5 stars")
.accessibilityAdjustableAction { direction in
switch direction {
case .increment: if rating < 5 { rating += 1 }
case .decrement: if rating > 1 { rating -= 1 }
@unknown default: break
}
}@AccessibilityFocusState private var focusOnTrigger: Bool
Button("Open Settings") { showSheet = true }
.accessibilityFocused($focusOnTrigger)
.sheet(isPresented: $showSheet) {
SettingsSheet()
.onDisappear {
Task { @MainActor in
try? await Task.sleep(for: .milliseconds(100))
focusOnTrigger = true
}
}
}enum A11yFocus: Hashable { case nameField, emailField, submitButton }
@AccessibilityFocusState private var focus: A11yFocus?@ScaledMetric(relativeTo: .title) private var iconSize: CGFloat = 24
@Environment(\.dynamicTypeSize) var dynamicTypeSize
var body: some View {
if dynamicTypeSize.isAccessibilitySize {
VStack(alignment: .leading) { icon; textContent }
} else {
HStack { icon; textContent }
}
}List(items) { item in ItemRow(item: item) }
.accessibilityRotor("Unread") {
ForEach(items.filter { !$0.isRead }) { item in
AccessibilityRotorEntry(item.title, id: item.id)
}
}@Environment(\.accessibilityReduceMotion) var reduceMotion
@Environment(\.accessibilityReduceTransparency) var reduceTransparency
@Environment(\.colorSchemeContrast) var contrast
@Environment(\.legibilityWeight) var legibilityWeightcustomButton.accessibilityTraits.insert(.button)
customButton.accessibilityTraits.remove(.staticText)
UIAccessibility.post(notification: .announcement, argument: "Upload complete")
UIAccessibility.post(notification: .layoutChanged, argument: targetView)
UIAccessibility.post(notification: .screenChanged, argument: newScreenView)AppKit accessibility centers on NSAccessibilityProtocol. Use standard AppKit controls when possible, then override or add accessibility behavior only where the default metadata is wrong or incomplete.
final class FavoriteToggleView: NSView {
var isFavorite = false {
didSet {
NSAccessibility.post(element: self, notification: .valueChanged)
}
}
override func isAccessibilityElement() -> Bool { true }
override func accessibilityRole() -> NSAccessibility.Role? { .button }
override func accessibilityLabel() -> String? { "Favorite" }
override func accessibilityValue() -> Any? { isFavorite ? "On" : "Off" }
override func accessibilityPerformPress() -> Bool {
isFavorite.toggle()
return true
}
}Use NSAccessibilityElement when an accessible item has no backing NSView, such as a virtual data point in a chart or a drawn annotation.
let pointElement = NSAccessibilityElement.element(
withRole: .button,
frame: pointFrame,
label: "March revenue",
parent: chartView
)NSAccessibility.post(element: saveStatusLabel, notification: .valueChanged)
NSAccessibility.post(
element: self,
notification: .announcementRequested,
userInfo: [
.announcement: "Export complete"
]
)Use .announcementRequested when assistive apps need to announce transient status. Use state-specific notifications such as .valueChanged when the accessible value changed.
.accessibilityAddTraitsVoice Control generates tap targets from accessibility labels. Labels must be speakable and unique within the visible screen.
Provide shorter spoken alternatives when the primary label is long:
// Primary label is descriptive but long to speak
Button(action: { startWorkout() }) {
VStack {
Image(systemName: "figure.run")
Text("Start Outdoor Running Workout")
}
}
.accessibilityLabel("Start Outdoor Running Workout")
.accessibilityInputLabels(["Start Run", "Run", "Start Workout"])// Navigation link with verbose label
NavigationLink {
AccountSettingsView()
} label: {
Label("Account and Privacy Settings", systemImage: "person.circle")
}
.accessibilityInputLabels(["Account", "Settings", "Privacy"])// Bad: emoji-only, unspeakable
Button("❤️") { toggleFavorite() }
// Good: speakable label
Button(action: { toggleFavorite() }) {
Image(systemName: "heart.fill")
}
.accessibilityLabel("Favorite")
// Bad: duplicate labels on same screen
ForEach(items) { item in
Button("Edit") { edit(item) } // Voice Control can't distinguish
}
// Good: unique labels
ForEach(items) { item in
Button("Edit") { edit(item) }
.accessibilityLabel("Edit \(item.name)")
}Switch Control scans elements sequentially. Reduce scan stops with grouping and provide custom actions for gesture-based interactions.
// Swipe-to-delete row: Switch Control can't swipe
TaskRow(task: task)
.accessibilityAction(named: "Complete") { completeTask(task) }
.accessibilityAction(named: "Delete") { deleteTask(task) }
.accessibilityAction(named: "Reschedule") { rescheduleTask(task) }// Long-press context menu: expose actions directly
PhotoThumbnail(photo: photo)
.contextMenu { /* ... */ }
.accessibilityAction(named: "Share") { sharePhoto(photo) }
.accessibilityAction(named: "Add to Album") { addToAlbum(photo) }
.accessibilityAction(named: "Delete") { deletePhoto(photo) }// Bad: 5 scan stops per row
HStack {
Image(systemName: "doc")
VStack {
Text(document.title)
Text(document.date.formatted())
}
Spacer()
Text(document.size)
Image(systemName: "chevron.right")
}
// Good: 1 scan stop per row
HStack {
Image(systemName: "doc")
VStack {
Text(document.title)
Text(document.date.formatted())
}
Spacer()
Text(document.size)
Image(systemName: "chevron.right")
}
.accessibilityElement(children: .combine)Full Keyboard Access (iOS/iPadOS 13.4+) uses Tab/Shift-Tab for navigation, Space/Enter for activation, and arrow keys for directional movement.
struct SelectableCard: View {
let title: String
let action: () -> Void
@FocusState private var isFocused: Bool
var body: some View {
RoundedRectangle(cornerRadius: 12)
.fill(isFocused ? Color.tint.opacity(0.1) : Color.clear)
.overlay {
Text(title)
}
.focusable()
.focused($isFocused)
.onKeyPress(.return) {
action()
return .handled
}
}
}Control which focus interactions a view supports:
// Tap-equivalent only (no text editing)
CustomButton(title: "Play")
.focusable(interactions: .activate)
// Text input only
CustomInputField()
.focusable(interactions: .edit)
// Both activation and editing
SearchBar()
.focusable(interactions: [.activate, .edit])Button("New Document") { createDocument() }
.keyboardShortcut("n", modifiers: .command)
Button("Find") { showSearch() }
.keyboardShortcut("f", modifiers: .command)
// Delete with confirmation
Button("Delete", role: .destructive) { confirmDelete() }
.keyboardShortcut(.delete, modifiers: .command)enum Field: Hashable {
case username, password, confirmPassword
}
struct SignupForm: View {
@FocusState private var focusedField: Field?
var body: some View {
Form {
TextField("Username", text: $username)
.focused($focusedField, equals: .username)
SecureField("Password", text: $password)
.focused($focusedField, equals: .password)
SecureField("Confirm", text: $confirm)
.focused($focusedField, equals: .confirmPassword)
}
.onSubmit {
switch focusedField {
case .username: focusedField = .password
case .password: focusedField = .confirmPassword
case .confirmPassword: submit()
case nil: break
}
}
}
}Use XCUIElement attributes to verify accessibility properties in UI tests.
func testAccessibilityLabels() throws {
let app = XCUIApplication()
app.launch()
// Verify buttons have meaningful labels
let settingsButton = app.buttons["Settings"]
XCTAssertTrue(settingsButton.exists, "Settings button must exist")
XCTAssertTrue(settingsButton.isEnabled, "Settings button must be enabled")
// Verify a cell groups content correctly
let productCell = app.cells.element(boundBy: 0)
XCTAssertFalse(productCell.label.isEmpty, "Product cell must have a combined label")
}func testTabNavigationOrder() throws {
let app = XCUIApplication()
app.launch()
let usernameField = app.textFields["Username"]
let passwordField = app.secureTextFields["Password"]
usernameField.tap()
XCTAssertTrue(usernameField.hasFocus)
// Tab to next field
usernameField.typeText("\t")
XCTAssertTrue(passwordField.hasFocus)
}func testSwipeToDeleteAlternative() throws {
let app = XCUIApplication()
app.launch()
let cell = app.cells["task-buy-groceries"]
XCTAssertTrue(cell.exists)
// Verify accessibility identifier is set for test targeting
XCTAssertEqual(cell.identifier, "task-buy-groceries")
}skills
accessorysetupkit
references
activitykit
references
adattributionkit
references
alarmkit
references
app-clips
app-intents
references
app-store-optimization
app-store-review
apple-on-device-ai
appmigrationkit
references
audioaccessorykit
references
authentication
references
avkit
references
background-processing
references
browserenginekit
references
callkit
references
carplay
references
cloudkit
references
contacts-framework
references
core-bluetooth
references
core-data
core-motion
references
core-nfc
references
coreml
references
cryptokit
references
cryptotokenkit
references
debugging-instruments
device-integrity
references
dockkit
references
energykit
references
eventkit
references
financekit
references
focus-engine
gamekit
references
healthkit
references
homekit
references
ios-accessibility
ios-localization
ios-networking
ios-simulator
references
mapkit
metrickit
references
musickit
references
natural-language
references
paperkit
references
passkit
references
pdfkit
references
pencilkit
references
permissionkit
references
photokit
push-notifications
realitykit
references
relevancekit
references
scenekit
references
sensorkit
references
speech-recognition
spritekit
references
storekit
swift-api-design-guidelines
swift-architecture
swift-charts
references
swift-codable
swift-concurrency
swift-formatstyle
swift-language
swift-security
references
swift-testing
swiftdata
swiftlint
swiftui-animation
swiftui-gestures
references
swiftui-layout-components
swiftui-liquid-glass
references
swiftui-patterns
swiftui-performance
swiftui-uikit-interop
swiftui-webkit
tabletopkit
references
tipkit
references
vision-framework
weatherkit
references
widgetkit
references