Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
128 commits
Select commit Hold shift + click to select a range
d55ae3c
make init
May 22, 2023
a6a97cf
added accessibility parser
May 22, 2023
3531e71
added accessibility plugin
May 22, 2023
8414389
added accessibility strategies
May 24, 2023
2db2ba4
added accessibility cell
May 24, 2023
ba6d2b7
renamed protocols
May 25, 2023
409472f
fixed collection plugin
May 25, 2023
fb3db9d
added table accessibility plugin
May 25, 2023
daa29db
added traits provider for base protocols
May 25, 2023
5bf16b7
fixed swiftlint
May 25, 2023
63beef1
added some example
May 25, 2023
81beafa
added comments
May 26, 2023
13e233f
update hooks
Ikeret May 30, 2023
e7fcce1
accessibility improvements
Ikeret May 30, 2023
69b1c92
fixed modifier methods
Ikeret May 30, 2023
107aaad
made default accessibility modifier public
Ikeret May 30, 2023
ca8ff8b
fixed modifier
Ikeret May 30, 2023
d9fc358
fixed tests
Ikeret May 30, 2023
d28f1a1
implemented accessibility for collection views
Ikeret May 30, 2023
102d52c
renamed default modifier, added custom actions support
Ikeret May 30, 2023
cde4c3f
removed separator
Ikeret May 30, 2023
561931a
expandable table cell accessibility
Ikeret May 30, 2023
2e4b661
update structure
Ikeret May 31, 2023
728d61b
added accessibility invalidation
Ikeret May 31, 2023
c513436
added accessibility to expandable table cell
Ikeret May 31, 2023
192ff53
added public modifiers
Ikeret May 31, 2023
ea858b8
added accessibility invalidator
Ikeret May 31, 2023
86d2c90
refactor, item fixes
Ikeret May 31, 2023
ae77ec5
added comments
Ikeret May 31, 2023
e5b7b98
fixed tests & run sourcery
Ikeret Jun 1, 2023
e0eb1fe
added marks for events
Ikeret Jun 1, 2023
10eda75
renamed parameter
Ikeret Jun 1, 2023
19274c2
removed default strategies
Ikeret Jun 1, 2023
8402007
added table views accessibility
Ikeret Jun 1, 2023
c307dfe
disable traits override for UI tests
NullIsOne Jun 2, 2023
245c26b
revert accessibility value updates with selected/highlighted states
NullIsOne Jun 2, 2023
96ff341
fix test for movable plugin
NullIsOne Jun 2, 2023
932daf8
Revert "revert accessibility value updates with selected/highlighted …
Ikeret Jun 2, 2023
4c33a1c
added insert remove methods
Ikeret Jun 2, 2023
9685f86
fixes for ui tests
Ikeret Jun 2, 2023
f8361a4
fixed ui tests
Ikeret Jun 2, 2023
b1215a5
add footers for table
NullIsOne Jun 1, 2023
6b50fd8
fix Unit and UI tests
NullIsOne Jun 1, 2023
46e6171
add generator to demonstrate table footer
NullIsOne Jun 1, 2023
7581c22
add required methods to table delegate
NullIsOne Jun 1, 2023
3e2d07e
fixed footer
Ikeret Jun 2, 2023
6e706d8
fixed ui tests from target branch
Ikeret Jun 2, 2023
0bfb1d4
fixed swiftlint issues
Ikeret Jun 2, 2023
e085b8a
fixed swiftlint warnings 2
Ikeret Jun 2, 2023
a140e29
enable a11y audit (red = failed on dynamic size check)
NullIsOne Jun 8, 2023
01b510e
add missed a11y label to resolve sufficientElementDescription issue
NullIsOne Jun 8, 2023
5b52a37
change screen titles to resolve textClipped issue
NullIsOne Jun 8, 2023
e82081f
extend size of title collection header generator
NullIsOne Jun 8, 2023
e951741
enable fonts from system category
NullIsOne Jun 8, 2023
c3c9b31
resolve dynamic size issues for main screen
NullIsOne Jun 8, 2023
8b82b17
set textstyle for labels and buttons in xib (issues decreased from 22…
NullIsOne Jun 9, 2023
df750d4
add missed fonts for stack examples (stack screens all green)
NullIsOne Jun 9, 2023
f593b4d
rename test utility method
NullIsOne Jun 9, 2023
f2b2f56
add height calculation for table headers (table all green and nice vi…
NullIsOne Jun 9, 2023
772a14e
prepare collection header/footers to resize (decrease issues from 49 …
NullIsOne Jun 9, 2023
68f6e36
allow word wrap in headers (46 to 44)
NullIsOne Jun 9, 2023
e1a9808
make title collection view cell calcualtable (issues from 44 to 40)
NullIsOne Jun 13, 2023
71a0491
make grid layout sizable (from 40 to 14 issues)
NullIsOne Jun 13, 2023
efad1f6
make movable cell generator sizable (from 14 to 9 issues)
NullIsOne Jun 13, 2023
ba6aa18
make compositional grid layout sizable (from 9 to 6 issues)
NullIsOne Jun 13, 2023
c60d0c7
fix issue in sizable collection example (all green)
NullIsOne Jun 13, 2023
e23f34f
fix linter issues
NullIsOne Jun 13, 2023
a2b7003
make title lowercased
NullIsOne Jun 14, 2023
81dd7fa
isolate audit by swift version
NullIsOne Jun 14, 2023
e2f539d
add more surroundings for iOS 17 to make sources compatible with old …
NullIsOne Jun 14, 2023
5da8d35
added documentation structure
Ikeret Jun 5, 2023
08cb389
finished accessibility basics
Ikeret Jun 6, 2023
61bb23e
fixed style
Ikeret Jun 6, 2023
1df23cb
fixed comments
Ikeret Jun 6, 2023
c0eec83
added usage
Ikeret Jun 6, 2023
62523ca
fixed usage
Ikeret Jun 6, 2023
a30786e
added methods to protocol to re-define ability
Ikeret Jun 16, 2023
52380c4
finished accessibility mds
Ikeret Jun 16, 2023
eb17bc4
fixed remark
Ikeret Jun 20, 2023
de7a19c
added mention about example
Ikeret Jun 20, 2023
40ce097
added workflows from merged PR #232
Ikeret Jun 21, 2023
b010b77
fix screen names in tests
NullIsOne Jun 21, 2023
90b6ebb
improve stability of highlightable test
NullIsOne Jun 21, 2023
9735866
improve stability of tests for dragable and movable plugins
NullIsOne Jun 21, 2023
a17b2c4
update macos version
NullIsOne Jun 21, 2023
a6524be
add failing job when tests failed
NullIsOne Jun 22, 2023
3bb73a3
remove storing in static component constructors
NullIsOne Jun 22, 2023
f176dd5
optimize build logs checking on CI
NullIsOne Jun 22, 2023
a6acb00
remove tvOS test target
NullIsOne Jun 23, 2023
a51a87f
improve movable/draggable tests
NullIsOne Jun 23, 2023
61740a2
decrease all delays for ui tests
NullIsOne Jun 23, 2023
9c4a99d
move build log storage to root folder
NullIsOne Jun 23, 2023
9b7f1ab
fatal error test with small delay
Ikeret Jun 27, 2023
f75f5e3
replaced fatal errors with utils call
Ikeret Jun 27, 2023
664d73f
increased wait duration
Ikeret Jun 27, 2023
bd1dbc5
increased wait time again
Ikeret Jun 27, 2023
f5b9339
fixed durations
Ikeret Jun 27, 2023
10b5135
fix Unit and UI tests
NullIsOne Jun 1, 2023
84e7b13
added traits provider for base protocols
May 25, 2023
8786d07
added table views accessibility
Ikeret Jun 1, 2023
197abc3
add footers for table
NullIsOne Jun 1, 2023
1dc8f76
make title collection view cell calcualtable (issues from 44 to 40)
NullIsOne Jun 13, 2023
e17ae61
refactor strategies
Ikeret Jun 22, 2023
8e9482c
moved files into folders & added AccessibilityContainer
Ikeret Jul 4, 2023
876911f
extend strategy methods
Ikeret Jul 4, 2023
5b33586
fixed example
Ikeret Jul 4, 2023
d06e8ad
add shortcuts to improve modification out of rddm
NullIsOne Jul 11, 2023
caf458b
make container not acessible element if accessibility is ignored
NullIsOne Jul 12, 2023
dfbaa61
fix rebase conflicts
NullIsOne Jul 12, 2023
bb209f4
update AccessibilityItemInvalidator protocol and implementations
NullIsOne Jul 14, 2023
8a2cdef
add filtration for a11y string values
NullIsOne Jul 17, 2023
795c1bd
add AccessibilityAction and Strategy to apply it in Modifier
NullIsOne Jul 17, 2023
2bdde92
fix linter issues
NullIsOne Jul 17, 2023
58f59fa
make invalidator configurable
NullIsOne Jul 18, 2023
d25de15
fix isAccessibilityIgnored
NullIsOne Jul 18, 2023
b5bb525
update documentation
NullIsOne Jul 18, 2023
72f69d7
fix init for ItemWrapper
NullIsOne Jul 19, 2023
5462585
Merge pull request #239 from surfstudio/feature/SPT-1480-optimizations
NullIsOne Jul 21, 2023
a74ff3a
update version to 7.4
NullIsOne Jul 21, 2023
4c9389d
Merge branch 'release/7.4' into feature/resolve_7.4_conflicts
NullIsOne Jul 21, 2023
f0713b4
fix linter issues
NullIsOne Jul 24, 2023
72f9b3f
fix short init for TableSection
NullIsOne Jul 24, 2023
87af96f
fix titles in UI tests
NullIsOne Jul 24, 2023
cfdfe2a
update font to prefered font
NullIsOne Jul 24, 2023
c6f5276
add missed accessibility plugin for new example controllers
NullIsOne Jul 24, 2023
9cab6d6
fix paginatable plugin UI tests
NullIsOne Jul 24, 2023
88b3cc5
fix a11y issues
NullIsOne Jul 24, 2023
7fd0ec7
add height calculation for titletableviewcell from example
NullIsOne Jul 25, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions .github/workflows/Build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ on:
jobs:

build_iOS:
runs-on: macOS-12
runs-on: macOS-13
steps:
- uses: actions/checkout@v1
- name: Generate projects
Expand All @@ -19,6 +19,8 @@ jobs:
run: make build_lib_iOS
- name: Run tests
run: make test_lib_iOS
- name: Check Log
run: make check_test_log
- name: Prepare Report
run: make prepare_report
- name: Upload Coverage
Expand All @@ -34,7 +36,7 @@ jobs:


build_Example:
runs-on: macOS-12
runs-on: macOS-13
steps:
- uses: actions/checkout@v1
- name: Generate projects
Expand All @@ -43,6 +45,8 @@ jobs:
run: make build_example_iOS
- name: Run tests
run: make test_example_iOS
- name: Check Log
run: make check_test_log
- name: Prepare Report
run: make prepare_example_report
- name: Upload Coverage
Expand All @@ -57,7 +61,7 @@ jobs:
verbose: true

build_tvOS:
runs-on: macOS-12
runs-on: macOS-13
steps:
- uses: actions/checkout@v1
- name: Generate projects
Expand Down
6 changes: 0 additions & 6 deletions .swiftlint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,6 @@ custom_rules:
message: "Every MARK should be surrounded with 1 newline before and 1 after it"
severity: warning

redundant_default_type:
included: ".*.swift"
name: "Redundant explicit type"
regex: "(var|let) [0-9a-zA-Z_]*: (Int|String|Double|Bool)[ ]*= "
message: "Redundant explicit declaration of default types should be avoided"

class_modificators_order:
include: ".*.swift"
name: "Class modificators order"
Expand Down
20 changes: 18 additions & 2 deletions Components/Sources/Collection/CollectionWrappedCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,15 @@ public final class CollectionWrappedCell<View: ConfigurableItem>: UICollectionVi
extension CollectionWrappedCell: CalculatableHeightItem where View: CalculatableHeightItem {

public static func getHeight(forWidth width: CGFloat, with model: Model) -> CGFloat {
return View.getHeight(forWidth: width, with: model)
let nestedViewHeight = View.getHeight(forWidth: width, with: model)
if let alignment = (model as? AlignmentProvider)?.alignment {
switch alignment {
case .leading(let insets), .trailing(let insets), .all(let insets):
return nestedViewHeight + insets.top + insets.bottom
}
} else {
return nestedViewHeight
}
}

}
Expand All @@ -38,7 +46,15 @@ extension CollectionWrappedCell: CalculatableHeightItem where View: Calculatable
extension CollectionWrappedCell: CalculatableWidthItem where View: CalculatableWidthItem {

public static func getWidth(forHeight height: CGFloat, with model: View.Model) -> CGFloat {
return View.getWidth(forHeight: height, with: model)
let nestedViewWidth = View.getWidth(forHeight: height, with: model)
if let alignment = (model as? AlignmentProvider)?.alignment {
switch alignment {
case .leading(let insets), .trailing(let insets), .all(let insets):
return nestedViewWidth + insets.left + insets.right
}
} else {
return nestedViewWidth
}
}

}
2 changes: 1 addition & 1 deletion Components/Sources/Common/Models/Styles/TextStyle.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public struct TextStyle: Equatable {
public let color: UIColor
public let font: UIFont

public init(color: UIColor = .black, font: UIFont = .systemFont(ofSize: 16)) {
public init(color: UIColor = .black, font: UIFont = .preferredFont(forTextStyle: .body)) {
self.color = color
self.font = font
}
Expand Down
10 changes: 9 additions & 1 deletion Components/Sources/Table/TableWrappedCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,15 @@ public final class TableWrappedCell<View: ConfigurableItem>: UITableViewCell, Vi
extension TableWrappedCell: CalculatableHeightItem where View: CalculatableHeightItem {

public static func getHeight(forWidth width: CGFloat, with model: Model) -> CGFloat {
return View.getHeight(forWidth: width, with: model)
let nestedViewHeight = View.getHeight(forWidth: width, with: model)
if let alignment = (model as? AlignmentProvider)?.alignment {
switch alignment {
case .leading(let insets), .trailing(let insets), .all(let insets):
return nestedViewHeight + insets.top + insets.bottom
}
} else {
return nestedViewHeight
}
}

}
7 changes: 7 additions & 0 deletions Documentation/Accessibility/Overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Overview

- [UIAccessibility Basics](Pages/UIAccessibility%20Basics.md)
- [Usage](Pages/Usage.md)
- [Invalidation](Pages/Invalidation.md)
- [Debugging](Pages/Debugging.md)
- [Advanced Usage](Pages/Advanced%20Usage.md)
89 changes: 89 additions & 0 deletions Documentation/Accessibility/Pages/Advanced Usage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
[⏎ Overview](../Overview.md)

# Advanced Usage

## How it works

The main logic is located in `TableAccessibilityPlugin` and `CollectionAccessibilityPlugin`. It automatically added to all builders to be ready work with `AccessibilityItems`. It's a common plugin that works with delgate events.

**Only each `AccessibilityItem` will be processed by these plugins.**

On `willDisplay` event every time 2 steps are happens:

1. Element will be passed to its defined modifier through corresponding methods `modify(item: accessibilityItem)` or `modify(item: accessibilityItem, generator: generator)` if generator implements `AccessibilityStrategyProvider`.
2. A new invalidator instance is setted with provided `indexPath` if supports invalidation mechanism. Its invalidate method calls delegate to pass the new event `invalidatedAccessibility` which will execute step 1.

On `didEndDisplay` event the plugin will remove invalidator if it exists.

<img src="https://i.ibb.co/SfbfzZJ/2023-06-16-18-43-17.png" alt="Plugin Example">

<br>

This is the essence how accessibility works in **RDDM**, so you don't have access to its functionality. Instead, you may have full control under modify and invalidate processes.

***

<br>

## Modify custom accessibility parameters

If you need to set a parameter which is not presented or change the way how they setting, you need to define your own strategy provider (or custom accessibility item) and modifier. You can combine your custom logic with base functionality.

*Example:*
```swift
enum CustomAccessibilityModifier: AccessibilityModifier {
static func modify(item: AccessibilityItem) {
// set base parameters
BaseAccessibilityModifier.modify(item)

// additional logic for custom item
guard let item = item as? CustomAccessibilityItem else { return }
item.accessibilityIdentifier = item.identifier.value
item.accessibilityHint = item.hintStrategy.value
}

// corresponding method implementation with generator
}

protocol CustomAccessibilityItem: AccessibilityItem {
var modifierType: AccessibilityModifierType { CustomAccessibilityModifier.self }

// expand base accessibility item with new parameters using string strategy
var identifier: AccessibilityStringStrategy { get }
var hintStrategy: AccessibilityStringStrategy { get }
}
```

By this way you can add any other functionality to accessibility items.

***

<br>

## Customize invalidation


### in table or collection
If you want to perform additional logic on invalidate, you can implement your own `AccessibilityItemInvalidator` and set `AccessibilityInvalidatorCreationBlock`

**For example**
```swift

private lazy var adapter = collectionView.rddm.baseBuilder
.add(plugin: .accessibility({ item, kind, delegate in
return CustomInvalidator(item, kind, delegate)
}))
.build()
```

*Note that we have `DelegatedAccessibilityItemInvalidator` which could be used as base for your custom invalidator. This class is needed to combine strategies from item and generator.*

### in other view

Any view can implement `AccessibilityItem`, but you need adittionaly call `AccessibilityItem.modifySelf()` to setup accessibility-properties for real.

`AcessibilityInvalidatable.setBasicInvalidator` is also available for invalidation.

*Note that is recomended to call*
- `setBasicInvalidator` when view become **visible**
- `removeInvalidator` when view become **invisible**
77 changes: 77 additions & 0 deletions Documentation/Accessibility/Pages/Debugging.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
[⏎ Overview](../Overview.md)

# Debugging

## Accessibility Inspector

Accessibility Inspector is an instrument provided with Xcode, so this is most common insturment to see accessibility parameters. It can be opened in Menu Bar: `Xcode -> Open Developer Tool -> Accessibility Inspector`.

<img src="https://i.ibb.co/wMkh1rG/2023-06-16-16-25-02.png" alt="Accessibility Inspector">

Here you can see common acceessibility parameters and perform accessibility actions. To select an element you need fisrt select a running simulator. Then you click target button on the right and select an element or click arrorws "<" and ">" to navigate through all elements. Optionally, you can enable reading for selected elements.

Be aware that not all accessibility parameters are displayed as they VoiceOver is reading. For example, if the element is `UISwitch`, in inspector you can see values 0 or 1, but VoiceOver will localize these values into "enabled" and "disabled". So it's better to also check elements with VoiceOver.

*Note: Accessibility parameters also can be seen in a view debugger, but it's non interactive.*
<img src="https://i.ibb.co/RQxVPGg/2023-06-16-16-32-20.png" alt="View Debugger">

***

<br>

## Accessibility Audit

Accessibility Inspector allows you to run audit of current screen to find and different accessibility issues if they exists. The second tab in right corner navigates you to audit window.

<img src="https://i.ibb.co/LPfx62N/2023-06-16-16-57-17.png" alt="Accessibility Audit">

***

<br>

## VoiceOver

If you will test your app on a device with VoiceOver you can enable some useful options.

<img src="https://i.ibb.co/TWJGG77/2023-06-16-17-07-42.jpg" alt="Caption Panel" width=49%>
<img src="https://i.ibb.co/8Prs6r6/2023-06-16-17-07-17.jpg" alt="Accessibility Shortcut" width=49%>

Caption panel allows you to read instead of listen to VoiceOver. And Accessibility Shortcut allows you to fast enable or disable VoiceOver by triple-click the lock button.

It also useful to read an article how to use VoiceOver gestures https://support.apple.com/guide/iphone/iph3e2e2281/ios

***

<br>

## UI Tests

Accessibility elements are also used for UI tests. So changing traits affects on `XCUIElement` type (any element with trait `.button` becomes a button `XCUIElement`).

If you manually change collection or table classes to become an accessibility elements your UI tests may fail, if they will not contains required accessibility.

<img src="https://i.ibb.co/gz049gW/2023-06-16-17-31-51.png" alt="UI test failure">

To fix this issue **RDDM** provides a built-in modifier for UI tests. You only need to provide the command line argument `"-rddm.XCUITestsCompatible"`

<img src="https://i.ibb.co/XbzffYF/2023-06-16-17-36-30.png" alt="UI tests modifier">

This modifier will add traits `.button` to all cells and `.staticText` to all headers and footers to prevent UI tests failures.

***

<br>

## UI Tests Accessibility Audit

In Xcode 15 you can run automatic accessibility audit with just a line of code:

```swift
try app.performAccessibilityAudit()
```

It's the same audit from Accessibility Inspector. If there will be any issues test will fail and you can see these issues in logs. See more at https://developer.apple.com/videos/play/wwdc2023/10035/.

<br>

Next [Advanced Usage →](./Advanced%20Usage.md)
44 changes: 44 additions & 0 deletions Documentation/Accessibility/Pages/Invalidation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
[⏎ Overview](../Overview.md)

# Invalidation

**RDDM**'s accessibility plugin allows you to use your dynamic properties in accessibility strategies and so on. But if some properties will changed while the cell is on screen, you need to invalidate current accessibility parameters. For these needs you need implement `AccessibilityInvalidatable` protocol.

```swift
protocol AccessibilityInvalidatable: AccessibilityItem {
var accessibilityInvalidator: AccessibilityItemInvalidator? { get set }
}
```

This protocol required you to define an invalidatior property. Through this invalidator you can call `invalidateParameters()` method which triggers re-set of all accessibility parameters.

*Important: this property is fully managed by accessibility plugin, so you don't need to set it. But if you want to add your own funtionality, see [Advanced Usage](./Advanced%20Usage.md). Invalidator may be `nil` if cell isn't on screen, so the latest parameters will be applied on `willDisplay` after the `configure()` method.*

*Example:*

```swift
func updateState(state: String, isSelected: Bool) {
valueStrategy = .just(state)
isSelected ? traitsStrategy.insert(.selected) : traitsStrategy.remove(.selected)
accessibilityInvalidator?.invalidateParameters()
}
```

<br>

If you manage cell states like `isSelected`, `isEnabled`, you may use corresponding accessibility traits. But in a default `AccessibilityItem` these states wouldn't be changes because of `shouldOverrideStateTraits` property which by default is `false`. So you need to define this property as `true`.

*Example:*

```swift
var labelStrategy: AccessibilityStringStrategy { .from(object: titleLabel) }
var valueStrategy: AccessibilityStringStrategy = .just(nil)
lazy var traitsStrategy: AccessibilityTraitsStrategy = .from(object: titleLabel)
var shouldOverrideStateTraits: Bool { true }

var accessibilityInvalidator: AccessibilityItemInvalidator?
```

<br>

Next [Debugging →](./Debugging.md)
Loading