Airo Global Software

Think Beyond Future !

SwiftUI

SwiftUI is a new way to build user interfaces for apps on Apple platforms. It allows developers to define the UI using Swift code.

Apple is well-known for its ease of use. With the introduction of the new Audio Graphs feature in iOS 15, iOS became even more accessible for visually impaired users.

This blog will teach you everything you need to know to start using Audio Graphs in your iOS app. We'll go over what Audio Graphs are and how we can incorporate them into our apps, as well as how to define axes and data points.

By the end of this blog, you'll be prepared to make your app more accessible than ever before and assist more people in using it.

According to Apple's session Bring accessibility to charts in your app.

What are Audio Graphs?

We will not look at how to create and use graphs in this blog. There are numerous effective methods for presenting charts and graphs to users, ranging from simple self-build solutions to ready-to-use frameworks such as charts. Instead, we'll work with a sample set of data points and concentrate on adding the Audio Graphs feature.

The displayed page also includes additional information about the data series, such as a summary, features, and statistics. All of this information will be read aloud to the user by VoiceOver.

Implementing Audio Graphs

The first step is to make the view controller displaying the chart conform to the AXChart protocol. This protocol is defined as follows: There is only one requirement: the property varies accessibility.

AXChartDescriptor, A descriptor of this type contains all of the information required to display the Audio Graph page, such as the title, summary, and series. A chart descriptor is made up of additional descriptors for the axes and data points. Let's take a closer look at these classes before combining them to make an AXChartDescriptor.

Describing the Axes

AXNumericDataAxisDescriptor and AXCategoricalDataAxisDescriptor are the two types of axis descriptors. Both implement the same AXDataAxisDescriptor basis protocol, which cannot be used directly. Both types of descriptors can be used on the x-axis. However, only a numeric descriptor can be used to define the y axis. This makes sense because the graph's points can only be numbers, whereas the x values can be both points and categories. Let's begin by making an x-axis, which can be done as follows:

private var xAxis: AXNumericDataAxisDescriptor {

   // 1

   AXNumericDataAxisDescriptor(

       title: "The x axis",

       // 2

       range: (0...9),

       // 3

       gridlinePositions: [],

       // 4

       valueDescriptionProvider: { (value: Double) -> String in

           "\(value)"
      }
   )
}
  • For the time being, we'll create a numerical axis with an example title. For a real-world app, you should use a more descriptive title, as this is what VoiceOver will read.
  • An axis must also be aware of its range. We'll create 10 data points in a later step, so the range in this example is (0...9). When creating your points based on real-world data, you can specify the number of values to display in the graph.
  • Finally, we can pass in an array of points to display grid lines. However, regardless of the values entered, this appears to have no effect on the created Audio Graph detail page.

Please let me know if you have any additional information about this property in the comments!

  • Finally, an axis must understand how to convert data points into strings that can be read by the user. This is accomplished by providing a closure that converts a Double value to a String. We just embed the value in a string in this case, but it could also be used for matters or other transformations.

The y axis is created in the same manner as the x-axis. It also requires a title, range, and gridline. Positions and with DescriptionProvider:

private var yAxis: AXNumericDataAxisDescriptor {

   AXNumericDataAxisDescriptor(

       title: "The y axis",

       range: (0...9),

       gridlinePositions: [],

       valueDescriptionProvider: { (value: Double) -> String in

           "\(value)"

       }
   )
    )

Describing the Data Points

The graph points are encapsulated in an AXDataSeriesDescriptor, which represents a single data series. An Audio Graph can have multiple data series, but for the time being, we'll only use one. An AXDataSeriesDescriptor is made up of a name, a boolean flag indicating whether or not the data series is continuous, and an array of AXDataPoint objects representing the actual points. A point has an x-axis value called xValue at all times. The y axis value, yValue, is optional. A point can also have a label to give the data point a name, as well as additionalValues, which can be numerical or categorical values for this data point. Given some example values, here's how to make an AXDataSeriesDesciptor:

