With introducing Xcode UI tests and growing of popularity of such tools as fastlane screenshots (that uses Xcode UI tests under the hood) and the fact that actually every UI automation tool uses accessibility labels and identifiers I think it will be very useful to support these values also.
What do you have in mind @ilyapuchka ? Exposing accessibility labels as enums? For every and all UIView that is an accessibilityElement? That's gonna be a huge one, right?
Nothing specific right now, didn't investigate this enough, just noticed that in a new project I started to work on (going to use SwiftGen there anyway) we use them not only in UI automated tests. And why then not to auto generate them as any other ids from storyboards?
Regarding using enum yes, we could have
enum Accessibility {
case Hint(String),
case Label(String),
case Identifier(Int)
}
and each view can have let's all of them (each as optional), but then each view will have separate property for each option? that will be a lot indeed
Instead maybe it would be better to just gather them in a tuple of optional values like
let accessibility = (hint: "String", label: "String", identifier: "String")
I don't like to make them optional, but the fact that this property is generated guarantees that at least one of them will be not nil and user can just use ?? to choose one (or there can be extension method that does that)
The other thing is that accessibility can be setup also in xibs but as I can see there is no xib parser in GenumKit, so it should be implemented.
Then the question arrises - how to group accessibility items that come from storyboards and xibs?
Or they should not be namespaces to storyboard scenes or anything else?
That feature will be cool to have for my current project, but we will move to defining layouts in xibs and use storyboards only to define workflow, so all accessibility configuration will be in xibs.
@AliSoftware what do you think?
I'm not sure I'm following actually (but I have to admit that I sadly haven't used accessibility enough yet).
For example I don't see the point of having your enum
enum Accessibility {
case Hint(String),
case Label(String),
case Identifier(Int)
}
How would that help? If the .Hint has an associated String value, and not a constant, then we loose the advantage of SwiftGen generating the constants, as we could create a .Hint("foo") even if there is no such hint, right?
I was more thinking about an enum that lists existing Labels and existing identifiers, like:
enum Accessibility {
case Foo
case Bar
case Baz
var label: String {
switch self {
case Foo: return "FooLabel"
case Bar: return "BarLabel"
case Baz: return "BazLabel"
}
}
// ... + similar computed var for hint and identifier
}
That way we can obtain the label of Foo as a constant using Accessibility.Foo.label at call site.
But as you point out, I'm not sure how to organize those labels/cases should we group them per storyboard, etc.
I think to imagine the best generated code we need to think in reverse and first think what we want the call site to look like. Do you have any use-case examples on where you use accessibility and when you need to access accessibility labels as constants in your code? That would help see what you want the code to look like at the call site then we can deduce what we would need to generate.
It looks like this case is a bit more complex then I thought at the beginning. There are actually two things.
Firstly accessibility labels and hits are intended to be localized and used by VoiceOver. You can localize them either through storyboard/xib localization or manually in code with NSLocalizableString(). First option is not supported by SwiftGen I suppose because keys of such labels will be generated by storyboards based on storyboard identifier of that particular view (that looks like kh9-bI-dsS). I can't check that because my Xcode is updating already for few hours... So please correct me if I'm wrong.
Secondly as I understood in UI tests items can be accessed either through accessibility labels or accessibility identifiers. As labels could be localized then I suppose that it is safer to access view through ids. Though if you use Base localization you probably will be able to access elements by labels in this localization in tests no matter with what language you test your app. Not sure what will happen if you don't use Base localization. Will check these ideas when will have chance.
So it looks like SwiftGen can generate accessibility identifiers. And labels and hints should be probably part of t's localization functionality?
Call site in UI tests could look something like this:
app.tables.element.cells["Call Mom"].buttons["More"].tap()
app.tables.element.cells[UILabel.Accessibility.CallMom].buttons[UIButton.Accessibility.More].tap()
That's what I'm talking about:
Accessibility enum for that but that will mix them all. We could group depending on their class like UILabel in your example, but then what if you use a subclass MyCustomLabel, will you then spit out a new enum for that containing the accessibility identifiers for all the instances?I'm not sure what's the best design for this, even what we want th call site to look like without it being a mess when we have a huge project with lots of those.
Yes, I was thinking about grouping by classes in their extensions. Maybe using AccessabilityId struct for namespacing will make it more readable.
Following my example generated code could look like this:
extension UITableViewCell {
public struct AccessibilityId {
public static let CallMom = "Call Mom"
//all over accessibility ids for UITableViewCell instances from storyboards and xibs, but not for its subclasses
}
}
extension UIButton {
public struct AccessibilityId {
public static let More = "More"
}
}
extension MyCustomButton {
public struct AccessibilityId {
public static let More = "More"
}
}
This way client code can extend those inner structs if some of the ids are defined in source code, not in IB:
extension UITableViewCell.AccessibilityId {
public static let CallDad = "Call Dad"
}
I think SwiftGen should not make difference if those ids come from storyboard or from xibs. It should care only about type of view.
Agreed that SwiftGen shouldn't make a difference if accessibilityIDs come from storyboards or XIBs.
Note that due to a bug in the Swift compiler as for now, there is a high risk of the compiler crashing or fail to compile in Release & WMO when you use extension on UIKit types to add subtypes (like a struct) to them.
That's a known bug that already affected UIStoryboard and UIColor (see #56 and #57), so until that's fixed we might want to avoid that.
Interesting. But then it works with nested enums in UIColor and UIImage?
Yeah it depends on internal implementation of the UIKit class and the way the swift compiler optimizes each chunk of code so it's not a stable assumption anyway to rely on it. Until the bug is fixed in LLVM we shouldn't risk it IMHO, we were just lucky with the other two (or maybe nobody tested those?)
@ilyapuchka triaging issues rn and still wondering what you expect from that feature, especially how you expect the generated code to look and how parsing XIBs and Storyboards will look like for this… Care to make a PR to make that happen?
What is the status of this PR? Nothing has been started yet?
I'm starting to use XCUITests, and see definitely the worth of this feature.
For the generated code, I think we can start with a _basic_ output containing just the accessibility identifier as suggested by @ilyapuchka. Not sure how we can parse them, but is there's an output validated, I'm ok to give a look into this :)
@tbaranes Nothing have been started yet, especially if I have to be honest because I don't do much XCUITest and don't have a use accessibility labels, so I'm not sure to be the best one suited to understand the need and what the output code would look like (grouped by class? all accessibility labels all in one giant enum?…)
The usage to get an element is
let _ = app.buttons["accessibility_label"]
let _ = app.staticText["accessibility_label"]`
where buttons and staticTexts are a property of XCUIElementTypeQueryProvider. There's a lot of others following which type are trying to get (tab, box, alert, datePickers...). I don't think we should try to match them or even group them.
So, I think there's two solutions that can be nice:
Anyway, even a big enum grouping all the labels would be better than the default usage 😬
I have created a PR to extract accessibility identifiers from storyboards/xibs: https://github.com/SwiftGen/SwiftGen/pull/369
Can't wait to see this feature in SwiftGen! Writing apps which require UI tests or automation using Fastlane Snapshot will become a whole lot easier if this was implemented <3
Most helpful comment
Can't wait to see this feature in SwiftGen! Writing apps which require UI tests or automation using Fastlane Snapshot will become a whole lot easier if this was implemented <3