diff --git a/Assets/AltTester/Runtime/AltDriver/AltAppiumHelper.cs b/Assets/AltTester/Runtime/AltDriver/AltAppiumHelper.cs
new file mode 100644
index 000000000..a0bda59cb
--- /dev/null
+++ b/Assets/AltTester/Runtime/AltDriver/AltAppiumHelper.cs
@@ -0,0 +1,168 @@
+/*
+ Copyright(C) 2025 Altom Consulting
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+#if !UNITY_EDITOR
+using System;
+using OpenQA.Selenium;
+using OpenQA.Selenium.Appium;
+using OpenQA.Selenium.Support.UI;
+using SeleniumExtras.WaitHelpers;
+
+namespace AltTester.AltTesterSDK.Driver
+{
+ ///
+ /// API to interact with the native popup dialog (implemented by AltTester) using Appium
+ ///
+ public class AltAppiumHelper
+ {
+ ///
+ /// Sets connection data in the native popup dialog
+ ///
+ /// The Appium driver instance
+ /// The platform (Android or iOS)
+ /// The host value to set. If not provided, the host field won't be updated
+ /// The port value to set. If not provided, the port field won't be updated
+ /// The app name value to set. If not provided, the app name field won't be updated
+ /// Timeout in seconds for waiting for elements (default: 60)
+ /// Thrown when appiumDriver is null
+ /// Thrown when no fields are provided for update
+ /// Thrown when host or port is invalid
+ /// Thrown when an error occurs while interacting with the popup
+ public static void SetConnectionData(AppiumDriver appiumDriver, string platform, string host = null, string port = null, string appName = null, int timeout = 60)
+ {
+ if (appiumDriver == null)
+ {
+ throw new ArgumentNullException(nameof(appiumDriver), "Appium driver cannot be null");
+ }
+
+ // Check that at least one field is provided
+ if (string.IsNullOrEmpty(host) && string.IsNullOrEmpty(port) && string.IsNullOrEmpty(appName))
+ {
+ throw new ArgumentException("At least one of 'host', 'port', or 'appName' must be provided");
+ }
+
+ // Validate connection data
+ if (!string.IsNullOrEmpty(host) && Uri.CheckHostName(host) == UriHostNameType.Unknown)
+ {
+ throw new ArgumentException($"Invalid host: {host}. The host should be a valid host.");
+ }
+
+ if (!string.IsNullOrEmpty(port))
+ {
+ if (!int.TryParse(port, out int portInt) || portInt <= 0 || portInt > 65535)
+ {
+ throw new ArgumentException($"Invalid port: {port}. The port number should be between 1 and 65535.");
+ }
+ }
+
+ if (!platform.Equals("Android", StringComparison.OrdinalIgnoreCase) &&
+ !platform.Equals("iOS", StringComparison.OrdinalIgnoreCase))
+ {
+ throw new ArgumentException($"Unsupported platform: {platform}. Supported platforms are 'Android' and 'iOS'.");
+ }
+
+ try
+ {
+ // Set XPath based on platform
+ string hostXPath = platform.Equals("iOS", StringComparison.OrdinalIgnoreCase)
+ ? "//XCUIElementTypeTextField[@value=\"Host\"]"
+ : "//android.widget.EditText[@text=\"Host\"]";
+
+ string portXPath = platform.Equals("iOS", StringComparison.OrdinalIgnoreCase)
+ ? "//XCUIElementTypeTextField[@value=\"Port\"]"
+ : "//android.widget.EditText[@text=\"Port\"]";
+
+ string appNameXPath = platform.Equals("iOS", StringComparison.OrdinalIgnoreCase)
+ ? "//XCUIElementTypeTextField[@value=\"App Name\"]"
+ : "//android.widget.EditText[@text=\"App Name\"]";
+
+ string okButtonXPath = platform.Equals("iOS", StringComparison.OrdinalIgnoreCase)
+ ? "//XCUIElementTypeButton[@name=\"OK\"]"
+ : "//android.widget.Button[@resource-id=\"android:id/button1\"]";
+
+ // Wait for the connection dialog to be present
+ var wait = new WebDriverWait(appiumDriver, TimeSpan.FromSeconds(timeout));
+ wait.Until(ExpectedConditions.ElementExists(OpenQA.Selenium.By.XPath(hostXPath)));
+
+ // Update host if provided
+ if (!string.IsNullOrEmpty(host))
+ {
+ var hostField = appiumDriver.FindElement(OpenQA.Selenium.By.XPath(hostXPath));
+ hostField.Clear();
+ hostField.SendKeys(host);
+ }
+
+ // Update port if provided
+ if (!string.IsNullOrEmpty(port))
+ {
+ var portField = appiumDriver.FindElement(OpenQA.Selenium.By.XPath(portXPath));
+ portField.Clear();
+ portField.SendKeys(port);
+ }
+
+ // Update app name if provided
+ if (!string.IsNullOrEmpty(appName))
+ {
+ var appNameField = appiumDriver.FindElement(OpenQA.Selenium.By.XPath(appNameXPath));
+ appNameField.Clear();
+ appNameField.SendKeys(appName);
+ }
+
+ // Press OK button
+ var okButton = appiumDriver.FindElement(OpenQA.Selenium.By.XPath(okButtonXPath));
+ okButton.Click();
+ }
+ catch (ArgumentException)
+ {
+ throw;
+ }
+ catch (Exception ex)
+ {
+ throw new AppiumHelperException($"Error while setting connection data on {platform} platform: {ex.Message}", ex);
+ }
+ }
+
+ ///
+ /// Sets connection data in the native popup dialog with integer port
+ ///
+ /// The Appium driver instance
+ /// The platform (Android or iOS)
+ /// The host value to set. If not provided, the host field won't be updated
+ /// The port value to set. If not provided, the port field won't be updated
+ /// The app name value to set. If not provided, the app name field won't be updated
+ /// Timeout in seconds for waiting for elements (default: 60)
+ public static void SetConnectionData(AppiumDriver appiumDriver, string platform, string host = null, int? port = null, string appName = null, int timeout = 60)
+ {
+ SetConnectionData(appiumDriver, platform, host, port?.ToString(), appName, timeout);
+ }
+ }
+
+ ///
+ /// Exception thrown when an error occurs while using AltAppiumHelper
+ ///
+ public class AppiumHelperException : Exception
+ {
+ public AppiumHelperException(string message) : base(message)
+ {
+ }
+
+ public AppiumHelperException(string message, Exception innerException) : base(message, innerException)
+ {
+ }
+ }
+}
+#endif
diff --git a/Bindings~/dotnet/AltDriver/AltDriver.csproj b/Bindings~/dotnet/AltDriver/AltDriver.csproj
index 05f9cdba3..02a4760f4 100644
--- a/Bindings~/dotnet/AltDriver/AltDriver.csproj
+++ b/Bindings~/dotnet/AltDriver/AltDriver.csproj
@@ -31,5 +31,7 @@
+
+
diff --git a/Bindings~/python/alttester/__init__.py b/Bindings~/python/alttester/__init__.py
index bda18de49..4805e44be 100644
--- a/Bindings~/python/alttester/__init__.py
+++ b/Bindings~/python/alttester/__init__.py
@@ -23,3 +23,4 @@
from alttester.logging import AltLogLevel, AltLogger
from alttester.exceptions import *
from alttester.reverse_port_forwarding import *
+from alttester.appium_helper import *
diff --git a/Bindings~/python/alttester/appium_helper.py b/Bindings~/python/alttester/appium_helper.py
new file mode 100644
index 000000000..c611ba8b7
--- /dev/null
+++ b/Bindings~/python/alttester/appium_helper.py
@@ -0,0 +1,124 @@
+"""
+ Copyright(C) 2025 Altom Consulting
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+"""
+
+import re
+import ipaddress
+
+from appium.webdriver.common.appiumby import AppiumBy
+from selenium.webdriver.support.ui import WebDriverWait
+from selenium.webdriver.support import expected_conditions as EC
+
+
+class AltAppiumHelper:
+ """API to interact with the native popup dialog (implemented by AltTester) using Appium"""
+
+ @staticmethod
+ def _is_valid_host(host):
+ if not host or not isinstance(host, str):
+ return False
+
+ # Try to parse as IP address (IPv4 or IPv6)
+ try:
+ ipaddress.ip_address(host)
+ return True
+ except ValueError:
+ pass
+
+ # Validate as hostname
+ # Hostname pattern: labels separated by dots, each label contains alphanumeric and hyphens
+ # Label cannot start or end with hyphen, max 63 characters per label
+ hostname_pattern = r'^(?!-)[a-zA-Z0-9-]{1,63}(? 65535:
+ raise ValueError(f"Invalid port: {port_int}. The port number should be between 1 and 65535.")
+
+ try:
+ # Set XPath based on platform
+ if platform.lower() == 'ios':
+ host_xpath = '//XCUIElementTypeTextField[@value="Host"]'
+ port_xpath = '//XCUIElementTypeTextField[@value="Port"]'
+ app_name_xpath = '//XCUIElementTypeTextField[@value="App Name"]'
+ ok_button_xpath = '//XCUIElementTypeButton[@name="OK"]'
+ elif platform.lower() == 'android':
+ host_xpath = '//android.widget.EditText[@text="Host"]'
+ port_xpath = '//android.widget.EditText[@text="Port"]'
+ app_name_xpath = '//android.widget.EditText[@text="App Name"]'
+ ok_button_xpath = '//android.widget.Button[@resource-id="android:id/button1"]'
+ else:
+ raise ValueError(f"Unsupported platform: {platform}. Supported platforms are 'android' and 'ios'.")
+
+ # Wait for the connection dialog to be present
+ wait = WebDriverWait(appium_driver, timeout)
+ wait.until(EC.presence_of_element_located((AppiumBy.XPATH, host_xpath)))
+
+ # Update host if provided
+ if host is not None:
+ host_field = appium_driver.find_element(AppiumBy.XPATH, host_xpath)
+ host_field.clear()
+ host_field.send_keys(host)
+
+ # Update port if provided
+ if port is not None:
+ port_field = appium_driver.find_element(AppiumBy.XPATH, port_xpath)
+ port_field.clear()
+ port_field.send_keys(port_str)
+
+ # Update app_name if provided
+ if app_name is not None:
+ app_name_field = appium_driver.find_element(AppiumBy.XPATH, app_name_xpath)
+ app_name_field.clear()
+ app_name_field.send_keys(app_name)
+
+ # Press OK button
+ ok_button = appium_driver.find_element(AppiumBy.XPATH, ok_button_xpath)
+ ok_button.click()
+
+ except Exception as ex:
+ raise Exception(f"Error while setting connection data on {platform} platform: {str(ex)}") from ex
diff --git a/Bindings~/python/alttester/reverse_port_forwarding.py b/Bindings~/python/alttester/reverse_port_forwarding.py
index 315bf340b..7b842fa8a 100644
--- a/Bindings~/python/alttester/reverse_port_forwarding.py
+++ b/Bindings~/python/alttester/reverse_port_forwarding.py
@@ -1,6 +1,5 @@
"""
Copyright(C) 2025 Altom Consulting
- Copyright(C) 2025 Altom Consulting
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by