Add a second macOS shipping channel alongside the App Store build:
- New VniDropDirect target (Release-Direct config) sharing VniDrop's sources via
an AppBase target template; links Sparkle behind the DIRECT_DISTRIBUTION flag so
the App Store binary never bundles a self-updater. arm64-only (core is arm64).
- Sparkle updater (SparkleUpdater.swift) + "Check for Updates" menu, compiled only
under DIRECT_DISTRIBUTION; Info.plist SUFeedURL points at the GitHub Release
/latest/download/appcast.xml, non-sandboxed entitlements for Developer ID.
- build-dmg.sh (archive → Developer ID export → DMG → sign → notarize → staple),
generate-appcast.sh, and ExportOptions-DeveloperID.plist.
- apple-release.yml: on tag v*.*.*, build/notarize the DMG, publish the GitHub
Release with appcast, and push the Homebrew cask to sudosylabs/homebrew-vnidrop.
apple.yml gains a PR compile-check of the direct target.
- CFBundleVersion is stamped at build time as a UTC YYMMDD.HHMM timestamp for both
channels, replacing the hand-maintained build number.
- Docs (RELEASE-MACOS.md, README), cask template + tap README, localized
updates_check string, Makefile targets, gitignore for dist/ artifacts.
The VniDrop target's SwiftLint pre-build phase is required (fails if missing), so
the Apple CI job must have SwiftLint available. Add a brew install step.
Localizable.xcstrings and Generated/L10n.swift are generated from
localization/strings.json — the single source of truth — yet were committed,
which caused redundant tracking and a spurious ~10k-line diff every time
Xcode reformatted the catalog on build.
Treat them like the (already gitignored) Rust bindings: generate at build
time instead of tracking them. gitignore both; make the `apple-project`
target depend on `localization` so `bun run generate` recreates them before
xcodegen; install Bun in the Apple CI job and trigger it on localization/**.
Android strings.xml stays tracked — it has no reformatting churn and its
build doesn't run the generator.
- Introduce a CoreGateway protocol so feature models depend on a seam
(CoreRepository conforms); enables faking the core in tests
- Add a VniDropTests target with 42 tests mirroring the KMP suites:
approval coordinator, send/receive/settings/app models, preferences,
file previews, invitation decode, message queue, error mapping
- Add a fake gateway/file-system/device-info and fixtures
- Add .github/workflows/apple.yml: build the Rust core, generate the
project, and run the tests on an iOS Simulator