Skip to content

feat!: remove the deprecated APIs - #2467

Open
mykola-mokhnach wants to merge 6 commits into
masterfrom
depr-rem
Open

mykola-mokhnach wants to merge 6 commits into
masterfrom
depr-rem

Conversation

@mykola-mokhnach

@mykola-mokhnach mykola-mokhnach commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Removes the APIs that were deprecated in v10 and makes the page factory and storage tests independent of real servers.
The legacy commands were kept only as fallbacks for servers without the mobile: extensions. Appium 3 removed most of the
legacy endpoints, and the UiAutomator2, XCUITest and Windows drivers have the mobile:/windows: replacements
(checked against Appium 3.8, UiAutomator2 8.7.0/9.0.0-beta.1, XCUITest 12.15.0/13.0.0-beta.1 and Windows 6.2.0).

Breaking changes

The footers for the squashed commit:

BREAKING CHANGE: The minimum supported Appium server version is now Appium 3. Only drivers and
plugins that are compatible with Appium 3 or later are supported.
BREAKING CHANGE: io.appium.java_client.functions.AppiumFunction is removed. Use
java.util.function.Function instead. It is not a drop-in replacement for the chains:
AppiumFunction#compose and AppiumFunction#andThen skipped the next step when the previous one
returned null, which FluentWait treats as 'keep polling', while Function#compose and
Function#andThen pass null on. The steps of a chain must handle null themselves, otherwise the wait
fails with a NullPointerException on the first null.
BREAKING CHANGE: AndroidMobileCommandHelper and IOSMobileCommandHelper are removed. Send the
`mobile:` extension with executeScript instead, for example driver.executeScript("mobile: shake").
BREAKING CHANGE: CanRememberExtensionPresence is removed, together with
AppiumDriver#assertExtensionExists and AppiumDriver#markExtensionAbsence. The driver interfaces do
not extend it anymore.
BREAKING CHANGE: The deprecated MobileCommand constants are removed, and the commands are not
defined in MobileCommand.commandRepository anymore: RESET, GET_STRINGS, SET_VALUE,
RUN_APP_IN_BACKGROUND, LAUNCH_APP, CLOSE_APP, GET_DEVICE_TIME, IS_APP_INSTALLED, INSTALL_APP,
ACTIVATE_APP, QUERY_APP_STATE, TERMINATE_APP, REMOVE_APP, GET_CLIPBOARD, SET_CLIPBOARD,
GET_PERFORMANCE_DATA, GET_SUPPORTED_PERFORMANCE_DATA_TYPES, HIDE_KEYBOARD, LOCK, SHAKE, TOUCH_ID,
TOUCH_ID_ENROLLMENT, CURRENT_ACTIVITY, END_TEST_COVERAGE, GET_DISPLAY_DENSITY,
GET_NETWORK_CONNECTION, GET_SYSTEM_BARS, IS_KEYBOARD_SHOWN, IS_LOCKED, LONG_PRESS_KEY_CODE,
FINGER_PRINT, OPEN_NOTIFICATIONS, PRESS_KEY_CODE, SET_NETWORK_CONNECTION, START_ACTIVITY,
TOGGLE_LOCATION_SERVICES, UNLOCK, REPLACE_VALUE, GET_CURRENT_PACKAGE, SEND_SMS, GSM_CALL,
GSM_SIGNAL, GSM_VOICE, NETWORK_SPEED, POWER_CAPACITY, POWER_AC_STATE, TOGGLE_WIFI,
TOGGLE_AIRPLANE_MODE, TOGGLE_DATA. PULL_FILE, PULL_FOLDER and PUSH_FILE stay and become public.
BREAKING CHANGE: The deprecated MobileCommand helper methods are removed: hideKeyboardCommand,
prepareArguments, pressKeyCodeCommand, longPressKeyCodeCommand (all overloads), lockDeviceCommand,
unlockDeviceCommand, getIsDeviceLockedCommand, pushFileCommand and isKeyboardShownCommand.
BREAKING CHANGE: The driver interfaces call the `mobile:` extensions only, without the fallback to
the legacy commands, so UnsupportedCommandException and InvalidArgumentException from the server are
not swallowed anymore. Affected: InteractsWithApps, LocksDevice, HidesKeyboard,
HidesKeyboardWithKeyName, HasOnScreenKeyboard, HasAppStrings, PullsFiles, PushesFiles, HasClipboard,
HasAndroidClipboard, PressesKey, AuthenticatesByFinger, CanReplaceElementValue,
HasAndroidDeviceDetails, HasNotifications, HasSupportedPerformanceDataType, StartsActivity,
SupportsGpsStateManagement, SupportsNetworkStateManagement, SupportsSpecialEmulatorCommands,
HasNetworkConnection and ShakesDevice. The Android and iOS drivers must serve the corresponding
extensions.
BREAKING CHANGE: AndroidDriver#setClipboard(label, contentType, base64Content) sends the setClipboard
extension with the label instead of the legacy setClipboard command.
BREAKING CHANGE: WindowsDriver#launchApp and WindowsDriver#closeApp send `windows: launchApp` and
`windows: closeApp` instead of the legacy /appium/app/launch and /appium/app/close routes.

