Squashes the working tree that had accumulated on cloud-agent. Two strands of work, kept in one commit at the maintainer's request. Crash and stability (this session): - Subscribe to MetricKit so crash, hang, CPU, and disk-write diagnostics are recorded on device. Keyboard-extension memory kills are the most likely way a user loses the keyboard mid-sentence and iOS does not classify them as crashes, so they never reach App Store Connect. - Upload those reports to POST /v1/analytics/diagnostics from internal builds only (local Debug and TestFlight), reusing the analytics installation identifier and skipping entirely when analytics are off. App Store builds keep everything on device. - Shed the Chinese engine, English lexicon, and in-flight pipeline work at the 40 MiB and 48 MiB budget thresholds instead of waiting for a system memory warning that jetsam often preempts. - Install audio taps with the input node's live format through one shared adaptive resampler, so an audio-route change mid-recording no longer raises an uncatchable format-mismatch exception. Covers the dictation preview and the Mac recorder, which still used the pattern the Flow capture path had already moved away from. - Harden the librime bridge: never pass a nil string across a nonnull boundary when a user dictionary holds malformed UTF-8, null-check the engine API vector, and distrust a candidate count without its array. - Guard the expanded candidate grid against a list that shrinks while the collection view is still laying out against the previous count. - Resolve localized text without the App Group so the screen shown when that container is missing renders its message instead of crashing on launch, which is the one situation it exists to handle. - Gate CI on the App Group and Keychain wiring that the keyboard extension hard-depends on, with a self-test proving the check fails. Clipboard, reply, and semantics (pre-existing working tree): - Unify the reply center, refresh keyboard AI features, and add native event and reminder exporters. - Expand clipboard semantic routing with authored corpora and a product-blind holdout, plus the scripts that build and validate them. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
764 lines
32 KiB
YAML
764 lines
32 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
|
||
# XcodeGen 2.37+ emits AppIcon.icon as folder.iconcomposer.icon natively.
|
||
# Older XcodeGen releases may need Scripts/patch-icon-composer.sh (fallback).
|
||
# 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 + iPad;
|
||
# no Mac Catalyst, no visionOS.
|
||
deploymentTarget:
|
||
iOS: "26.0"
|
||
developmentLanguage: en
|
||
createIntermediateGroups: true
|
||
generateEmptyDirectories: true
|
||
groupSortPosition: top
|
||
|
||
# iOS local ASR uses iOS 26 `SpeechAnalyzer` + `DictationTranscriber`.
|
||
# The shared iOS framework links librime for Chinese typing. Optional
|
||
# post-ASR polish and AI mode use the user-selected LLM provider.
|
||
# macOS local ASR links mlx-audio-swift (ThirdParty/) for Qwen3 MLX
|
||
# streaming; model weights are installed through the bundled catalog.
|
||
|
||
packages:
|
||
MLXAudio:
|
||
path: ThirdParty/mlx-audio-swift
|
||
Librime:
|
||
url: https://github.com/ghostflyby/librime-xcframework.git
|
||
exactVersion: 1.17.0-pack.1
|
||
|
||
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
|
||
MODULE_VERIFIER_SUPPORTED_LANGUAGES: "objective-c objective-c++"
|
||
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS: "gnu17 gnu++17"
|
||
CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED: YES
|
||
ASSETCATALOG_COMPILER_GENERATE_ASSET_SYMBOLS: YES
|
||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS: YES
|
||
STRING_CATALOG_GENERATE_SYMBOLS: YES
|
||
CLANG_CXX_LANGUAGE_STANDARD: c++17
|
||
MARKETING_VERSION: "2.2.0"
|
||
CURRENT_PROJECT_VERSION: "96"
|
||
# 签名配置来自 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
|
||
buildPhase: resources
|
||
# Empty Settings bundle: guarantees the app owns a Settings entry so
|
||
# `openSettingsURLString` lands on the app's own page instead of the
|
||
# Settings root or the Apps list.
|
||
- path: OSGKeyboard/Settings.bundle
|
||
type: folder
|
||
buildPhase: resources
|
||
- path: OSGKeyboard
|
||
excludes:
|
||
- "AppIcon.icon"
|
||
- "OSGKeyboard.icon"
|
||
# Declared explicitly under `resources` so it ships as a bundle.
|
||
- "Settings.bundle/**"
|
||
# 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"
|
||
- "Resources/CustomLanguageModel/**"
|
||
- "Resources/Shortcuts/**"
|
||
# Real keyboard chrome for DEBUG what's-new recording (same UI as Ext).
|
||
- path: OSGKeyboardExt/Utilities/ExtL10n.swift
|
||
- path: OSGKeyboardExt/Views/AIKeyboardView.swift
|
||
- path: OSGKeyboardExt/Views/LastInputEditView.swift
|
||
- path: OSGKeyboardExt/Views/KeyboardTopControls.swift
|
||
- path: OSGKeyboardExt/Views/ClipboardKeyboardViews.swift
|
||
- path: OSGKeyboardExt/Views/ToolbarActionButtons.swift
|
||
- path: OSGKeyboardExt/Views/GlobeInputModeButton.swift
|
||
- path: OSGKeyboardExt/Views/NativeKeyboardKeyStyle.swift
|
||
- path: OSGKeyboardExt/Utilities/KeyboardHapticFeedback.swift
|
||
- path: OSGKeyboardExt/Utilities/KeyboardSoundFeedback.swift
|
||
- path: OSGKeyboardExt/en.lproj/Keyboard.strings
|
||
buildPhase: resources
|
||
- path: OSGKeyboardExt/zh-Hans.lproj/Keyboard.strings
|
||
buildPhase: resources
|
||
# `.shortcut` is not a XcodeGen-known resource type; list each file
|
||
# as an explicit resource source or it never lands in the app bundle.
|
||
- path: OSGKeyboard/Resources/Shortcuts/OSGSaveToNotes.shortcut
|
||
type: file
|
||
buildPhase: resources
|
||
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
|
||
# Host-only account sessions use this second group explicitly.
|
||
# The keyboard extension is intentionally not entitled to it.
|
||
- $(AppIdentifierPrefix)com.osgkeyboard.ios
|
||
com.apple.developer.applesignin:
|
||
- Default
|
||
com.apple.developer.devicecheck.appattest-environment: $(APP_ATTEST_ENVIRONMENT)
|
||
com.apple.developer.associated-domains:
|
||
- applinks:osglab.com
|
||
# iCloud Key-Value Store — personal dictionary sync across
|
||
# iPhone / iPad / Mac (Designed for iPad). Main app only.
|
||
# NOTE: this entitlement is a single STRING, not an array — an
|
||
# array value never matches the profile and breaks automatic
|
||
# signing ("doesn't match the entitlements file's value").
|
||
com.apple.developer.ubiquity-kvstore-identifier: $(TeamIdentifierPrefix)com.osgkeyboard.ios
|
||
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
|
||
# Host-only heavy assets — keep out of the keyboard extension Shared framework.
|
||
# CLM uses a unique iOS-only path (`HostCLM/`): XcodeGen dedupes identical
|
||
# file paths across iOS/Mac, which previously left the iOS app without CLM.
|
||
- path: OSGKeyboard/Resources/Typing/Rime/osg_pinyin.dict.yaml
|
||
- path: OSGKeyboard/Resources/Typing/Rime/manifest.json
|
||
- path: OSGKeyboard/Resources/HostCLM/v1/OSGKeyboardCLM.bin
|
||
- path: OSGKeyboard/Resources/HostCLM/v1/compiled-manifest.json
|
||
- path: OSGKeyboard/Resources/LocalASR/local-asr-catalog.json
|
||
- path: OSGKeyboard/Resources/Typing/Licenses/NOTICE.txt
|
||
- path: OSGKeyboard/Resources/Typing/Licenses/LICENSE.txt
|
||
- path: OSGKeyboard/Resources/Typing/Licenses/LICENSE-PINYIN-SIMP-APACHE.txt
|
||
- path: OSGKeyboard/Resources/Typing/Licenses/LICENSE-MATERIAL-ICONS-APACHE.txt
|
||
- path: OSGKeyboard/Resources/Typing/Licenses/LICENSE-JIEBA-MIT.txt
|
||
- path: OSGKeyboard/Resources/Typing/Licenses/LICENSE-PHRASE-PINYIN-DATA-MIT.txt
|
||
- path: OSGKeyboard/Resources/Typing/Licenses/LICENSE-PINYIN-DATA-MIT.txt
|
||
- path: OSGKeyboard/Resources/Typing/Licenses/LICENSE-NORVIG-PD.txt
|
||
- path: OSGKeyboard/Resources/Typing/Licenses/THIRD_PARTY_NOTICES.md
|
||
- path: OSGKeyboard/Resources/Typing/Licenses/third-party-notices.zip
|
||
- path: OSGKeyboard/Resources/Typing/Licenses/LIBRIME-COMBINED-NOTICES.txt
|
||
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
|
||
UISupportedInterfaceOrientations~ipad:
|
||
- UIInterfaceOrientationPortrait
|
||
- UIInterfaceOrientationPortraitUpsideDown
|
||
- UIInterfaceOrientationLandscapeLeft
|
||
- UIInterfaceOrientationLandscapeRight
|
||
UIApplicationSceneManifest:
|
||
UIApplicationSupportsMultipleScenes: false
|
||
NSMicrophoneUsageDescription: "OSGKeyboard uses the microphone for voice dictation, including active Flow sessions while you type in other apps."
|
||
NSSpeechRecognitionUsageDescription: "OSGKeyboard uses speech recognition to transcribe your voice. Audio is processed on-device by default, or sent to your configured speech provider only when you enable cloud recognition."
|
||
NSContactsUsageDescription: "OSGKeyboard uses Contacts only when you choose Create Contact for a copied phone number."
|
||
NSRemindersFullAccessUsageDescription: "OSGKeyboard adds reminders only when you choose Extract To-dos for copied text."
|
||
NSCalendarsFullAccessUsageDescription: "OSGKeyboard adds calendar events only when you choose Extract Events for copied text."
|
||
UIBackgroundModes:
|
||
- audio
|
||
BGTaskSchedulerPermittedIdentifiers:
|
||
- com.osgkeyboard.ios.analytics-sync
|
||
NSAppTransportSecurity:
|
||
NSAllowsArbitraryLoads: false
|
||
ITSAppUsesNonExemptEncryption: false
|
||
# StoreKit 2 can return finished consumable credit purchases in
|
||
# Transaction.all so the account center can show purchase history.
|
||
SKIncludeConsumableInAppPurchaseHistory: true
|
||
# Runtime-resolved, team-prefixed group used by the host-private
|
||
# account Keychain store. Never copy this key to the extension plist.
|
||
OSGPrivateKeychainAccessGroup: $(AppIdentifierPrefix)com.osgkeyboard.ios
|
||
CFBundleURLTypes:
|
||
- CFBundleURLName: com.osgkeyboard.ios.settings
|
||
CFBundleURLSchemes:
|
||
- osgkeyboard
|
||
LSApplicationQueriesSchemes:
|
||
- weixin
|
||
- mqq
|
||
- wxwork
|
||
- dingtalk
|
||
- lark
|
||
- tg
|
||
- whatsapp
|
||
- line
|
||
- fb-messenger
|
||
- slack
|
||
- msteams
|
||
- discord
|
||
- notion
|
||
- bear
|
||
- obsidian
|
||
- drafts5
|
||
- googlegmail
|
||
- ms-outlook
|
||
- googlechrome
|
||
- sinaweibo
|
||
- xhsdiscover
|
||
- sgnl
|
||
- kakaotalk
|
||
- viber
|
||
- zalo
|
||
- skype
|
||
- zoomus
|
||
- shortcuts
|
||
- iosamap
|
||
- amapuri
|
||
- baidumap
|
||
- maps
|
||
- things
|
||
- todoist
|
||
- evernote
|
||
- onenote
|
||
- readdle-spark
|
||
- firefox
|
||
- microsoft-edge
|
||
- fb
|
||
- instagram
|
||
- twitter
|
||
- barcelona
|
||
- snapchat
|
||
- reddit
|
||
- pinterest
|
||
- zhihu
|
||
- bilibili
|
||
- snssdk1128
|
||
- tiktok
|
||
# All network calls are HTTPS (NSAppTransportSecurity above);
|
||
# nothing in the app uses non-exempt encryption. Declaring
|
||
# `ITSAppUsesNonExemptEncryption: false` lets us skip the
|
||
# annual self-classification questionnaire in App Store
|
||
# Connect on every upload.
|
||
ITSAppUsesNonExemptEncryption: false
|
||
settings:
|
||
base:
|
||
PRODUCT_BUNDLE_IDENTIFIER: com.osgkeyboard.ios
|
||
TARGETED_DEVICE_FAMILY: "1,2"
|
||
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
|
||
# TestFlight upload: Automatic signing picks the right App Store profile.
|
||
CODE_SIGN_STYLE: Automatic
|
||
DEVELOPMENT_TEAM: X329MZU23S
|
||
configs:
|
||
Debug:
|
||
APP_ATTEST_ENVIRONMENT: development
|
||
Release:
|
||
APP_ATTEST_ENVIRONMENT: production
|
||
dependencies:
|
||
- target: OSGKeyboardShared
|
||
embed: true
|
||
- target: OSGKeyboardHostSupport
|
||
embed: true
|
||
- target: OSGKeyboardExt
|
||
# Keyboard Extension is a plugin of the main App; embed it.
|
||
embed: true
|
||
buildPhase: embedAppExtensions
|
||
- sdk: Speech.framework
|
||
- sdk: StoreKit.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
|
||
# The extension shows its own microphone / speech prompts, so it needs
|
||
# its own InfoPlist.strings — the host app's copy does not cover it.
|
||
- path: OSGKeyboardExt/en.lproj/InfoPlist.strings
|
||
buildPhase: resources
|
||
- path: OSGKeyboardExt/zh-Hans.lproj/InfoPlist.strings
|
||
buildPhase: resources
|
||
resources:
|
||
- path: OSGKeyboardExt/Assets.xcassets
|
||
- 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, including active Flow sessions while you type in other apps."
|
||
NSSpeechRecognitionUsageDescription: "OSGKeyboard uses speech recognition to transcribe your voice. Audio is processed on-device by default, or sent to your configured speech provider only when you enable cloud recognition."
|
||
NSExtension:
|
||
NSExtensionAttributes:
|
||
IsASCIICapable: false
|
||
PrefersRightToLeft: false
|
||
PrimaryLanguage: "mis"
|
||
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,2"
|
||
SUPPORTS_MACCATALYST: NO
|
||
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD: NO
|
||
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD: NO
|
||
# TestFlight upload: Automatic signing picks the right App Store profile.
|
||
CODE_SIGN_STYLE: Automatic
|
||
DEVELOPMENT_TEAM: X329MZU23S
|
||
dependencies:
|
||
- target: OSGKeyboardShared
|
||
embed: false
|
||
|
||
# Shared Framework (主 App 与扩展共用)
|
||
# =========================================================
|
||
OSGKeyboardShared:
|
||
type: framework
|
||
platform: iOS
|
||
sources:
|
||
- path: OSGKeyboardShared
|
||
excludes:
|
||
- "en.lproj"
|
||
- "zh-Hans.lproj"
|
||
- "**/*.swift.example"
|
||
- "Resources/**"
|
||
- "PrivacyInfo.xcprivacy"
|
||
- path: OSGKeyboardShared/en.lproj/Shared.strings
|
||
buildPhase: resources
|
||
- path: OSGKeyboardShared/zh-Hans.lproj/Shared.strings
|
||
buildPhase: resources
|
||
# Privacy manifest required for LevelDB statically linked via librime
|
||
# (ITMS-91061). Must live in this framework bundle, not only the app/ext.
|
||
- path: OSGKeyboardShared/PrivacyInfo.xcprivacy
|
||
buildPhase: resources
|
||
# Prefer sources+buildPhase over target-level `resources:` — the latter
|
||
# was not emitting Copy Bundle Resources entries for this framework.
|
||
- path: OSGKeyboardShared/Resources/Typing/English/english_lexicon.bin
|
||
buildPhase: resources
|
||
- path: OSGKeyboardShared/Resources/PolishStyles
|
||
buildPhase: resources
|
||
- path: OSGKeyboardShared/Resources/ClipboardSemantics
|
||
buildPhase: resources
|
||
- path: OSGRimeBridge
|
||
excludes:
|
||
- "include/**"
|
||
info:
|
||
path: OSGKeyboardShared/Info.plist
|
||
settings:
|
||
base:
|
||
PRODUCT_BUNDLE_IDENTIFIER: com.osgkeyboard.ios.shared
|
||
TARGETED_DEVICE_FAMILY: "1,2"
|
||
DEFINES_MODULE: YES
|
||
SKIP_INSTALL: YES
|
||
BUILD_LIBRARY_FOR_DISTRIBUTION: NO
|
||
DYLIB_INSTALL_NAME_BASE: "@rpath"
|
||
APPLICATION_EXTENSION_API_ONLY: YES
|
||
SWIFT_OBJC_BRIDGING_HEADER: OSGRimeBridge/include/OSGRimeBridge.h
|
||
CLANG_ENABLE_MODULES: YES
|
||
OTHER_CPLUSPLUSFLAGS: "$(inherited) -fmodules -fcxx-modules"
|
||
ENABLE_MODULE_VERIFIER: YES
|
||
# Embedded frameworks are re-signed by the containing app at package
|
||
# time. Signing this framework as a standalone build product is noisy
|
||
# and not recommended by Xcode.
|
||
CODE_SIGNING_ALLOWED: NO
|
||
CODE_SIGNING_REQUIRED: NO
|
||
CODE_SIGN_IDENTITY: ""
|
||
dependencies:
|
||
- package: Librime
|
||
product: RimeStatic
|
||
- sdk: libsqlite3.tbd
|
||
|
||
# Host-only support framework (ASR / CLM / Cloud / Charts / StoreKit).
|
||
# Keyboard extension does NOT link this — keeps Speech/AVFoundation/Charts
|
||
# out of the extension jetsam budget.
|
||
OSGKeyboardHostSupport:
|
||
type: framework
|
||
platform: iOS
|
||
sources:
|
||
- path: OSGKeyboardHostSupport
|
||
info:
|
||
path: OSGKeyboardHostSupport/Info.plist
|
||
settings:
|
||
base:
|
||
PRODUCT_BUNDLE_IDENTIFIER: com.osgkeyboard.ios.hostsupport
|
||
TARGETED_DEVICE_FAMILY: "1,2"
|
||
DEFINES_MODULE: YES
|
||
SKIP_INSTALL: YES
|
||
BUILD_LIBRARY_FOR_DISTRIBUTION: NO
|
||
DYLIB_INSTALL_NAME_BASE: "@rpath"
|
||
APPLICATION_EXTENSION_API_ONLY: NO
|
||
ENABLE_MODULE_VERIFIER: YES
|
||
CODE_SIGNING_ALLOWED: NO
|
||
CODE_SIGNING_REQUIRED: NO
|
||
CODE_SIGN_IDENTITY: ""
|
||
dependencies:
|
||
- target: OSGKeyboardShared
|
||
embed: false
|
||
- sdk: Speech.framework
|
||
- sdk: AVFoundation.framework
|
||
- sdk: Charts.framework
|
||
- sdk: StoreKit.framework
|
||
|
||
# =========================================================
|
||
# 单元测试
|
||
# =========================================================
|
||
OSGKeyboardTests:
|
||
type: bundle.unit-test
|
||
platform: iOS
|
||
sources:
|
||
- path: OSGKeyboardTests
|
||
- path: Tests/ProviderToolRequestCoordinatorTests.swift
|
||
info:
|
||
path: OSGKeyboardTests/Info.plist
|
||
dependencies:
|
||
- target: OSGKeyboard
|
||
- target: OSGKeyboardHostSupport
|
||
settings:
|
||
base:
|
||
PRODUCT_BUNDLE_IDENTIFIER: com.osgkeyboard.ios.tests
|
||
TARGETED_DEVICE_FAMILY: "1,2"
|
||
|
||
# =========================================================
|
||
# 键盘扩展单元测试 (TEST-4)
|
||
# =========================================================
|
||
OSGKeyboardExtTests:
|
||
type: bundle.unit-test
|
||
platform: iOS
|
||
sources:
|
||
- path: OSGKeyboardExtTests
|
||
- path: OSGKeyboardExt/Services/EditHintScheduler.swift
|
||
- path: OSGKeyboardExt/Services/ClipboardCaptureCoordinator.swift
|
||
# Host-only dict fixture for librime deploy tests (not linked into Ext).
|
||
- path: OSGKeyboard/Resources/Typing/Rime/osg_pinyin.dict.yaml
|
||
buildPhase: resources
|
||
info:
|
||
path: OSGKeyboardExtTests/Info.plist
|
||
dependencies:
|
||
- target: OSGKeyboardShared
|
||
settings:
|
||
base:
|
||
PRODUCT_BUNDLE_IDENTIFIER: com.osgkeyboard.ios.ext.tests
|
||
TARGETED_DEVICE_FAMILY: "1,2"
|
||
|
||
# =========================================================
|
||
# UI 测试:确定性宿主在模拟器运行,PiP/真实键盘链路保留实机验收
|
||
# =========================================================
|
||
# XCUITest 是 Apple 唯一开放的物理设备触摸注入通道;模拟器可运行
|
||
# 内嵌宿主回归,但无法复现 PiP(isPictureInPictureSupported 恒为 false)。
|
||
OSGKeyboardUITests:
|
||
type: bundle.ui-testing
|
||
platform: iOS
|
||
sources:
|
||
- path: OSGKeyboardUITests
|
||
info:
|
||
path: OSGKeyboardUITests/Info.plist
|
||
dependencies:
|
||
- target: OSGKeyboard
|
||
settings:
|
||
base:
|
||
PRODUCT_BUNDLE_IDENTIFIER: com.osgkeyboard.ios.uitests
|
||
TARGETED_DEVICE_FAMILY: "1,2"
|
||
CODE_SIGN_STYLE: Automatic
|
||
DEVELOPMENT_TEAM: X329MZU23S
|
||
|
||
# =========================================================
|
||
# macOS 单元测试
|
||
# =========================================================
|
||
# Hosted in the Mac app so `@testable import OSGKeyboard` reaches the
|
||
# macOS-only types (MacAudioRecorder, overlay sizing) that cannot compile
|
||
# against the iOS targets.
|
||
OSGKeyboardMacTests:
|
||
type: bundle.unit-test
|
||
platform: macOS
|
||
deploymentTarget: "15.0"
|
||
sources:
|
||
- path: OSGKeyboardMacTests
|
||
- path: Tests/ProviderToolRequestCoordinatorTests.swift
|
||
info:
|
||
path: OSGKeyboardMacTests/Info.plist
|
||
dependencies:
|
||
- target: OSGKeyboardMac
|
||
settings:
|
||
base:
|
||
PRODUCT_BUNDLE_IDENTIFIER: com.osgkeyboard.mac.tests
|
||
MACOSX_DEPLOYMENT_TARGET: "15.0"
|
||
CODE_SIGN_STYLE: Automatic
|
||
DEVELOPMENT_TEAM: X329MZU23S
|
||
# The host target is named OSGKeyboardMac but ships as OSGKeyboard.app,
|
||
# so the path XcodeGen infers from the target name does not exist.
|
||
TEST_HOST: "$(BUILT_PRODUCTS_DIR)/OSGKeyboard.app/Contents/MacOS/OSGKeyboard"
|
||
BUNDLE_LOADER: "$(TEST_HOST)"
|
||
|
||
# =========================================================
|
||
# macOS 菜单栏 App
|
||
# =========================================================
|
||
# Standalone menu-bar utility: record → local/cloud ASR → polish → insert.
|
||
# Reuses the platform-agnostic core files from OSGKeyboardShared at the
|
||
# source level (no framework), excluding the iOS-only files that import
|
||
# SpeechAnalyzer / AVAudioSession / UIKit / SwiftUI views. Local mode uses
|
||
# Qwen3 MLX streaming via mlx-audio-swift plus catalog-managed weights.
|
||
OSGKeyboardMac:
|
||
type: application
|
||
platform: macOS
|
||
deploymentTarget: "15.0"
|
||
sources:
|
||
# Same Icon Composer asset as the iOS app — do not ship a separate PNG
|
||
# appiconset; actool crashes if both coexist.
|
||
- path: OSGKeyboard/AppIcon.icon
|
||
buildPhase: resources
|
||
# Reuse the iOS asset catalog for provider logos + brand mark. Exclude
|
||
# the PNG appiconset so it doesn't clash with AppIcon.icon above.
|
||
- path: OSGKeyboard/Assets.xcassets
|
||
excludes:
|
||
- "AppIcon.appiconset"
|
||
- path: OSGKeyboardMac
|
||
- path: OSGKeyboard/Services/OpenSourceLicenseCatalog.swift
|
||
# Account stack shared with the iOS app at the source level. These files
|
||
# are platform-agnostic (no UIKit); the iOS-only SwiftUI account center
|
||
# stays behind in OSGKeyboard/Views/Account and Mac ships MacAccountView.
|
||
# AccountCreditStore / AccountCreditPurchaseManager compile here because
|
||
# AccountSessionCoordinator owns one, but Mac never surfaces purchase UI:
|
||
# Developer ID builds cannot use StoreKit. Credits are bought on iOS/web
|
||
# and only consumed here.
|
||
- path: OSGKeyboard/Views/Account/AccountCenterModels.swift
|
||
- path: OSGKeyboard/Views/Account/AccountSessionCoordinator.swift
|
||
- path: OSGKeyboard/Views/Account/AccountCreditStore.swift
|
||
- path: OSGKeyboard/Views/Account/AccountCreditPurchaseManager.swift
|
||
- path: OSGKeyboard/Views/Account/LiveAccountServices.swift
|
||
- path: OSGKeyboard/Views/Account/ReferralProfileViewModel.swift
|
||
- path: OSGKeyboard/Views/Account/AccountAppleAuthorizationFactories.swift
|
||
- path: OSGKeyboard/Services/AccountAnalyticsBearerBridge.swift
|
||
- path: OSGKeyboardHostSupport
|
||
excludes:
|
||
# iOS SpeechAnalyzer / AVAudioSession capture — Mac has its own paths.
|
||
- "Services/ASRService.swift"
|
||
- "Services/CloudASR/CloudASRService.swift"
|
||
- "Services/FlowAudioSessionCoordinator.swift"
|
||
- "Services/FlowCaptureVoiceProcessing.swift"
|
||
- "Services/FlowContinuousCapture.swift"
|
||
- "Services/LiveDictationController.swift"
|
||
- "Models/AudioBufferSnapshot+AVFoundation.swift"
|
||
- "Info.plist"
|
||
- path: OSGKeyboardShared
|
||
excludes:
|
||
- "en.lproj"
|
||
- "zh-Hans.lproj"
|
||
- "Info.plist"
|
||
- "**/*.swift.example"
|
||
- "Resources/**"
|
||
- "DesignSystem/WaveformView.swift"
|
||
- "DesignSystem/RecordButton.swift"
|
||
- "DesignSystem/RecordButtonGesturePolicy.swift"
|
||
- "Services/KeyboardState.swift"
|
||
- "Services/KeyboardOpenSurfacePolicy.swift"
|
||
- "Models/MicVoiceAvailability+Keyboard.swift"
|
||
- "Models/TypingInputConfiguration.swift"
|
||
- "Models/TypingSurfaceMetrics.swift"
|
||
- "Typing/**"
|
||
- "Utilities/ProgressiveDictationTranscriptAccumulator.swift"
|
||
- path: OSGKeyboardShared/en.lproj/Shared.strings
|
||
buildPhase: resources
|
||
- path: OSGKeyboardShared/zh-Hans.lproj/Shared.strings
|
||
buildPhase: resources
|
||
- path: OSGKeyboardShared/Resources/PolishStyles
|
||
buildPhase: resources
|
||
- path: OSGKeyboard/Resources/CustomLanguageModel/v1/phrases.tsv
|
||
buildPhase: resources
|
||
- path: OSGKeyboard/Resources/CustomLanguageModel/v1/OSGKeyboardCLM.bin
|
||
buildPhase: resources
|
||
- path: OSGKeyboard/Resources/CustomLanguageModel/v1/compiled-manifest.json
|
||
buildPhase: resources
|
||
- path: OSGKeyboard/Resources/PrivacyPolicy.html
|
||
buildPhase: resources
|
||
- path: OSGKeyboard/Resources/LocalASR/local-asr-catalog.json
|
||
buildPhase: resources
|
||
- path: OSGKeyboard/Resources/Typing/Licenses/LIBRIME-COMBINED-NOTICES.txt
|
||
buildPhase: resources
|
||
- path: OSGKeyboard/Resources/Typing/Licenses/LICENSE-PINYIN-SIMP-APACHE.txt
|
||
buildPhase: resources
|
||
- path: OSGKeyboard/Resources/Typing/Licenses/LICENSE-MATERIAL-ICONS-APACHE.txt
|
||
buildPhase: resources
|
||
- path: OSGKeyboard/Resources/Typing/Licenses/LICENSE-JIEBA-MIT.txt
|
||
buildPhase: resources
|
||
- path: OSGKeyboard/Resources/Typing/Licenses/LICENSE-PHRASE-PINYIN-DATA-MIT.txt
|
||
buildPhase: resources
|
||
- path: OSGKeyboard/Resources/Typing/Licenses/LICENSE-PINYIN-DATA-MIT.txt
|
||
buildPhase: resources
|
||
- path: OSGKeyboard/Resources/Typing/Licenses/LICENSE-NORVIG-PD.txt
|
||
buildPhase: resources
|
||
- path: OSGKeyboard/Resources/Typing/Licenses/LICENSE.txt
|
||
buildPhase: resources
|
||
- path: OSGKeyboard/Resources/Typing/Licenses/NOTICE.txt
|
||
buildPhase: resources
|
||
entitlements:
|
||
path: OSGKeyboardMac/OSGKeyboardMac.entitlements
|
||
properties:
|
||
# Sandbox disabled on purpose. Precisely: App Sandbox does not itself
|
||
# ban the Accessibility API — TCC does, and sandboxed Mac App Store
|
||
# apps can be granted it. What makes the sandbox a bad trade here is
|
||
# the combination this app needs: a global `.flagsChanged` monitor over
|
||
# every keystroke (MacHotkeyService) plus a synthesized ⌘V into other
|
||
# apps (MacTextInsertionService). That pairing is a review risk not
|
||
# worth taking just to gain StoreKit. Ship Developer ID + notarized.
|
||
#
|
||
# Consequence for the account stack: no in-app purchases here. Credits
|
||
# are bought on iOS/web and only spent on Mac. Sign in with Apple and
|
||
# the account API need no sandbox and no Mac App Store.
|
||
# NSPasteboard, for the record, is unrestricted under the sandbox —
|
||
# clipboard capture was never the reason to opt out.
|
||
com.apple.security.app-sandbox: false
|
||
com.apple.security.device.audio-input: true
|
||
com.apple.security.network.client: true
|
||
com.apple.developer.ubiquity-kvstore-identifier: $(TeamIdentifierPrefix)com.osgkeyboard.ios
|
||
# Sign in with Apple needs no sandbox and no Mac App Store — a
|
||
# Developer ID + notarized build is enough. The Mac App ID must be
|
||
# grouped under the iOS primary App ID in the developer portal, or the
|
||
# same person gets a different Apple `sub` here than on iOS and the
|
||
# server treats them as two accounts.
|
||
com.apple.developer.applesignin:
|
||
- Default
|
||
keychain-access-groups:
|
||
- $(AppIdentifierPrefix)com.osgkeyboard.shared
|
||
# Host-private account credentials (session tokens, Apple user id).
|
||
# Never the .shared group — that one is reachable by the iOS keyboard
|
||
# extension; see HostPrivateAccountKeychainDescriptor.
|
||
- $(AppIdentifierPrefix)com.osgkeyboard.mac
|
||
info:
|
||
path: OSGKeyboardMac/Info.plist
|
||
properties:
|
||
CFBundleDisplayName: OSGKeyboard
|
||
CFBundleShortVersionString: "$(MARKETING_VERSION)"
|
||
CFBundleVersion: "$(CURRENT_PROJECT_VERSION)"
|
||
CFBundleDevelopmentRegion: en
|
||
CFBundleLocalizations:
|
||
- en
|
||
- zh-Hans
|
||
LSApplicationCategoryType: public.app-category.utilities
|
||
OSGPrivateKeychainAccessGroup: $(AppIdentifierPrefix)com.osgkeyboard.mac
|
||
NSMicrophoneUsageDescription: "OSGKeyboard uses the microphone to transcribe your voice via your configured cloud speech provider."
|
||
NSSpeechRecognitionUsageDescription: "OSGKeyboard uses on-device speech recognition for local dictation mode."
|
||
NSHumanReadableCopyright: "OSGKeyboard"
|
||
ITSAppUsesNonExemptEncryption: false
|
||
CFBundleURLTypes:
|
||
- CFBundleURLName: com.osgkeyboard.mac.seed
|
||
CFBundleURLSchemes:
|
||
- osgkeyboard
|
||
settings:
|
||
base:
|
||
PRODUCT_NAME: OSGKeyboard
|
||
INFOPLIST_KEY_CFBundleDisplayName: OSGKeyboard
|
||
INFOPLIST_KEY_CFBundleName: OSGKeyboard
|
||
PRODUCT_BUNDLE_IDENTIFIER: com.osgkeyboard.mac
|
||
MACOSX_DEPLOYMENT_TARGET: "15.0"
|
||
ASSETCATALOG_COMPILER_APPICON_NAME: AppIcon
|
||
# macOS app is not an app extension; override the framework-wide flag.
|
||
APPLICATION_EXTENSION_API_ONLY: NO
|
||
CODE_SIGN_STYLE: Automatic
|
||
DEVELOPMENT_TEAM: X329MZU23S
|
||
# Required for Developer ID distribution + notarization (outside App Store).
|
||
ENABLE_HARDENED_RUNTIME: YES
|
||
dependencies:
|
||
- package: MLXAudio
|
||
product: MLXAudioSTT
|
||
- sdk: Speech.framework
|
||
- sdk: AVFoundation.framework
|
||
- sdk: Charts.framework
|
||
- sdk: StoreKit.framework
|
||
- sdk: libsqlite3.tbd
|
||
|
||
schemes:
|
||
OSGKeyboard:
|
||
build:
|
||
targets:
|
||
OSGKeyboard: all
|
||
OSGKeyboardExt: all
|
||
run:
|
||
config: Debug
|
||
storeKitConfiguration: OSGKeyboard.storekit
|
||
test:
|
||
config: Debug
|
||
targets:
|
||
- OSGKeyboardTests
|
||
- OSGKeyboardExtTests
|
||
archive:
|
||
config: Release
|
||
|
||
# Device-only reproduction harness; kept out of the main scheme so CI and
|
||
# Scripts/run-tests.sh keep running unit tests without a physical device.
|
||
OSGKeyboardUITests:
|
||
build:
|
||
targets:
|
||
OSGKeyboard: all
|
||
OSGKeyboardExt: all
|
||
OSGKeyboardUITests: [test]
|
||
test:
|
||
config: Debug
|
||
targets:
|
||
- OSGKeyboardUITests
|
||
|
||
OSGKeyboardMac:
|
||
build:
|
||
targets:
|
||
OSGKeyboardMac: all
|
||
run:
|
||
config: Debug
|
||
test:
|
||
config: Debug
|
||
targets:
|
||
- OSGKeyboardMacTests
|
||
archive:
|
||
config: Release
|