How to tap on a specific point using Xcode UITests

19,951

Solution 1

You can tap a specific point with the XCUICoordinate API. Unfortunately you can't just say "tap 10,10" referencing a pixel coordinate. You will need to create the coordinate with a relative offset to an actual view.

We can use the mentioned web view to interact with the relative coordinate.

let app = XCUIApplication()
let webView = app.webViews.element
let coordinate = webView.coordinateWithNormalizedOffset(CGVector(dx: 10, dy: 10))
coordinate.tap()

Side note, but have you tried interacting with the web view directly? I've had a lot of success using app.links["Link title"].tap() or app.staticTexts["A different link title"].tap(). Here's a demo app I put together demonstrating interacting with a web view.


Update: As Michal W. pointed out in the comments, you can now tap a coordinate directly, without worrying about normalizing the offset.

let normalized = webView.coordinate(withNormalizedOffset: CGVector(dx: 0, dy: 0))
let coordinate = normalized.withOffset(CGVector(dx: 10, dy: 10))
coordinate.tap()

Notice that I pass 0,0 to the normalized vector and then the actual point, 10,10, to the second call.

Solution 2

@joe To go a little further off of Joe Masilotti's approach I put mine in an extensionand gave prepositional phrases to the global and local params.

func tapCoordinate(at xCoordinate: Double, and yCoordinate: Double) {
    let normalized = app.coordinate(withNormalizedOffset: CGVector(dx: 0, dy: 0))
    let coordinate = normalized.withOffset(CGVector(dx: xCoordinate, dy: yCoordinate))
    coordinate.tap()
}

By giving the global an identifiable name I can easily understand the instance for example:

tapCoordinate(at x: 100, and y: 200)

Solution 3

I found Laser's answer to work fine with Xcode 11, but made a few tweaks to easily integrate it into my testing.

extension XCUIApplication {
    func tapCoordinate(at point: CGPoint) {
        let normalized = coordinate(withNormalizedOffset: .zero)
        let offset = CGVector(dx: point.x, dy: point.y)
        let coordinate = normalized.withOffset(offset)
        coordinate.tap()
    }
}

Now, when I need to tap on a given location, I just provide a CGPoint and call this against my XCUIApplication like so:

let point = CGPoint(x: xCoord, y: yCoord)
app.tapCoordinate(at: point)

Solution 4

<something>.coordinate(withNormalizedOffset: CGVector.zero).withOffset(CGVector(dx:10,dy:60)).tap()

Pass .zero to the normalized vector and then the actual point (10,60)

Share:
19,951

Related videos on Youtube

Dmitry Sokurenko
Author by

Dmitry Sokurenko

Ruby &amp; JavaScript Developer

Updated on June 19, 2022

Comments

  • Dmitry Sokurenko
    Dmitry Sokurenko almost 2 years

    I want to use Xcode UI tests with the Fastlane Snapshot to make screenshots of the Cordova app. Basically, as my entire app is just a web view, all the Xcode UI test helper methods become irrelevant, and I just want to tap on specific points, e.g. tap(x: 10, y: 10) should produce a tap at the point {10px; 10px}.

    That's probably very simple, but I can't figure out how to do it.

    Thanks.

  • Michał W.
    Michał W. almost 8 years
    I'm not sure that is correct as I needed to add 'coordinateWithOffset' to perform x/y tap: let cooridnate = self.coordinateWithNormalizedOffset(CGVector(dx: 0, dy: 0)).coordinateWithOffset(CGVector(dx: position.x, dy: position.y)) cooridnate.tap() forums.developer.apple.com/thread/13373
  • Joe Masilotti
    Joe Masilotti almost 8 years
    Good idea! Thanks @MichałW. I noted the comment in an update to the answer as it was already accepted.
  • mmd1080
    mmd1080 about 7 years
    Worked for me in Swift 3!
  • Doug Amos
    Doug Amos over 6 years
    This doesn't compile in Swift 3
  • Olivia Brown
    Olivia Brown almost 6 years
    This is great for Swift 4 :)
  • adamjansch
    adamjansch almost 6 years
    Another small optimization is to use CGVector.zero instead of CGVector(dx: 0, dy: 0).
  • Tadej Magajna
    Tadej Magajna over 4 years
    I had to run it as tapCoordinate(at:100, and:200) and it worked perfectly! Unlike the exact example you used which didn't compile in Swift 5
  • Neelam Verma
    Neelam Verma about 2 years
    You saved my Day!