private var series: [AXDataSeriesDescriptor] {

   // 1

   let yValuesSeries = [4.0, 5.0, 6.0, 3.0, 2.0, 1.0, 1.0, 3.0, 6.0, 9.0]

   let dataPointsSeries = yValuesSeries.enumerated().map { index, yValue in

       AXDataPoint(x: Double(index), y: yValue)
   }

   // 2

   return [

       AXDataSeriesDescriptor(

           name: "Data Series 1",

           isContinuous: true,

           dataPoints: dataPointsSeries

       )
   ]
}
  • For the y axis, we create data points with an array of values. The value of the x-axis corresponds to the index of a number in the array.

  • We then use the array of AXDataPoint objects we just created to wrap them in an AXDataSeriesDescriptor. We use true for isContinuous to display a single coherent graph and an empty string as the name for this data series.

  • To display all points separately, use false for isContinuous. Check it out for yourself or wait for the next section, where we'll go over more options in depth.

Putting all Descriptors together

We've made two descriptors for the axes and one for a data series. We are now ready to combine them to form one AXChartDescriptor. Here's how we can go about it:

// 1
var accessibilityChartDescriptor: AXChartDescriptor? {

   // 2

   get {

       AXChartDescriptor(

           title: "Example Graph",

           summary: "This graph shows example data.",

           xAxis: xAxis,

           yAxis: yAxis,

           series: series

       )

   }

   // 3
   set { }
}
  • As previously stated, in order to implement the AXChart protocol, we must provide the property accessibility ChartDescriptor of type AXChartDescriptor.
  • To do so, we specify a title and a summary that will be displayed and read to the user on the Audio Graphs detail page. We also pass in the axis and data series descriptors that we created earlier.
  • We leave the setter empty because this property will never be set from anywhere else.

Using Audio Graphs

Let's take a look at our audio graph in action. It can be intimidating to use VoiceOver if you are not used to it. A double or triple tap on the iPhone's back is the best way to enable or disable it. Scroll down to Back Tap in Settings > Accessibility > Touch. A variety of actions can be defined here to be triggered by a double or triple back tap.

Next, launch your app and navigate to the graph for which you have enabled the Audio Graph feature. Enable VoiceOver and swipe until the graph is selected.

If you're not sure how to use VoiceOver, you can consult Apple's VoiceOver gesture guide or raywenderlich.com's iOS Accessibility: Getting Started.

Let's take a look at our audio graph in action.

It can be intimidating to use VoiceOver if you are not used to it. A double or triple tap on the iPhone's back is the best way to enable or disable it. Scroll down to Back Tap in Settings > Accessibility > Touch. A variety of actions can be defined here to be triggered by a double or triple back tap.

Next, launch your app and navigate to the graph for which you have enabled the Audio Graph feature. Enable VoiceOver and swipe until the graph is selected.

If you're not sure how to use VoiceOver, you can consult Apple's VoiceOver gesture guide or raywenderlich.com's iOS Accessibility: Getting Started.

Open the Audio Graph detail page — this is the result of our efforts!

Swipe right until the Play button appears, then double-tap to listen to the Audio Graph. It's easy to see (or hear) why this new feature improves graph accessibility for visually impaired users so much.

Where To Go From Here

As demonstrated in this tutorial, Apple made it very simple to add audio representations to existing graphs. All you have to do is wrap your data points in an AxDataSeriesDescriptor and add some metadata.

In the following section, we'll look at how adaptable they are. We'll go over various types of axes and show more than one data series. This section will be published next week, so stay tuned for more information!

Audio Graphs can help you make your apps more accessible to a wider audience. This will provide your users with a better experience.

If you have questions or remarks, please let us know in the given email below. Airo Global Software will be your digital partner.

E-mail id: [email protected]

enter image description here

Author - Johnson Augustine
Chief Technical Director and Programmer
Founder: Airo Global Software Inc
LinkedIn Profile: www.linkedin.com/in/johnsontaugustine/

Equatable, Comparable, Identifiable, and Hashable solutions

Protocols are not new to iOS or its cousin OS X; in fact, the delegate protocol is the bread and butter of more than half of the frameworks, though this may change in the coming years with the introduction of async/await. Having said that, since SwiftUI's release in 2019, protocols appear to be changing their colors in the meantime.

That is because SwiftUI includes a number of mandatory protocols that are linked to the language itself. Although it is not always clear what is going on, basic protocols such as Equatable, Comparable, Identifiable, and Hashable are used.

Identifiable

This is the first protocol you'll likely encounter as a new SwiftUI coder when attempting to define a ForEach loop, for example, within a List — assuming we have an array of dice containing a custom struct.

struct ContentView: View {
 @State var dice = [Dice]()
 var body: some View {
   ForEach(dice) {
     Text(String($0.value))
   }
 }
}