Not breaking

  • WindowsDriver overrides pullFile, pullFolder and pushFile to keep sending the driver-level commands, because the Windows
    driver has no extensions for them. The requests are the same as before.
  • START_RECORDING_SCREEN, STOP_RECORDING_SCREEN and SET_SETTINGS are not deprecated anymore: the drivers still serve them and they
    have no replacements. GET_SESSION and GET_ALLSESSION stay deprecated.
  • setPowerAC requested mobile: powerAC, but the driver registers mobile: powerAc, so it always used the legacy route. It uses the
    extension now.
  • StorageClient#add reports the error that the server sends for a failed upload instead of a generic exception that wraps a
    NullPointerException.
  • Tests: the page factory tests (TimeoutTest, DesktopBrowserCompatibilityTest) use the stub driver instead of headless Chrome, and
    TimeoutTest takes 1.5s instead of about 12s. StorageTest runs against a fake of the storage plugin instead of a server at
    127.0.0.1:4723, so it is not skipped on CI anymore. The selenium-chrome-driver and webdrivermanager dependencies of the unit
    test suite are removed.

Not covered

  • GET_LOCATION and SET_LOCATION (/location, used by SupportsLocation) are not deprecated, but Appium 4 removes the route.
    They need a mobile: path (getGeolocation/setGeolocation on Android, getSimulatedLocation/setSimulatedLocation on iOS
    simulators) in a follow-up.
  • The v10 to v11 migration guide and the OpenRewrite recipe are not updated yet.

Verification

  • Unit tests (194, 0 failures), checkstyle, and compilation of the e2e source sets.
  • The commands.json golden changes by removing the commands only.
  • New ExtensionCommandsOverHttpTest covers mobile: powerAc and the Windows file, launch and close commands over a real HTTP server.

🤖 Generated with Claude Code

mykola-mokhnach and others added 4 commits October 5, 2026 20:14
BREAKING CHANGE: io.appium.java_client.functions.AppiumFunction is
removed. Use java.util.function.Function instead.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The events of a failed upload have no success field, so the null
Boolean cast threw and the error was wrapped into a generic exception
with the raw payload as its message. Check it null-safely so that the
error sent by the server is decoded.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
DesktopBrowserCompatibilityTest and TimeoutTest use the stub driver
instead of headless Chrome, and TimeoutTest now asserts a lower bound of
the waiting time and runs in 1.5s instead of about 12s. StorageTest runs
against a fake of the storage plugin of the Appium server rather than a
server at 127.0.0.1:4723, so it is not skipped on CI anymore.

The selenium-chrome-driver and webdrivermanager dependencies of the unit
test suite are removed, along with the deprecated timeout overloads of
the stub driver.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The drivers call the `mobile:` extensions only, as the legacy endpoints
were removed in Appium 3 or have extension replacements in the
UiAutomator2, XCUITest and Windows drivers. The extension presence
tracking served the fallbacks only, so it is removed as well.

WindowsDriver keeps the driver-level file transfer commands, because the
Windows driver has no extensions for them, and uses the `windows:`
extensions to launch and close the app.

The power AC extension is requested under its actual name,
`mobile: powerAc`, so it is not routed to the removed endpoint anymore.

BREAKING CHANGE: AndroidMobileCommandHelper, IOSMobileCommandHelper,
CanRememberExtensionPresence and the deprecated MobileCommand constants
and helper methods are removed. Android and iOS drivers require a server
and drivers that support the corresponding `mobile:` extensions.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

@KazuCocoa KazuCocoa left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the changes and the replacement command contracts. The unit-test build passes (194 root-project tests, zero failures), but the Function migration introduces the Android test regression noted inline. The Android device tests were not run.

public class AndroidFunctionTest extends BaseAndroidTest {

private final AppiumFunction<WebDriver, List<WebElement>> searchingFunction = input -> {
private final Function<WebDriver, List<WebElement>> searchingFunction = input -> {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] Preserve null short-circuiting when migrating the wait functions

AppiumFunction.compose and andThen skipped the next function when the previous result was null; java.util.function.Function passes that null onward. Consequently, both nullPointerExceptionSafetyTestWithPrecondition and nullPointerExceptionSafetyTestWithPostConditions now fail: Fake_context makes contextFunction return null, searchingFunction dereferences it here, and FluentWait propagates NullPointerException instead of the asserted TimeoutException. The normal wait tests can also hit this while the webview is unavailable. Please make these compositions explicitly null-safe (or revise the tests if that behavior is intentionally being removed). This class is excluded from the default Android suite, so compilation and the passing unit tests do not catch it.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Confirmed on an emulator. Both nullPointerExceptionSafety tests fail on this branch with NullPointerException and pass on master. The two complexWaiting tests also failed with the same NPE on the first poll.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, and thanks for running it on the emulator. We are removing AppiumFunction on purpose, so I kept the Function semantics. The test steps handle null explicitly now (b7ab4e8), and the breaking-change note says that compose/andThen pass null on, so chains that relied on the old short-circuit have to check for it.

@FunctionalInterface
public interface AppiumFunction<F, T> extends java.util.function.Function<F, T> {

@Override default <V> AppiumFunction<V, T> compose(java.util.function.Function<? super V, ? extends F> before) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AppiumFunction.compose and andThen skip the next step when the previous one returns null. java.util.function.Function does not. Inside a FluentWait, null means keep polling. So chained waits used to retry and now throw NullPointerException on the first null.

The breaking change note says to use java.util.function.Function instead. That reads like a drop-in swap, but it is not one for code that chains compose or andThen.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, and thanks for running it on the emulator. We are removing AppiumFunction on purpose, so I kept the Function semantics. The test steps handle null explicitly now (b7ab4e8), and the breaking-change note says that compose/andThen pass null on, so chains that relied on the old short-circuit have to check for it.

Function#compose and Function#andThen pass null on, unlike the removed
AppiumFunction, which skipped the next step. FluentWait treats null as
"keep polling", so the steps have to handle a null input themselves.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants