Setup axeDevToolsUIKit.xcframework

Link to Setup axeDevToolsUIKit.xcframework copied to clipboard

Requires:



Load the Accessibility Layer

For simulators, open the Accessibility Inspector by right-clicking on the Xcode icon and navigating to Open Developer Tool > Accessibility Inspector.

For devices, turn on VoiceOver (alternatively a switch control) by navigating to Settings > Accessibility > VoiceOver > On.

For automated testing or CI/CD pipelines, execute the following command as a Build Phase or as a scheme Pre-Action. open -a /Applications/Xcode.app/Contents/Applications/Accessibility\ Inspector.app/

Download & Embed Framework

Using Swift Package Manager

Package URL: https://github.com/dequelabs/axe-devtools-ios.

  1. In Xcode, select your main project to open project settings.
  2. In the settings pane, ensure the main project is selected and not a target.
  3. Select the Package Dependencies tab.
  4. Select the plus icon to add a package dependency.
  5. Insert the package URL, and select the axe-devtools-ios package.
  6. For the Dependency Rule, select the exact version and enter the tag to retrieve.
  7. Click the Add Package button.
  8. You'll be prompted to select a target to add the framework to. For axeDevToolsUIKit, select the checkbox to the left, and ensure your application's target is selected. Click the Add Package button, and Xcode will begin fetching the assets.


Screenshot of Xcode's Swift Package Window to select a target where the framework will be added.

Using Artifactory

View the instructions for using Artifactory.

Embed Framework

Now that you've downloaded the framework, it's time to embed it into your application.

  1. Select Your Application's target.
  2. Select the General tab.
  3. Drag the framework to Frameworks, Libraries, and Embedded Content.
  4. Try building. If the error "axeDevToolsUIKit not found" appears, ensure that
    1. The framework file is in the correct location (project folder, designated frameworks folder, etc)
    2. Framework Search Path in Build Settings includes the correct path to the framework


screenshot of Xcode highlighting steps to embed the framework.

Manual Testing

Add the below code to SceneDelegate:

//In SceneDelegate.swift
import axeDevToolsUIKit

Setup the Framework

Add a variable within the SceneDelegate object to store the instance of axe DevTools to interact with:

var axe: AxeDevTools?

Connect for a different user

If you are creating a build for another user to test with, initiate the axe login floating action button in sceneDidBecomeActive and they will be able to authenticate with their user:

axe = AxeDevTools.showAxeFAB()

Learn more about the login feature. That's it! Upon using this build, another user will be able to use the axe floating action button to log in to the framework and start initiating their scans. Note: You may want to send them the documentation for the login feature if it's their first time with the library.

Connect with your user

If you want to get started testing with your user, initialize the framework with one of the following authentication snippets in sceneDidBecomeActive :

Connect with an API key:

Generate an API key at axe.deque.com.

axe = try? AxeDevTools.login(withAPIKey: "<DEQUE_APIKEY>")
Connect with username and password:
axe = try? AxeDevTools.login(withUsername: "<DEQUE_USERNAME>", andPassword: "<DEQUE_PASSWORD>")
Connect to the desktop application:

To get started with the desktop application, be sure to reference Desktop Setup.

axe = try? AxeDevTools.setLocalConnection()
Enable local networking and arbitrary loads within your `Info.plist`


        <key>NSAppTransportSecurity</key>
        <dict>
            <key>NSAllowsArbitraryLoads</key>
            <true/>
            <key>NSAllowsLocalNetworking</key>
            <true/>
        </dict>

Load the Floating Action Button (FAB)

After authenticating with your user and initializing the AxeDevTools object, start the floating action button for manual testing:

axe?.showA11yFAB()

In sceneWillResignActive stop testing and remove the floating action button.

axe?.hideA11yFAB()

From here, you can now run your application to access the floating action button. The button can be moved around the screen to not obstruct development. When you're ready for a scan, tap the button and a scan of the current screen will be sent to axe DevTools Mobile Dashboard where you can view the results.

Automated Testing

This framework works with many iOS testing frameworks, but we'll use XCTest in the below examples. Be sure to check out Advanced Automation for other testing framework examples.

Setup for Testing

In any file used for accessibility testing, import the framework.

import axeDevToolsUIKit

Create an object within your testing class to hold onto the axe DevTools instance:

var axeDevTools: AxeDevTools?

Whether unit or UI testing, initialize the framework within the setUp or setUpWithError methods.

Connect with an API key:

Generate an API key at axe.deque.com.

axe = try? AxeDevTools.login(withAPIKey: "<DEQUE_APIKEY>")

Connect with username and password:

axe = try? AxeDevTools.login(withUsername: "<DEQUE_USERNAME>", andPassword: "<DEQUE_PASSWORD>")

Connect to the desktop application:

To get started with the desktop application, be sure to reference Desktop Setup.

