Sometimes we want to automate an iOS device, but don’t want to automate any app in particular, or want to start from the Home Screen as part of a multi-app flow, or simply want to automate a set of built-in apps the way a user would approach things. In this case, it’s actually possible to start an Appium iOS session without a specific application. To do this we make use of the concept of the iOS Springboard, which is essentially another word for the home screen. The Springboard is essentially an application, though it is one that can’t be terminated. As an “app”, it has its own bundle ID: com.apple.springboard. So we can actually use this to start an Appium session without referring to any real application in particular:
capabilities.setCapability(“app”, “com.apple.springboard”);
On its own, however, this isn’t going to work, because Appium will try to launch this app, and deep in the XCUITest code related to app launching is some logic that makes sure the app is terminated before launch. As mentioned earlier, the Springboard can’t be terminated, so trying to start an Appium session this way will lead to a hanging server. What we can do is include another capability, autoLaunch, and set it to false, which tells Appium not to bother with initializing and launching the app, but just to start a session and give back control immediately:
capabilities.setCapability(“autoLaunch”, false);
At this point, starting an Appium session in this way will drop you at the Springboard. It won’t necessarily drop you at any particular page of the Springboard, however. If you are an iOS user, you will know that the Home Screen is really a whole set of screens, depending on how many apps you have and how you have organized them. One of the main things you would want to do from the home screen is find and interact with an icon for a given app. We will see, how can we do this,
Let’s imagine that below is our test method implementation,
@Test
public void testSpringboard() {
wait.until(AppIconPresent(“FaceTime”)).click();
pressHome();
wait.until(AppIconPresent(“Camera”)).click();
pressHome();
}
Here we have created a custom expected condition called AppIconPresent, which takes the app icon text, and will attempt to find that icon, navigating through the different pages of the Springboard if the icon is not already present. This is actually conceptually a bit tricky, because of how the Springboard app is implemented. No matter how many pages you have in your Springboard, all pages show up within the current UI hierarchy. This means it is easy to find an icon for an app even if it’s not on the currently-displayed page. However, if you try to tap that icon, it will not work because the icon is not actually visible. So, we need some way of figuring out how to move to the correct page before tapping. Let’s see the the implementation of AppIconPresent below:
protected ExpectedCondition AppIconPresent(final String appName) {
pressHome();
currPage = 1;
return new ExpectedCondition() {
@Override
public WebElement apply(WebDriver driver) {
try {
return driver.findElement(By.xpath(
“//*[@name=’Home screen icons’]” +
“//XCUIElementTypeIcon[” + currPage+ “]” +
“/XCUIElementTypeIcon[@name='” + appName + “‘]”
));
} catch (NoSuchElementException err) {
swipeToNextScreen();
currPage+= 1;
throw err;
}
}
};
}
The first thing we do is call our pressHome helper method which is just another method implemented in the current class. Below is the implementation:
protected void pressHome() {
driver.executeScript(“mobile: pressButton”, ImmutableMap.of(“name”, “home”));
}
What calling pressHome here does is ensure that we are always on the first page of the Springboard. Then, we set a class field to define what page we are on. We initialize it to 1, because after pressing the home button, we know we are on the first page. Then, in our actual condition check implementation, we try to find an icon that has the name we have been given to find.
Here is the tricky part, we don’t want to just find any icon that has the correct name because then we would find the icon even if it’s not on the current page. We only want to find an icon on the current page and then swipe to the next page if we can’t find it. To do that, we take advantage of a fact about Springboard’s UI hierarchy, which is that each page is actually coded up as an XCUIElementTypeIcon, which contains the actual app icons as children. So we can write an XPath query that restricts our search to the XML nodes corresponding to the current page. If we are unable to find an icon on the current page, we call another helper method, swipeToNextScreen. Below is the simple implementation that just performs a swipe from an area near the right edge of the screen over to the left:
protected void swipeToNextScreen() {
swipe(0.9, 0.5, 0.1, 0.5, Duration.ofMillis(750));
}
Once we have swiped to the next screen, we increment our page counter because we have now moved to the next screen. We are relying on the assumption that we will eventually find the app by the time we reach the last page, because we don’t have any logic to detect whether our swipeToNextScreen was actually successful. In general, AppIconPresent is a great example of a useful custom expected condition that has a side effect. We build it into an expected condition so we can use it flexibly with the WebDriverWait interface, and so we don’t need to write any of the looping or retry logic ourselves.
This is all about automation iOS Springboard with Appium. I hope you really enjoyed to read and learn this automation workflow. Please try to utilize this reusable utility in your iOS automation flow wherever you need it.
Reference: Appium Pro
make it perfect!
Leave a Reply