Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 35 additions & 7 deletions clients/ios/App/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ class AppDelegate: NSObject, UIApplicationDelegate {
// the new approval flow. Runs once; the flag persists across future launches.
migrateToPairingV4IfNeeded()

// Separate migration: clear stale override values when the old toggle was OFF.
// This runs independently of the v4 migration so users who already completed
// v4 migration still get the override cleanup.
migratePairingOverridesIfNeeded()
Comment thread
ashleeradka marked this conversation as resolved.

// Initial connect is handled by SceneDelegate.sceneWillEnterForeground, which fires
// during launch and on every background→foreground transition. Calling connect() here
// too would race with the scene's connect() since isConnected is false while in-flight.
Expand Down Expand Up @@ -181,13 +186,6 @@ class AppDelegate: NSObject, UIApplicationDelegate {
defaults.removeObject(forKey: "devLocalPairingEnabled")
defaults.removeObject(forKey: "iosPairingUseOverride")
Comment thread
ashleeradka marked this conversation as resolved.

// Clear stale override values — the isOverrideEnabled gate was removed
// in M9, so any non-empty override now applies unconditionally. Users
// who had values typed in but the toggle OFF would have those stale
// values silently activate on upgrade.
defaults.removeObject(forKey: PairingConfiguration.gatewayOverrideKey)
defaults.removeObject(forKey: PairingConfiguration.tokenOverrideKey)

// Mark migration as done
defaults.set(true, forKey: Self.pairingV4MigrationKey)

Expand All @@ -197,6 +195,36 @@ class AppDelegate: NSObject, UIApplicationDelegate {
log.info("v4 pairing migration complete — legacy pairing state cleared")
}

// MARK: - Pairing Override Migration

/// Key that tracks whether the pairing override migration has run.
private static let pairingOverrideMigrationKey = "pairing_override_migration_done"

/// Clears stale gateway/token override values when the old toggle was OFF.
/// Runs once; the flag persists across future launches.
///
/// This is separate from the v4 migration so users who already completed
/// v4 migration still get the override cleanup.
private func migratePairingOverridesIfNeeded() {
let defaults = UserDefaults.standard
guard !defaults.bool(forKey: Self.pairingOverrideMigrationKey) else { return }

// Only clean up when the legacy toggle key is actually present.
// After M9 the toggle is no longer persisted, so absence means
// the user may have intentionally set overrides post-M9.
if defaults.object(forKey: "iosPairingUseOverride") != nil {
let overrideWasEnabled = defaults.bool(forKey: "iosPairingUseOverride")
if !overrideWasEnabled {
defaults.removeObject(forKey: PairingConfiguration.gatewayOverrideKey)
defaults.removeObject(forKey: PairingConfiguration.tokenOverrideKey)
}
// Clean up the legacy toggle key itself — no longer used.
defaults.removeObject(forKey: "iosPairingUseOverride")
}

defaults.set(true, forKey: Self.pairingOverrideMigrationKey)
}

func application(
_ application: UIApplication,
configurationForConnecting connectingSceneSession: UISceneSession,
Expand Down
29 changes: 17 additions & 12 deletions clients/macos/vellum-assistant/App/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -600,24 +600,29 @@ public final class AppDelegate: NSObject, NSApplicationDelegate {
/// Key that tracks whether the pairing override migration has run.
private static let pairingOverrideMigrationKey = "pairing_override_migration_done"

/// Clears stale gateway/token override values when the old toggle was OFF
/// (or absent). Runs once; the flag persists across future launches.
/// Clears stale gateway/token override values when the old toggle was OFF.
/// Runs once; the flag persists across future launches.
///
/// Only acts when the legacy `iosPairingUseOverride` key is actually present.
/// After M9 the toggle is no longer persisted, so absence means the user
/// may have intentionally set overrides post-M9 — skip cleanup to preserve them.
private func migratePairingOverridesIfNeeded() {
let defaults = UserDefaults.standard
guard !defaults.bool(forKey: Self.pairingOverrideMigrationKey) else { return }

// If the legacy toggle was off (false or absent), the user did not
// intend for overrides to be active. Clear them so they don't
// silently take effect now that the gate is removed.
let overrideWasEnabled = defaults.bool(forKey: "iosPairingUseOverride")
if !overrideWasEnabled {
defaults.removeObject(forKey: PairingConfiguration.gatewayOverrideKey)
defaults.removeObject(forKey: PairingConfiguration.tokenOverrideKey)
// Only clean up when the legacy toggle key is actually present.
// After M9 the toggle is no longer persisted, so absence means
// the user may have intentionally set overrides post-M9.
if defaults.object(forKey: "iosPairingUseOverride") != nil {
let overrideWasEnabled = defaults.bool(forKey: "iosPairingUseOverride")
if !overrideWasEnabled {
defaults.removeObject(forKey: PairingConfiguration.gatewayOverrideKey)
defaults.removeObject(forKey: PairingConfiguration.tokenOverrideKey)
}
// Clean up the legacy toggle key itself — no longer used.
defaults.removeObject(forKey: "iosPairingUseOverride")
}

// Clean up the legacy toggle key itself — no longer used.
defaults.removeObject(forKey: "iosPairingUseOverride")

defaults.set(true, forKey: Self.pairingOverrideMigrationKey)
}

Expand Down
Loading