axe = try? AxeDevTools.setLocalConnection()
Enable local networking and arbitrary loads within your `Info.plist`


        <key>NSAppTransportSecurity</key>
        <dict>
            <key>NSAllowsArbitraryLoads</key>
            <true/>
            <key>NSAllowsLocalNetworking</key>
            <true/>
        </dict>

Setup Example

var axe: AxeDevTools?

override func setUpWithError() throws {
    axe = try AxeDevTools.login(withAPIKey: "<DEQUE_APIKEY>")
}

Unit Testing

To begin testing, there are a few different APIs you can call based on how your application is developed. Any view controller or view that is passed in is assumed to be the parent view and will automatically include the child views in the scan.

To test the initial view of a storyboard:

let result = try? axe?.run(onStoryboardName: "Main")

To test a view controller:

let result = try? axe?.run(onViewController: UIViewController)

To test a view:

let result = try? axe?.run(onView: UIView)

Send Results to the Dashboard

Utilize postResult() from the axe DevTools object in any of the above examples to send the result of the scan to the server.

import axeDevToolsUIKit

class UnitTests: XCTestCase {
    var axe: AxeDevTools?

    override func setUpWithError() throws {
        axe = try AxeDevTools.login(withAPIKey: "<DEQUE_APIKEY>")
    }

    func testAccessibility() throws {
        let view = CustomView()
        let result = try axe?.run(onView: view)
        try axe?.postResult(result)
    }
}
tip

You can use the axe DevTools object for other great features as well, such as saving results locally on your machine and tagging results in the dashboard.

UI Testing

To switch from manual testing to automated UI testing, a small change to the setup code you added in Interactive Testing is required. As the floating action button is still required, we'll need to change axe?.showA11yFAB() to use the automated instance instead. The automated floating action button is invisible to not interfere with any screenshot testing you have but is still programmatically tappable to capture a scan.

After setting the server in sceneDidBecomeActive, replace the call to axe?.showA11yFAB() with the below snippet for automated testing:

axe?.showA11yFAB(customFAB: AutomatedTestFAB())

Setup for Testing

In your UI test file, import the framework.

import axeDevToolsUIKit

As you won't have access to your application's axe DevTools object, you can utilize the setUp method to have one available for your tests by logging in again. Use this object to interact with available features.

import axeDevToolsUIKit
import XCTest

class MyUITests: XCTestCase {
    var axe: AxeDevTools?
    var app = XCUIApplication()

    override func setUpWithError() throws {
        axe = try AxeDevTools.login(withAPIKey: "<DEQUE_APIKEY>")

        app.launch()
    }
}

Capture a Scan

When you're ready to capture a scan, use the floating action button's (FAB) accessibility identifier "com.deque.axeDevTools.accessibilityFab" to programmatically tap it and send a scan to the dashboard:

XCUIApplication().buttons["com.deque.axeDevTools.accessibilityFab"].tap()

Wait for the FAB to return to know it has finished uploading. The FAB's label will now hold a key you can use to retrieve the scan from the server.

XCTAssert(fab.waitForExistence(timeout: 5))

// Grab the result from the server
guard let key = AxeDevToolsResultKey(fabTitle: fab.label) else {
    XCTFail("Could not retrieve scan from the server.")
    return
}
tip

The result key looks like this:

{
  "userId": "55555555-5555-5555-5555-555555555555",
  "packageName": "com.example.app",
  "resultId": "123456789ab"
}

With the key, you can now get additional information from the scan such as passes and failures, and update the scan with any tags you want to include.

// Tag the result with the tags provided
try? axe?.tagResult(key, withTags: tags)

Full Example


import axe_devtools_ios_sample_app
import axeDevToolsUIKit
import XCTest

class MyAppUITests: XCTestCase {
    var axe: AxeDevTools?
    var app = XCUIApplication()

    override func setUpWithError() throws {
        axe = try AxeDevTools.login(withAPIKey: "<DEQUE_APIKEY>")

        app.launch()
    }

    func testAccessibility() throws {
        let FAB_ID = "com.deque.axeDevTools.accessibilityFab"
        let fab = app.buttons[FAB_ID]

        // Tap the FAB to send the scan to the server
        fab.tap()

        // Once it returns, the result from the scan can be seen on the Dashboard.
        // The FAB's label has the key needed to retrieve the scan from the server.
        XCTAssert(fab.waitForExistence(timeout: 5))

        // Grab the result from the server
        guard let key = AxeDevToolsResultKey(fabTitle: fab.label) else {
            XCTFail("Could not retrieve scan from the server.")
            return
        }

        // Tag the result with the tags provided
        try axe?.tagResult(key, withTags: ["sauce testing"])

        guard let result = try axe?.getResult(key) else {
            XCTFail("Not logged in. Result could not be fetched.")
            return
        }

        // Fail the build if accessibility issues were found
        XCTAssertEqual(result.failures.count, 0)
    }
}