Skip to content

Commit c556500

Browse files
committed
feat(ios): enable alerts on physical devices
1 parent 4c01d2a commit c556500

5 files changed

Lines changed: 36 additions & 8 deletions

File tree

src/core/__tests__/capabilities.test.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,14 @@ const iosDevice: DeviceInfo = {
1818
kind: 'device',
1919
};
2020

21+
const iPadOsDevice: DeviceInfo = {
22+
platform: 'apple',
23+
appleOs: 'ipados',
24+
id: 'ipad-dev-1',
25+
name: 'iPad',
26+
kind: 'device',
27+
};
28+
2129
const androidDevice: DeviceInfo = {
2230
platform: 'android',
2331
id: 'and-1',
@@ -91,7 +99,8 @@ test('device capability matrix stays consistent across shared command groups', (
9199
commands: ['alert'],
92100
checks: [
93101
{ device: iosSimulator, expected: true, label: 'on iOS sim' },
94-
{ device: iosDevice, expected: false, label: 'on iOS device' },
102+
{ device: iosDevice, expected: true, label: 'on iOS device' },
103+
{ device: iPadOsDevice, expected: false, label: 'on iPadOS device' },
95104
{ device: androidDevice, expected: true, label: 'on Android' },
96105
{ device: macOsDevice, expected: true, label: 'on macOS' },
97106
],

src/core/__tests__/capability-plugin-routing-parity.test.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,9 @@ const SAMPLE_DEVICES: DeviceInfo[] = [
106106
const isNotMacOs = (device: DeviceInfo): boolean => !isMacOs(device);
107107
const isMacOsOrAppleSimulator = (device: DeviceInfo): boolean =>
108108
isMacOs(device) || device.kind === 'simulator';
109+
const isIosOs = (device: DeviceInfo): boolean =>
110+
device.platform === 'apple' &&
111+
(device.appleOs ? device.appleOs === 'ios' : device.target !== 'tv');
109112
const supportsAndroidOrIosNonTv = (device: DeviceInfo): boolean =>
110113
device.platform === 'android' || (isIosFamily(device) && device.target !== 'tv');
111114
const supportsTvRemote = (device: DeviceInfo): boolean =>
@@ -136,7 +139,8 @@ const SUPPORTS_REF: Record<string, (device: DeviceInfo) => boolean> = {
136139
keyboard: supportsAndroidOrIosNonTv,
137140
rotate: supportsAndroidOrIosNonTv,
138141
'tv-remote': supportsTvRemote,
139-
alert: (device) => device.platform === 'android' || isMacOsOrAppleSimulator(device),
142+
alert: (device) =>
143+
device.platform === 'android' || isIosOs(device) || isMacOsOrAppleSimulator(device),
140144
settings: (device) =>
141145
device.platform === 'android' || isMacOs(device) || device.kind === 'simulator',
142146
audio: supportsHostAudioProbe,

src/core/platform-plugin/__tests__/apple-os-capability-table-parity.test.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ registerBuiltinPlatformPlugins();
4747
const isNotMacOs = (device: DeviceInfo): boolean => !isMacOs(device);
4848
const isMacOsOrAppleSimulator = (device: DeviceInfo): boolean =>
4949
isMacOs(device) || device.kind === 'simulator';
50+
const isIosOs = (device: DeviceInfo): boolean =>
51+
device.platform === 'apple' &&
52+
(device.appleOs ? device.appleOs === 'ios' : device.target !== 'tv');
5053
const supportsAndroidOrIosNonTv = (device: DeviceInfo): boolean =>
5154
device.platform === 'android' || (isIosFamily(device) && device.target !== 'tv');
5255
const supportsTvRemote = (device: DeviceInfo): boolean =>
@@ -68,7 +71,8 @@ const SUPPORTS_REF: Record<string, (device: DeviceInfo) => boolean> = {
6871
keyboard: supportsAndroidOrIosNonTv,
6972
rotate: supportsAndroidOrIosNonTv,
7073
'tv-remote': supportsTvRemote,
71-
alert: (device) => device.platform === 'android' || isMacOsOrAppleSimulator(device),
74+
alert: (device) =>
75+
device.platform === 'android' || isIosOs(device) || isMacOsOrAppleSimulator(device),
7276
settings: (device) =>
7377
device.platform === 'android' || isMacOs(device) || device.kind === 'simulator',
7478
// `audio` is NOT part of the AppleOS-table relocation — it stays the standalone

src/platforms/apple/capabilities.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,10 @@ export type AppleOsCapabilityProfile = {
3535
/** `rotate` — device orientation. tvOS (focus-only) and macOS lack it. */
3636
readonly orientation: boolean;
3737
/**
38-
* Whether `clipboard` / `alert` / `settings` are reachable on a PHYSICAL device of
38+
* Whether `clipboard` / `settings` are reachable on a PHYSICAL device of
3939
* this OS. Only the macOS host exposes them without a simulator; the reading closure
4040
* still admits every Apple *simulator* via its own `kind === 'simulator'` check.
41+
* Alert support has a separate command predicate because physical iOS is verified.
4142
*/
4243
readonly physicalDeviceSurfaces: boolean;
4344
};

src/platforms/apple/plugin.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,12 @@ import {
66
shouldUseHostMacFastPath,
77
type DeviceInventoryRequest,
88
} from '../../contracts/device-inventory.ts';
9-
import { isMacOs, isTvOsDevice, type DeviceInfo } from '../../kernel/device.ts';
9+
import {
10+
isMacOs,
11+
isTvOsDevice,
12+
resolveDeviceAppleOs,
13+
type DeviceInfo,
14+
} from '../../kernel/device.ts';
1015
import type { RunnerContext } from '../../core/interactor-types.ts';
1116

1217
// ---------------------------------------------------------------------------
@@ -44,7 +49,7 @@ const supportsOrientation = (device: DeviceInfo): boolean => {
4449
return caps ? caps.orientation : device.platform === 'android';
4550
};
4651

47-
// The Apple arm shared by `clipboard`/`alert`/`settings` (was `macos || simulator`):
52+
// The Apple arm shared by `clipboard`/`settings` (was `macos || simulator`):
4853
// reachable on the macOS host directly, on every other Apple OS only on the simulator.
4954
// Off Apple this preserves the trailing `device.kind === 'simulator'` term verbatim.
5055
const supportsHostOrSimulatorSurface = (device: DeviceInfo): boolean => {
@@ -54,6 +59,12 @@ const supportsHostOrSimulatorSurface = (device: DeviceInfo): boolean => {
5459
: device.kind === 'simulator';
5560
};
5661

62+
// Alerts use the host/simulator surface plus physical iOS, whose XCTest path is
63+
// device-verified. iPadOS/visionOS remain closed until independently verified.
64+
const supportsAlertSurface = (device: DeviceInfo): boolean =>
65+
device.platform === 'android' ||
66+
(device.platform === 'apple' && resolveDeviceAppleOs(device) === 'ios') ||
67+
supportsHostOrSimulatorSurface(device);
5768
// `tv-remote` is Android-TV or tvOS only. Off Apple this preserves the Android-TV
5869
// branch so the relocated Apple closure stays equivalent to the full original
5970
// supports predicate under the parity guard; the closure is only consulted for Apple
@@ -80,8 +91,7 @@ const APPLE_SUPPORTS_BY_DEFAULT: Record<string, (device: DeviceInfo) => boolean>
8091
[PUBLIC_COMMANDS.keyboard]: supportsKeyboard,
8192
[PUBLIC_COMMANDS.rotate]: supportsOrientation,
8293
[PUBLIC_COMMANDS.tvRemote]: supportsTvRemote,
83-
[PUBLIC_COMMANDS.alert]: (device) =>
84-
device.platform === 'android' || supportsHostOrSimulatorSurface(device),
94+
[PUBLIC_COMMANDS.alert]: supportsAlertSurface,
8595
[PUBLIC_COMMANDS.settings]: (device) =>
8696
device.platform === 'android' || supportsHostOrSimulatorSurface(device),
8797
[PUBLIC_COMMANDS.audio]: isAudioProbeSupportedDevice,

0 commit comments

Comments
 (0)