Clear the Safari cache on iOS devices
There is no existing function to clear the cache of the Safari browser, but you can create a script that mimics the behavior of a real user. The script includes the following steps:
- On the iOS device, open the Settings app.
- Scroll down and find Safari.
- Click Safari.
- Scroll down and click Clear History and Website Data.
The following script is Appium 2-compliant and has been tested on iPhones and iPads running versions of iOS 14, 15, 16, 17, and 18.
Copy
package remote.pureAppium;
import com.perfecto.reportium.client.ReportiumClient;
import com.perfecto.reportium.client.ReportiumClientFactory;
import com.perfecto.reportium.model.Job;
import com.perfecto.reportium.model.PerfectoExecutionContext;
import com.perfecto.reportium.model.Project;
import com.perfecto.reportium.test.TestContext;
import com.perfecto.reportium.test.result.TestResultFactory;
import io.appium.java_client.AppiumBy;
import io.appium.java_client.ios.IOSDriver;
import io.appium.java_client.ios.options.XCUITestOptions;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.testng.ITestResult;
import org.testng.annotations.AfterClass;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.time.Duration;
import java.util.HashMap;
import static java.lang.Integer.parseInt;
public class IOS_Clear_Safari_Cache {
IOSDriver driver;
int iosMajorVersion;
static ReportiumClient reportiumClient;
String cloudName = "demo";
@BeforeClass
public void setUp() throws Exception {
var options = new XCUITestOptions()
// .setDeviceName("00008101-00051D820C03001E")
;
var perfectoOptions = new HashMap<>();
// perfectoOptions.put("model", ".*iPad.*");
// perfectoOptions.put("platformVersion", "18.*");
perfectoOptions.put("securityToken", "");
options.setCapability("perfecto:options", perfectoOptions);
driver = new IOSDriver(
new URL("https://" + cloudName + ".perfectomobile.com/nexperience/perfectomobile/wd/hub"), options
);
var perfectoExecutionContext = new PerfectoExecutionContext.PerfectoExecutionContextBuilder()
.withProject(new Project("Perfecto.Support IOS tests", "1.0"))
.withJob(new Job("Clear Safari Cache", 3))
.withWebDriver(driver)
.build();
reportiumClient = new ReportiumClientFactory().createPerfectoReportiumClient(perfectoExecutionContext);
reportiumClient.testStart("Clear Safari Cache on iOS", new TestContext());
}
@AfterMethod
public void testEnded(ITestResult result) {
if (result.getStatus() == ITestResult.FAILURE) {
reportiumClient.testStop(TestResultFactory.createFailure(result.getThrowable()));
}
else {
reportiumClient.testStop(TestResultFactory.createSuccess());
}
}
@AfterClass
public void tearDown() throws URISyntaxException, IOException {
driver.quit();
// java.awt.Desktop.getDesktop().browse(new URI(reportiumClient.getReportUrl()));
}
@Test
public void clearSafariCache() throws Exception {
WebElement element;
iosMajorVersion = getIosVersion(driver);
var params = new HashMap<>();
params.put("identifier", "com.apple.Preferences");
driver.executeScript("mobile:application:open", params);
driver.executeScript("mobile:application:close", params);
driver.executeScript("mobile:application:open", params);
driver.context("NATIVE_APP");
if (iosMajorVersion >= 18) {
element = findAppsContainerButton(driver);
element.click();
}
element = findSafariContainerButton(driver);
element.click();
element = findClearCacheButton(driver);
if (!element.isEnabled()) {
System.out.println("'Clear History and Website Data' button is disabled, which means that cache is already cleared! Exiting");
return;
}
element.click();
if (iosMajorVersion >= 17) {
clearCacheInPopup(driver);
}
else {
driver.findElement(AppiumBy.xpath("//XCUIElementTypeButton[starts-with(@label,'Clear')]"))
.click();
// Close Tabs button was added in iOS 15.x so it might be missing on some devices
try {
driver.findElement(AppiumBy.xpath("//XCUIElementTypeButton[@label=\"Close Tabs\"]"))
.click();
} catch (Exception ex) {
}
}
Thread.sleep(500);
if (driver.findElement(AppiumBy.xpath("//*[@name=\"Clear History and Website Data\"]")).isEnabled()) {
throw new Exception("Clear Safari cache result not expected. Clear History and Website Data button is still enabled");
}
}
private int getIosVersion(IOSDriver driver) {
var versionString = getDeviceInfo(driver, "osVersion");
var parts = versionString.split("\\.");
return parts.length > 1 ? parseInt(parts[0]) : parseInt(versionString);
}
private WebElement findAppsContainerButton(IOSDriver driver) throws Exception {
int current = 1;
int maxRetries = 7;
var exception = new Exception(String.format("Apps element not found after %s retries ", maxRetries));
var params = new HashMap<>();
params.put("start", "10%,90%");
params.put("end", "10%,20%");
params.put("duration", "1");
while (current <= maxRetries) {
driver.executeScript("mobile:touch:swipe", params);
Thread.sleep(500);
try {
return driver.findElement(AppiumBy.xpath("//*[@label=\"Apps\"]"));
} catch (Exception ex) {
exception = ex;
}
current++;
}
throw exception;
}
public static String getDeviceInfo(IOSDriver driver, String deviceProperty){
var params = new HashMap<>();
params.put("property", deviceProperty);
return (String) driver.executeScript("mobile:device:info", params);
}
public WebElement findSafariContainerButton(IOSDriver driver) throws Exception {
var params = new HashMap<>();
// On iPad devices the Settings app is split in 2 columns
// On iOS >= 18 Safari Settings is under the new App section, so on iPad devices
// if iOS >= 18 we are already within the App section so swipe on the right hand side
// If iOS <= 17 and lower, we are still in the main Settings container, so swipe on the left hand side
var x = iosMajorVersion >= 18 ? "90%" : "10%";
params.put("start", x + ",90%");
params.put("end", x + ",30%");
params.put("duration", "2");
int currentRetry = 1;
int maxRetries = 7;
var exception = new Exception(String.format("Safari settings element not found after %s retries ", maxRetries));
while (currentRetry <= maxRetries) {
driver.executeScript("mobile:touch:swipe", params);
Thread.sleep(500);
try {
return driver.findElement(AppiumBy.xpath("//*[@value='Safari' and @visible='true']"));
} catch (Exception ex) {
exception = ex;
}
currentRetry++;
}
throw exception;
}
private static WebElement findClearCacheButton(IOSDriver driver) throws Exception {
int current = 1;
int maxRetries = 3;
var exception = new Exception(String.format("'Clear History and Website Data' element not found after %s retries ", maxRetries));
var params = new HashMap<>();
params.put("start", "90%,90%");
params.put("end", "90%,20%");
params.put("duration", "1");
while (current <= maxRetries) {
driver.executeScript("mobile:touch:swipe", params);
Thread.sleep(500);
try {
return driver.findElement(AppiumBy.xpath("//*[@name=\"Clear History and Website Data\"]"));
} catch (Exception ex) {
exception = ex;
}
current++;
}
throw exception;
}
private static void clearCacheInPopup(IOSDriver driver) throws InterruptedException {
var wait = new WebDriverWait(driver, Duration.ofSeconds(5));
var element = wait.until(ExpectedConditions.elementToBeClickable(AppiumBy.xpath("//*[@label=\"All history\"]")));
element.click();
Thread.sleep(500);
element = driver.findElement(AppiumBy.id("CloseAllTabsSwitch"));
if (element.getAttribute("value").equals("0"))
element.click();
Thread.sleep(500);
driver.findElement(AppiumBy.id("ClearHistoryButton"))
.click();
}
}