Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to use Xcode UI Testing on an app using SpriteKit?

I want to use UI testing for a game using SKSpriteKit.

As my first tries did not work I wonder if it possible to use Xcode UI Testing with SpriteKit.

like image 320
Dominique Vial Avatar asked Dec 15 '15 15:12

Dominique Vial


People also ask

Does SpriteKit work with SwiftUI?

Even though the default Game Xcode Template creates the project based on a UIKit application, you can create a SwiftUI app and put your SpriteKit game inside it without any hustle thanks to the SpriteView view!

How do I run a UI test in Xcode?

If you are making a new app, make sure you select the “Use UI Tests” checkbox when creating a new Xcode project. To add it in an existing project go to the build folder and on the bottom left you will see the plus button. Selecting that will give you a dropdown menu, where you can then select “UI Testing Bundle”.

How do I test my IOS UI?

Recording a UI TestFrom the debug bar, click the Record UI Test button. Xcode will launch the app and run it. You can interact with the element on-screen and perform a sequence of interactions for any UI test. Whenever you interact with an element, Xcode writes the corresponding code for it into your method.

Is SpriteKit dead?

SpriteKit is definitely not dead and still supported by Apple. However it has been overshadowed by other technologies from Apple such as AR, Metal, etc. Additionally, it's not portable to other platforms (unlike Unity).


1 Answers

The main idea is to create the accessibility material for elements that you want to UI test. That's mean:

  1. List all accessible elements contained in the scene

  2. Configure settings for each of these elements, especially framedata.

Step by Step

This answer is for Swift 3 and is mainly based on Accessibility (Voice Over) with Sprite Kit

Let's say I want to make the SpriteKit button named tapMe accessible.

List of accessible elements.

Add an array of UIAccessibilityElementto the Scene.

 var accessibleElements: [UIAccessibilityElement] = []

Scene's cycle life

I need to update two methods: didMove(to:)and willMove(from:).

override func didMove(to view: SKView) {
    isAccessibilityElement       = false
    tapMe.isAccessibilityElement = true
}

As scene is the accessibility controller, documentation stated it must return False to isAccessibilityElement.

And:

override func willMove(from view: SKView) {
    accessibleElements.removeAll()
}

Override UIAccessibilityContainer methods

3 methods are involved: accessibilityElementCount(), accessibilityElement(at index:) and index(ofAccessibilityElement. Please allow me to introduce an initAccessibility() method I'll describe later.

override func accessibilityElementCount() -> Int {
    initAccessibility()
    return accessibleElements.count
}

override func accessibilityElement(at index: Int) -> Any? {

    initAccessibility()
    if (index < accessibleElements.count) {
        return accessibleElements[index]
    } else {
        return nil
    }
}

override func index(ofAccessibilityElement element: Any) -> Int {
    initAccessibility()
    return accessibleElements.index(of: element as! UIAccessibilityElement)!
}

Initialize accessibility for the Scene

func initAccessibility() {

    if accessibleElements.count == 0 {

        // 1.
        let elementForTapMe   = UIAccessibilityElement(accessibilityContainer: self.view!)

        // 2.
        var frameForTapMe = tapMe.frame

        // From Scene to View
        frameForTapMe.origin = (view?.convert(frameForTapMe.origin, from: self))!

        // Don't forget origins are different for SpriteKit and UIKit:
        // - SpriteKit is bottom/left
        // - UIKit is top/left
        //              y
        //  ┌────┐       ▲
        //  │    │       │   x
        //  ◉────┘       └──▶
        //
        //                   x
        //  ◉────┐       ┌──▶
        //  │    │       │
        //  └────┘     y ▼
        //
        // Thus before the following conversion, origin value indicate the bottom/left edge of the frame.
        // We then need to move it to top/left by retrieving the height of the frame.
        //


        frameForTapMe.origin.y = frameForTapMe.origin.y - frameForTapMe.size.height


        // 3.
        elementForTapMe.accessibilityLabel   = "tap Me"
        elementForTapMe.accessibilityFrame   = frameForTapMe
        elementForTapMe.accessibilityTraits  = UIAccessibilityTraitButton

        // 4.
        accessibleElements.append(elementForTapMe)

    }
}
  1. Create UIAccessibilityElement for tapMe
  2. Compute frame data on device's coordinates. Don't forget that frame's origin is the top/left corner for UIKit
  3. Set data for UIAccessibilityElement
  4. Add this UIAccessibilityElement to list of all accessible elements in scene.

Now tapMe is accessible from UI testing perspective.

References

  • Session 406, UI Testing in Xcode, WWDC 2015

  • eyes off eyes on — Voiceover accessibility in SpriteKit

  • How do I support VoiceOver in a SpriteKit game? | Apple Developer Forums

  • swift - Accessibility (Voice Over) with Sprite Kit - Stack Overflow

like image 112
Dominique Vial Avatar answered Sep 18 '22 16:09

Dominique Vial