Skip to content

Commit 14eb8a5

Browse files
committed
feat(ios): enable alerts on physical devices
1 parent 791dc82 commit 14eb8a5

5 files changed

Lines changed: 32 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',
@@ -101,7 +109,8 @@ test('device capability matrix stays consistent across shared command groups', (
101109
commands: ['alert'],
102110
checks: [
103111
{ device: iosSimulator, expected: true, label: 'on iOS sim' },
104-
{ device: iosDevice, expected: false, label: 'on iOS device' },
112+
{ device: iosDevice, expected: true, label: 'on iOS device' },
113+
{ device: iPadOsDevice, expected: false, label: 'on iPadOS device' },
105114
{ device: androidDevice, expected: true, label: 'on Android' },
106115
{ device: macOsDevice, expected: true, label: 'on macOS' },
107116
],

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 isIosMobileSimulator = (device: DeviceInfo): boolean =>
110113
isIosFamily(device) && device.kind === 'simulator' && device.target !== 'tv';
111114
const supportsSynthesisGesture = (device: DeviceInfo): boolean =>
@@ -150,7 +153,8 @@ const SUPPORTS_REF: Record<string, (device: DeviceInfo) => boolean> = {
150153
keyboard: supportsAndroidOrIosNonTv,
151154
rotate: supportsAndroidOrIosNonTv,
152155
'tv-remote': supportsTvRemote,
153-
alert: (device) => device.platform === 'android' || isMacOsOrAppleSimulator(device),
156+
alert: (device) =>
157+
device.platform === 'android' || isIosOs(device) || isMacOsOrAppleSimulator(device),
154158
settings: (device) =>
155159
device.platform === 'android' || isMacOs(device) || device.kind === 'simulator',
156160
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
@@ -49,6 +49,9 @@ registerBuiltinPlatformPlugins();
4949
const isNotMacOs = (device: DeviceInfo): boolean => !isMacOs(device);
5050
const isMacOsOrAppleSimulator = (device: DeviceInfo): boolean =>
5151
isMacOs(device) || device.kind === 'simulator';
52+
const isIosOs = (device: DeviceInfo): boolean =>
53+
device.platform === 'apple' &&
54+
(device.appleOs ? device.appleOs === 'ios' : device.target !== 'tv');
5255
const isIosMobileSimulator = (device: DeviceInfo): boolean =>
5356
isIosFamily(device) && device.kind === 'simulator' && device.target !== 'tv';
5457
const supportsSynthesisGesture = (device: DeviceInfo): boolean =>
@@ -84,7 +87,8 @@ const SUPPORTS_REF: Record<string, (device: DeviceInfo) => boolean> = {
8487
keyboard: supportsAndroidOrIosNonTv,
8588
rotate: supportsAndroidOrIosNonTv,
8689
'tv-remote': supportsTvRemote,
87-
alert: (device) => device.platform === 'android' || isMacOsOrAppleSimulator(device),
90+
alert: (device) =>
91+
device.platform === 'android' || isIosOs(device) || isMacOsOrAppleSimulator(device),
8892
settings: (device) =>
8993
device.platform === 'android' || isMacOs(device) || device.kind === 'simulator',
9094
// `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
@@ -36,9 +36,10 @@ export type AppleOsCapabilityProfile = {
3636
/** `rotate` — device orientation. tvOS (focus-only) and macOS lack it. */
3737
readonly orientation: boolean;
3838
/**
39-
* Whether `clipboard` / `alert` / `settings` are reachable on a PHYSICAL device of
39+
* Whether `clipboard` / `settings` are reachable on a PHYSICAL device of
4040
* this OS. Only the macOS host exposes them without a simulator; the reading closure
4141
* still admits every Apple *simulator* via its own `kind === 'simulator'` check.
42+
* Alert support has a separate command predicate because physical iOS is verified.
4243
*/
4344
readonly physicalDeviceSurfaces: boolean;
4445
/**

src/platforms/apple/plugin.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { appleOsCapabilities } from './capabilities.ts';
1+
import { appleOsCapabilities, resolveDeviceAppleOs } from './capabilities.ts';
22
import type { PlatformPlugin } from '../../core/platform-plugin/plugin.ts';
33
import { PUBLIC_COMMANDS } from '../../command-catalog.ts';
44
import { isAudioProbeSupportedDevice } from '../../kernel/audio-probe-support.ts';
@@ -44,7 +44,7 @@ const supportsOrientation = (device: DeviceInfo): boolean => {
4444
return caps ? caps.orientation : device.platform === 'android';
4545
};
4646

47-
// The Apple arm shared by `clipboard`/`alert`/`settings` (was `macos || simulator`):
47+
// The Apple arm shared by `clipboard`/`settings` (was `macos || simulator`):
4848
// reachable on the macOS host directly, on every other Apple OS only on the simulator.
4949
// Off Apple this preserves the trailing `device.kind === 'simulator'` term verbatim.
5050
const supportsHostOrSimulatorSurface = (device: DeviceInfo): boolean => {
@@ -54,6 +54,13 @@ const supportsHostOrSimulatorSurface = (device: DeviceInfo): boolean => {
5454
: device.kind === 'simulator';
5555
};
5656

57+
// Alerts use the host/simulator surface plus physical iOS, whose XCTest path is
58+
// device-verified. iPadOS/visionOS remain closed until independently verified.
59+
const supportsAlertSurface = (device: DeviceInfo): boolean =>
60+
device.platform === 'android' ||
61+
(device.platform === 'apple' && resolveDeviceAppleOs(device) === 'ios') ||
62+
supportsHostOrSimulatorSurface(device);
63+
5764
// `pinch`/`rotate-gesture`/`transform-gesture` (was `android || (ios && simulator &&
5865
// target !== 'tv')`). Apple: the OS multi-touch model AND a simulator (physical iOS
5966
// cannot synthesize). Off Apple: only `android`.
@@ -103,8 +110,7 @@ const APPLE_SUPPORTS_BY_DEFAULT: Record<string, (device: DeviceInfo) => boolean>
103110
[PUBLIC_COMMANDS.keyboard]: supportsKeyboard,
104111
[PUBLIC_COMMANDS.rotate]: supportsOrientation,
105112
[PUBLIC_COMMANDS.tvRemote]: supportsTvRemote,
106-
[PUBLIC_COMMANDS.alert]: (device) =>
107-
device.platform === 'android' || supportsHostOrSimulatorSurface(device),
113+
[PUBLIC_COMMANDS.alert]: supportsAlertSurface,
108114
[PUBLIC_COMMANDS.settings]: (device) =>
109115
device.platform === 'android' || supportsHostOrSimulatorSurface(device),
110116
[PUBLIC_COMMANDS.audio]: isAudioProbeSupportedDevice,

0 commit comments

Comments
 (0)