The compiler is looking for a way to uniquely identify each row within your struct's loop. The dice variable shown here must be Identifiable. Conformance is obtained through the use of code such as this.

struct Dice: Identifiable {
 //  var id = UUID()
 var id = Date().timeIntervalSince1970 // epoch [dies Jan 19, 2038]
 var value: Int!
}

Hashable

The second protocol you're likely to encounter is Hashable, which SwiftUI requires for loops like the one shown here.

ForEach(dice, id: \.self) { die in
 Text("Die: \(die.value)")
}

But be careful, because including a third protocol Equatable with a definition shown will cause your code to crash.

struct Dice: Equatable, Hashable {
 var id = UUID()
 var value: Int!
 static func ==(lhs: Dice, rhs: Dice) -> Bool {
   lhs.value == rhs.value
 }
}

The hashable needs here necessitate the use of a unique identifier, similar to the identifiable protocol.

To use both the Hashable and the Equatable protocols, you must instruct the Hashable protocol to focus on the id, which is, of course, that unique Identifiable value.

extension Dice: Hashable {
 static func ==(lhs: Dice, rhs: Dice) -> Bool {
   lhs.id == rhs.id
 }
 func hash(into hasher: inout Hasher) {
   hasher.combine(id)
 }
}

However, the hash function can also be useful in this case because it guarantees that it will produce the same output given the same input. Although this example may appear to be a little pointless, you can use code like this to generate the same key repeatedly.

.onAppear {
 var hash = Hasher()
 hash.combine(die.id)
 print("hash \(hash.finalize()) \(die.hashValue)")
}

The main page at Apple provides a more real-world example of how this protocol can be used.

Comparable

Comparable, which appears to be nearly identical to Equatable, is the next protocol on my shortlist.

extension Dice: Comparable {
 static func < (lhs: Dice, rhs: Dice) -> Bool {
   lhs.value < rhs.value
 }
}

This code was added to our SwiftUI interface to enable us to use the new protocol/property.

if dice.count == 2 {
 if dice.first! > dice.last! {
   Text("Winner 1st")
 } else {
   Text("Winner 2nd")
 }
}

However, there is a catch. I can't use the == in the same way because I had to point to the id to conform to the Hashable protocol.

if dice.first! == dice.last! {
 Text("Unequal \(dice.hashValue)")
} else {
 Text("Equal \(dice.hashValue)")
}

To get around/fix this, I'll need to hire a new operator. The fix for the above necessitates the creation of a new infix operator, such as ==== Obviously, I'd need to change the code snippet above to use the ==== instead of the == shown.

infix operator ==== : DefaultPrecedence
extension Dice {
 static func ====(lhs: Dice, rhs: Dice) -> Bool {
   lhs.face == rhs.face
 }
}

I'm sure Apple would prefer that you use protocols in your everyday code to make it clear what you're trying to accomplish, essentially an extension of types that you can use on your custom objects.

Equatable

Okay, I admit that the infix operator isn't for everyone, especially Swift purists. So here's an alternative that's more equitable and doesn't require an infix. Within it, I define the view as adhering to the equatable protocol in order to target the face of my die.

Please take note of what I used.

onAppear to initialize die1 and die2, and then.onChange to handle all subsequent reloads of the dice whenever I rolled a new pair.

struct EqualView: View, Equatable {

 static func == (lhs: EqualView, rhs: EqualView) -> Bool {
   lhs.die1?.face == rhs.die2?.face
 }
 @State var die1:Dice? = nil
 @State var die2:Dice? = nil
 @Binding var dice:[Dice]
 var body: some View {
   Color.clear
     .frame(width: 0, height: 0, alignment: .center)
     .onAppear {
       die1 = dice.first!
       die2 = dice.last!
     }
     .onChange(of: dice) { values in
       die1 = dice.first!
       die2 = dice.last!
     }
   if die1?.face == die2?.face {
     Text("Equal ")
   } else {
     Text("Unequal ")
   }
 }
}

This is all about the swift protocols that are commonly used, hope you all understand this topic. If you have any doubt about the Swift protocols used in Swift UI. Don’t hesitate to contact us. Airo Global Software will be your digital partner.

E-mail id: [email protected]

enter image description here

Author - Johnson Augustine
Chief Technical Director and Programmer
Founder: Airo Global Software Inc
LinkedIn Profile: www.linkedin.com/in/johnsontaugustine/