c07cf4db9f
Rolls back the v0.2.0 Qwen3 CoreML on-device ASR stack and replaces the
'local engine' UX with iOS 26 SpeechAnalyzer + DictationTranscriber only.
The 'Cloud polish after ASR' toggle (ProviderConfig.localModeCloudPolishEnabled)
lets users opt into a post-ASR DeepSeek round-trip from the local engine.
Defaults to off so the local engine stays genuinely local. New PolishError.missingAPIError
surfaces an inline 'fill in your key' warning when the toggle is on but the
Keychain is empty. DeepSeek preset default model bumped to deepseek-v4-flash.
Deleted:
- OSGKeyboard/ThirdParty/Qwen3Speech/ (74 files, ~16k LoC)
- OSGKeyboard/Services/ModelManager.swift (492)
- OSGKeyboard/Services/OnDeviceModelWarmup.swift (197)
- OSGKeyboard/Services/Qwen3ASRService.swift (257)
- OSGKeyboard/Services/ModelDownloadSourcePicker.swift (126)
- OSGKeyboard/Views/OnDeviceModelsView.swift (184)
- OSGKeyboard/Views/DownloadConfirmSheet.swift (96)
- OSGKeyboardShared/Models/OnDeviceModel.swift (140)
- OSGKeyboardShared/Services/OnDeviceModelStatus.swift (104)
- Qwen3ASRServiceProvider registration in OSGKeyboardApp
- Qwen3Speech package declaration in project.yml
- 5 .qwen3ASR enum / branch reference sites in HomeView, OnboardingView,
LocalEngineSettingsRows, FlowSessionManager, ASRService, EngineServiceLabel
- Two pre-existing Swift 6 strict-concurrency errors in
LiveDictationController + FlowSessionManager (the weak [weak self] in
detached-task MainActor.run blocks) that were blocking clean builds
Added:
- LocalModelsGroup: 'Built-in iOS SpeechAnalyzer' badge + 'Cloud polish
after ASR' Switch toggle
- PolishingService: honour localModeCloudPolishEnabled; new .missingAPIKey
error case with localised warning
- AppGroupStore.localModeCloudPolishEnabled (mirrored into App Group
so the keyboard extension honours the toggle during live dictation)
- SettingsView: show provider/api sections when local-mode cloud polish
is on so the user can paste a DeepSeek key
- FlowSessionManager: route through PolishingService for local + polish-on
flow; translate missingAPIKey into a polished warning
- KeyboardViewController: handle PolishingService.PolishError.missingAPIKey
in the keyboard-side live polish path
- CHANGELOG v0.2.1: documents the rollback + new toggle
- README.md / README.zh.md: engine matrix section, data flow note
Verified: xcodebuild -scheme OSGKeyboard -destination 'generic/platform=iOS Simulator'
build succeeds under SWIFT_STRICT_CONCURRENCY=complete.
279 lines
10 KiB
YAML
279 lines
10 KiB
YAML
# XcodeGen configuration for OSGKeyboard
|
||
# Run `./Scripts/generate-xcodeproj.sh` to create OSGKeyboard.xcodeproj.
|
||
# App home-screen icon: OSGKeyboard/AppIcon.icon (Icon Composer, iOS 26).
|
||
# Repo only tracks project.yml; .xcodeproj is gitignored.
|
||
|
||
name: OSGKeyboard
|
||
options:
|
||
bundleIdPrefix: com.osgkeyboard
|
||
# Xcode 26 Icon Composer bundles must be treated as a single file,
|
||
# not expanded into icon.json + SVG children (see XcodeGen #1556).
|
||
fileTypes:
|
||
icon:
|
||
file: true
|
||
# Minimum OS: iOS 26. We dropped older iOS support so the
|
||
# legacy speech + AVAudioSession branching could be removed in
|
||
# favour of iOS 26's `SpeechAnalyzer` (always on-device) and
|
||
# `AVAudioApplication.requestRecordPermission` (iOS 17+). iPhone
|
||
# only — no Mac Catalyst, no visionOS.
|
||
deploymentTarget:
|
||
iOS: "26.0"
|
||
developmentLanguage: en
|
||
createIntermediateGroups: true
|
||
generateEmptyDirectories: true
|
||
groupSortPosition: top
|
||
|
||
# v0.2.0: dropped the local `Qwen3Speech` SPM fork. The "local" engine
|
||
# now uses iOS 26 `SpeechAnalyzer` + `DictationTranscriber` exclusively,
|
||
# which keeps the dependency surface at zero SPM packages / Pods /
|
||
# Carthage (matches the long-standing "zero dependencies" promise in the
|
||
# README). Optional post-ASR cloud polish routes through DeepSeek (or
|
||
# any OpenAI-compatible endpoint) using the existing `LLMClient`.
|
||
|
||
settings:
|
||
base:
|
||
SWIFT_VERSION: "6.0"
|
||
IPHONEOS_DEPLOYMENT_TARGET: "26.0"
|
||
ENABLE_USER_SCRIPT_SANDBOXING: YES
|
||
SWIFT_STRICT_CONCURRENCY: complete
|
||
GENERATE_INFOPLIST_FILE: NO
|
||
ENABLE_MODULE_VERIFIER: YES
|
||
CLANG_CXX_LANGUAGE_STANDARD: c++17
|
||
MARKETING_VERSION: "0.2.0"
|
||
CURRENT_PROJECT_VERSION: "4"
|
||
# 签名配置来自 Signing.local.xcconfig(gitignored,不会被覆盖)
|
||
|
||
# 项目级签名 xcconfig,适用于所有 target
|
||
configFiles:
|
||
Debug: Signing.local.xcconfig
|
||
Release: Signing.local.xcconfig
|
||
|
||
targets:
|
||
|
||
# =========================================================
|
||
# 主 App
|
||
# =========================================================
|
||
OSGKeyboard:
|
||
type: application
|
||
platform: iOS
|
||
sources:
|
||
- path: OSGKeyboard/AppIcon.icon
|
||
type: file
|
||
buildPhase: resources
|
||
- path: OSGKeyboard
|
||
excludes:
|
||
- "AppIcon.icon"
|
||
- "OSGKeyboard.icon"
|
||
# Legacy PNG app icons must not coexist with AppIcon.icon — actool
|
||
# crashes when both are passed. iOS 26 uses Icon Composer only.
|
||
- "Assets.xcassets/AppIcon.appiconset"
|
||
entitlements:
|
||
path: OSGKeyboard/OSGKeyboard.entitlements
|
||
properties:
|
||
com.apple.security.application-groups:
|
||
- group.com.osgkeyboard.shared
|
||
com.apple.security.device.audio-input: true
|
||
# Shared Keychain access group: the host app writes the LLM API
|
||
# key here in Settings; the keyboard extension reads it before
|
||
# each request. The first entry below becomes each process's
|
||
# default access group, so the Keychain helper does not need to
|
||
# specify kSecAttrAccessGroup explicitly.
|
||
keychain-access-groups:
|
||
- $(AppIdentifierPrefix)com.osgkeyboard.shared
|
||
resources:
|
||
- path: OSGKeyboard/Assets.xcassets
|
||
- path: OSGKeyboard/en.lproj
|
||
- path: OSGKeyboard/zh-Hans.lproj
|
||
- path: OSGKeyboard/Resources/Fonts/MaterialIcons-Regular.ttf
|
||
- path: OSGKeyboard/Resources/PrivacyPolicy.html
|
||
- path: OSGKeyboard/PrivacyInfo.xcprivacy
|
||
info:
|
||
path: OSGKeyboard/Info.plist
|
||
properties:
|
||
CFBundleDisplayName: OSGKeyboard
|
||
CFBundleShortVersionString: "$(MARKETING_VERSION)"
|
||
CFBundleVersion: "$(CURRENT_PROJECT_VERSION)"
|
||
LSApplicationCategoryType: public.app-category.utilities
|
||
# English is the development language; zh-Hans is the only
|
||
# translation right now. The list also tells iOS to surface
|
||
# the keyboard in the per-app language picker for both.
|
||
CFBundleDevelopmentRegion: en
|
||
CFBundleLocalizations:
|
||
- en
|
||
- zh-Hans
|
||
UILaunchScreen:
|
||
UIColorName: "BackgroundColor"
|
||
UISupportedInterfaceOrientations:
|
||
- UIInterfaceOrientationPortrait
|
||
UIApplicationSceneManifest:
|
||
UIApplicationSupportsMultipleScenes: false
|
||
NSMicrophoneUsageDescription: "OSGKeyboard uses the microphone for voice dictation and keeps a background audio session active while a voice session is running."
|
||
NSSpeechRecognitionUsageDescription: "OSGKeyboard uses on-device speech recognition to transcribe your voice. Audio is processed on your device and is not uploaded for transcription."
|
||
UIBackgroundModes:
|
||
- audio
|
||
NSAppTransportSecurity:
|
||
NSAllowsArbitraryLoads: false
|
||
ITSAppUsesNonExemptEncryption: false
|
||
CFBundleURLTypes:
|
||
- CFBundleURLName: com.osgkeyboard.ios.settings
|
||
CFBundleURLSchemes:
|
||
- osgkeyboard
|
||
settings:
|
||
base:
|
||
PRODUCT_BUNDLE_IDENTIFIER: com.osgkeyboard.ios
|
||
TARGETED_DEVICE_FAMILY: "1"
|
||
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents: YES
|
||
INFOPLIST_KEY_LSApplicationCategoryType: public.app-category.utilities
|
||
SUPPORTS_MACCATALYST: NO
|
||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD: NO
|
||
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD: NO
|
||
ASSETCATALOG_COMPILER_APPICON_NAME: AppIcon
|
||
dependencies:
|
||
- target: OSGKeyboardShared
|
||
embed: true
|
||
- target: OSGKeyboardExt
|
||
# Keyboard Extension is a plugin of the main App; embed it.
|
||
- sdk: Speech.framework
|
||
|
||
# =========================================================
|
||
# Keyboard Extension
|
||
# =========================================================
|
||
OSGKeyboardExt:
|
||
type: app-extension
|
||
platform: iOS
|
||
sources:
|
||
- path: OSGKeyboardExt
|
||
excludes:
|
||
- "en.lproj"
|
||
- "zh-Hans.lproj"
|
||
- path: OSGKeyboardExt/en.lproj/Keyboard.strings
|
||
buildPhase: resources
|
||
- path: OSGKeyboardExt/zh-Hans.lproj/Keyboard.strings
|
||
buildPhase: resources
|
||
resources:
|
||
- path: OSGKeyboardExt/PrivacyInfo.xcprivacy
|
||
settings:
|
||
base:
|
||
IPHONEOS_DEPLOYMENT_TARGET: "26.0"
|
||
entitlements:
|
||
path: OSGKeyboardExt/OSGKeyboardExt.entitlements
|
||
properties:
|
||
com.apple.security.application-groups:
|
||
- group.com.osgkeyboard.shared
|
||
# Shared with the host app so the keyboard extension can read the
|
||
# user's LLM API key. See OSGKeyboard/OSGKeyboard.entitlements
|
||
# for the full rationale.
|
||
keychain-access-groups:
|
||
- $(AppIdentifierPrefix)com.osgkeyboard.shared
|
||
info:
|
||
path: OSGKeyboardExt/Info.plist
|
||
properties:
|
||
CFBundleDisplayName: OSGKeyboard
|
||
CFBundleShortVersionString: "$(MARKETING_VERSION)"
|
||
CFBundleVersion: "$(CURRENT_PROJECT_VERSION)"
|
||
CFBundleDevelopmentRegion: en
|
||
CFBundleLocalizations:
|
||
- en
|
||
- zh-Hans
|
||
NSMicrophoneUsageDescription: "OSGKeyboard uses the microphone for voice dictation and keeps a background audio session active while a voice session is running."
|
||
NSSpeechRecognitionUsageDescription: "OSGKeyboard uses on-device speech recognition to transcribe your voice. Audio is processed on your device and is not uploaded for transcription."
|
||
NSExtension:
|
||
NSExtensionAttributes:
|
||
IsASCIICapable: false
|
||
PrefersRightToLeft: false
|
||
PrimaryLanguage: "en-US"
|
||
RequestsOpenAccess: true
|
||
NSExtensionPointIdentifier: com.apple.keyboard-service
|
||
NSExtensionPrincipalClass: $(PRODUCT_MODULE_NAME).KeyboardViewController
|
||
settings:
|
||
base:
|
||
PRODUCT_BUNDLE_IDENTIFIER: com.osgkeyboard.ios.keyboard
|
||
TARGETED_DEVICE_FAMILY: "1"
|
||
SUPPORTS_MACCATALYST: NO
|
||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD: NO
|
||
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD: NO
|
||
dependencies:
|
||
- target: OSGKeyboardShared
|
||
embed: false
|
||
|
||
# =========================================================
|
||
# Shared Framework (主 App 与扩展共用)
|
||
# =========================================================
|
||
OSGKeyboardShared:
|
||
type: framework
|
||
platform: iOS
|
||
sources:
|
||
- path: OSGKeyboardShared
|
||
excludes:
|
||
- "en.lproj"
|
||
- "zh-Hans.lproj"
|
||
- path: OSGKeyboardShared/en.lproj/Shared.strings
|
||
buildPhase: resources
|
||
- path: OSGKeyboardShared/zh-Hans.lproj/Shared.strings
|
||
buildPhase: resources
|
||
info:
|
||
path: OSGKeyboardShared/Info.plist
|
||
settings:
|
||
base:
|
||
PRODUCT_BUNDLE_IDENTIFIER: com.osgkeyboard.ios.shared
|
||
TARGETED_DEVICE_FAMILY: "1"
|
||
DEFINES_MODULE: YES
|
||
SKIP_INSTALL: YES
|
||
BUILD_LIBRARY_FOR_DISTRIBUTION: NO
|
||
DYLIB_INSTALL_NAME_BASE: "@rpath"
|
||
APPLICATION_EXTENSION_API_ONLY: YES
|
||
ENABLE_MODULE_VERIFIER: YES
|
||
dependencies:
|
||
- sdk: Speech.framework
|
||
- sdk: AVFoundation.framework
|
||
|
||
# =========================================================
|
||
# 单元测试
|
||
# =========================================================
|
||
OSGKeyboardTests:
|
||
type: bundle.unit-test
|
||
platform: iOS
|
||
sources:
|
||
- path: OSGKeyboardTests
|
||
info:
|
||
path: OSGKeyboardTests/Info.plist
|
||
dependencies:
|
||
- target: OSGKeyboard
|
||
settings:
|
||
base:
|
||
PRODUCT_BUNDLE_IDENTIFIER: com.osgkeyboard.ios.tests
|
||
TARGETED_DEVICE_FAMILY: "1"
|
||
|
||
# =========================================================
|
||
# 键盘扩展单元测试 (TEST-4)
|
||
# =========================================================
|
||
OSGKeyboardExtTests:
|
||
type: bundle.unit-test
|
||
platform: iOS
|
||
sources:
|
||
- path: OSGKeyboardExtTests
|
||
info:
|
||
path: OSGKeyboardExtTests/Info.plist
|
||
dependencies:
|
||
- target: OSGKeyboardShared
|
||
settings:
|
||
base:
|
||
PRODUCT_BUNDLE_IDENTIFIER: com.osgkeyboard.ios.ext.tests
|
||
TARGETED_DEVICE_FAMILY: "1"
|
||
|
||
schemes:
|
||
OSGKeyboard:
|
||
build:
|
||
targets:
|
||
OSGKeyboard: all
|
||
OSGKeyboardExt: all
|
||
run:
|
||
config: Debug
|
||
test:
|
||
config: Debug
|
||
targets:
|
||
- OSGKeyboardTests
|
||
- OSGKeyboardExtTests
|
||
archive:
|
||
config: Release
|