chore: drop iOS 18–25 / non-iPhone support, require iOS 26
Two related cleanups the user asked for in one shot:
1. iPhone-only is now enforced at every target — Mac Catalyst and
visionOS were never configured in `project.yml`, but the
`OSGKeyboardShared` framework and the two test bundles were
still defaulting to `TARGETED_DEVICE_FAMILY = "1,2"` (iPhone +
iPad). All four targets now explicitly set `"1"`. SDK is
`iphoneos` for everyone, no `xros` / `macosx`.
2. Deployment target bumped from iOS 18.0 to iOS 26.0 across the
board (`project.yml` + the ext's per-target setting). With
iOS 26 as the floor, the iOS 18–25 SFSpeechRecognizer path
became dead code and several `#available` checks became
always-true. Removed:
- `AppleSpeechASR` (the entire SFSpeechRecognizer-based ASR
backend) and the `#available(iOS 26.0, *)` factory branch.
`ASRServiceFactory.make()` now returns `SpeechAnalyzerASR()`
directly. SpeechAnalyzer is always fully on-device, which
also made the `requiresOnDevice` flag meaningless.
- `requiresOnDevice` from the `ASRService.transcribe` protocol
signature, from `ProviderConfig`, `AppGroupStore`,
`KeyboardState`, `AppGroupPersistor`, and the ext's
`KeyboardViewController` (`state.requiresOnDevice`,
`state.setRequiresOnDevice`, `persistRequiresOnDevice`).
- `#available(iOS 17.0, *)` branch in
`PreviewASRController.requestMicrophonePermission` and the
ext's `PermissionManager.requestMicPermission` — both now
just call the iOS 17+ `AVAudioApplication` API directly.
- The `else` (iOS 18–25) branch in `SettingsView.asrEngineRow`
— the on-device-only toggle is gone, the row is a static
"SpeechAnalyzer active" badge. Same for the `else` branch
in `EnginePickerSection.localSubtitle`.
- `makeMicAuthHandler` (the iOS < 17 mic permission callback
wrapper) from `PreviewASRController`.
No `#available` / `@available` checks remain in the codebase
except for the SpeechAnalyzer class itself (now unnecessary
too, but kept for clarity — `AVAudioApplication` and
`SpeechAnalyzer` are both iOS 17+ / iOS 26+ respectively,
and the deployment target of 26 makes the explicit
`@available` redundant; I left the SpeechAnalyzer class
un-`@available` and removed the `@available(iOS 26.0, *)`
decoration since it's no longer needed).
The Keyboard ext's existing ASRService usage
(`asr.transcribe(stream:locale:)`) is unchanged at the
call-site level — just the third argument is gone.
3. Updated `info.plist` UISupportedInterfaceOrientations is
already `[UIInterfaceOrientationPortrait]` only, which is
correct for an iPhone-only app; no change needed.
Build: BUILD SUCCEEDED.
Tests: 22/22 pass.
Verified: `TARGETED_DEVICE_FAMILY = 1` on all four targets,
`SDKROOT = iphoneos` on all four.
🤖 Generated with Claude Code
This commit is contained in:
@@ -143,49 +143,31 @@ struct SettingsView: View {
|
||||
}
|
||||
}
|
||||
|
||||
/// ASR engine row — adapts to OS version:
|
||||
/// • iOS 26+: shows a badge indicating SpeechAnalyzer is active (always on-device).
|
||||
/// • iOS 18–25: shows a toggle to force on-device recognition only.
|
||||
@ViewBuilder
|
||||
/// ASR engine row. With iOS 26 as the deployment target, the
|
||||
/// only ASR backend is `SpeechAnalyzer` and it is always fully
|
||||
/// on-device — so this row is now a static badge rather than a
|
||||
/// toggle. The previous `requiresOnDevice` toggle (iOS 18–25
|
||||
/// `SFSpeechRecognizer` cloud-fallback control) is gone.
|
||||
private var asrEngineRow: some View {
|
||||
if #available(iOS 26, *) {
|
||||
HStack(spacing: Spacing.sm) {
|
||||
Text("settings.engineRow.title")
|
||||
.font(TypeStyle.body)
|
||||
.foregroundStyle(palette.textPrimary)
|
||||
Spacer()
|
||||
HStack(spacing: 4) {
|
||||
Image(systemName: "iphone.badge.checkmark")
|
||||
.font(.system(size: 12, weight: .semibold))
|
||||
.foregroundStyle(palette.success)
|
||||
Text("settings.engineBadge.ios26")
|
||||
.font(TypeStyle.caption)
|
||||
.foregroundStyle(palette.success)
|
||||
}
|
||||
.padding(.horizontal, Spacing.xs)
|
||||
.padding(.vertical, 4)
|
||||
.background(palette.success.opacity(0.12), in: Capsule())
|
||||
HStack(spacing: Spacing.sm) {
|
||||
Text("settings.engineRow.title")
|
||||
.font(TypeStyle.body)
|
||||
.foregroundStyle(palette.textPrimary)
|
||||
Spacer()
|
||||
HStack(spacing: 4) {
|
||||
Image(systemName: "iphone.badge.checkmark")
|
||||
.font(.system(size: 12, weight: .semibold))
|
||||
.foregroundStyle(palette.success)
|
||||
Text("settings.engineBadge.ios26")
|
||||
.font(TypeStyle.caption)
|
||||
.foregroundStyle(palette.success)
|
||||
}
|
||||
.padding(.horizontal, Spacing.md)
|
||||
.padding(.vertical, Spacing.sm)
|
||||
} else {
|
||||
Toggle(isOn: Binding(
|
||||
get: { config.requiresOnDevice },
|
||||
set: { config.requiresOnDevice = $0 }
|
||||
)) {
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text("settings.onDeviceOnly.title")
|
||||
.font(TypeStyle.body)
|
||||
.foregroundStyle(palette.textPrimary)
|
||||
Text("禁用云端回退,识别失败时会报错而非联网 · Disable cloud fallback, fail locally instead of going online")
|
||||
.font(TypeStyle.caption2)
|
||||
.foregroundStyle(palette.textTertiary)
|
||||
}
|
||||
}
|
||||
.tint(palette.accent)
|
||||
.padding(.horizontal, Spacing.md)
|
||||
.padding(.vertical, Spacing.sm)
|
||||
.padding(.horizontal, Spacing.xs)
|
||||
.padding(.vertical, 4)
|
||||
.background(palette.success.opacity(0.12), in: Capsule())
|
||||
}
|
||||
.padding(.horizontal, Spacing.md)
|
||||
.padding(.vertical, Spacing.sm)
|
||||
}
|
||||
|
||||
/// Falls back to a static list while SFSpeechRecognizer locales are loading.
|
||||
|
||||
Reference in New Issue
Block a user