# 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 # Local package: `Qwen3Speech`. Vendored fork of # `soniqo/speech-swift`'s Qwen3-ASR + Qwen3-Chat modules (plus the # AudioCommon / MLXCommon / SpeechVAD slices they depend on). # Upstream's `Package.swift` references a `CSpeechCore` binary # target whose URL doesn't match its declared filename (the file # is `SpeechCore.xcframework.zip`, the target is `CSpeechCore`), # which breaks SwiftPM resolve on a clean checkout. Vendoring # lets us ship the four targets we actually link without waiting # for the upstream fix; the `audio` / `audio-server` CLIs and the # `AudioServer` HTTP backend that needed `CSpeechCore` aren't part # of OSGKeyboard's build graph. Sources carry their original # Apache-2.0 copyright headers; the `Package.swift` here adds # only what we need. packages: Qwen3Speech: path: OSGKeyboard/ThirdParty/Qwen3Speech 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" # `ThirdParty/Qwen3Speech` is a local SPM package pulled in # by `packages:` below — it builds as its own targets # (`Qwen3ASR`, …) and we link the products. # If we don't exclude the source folder, xcodegen walks # into the package's `Sources/*/` directories and pulls # the swift files straight into the host app target, # which collides on duplicate type names (e.g. two # `Configuration.swift` files in `Qwen3ASR` and # `SpeechVAD`) and on multiple `@main` / top-level # declarations. Keep the folder as a package; don't # flatten it into the app. - "ThirdParty/Qwen3Speech" 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 # Local fork of soniqo/speech-swift. See the `packages:` # block at the top of this file for the why-fork rationale. # Only the two products we actually link are listed; AudioServer # / AudioCLI (which is what the upstream binary target was # gating) aren't in our build graph. - package: Qwen3Speech product: Qwen3ASR # ========================================================= # 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