Files
obsidian-vault/work/projects/vscode-migration/findings.md
T

3.5 KiB

VSCode Migration — Technical Findings

Back to projects/vscode-migration/vscode-migration. Verified on Xcode 26.6 / Swift 6.3.3, Sep 2026.

App target readiness

  • DMG and App Store targets compile identical sources (1398 files); FEATURE_FLAGS is empty for the app — only DEBUG remains (SPM handles it).
  • No ObjC/C in macOS/DuckDuckGo/, empty bridging header.
  • Unit Tests compile only 2 app files directly; the rest is @testable import → can switch to the library.
  • 46 app files are shared with helper targets (VPN 36, DBP agent 18, NetP extensions 14 each): UserText.swift, UserDefaultsWrapper, ConfigurationManager, Subscription*, TipKit*, VPN*, DuckDuckGoUserAgent, …
  • Remaining Xcode-only resources: storyboards PasswordManager, BookmarksBar, NavigationBar, Feedback + BookmarksBarCollectionViewItem.xib; 8 .xcdatamodeld in the app, 3 in BSK.

Asset catalogs

  • Xcode generates asset symbols with actool --bundle-identifier … --generate-swift-asset-symbols X.swift. In that mode actool writes no Assets.car, so compiling is a second invocation (same as Xcode's GenerateAssetSymbols + CompileAssetCatalog).
  • For package targets Xcode and swiftbuild generate ImageResource/ColorResource symbols but not NSImage.foo / NSColor.foo extensions. The app target uses extensions (e.g. NSImage.menuVertical16) → those call sites must change when moving into a package.
  • Native swift build (also what SourceKit-LSP uses) neither compiles catalogs nor generates symbols.
  • Native SwiftPM resource bundles are flat and lack Info.plist; CoreUI ignores Assets.car without CFBundleIdentifier.
  • In Xcode's plugin sandbox actool fails for iOS ("No available simulator runtimes"); macOS works. So a plugin can't replace Xcode's processing on iOS.
  • Plugin environment is identical for native and swiftbuild → can't tell them apart; Xcode is detectable by the /BuildToolPluginIntermediates/ work dir.
  • Asset usage in the app catalog: ~415 via symbols, ~57 via string names, 20 only from storyboards, ~300 apparently unreferenced (heuristic).

Extensions in VSCode tasks

  • tasks.json dependsOn has no up-to-date check → needs a gate script (input hash via git ls-files -s + diff, stamp file).
  • xcodebuild always visits explicit target dependencies (planning, Provisional Inputs) even when nothing changed; no flag to skip. Gating extensions only pays off once the app is built with swift build and assembled by a script.
  • Assemble: copy pre-signed extensions, sign the outer app last without --deep; same Team ID; embed provisioning profiles for NetworkExtension entitlements; system extension activates only from /Applications.
  • Embedded: Login Items DuckDuckGo VPN.app, DuckDuckGo Personal Information Removal.app; PlugIns NetworkProtectionAppExtension.appex, VPNProxyExtension.appex.

Toolchain

  • ~/.swiftly/bin/swift (6.2) is first in PATH; it can't compile packages importing WebKit against the macOS 26 SDK. Use xcrun swift, and set swift.path in VSCode to Xcode's toolchain.
  • Native builds rewrite stale Package.resolved files (e.g. SharedPackages/Onboarding) — don't commit those.

Pre-existing issues found

  • UIComponentsTests: macOS 15 API without @available (package min 12.3).
  • PerformanceTestTests: reference removed types (CoreWebVitalsAssessment, averageResourceSize).
  • DataBrokerProtectionCore: empty DBP.xcassets inside .copy("BundleResources") — dead, never compiled.