# XcodeGen spec for the native SwiftUI VniDrop app (iOS/iPadOS/macOS). # Regenerate the project with: xcodegen generate (run from apple/) # Requires three generated inputs first (all gitignored), before xcodegen: # - Rust core: apple/scripts/build-core.sh debug # - Localization: (cd localization && bun run src/cli.ts generate) # -> VniDrop/Resources/Localizable.xcstrings, VniDrop/Generated/L10n.swift # - Versions: packaging/version/generate-apple-xcconfig.sh all # -> Generated/StoreVersion.xcconfig, Generated/DirectVersion.xcconfig name: VniDrop options: bundleIdPrefix: com.vnidrop deploymentTarget: iOS: "18.2" macOS: "15.0" createIntermediateGroups: true # Build configurations. Declaring `configs` replaces XcodeGen's Debug/Release # defaults, so both are re-listed here. `Release-Direct` is a release-type config # used only by the VniDropDirect (notarized DMG + Sparkle) target; the App Store # `VniDrop` target ships under plain `Release`. configs: Debug: debug Release: release Release-Direct: release # Project-wide build settings (applied to every target/config). settings: base: # Apple Silicon only. Intel Macs are unsupported (going EOL with macOS 28), and # the Rust core's macOS slice (vnidrop.xcframework) is built arm64-only, so a # universal link would fail looking for x86_64 symbols anyway. ARCHS: arm64 # Strip unreachable code from release binaries. DEAD_CODE_STRIPPING: YES # Flag user-facing strings that aren't localized (the app ships 9 languages). CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED: YES packages: VnidropCore: path: VnidropCore SFSafeSymbols: url: https://github.com/SFSafeSymbols/SFSafeSymbols from: "5.3.0" # Sparkle powers in-app auto-updates for the direct-download (.dmg) build only. # It is linked exclusively by the VniDropDirect target — SwiftPM links products # per target, not per config, so keeping it off the App Store target is what # guarantees the store binary never bundles a self-updater (App Store forbids it). Sparkle: url: https://github.com/sparkle-project/Sparkle from: "2.9.4" # Shared definition for the two shipping app targets. `VniDrop` (App Store / # TestFlight) and `VniDropDirect` (notarized DMG + Sparkle) build the exact same # sources; only their destinations, extra dependencies, and the DIRECT_DISTRIBUTION # compile flag differ (set per target below). targetTemplates: AppBase: type: application sources: - path: VniDrop excludes: - "Resources/Info.plist" - "Resources/VniDrop.entitlements" - "Resources/VniDropDirect.entitlements" - "Resources/**/.DS_Store" settings: base: PRODUCT_NAME: VniDrop PRODUCT_BUNDLE_IDENTIFIER: com.vnidrop.app MARKETING_VERSION: "$(PRODUCT_VERSION)" GENERATE_INFOPLIST_FILE: NO INFOPLIST_FILE: VniDrop/Resources/Info.plist CODE_SIGN_ENTITLEMENTS: VniDrop/Resources/VniDrop.entitlements # Mirror the Info.plist identity so Xcode's Identity editor shows it too # (the editor reads these build settings, not the manual plist). INFOPLIST_KEY_CFBundleDisplayName: VniDrop INFOPLIST_KEY_LSApplicationCategoryType: public.app-category.utilities SWIFT_VERSION: "6.0" SWIFT_STRICT_CONCURRENCY: complete ENABLE_USER_SCRIPT_SANDBOXING: NO ASSETCATALOG_COMPILER_APPICON_NAME: AppIcon # App-wide accent (macOS sidebar selection, default control tints). The # AccentColor asset mirrors VniDropColors.brandPurple — keep them in sync. ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME: AccentColor configs: release: # Produce a dSYM in the archive so symbol upload succeeds. DEBUG_INFORMATION_FORMAT: dwarf-with-dsym release-direct: DEBUG_INFORMATION_FORMAT: dwarf-with-dsym dependencies: - package: VnidropCore - package: SFSafeSymbols - sdk: SystemConfiguration.framework - sdk: Security.framework - sdk: libresolv.tbd preBuildScripts: # Enforce the typed-resources convention (see .swiftlint.yml). Required: fails # the build if SwiftLint is missing so the rules can't be silently bypassed. - name: SwiftLint (typed resources) basedOnDependencyAnalysis: false script: | # Xcode runs build phases with a minimal PATH that omits Homebrew, so add # the common Homebrew bin dirs (Apple Silicon + Intel) before resolving it. export PATH="/opt/homebrew/bin:/usr/local/bin:$PATH" if which swiftlint >/dev/null; then swiftlint lint --config "${SRCROOT}/.swiftlint.yml" else echo "error: SwiftLint not installed — run 'brew install swiftlint'" exit 1 fi targets: # App Store / TestFlight target. iOS + macOS, sandboxed, no self-updater. VniDrop: templates: [AppBase] supportedDestinations: [iOS, macOS] configFiles: Debug: Generated/StoreVersion.xcconfig Release: Generated/StoreVersion.xcconfig Release-Direct: Generated/StoreVersion.xcconfig # Direct-download macOS target: Developer ID signed, notarized, ships in a .dmg # and self-updates via Sparkle. DIRECT_DISTRIBUTION gates all Sparkle code so the # App Store target above never compiles or links it. VniDropDirect: templates: [AppBase] supportedDestinations: [macOS] configFiles: Debug: Generated/DirectVersion.xcconfig Release: Generated/DirectVersion.xcconfig Release-Direct: Generated/DirectVersion.xcconfig settings: base: SWIFT_ACTIVE_COMPILATION_CONDITIONS: "$(inherited) DIRECT_DISTRIBUTION" # Notarization requires the hardened runtime. ENABLE_HARDENED_RUNTIME: YES # Non-sandboxed entitlements: a sandboxed Developer ID app needs a # provisioning profile, which direct distribution avoids. (App Store target # keeps VniDrop.entitlements with the sandbox.) CODE_SIGN_ENTITLEMENTS: VniDrop/Resources/VniDropDirect.entitlements dependencies: - package: Sparkle VniDropTests: type: bundle.unit-test supportedDestinations: [iOS, macOS] configFiles: Debug: Signing.xcconfig Release: Signing.xcconfig Release-Direct: Signing.xcconfig sources: - path: Tests settings: base: GENERATE_INFOPLIST_FILE: YES SWIFT_VERSION: "6.0" SWIFT_STRICT_CONCURRENCY: complete dependencies: - target: VniDrop schemes: VniDrop: build: targets: VniDrop: all run: config: Debug test: config: Debug targets: - VniDropTests # TestFlight ships the Release build; make the Archive/Profile actions explicit # so Product → Archive can never pick up a Debug configuration. profile: config: Release archive: config: Release # Direct-download build: always the Release-Direct config (Sparkle + notarization). VniDropDirect: build: targets: VniDropDirect: all run: config: Release-Direct profile: config: Release-Direct archive: config: Release-Direct