I have tests in XCTest using Swift and I am trying to swipe through a feed until a condition is met. I am using the swipeUp()
, but the problem with this is it acts like a human swipe and has this painful animation of slowing down when it lets go. It doesn't try the swipe again until the slowing-down-animation is complete. Additionally, that method takes no arguments. I would like to see if there is something like Calabash or shoot even Androids Espresso where there are properties to a swipe method like swipeUp(fast)
or swipeUp(medium)
or even swipeUp(x0,y200)
.
Here is what I am trying to do in code:
func scrollDownUntilYouFindSomething() {
while !findingSomehting.exists {
app.swipeUp()
}
}
Simple enough, but the swipeUp() in XCUIApplication is painfully slow. I want to be able to swipe with precision and even by coordinate. I tried to use the coordinate approach taken from Replicate pull to refresh in XCTest UI testing
but its equally as slow when put into a loop.
Updated the first answer for Swift 4.
extension XCUIElement {
enum Direction: Int {
case up, down, left, right
}
func gentleSwipe(_ direction: Direction) {
let half: CGFloat = 0.5
let adjustment: CGFloat = 0.25
let pressDuration: TimeInterval = 0.05
let lessThanHalf = half - adjustment
let moreThanHalf = half + adjustment
let centre = self.coordinate(withNormalizedOffset: CGVector(dx: half, dy: half))
let aboveCentre = self.coordinate(withNormalizedOffset: CGVector(dx: half, dy: lessThanHalf))
let belowCentre = self.coordinate(withNormalizedOffset: CGVector(dx: half, dy: moreThanHalf))
let leftOfCentre = self.coordinate(withNormalizedOffset: CGVector(dx: lessThanHalf, dy: half))
let rightOfCentre = self.coordinate(withNormalizedOffset: CGVector(dx: moreThanHalf, dy: half))
switch direction {
case .up:
centre.press(forDuration: pressDuration, thenDragTo: aboveCentre)
case .down:
centre.press(forDuration: pressDuration, thenDragTo: belowCentre)
case .left:
centre.press(forDuration: pressDuration, thenDragTo: leftOfCentre)
case .right:
centre.press(forDuration: pressDuration, thenDragTo: rightOfCentre)
}
}
}
I wrote an extension based on the excellent answer given by Bharathram C.
I find it to be a much gentler swipe than XCUIElement.swipeUp()
// XCUIElement+GentleSwipe.swift
import Foundation
import XCTest
extension XCUIElement
{
enum direction : Int {
case Up, Down, Left, Right
}
func gentleSwipe(_ direction : direction) {
let half : CGFloat = 0.5
let adjustment : CGFloat = 0.25
let pressDuration : TimeInterval = 0.05
let lessThanHalf = half - adjustment
let moreThanHalf = half + adjustment
let centre = self.coordinate(withNormalizedOffset: CGVector(dx: half, dy: half))
let aboveCentre = self.coordinate(withNormalizedOffset: CGVector(dx: half, dy: lessThanHalf))
let belowCentre = self.coordinate(withNormalizedOffset: CGVector(dx: half, dy: moreThanHalf))
let leftOfCentre = self.coordinate(withNormalizedOffset: CGVector(dx: lessThanHalf, dy: half))
let rightOfCentre = self.coordinate(withNormalizedOffset: CGVector(dx: moreThanHalf, dy: half))
switch direction {
case .Up:
centre.press(forDuration: pressDuration, thenDragTo: aboveCentre)
break
case .Down:
centre.press(forDuration: pressDuration, thenDragTo: belowCentre)
break
case .Left:
centre.press(forDuration: pressDuration, thenDragTo: leftOfCentre)
break
case .Right:
centre.press(forDuration: pressDuration, thenDragTo: rightOfCentre)
break
}
}
}
Yes, we can have a co-ordinate based swipe in Swift. We can create our own swipe action by using the pressForDuration(duration, thenDragToElement: XCElement). Please see an example below.
For this we will need
Then we can have a function similar to below (in Swift 3)
func customSwipe(refElement:XCUIElement,startdelxy:CGVector,enddeltaxy: CGVector){
let swipeStartPoint = refElement.coordinate(withNormalizedOffset: startdelxy)
let swipeEndPoint = refElement.coordinate(withNormalizedOffset: enddeltaxy)
swipeStartPoint.press(forDuration: 0.1, thenDragTo: swipeEndPoint)
}
Here if startdelxy is (0.0,0.0) and enddeltaxy is (0.0,-ve), swipe will go upward from the refElement. If enddeltaxy is (0.0,+ve) swipe will go down.This is because our reference element will be the origin, X is +ve going right and Y is +ve going below our reference element. Start with smaller values of Y (say -0.75) and change as necessary. More the Difference, Longer the Swipe.
The swipeStartPoint and swipeEndPoint can be replaced with an element (if such exists in the view). Usually, for swipeEndPoint we can go for an element in the Nav Bar for a swipeUp action. Also, Changing the X can give us customSwipes in the left and right directions.
As for the Speed of swiping, I'm not aware of any API that can swipe faster/slower than the default rate.
I tried to refactor gentleSwipe function to target element. Following refactor focus to reach targeted element. We can pass element in swipeUp function and it will call gentleSwipe that's will help us to reach targeted element. I hope it will help others as well.
Here is code :
public func swipeUp(to element: XCUIElement) {
while !(elementIsWithinWindow(element: element)) {
XCUIApplication().gentleSwipe(.up)
}
}
private func elementIsWithinWindow(element: XCUIElement) -> Bool{
guard element.exists && element.isHittable else {return false}
return true
}
enum direction : Int {
case up, down, left, right
}
func gentleSwipe(_ direction : direction) {
let half : CGFloat = 0.5
let adjustment : CGFloat = 0.25
let pressDuration : TimeInterval = 0.05
let lessThanHalf = half - adjustment
let moreThanHalf = half + adjustment
let centre = self.coordinate(withNormalizedOffset: CGVector(dx: half, dy: half))
let aboveCentre = self.coordinate(withNormalizedOffset: CGVector(dx: half, dy: lessThanHalf))
let belowCentre = self.coordinate(withNormalizedOffset: CGVector(dx: half, dy: moreThanHalf))
let leftOfCentre = self.coordinate(withNormalizedOffset: CGVector(dx: lessThanHalf, dy: half))
let rightOfCentre = self.coordinate(withNormalizedOffset: CGVector(dx: moreThanHalf, dy: half))
switch direction {
case .up:
centre.press(forDuration: pressDuration, thenDragTo: aboveCentre)
break
case .down:
centre.press(forDuration: pressDuration, thenDragTo: belowCentre)
break
case .left:
centre.press(forDuration: pressDuration, thenDragTo: leftOfCentre)
break
case .right:
centre.press(forDuration: pressDuration, thenDragTo: rightOfCentre)
break
}
}
Call Tips
app.swipeUp(to: "element")
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With