From e57bfbc5253183b9f9ed093b7c40ed11e7ea4349 Mon Sep 17 00:00:00 2001 From: Taksh Date: Mon, 27 Jul 2026 10:19:54 +0300 Subject: [PATCH] fix: add setting to disable BLE wake-on-proximity Lets people opt out of background pending connects that surface as the iOS 26 "accessory would like to open bitchat" prompt. Closes #1427 --- .../BLE/BLEProximityWakeSettings.swift | 26 +++++++++++++++++++ bitchat/Views/AppInfoView.swift | 21 ++++++++++++++- 2 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 bitchat/Services/BLE/BLEProximityWakeSettings.swift diff --git a/bitchat/Services/BLE/BLEProximityWakeSettings.swift b/bitchat/Services/BLE/BLEProximityWakeSettings.swift new file mode 100644 index 00000000..95f40ca8 --- /dev/null +++ b/bitchat/Services/BLE/BLEProximityWakeSettings.swift @@ -0,0 +1,26 @@ +// +// BLEProximityWakeSettings.swift +// bitchat +// +// This is free and unencumbered software released into the public domain. +// For more information, see +// + +import Foundation + +/// Controls background wake-on-proximity: pending BLE connects that let iOS +/// relaunch the app when a recent peer returns into range (#1396). +/// +/// On iOS 26 that relaunch surfaces as an "accessory would like to open +/// bitchat" consent sheet (#1427). Default remains on so the mesh stays +/// reachable while backgrounded; people who prefer not to see the prompt +/// can turn it off. +enum BLEProximityWakeSettings { + private static let enabledKey = "ble.proximityWakeEnabled" + + /// When false, bitchat will not arm pending background connects. + static var enabled: Bool { + get { UserDefaults.standard.object(forKey: enabledKey) as? Bool ?? true } + set { UserDefaults.standard.set(newValue, forKey: enabledKey) } + } +} diff --git a/bitchat/Views/AppInfoView.swift b/bitchat/Views/AppInfoView.swift index cb99fbac..04545cad 100644 --- a/bitchat/Views/AppInfoView.swift +++ b/bitchat/Views/AppInfoView.swift @@ -21,6 +21,7 @@ struct AppInfoView: View { @State private var showTopology = false @State private var liveVoiceEnabled = PTTSettings.liveVoiceEnabled @State private var locationNotesEnabled = LocationNotesSettings.enabled + @State private var proximityWakeEnabled = BLEProximityWakeSettings.enabled @State private var hideMessagePreviews = NotificationPrivacySettings.hideMessagePreviews @State private var customRelays = NostrRelaySettings.customRelays() @State private var relayInput = "" @@ -118,6 +119,9 @@ struct AppInfoView: View { static let hidePreviewsTitle = String(localized: "app_info.settings.hide_previews.title", defaultValue: "hide message previews", comment: "Title of the setting that keeps message text, sender names, and geohashes out of lock-screen notifications") static let hidePreviewsSubtitle = String(localized: "app_info.settings.hide_previews.subtitle", defaultValue: "notifications say that something arrived without showing the message, who sent it, or which location channel it came from. anyone holding your locked phone learns nothing from the lock screen. on by default.", comment: "Subtitle explaining what hiding notification message previews does") + static let proximityWakeTitle = String(localized: "app_info.settings.proximity_wake.title", defaultValue: "wake when nearby peers return", comment: "Title of the setting that lets Bluetooth relaunch bitchat when a recent peer comes back into range") + static let proximityWakeSubtitle = String(localized: "app_info.settings.proximity_wake.subtitle", defaultValue: "while bitchat is backgrounded, ask the system to reconnect to recent mesh peers so a nearby phone can wake the app. on ios 26 that wake can show an \"accessory would like to open bitchat\" prompt — safe to allow, or turn this off if you'd rather not see it. on by default so the mesh stays reachable.", comment: "Subtitle explaining wake-on-proximity and the ios 26 accessory open prompt") + static let dangerTitle = String(localized: "app_info.settings.danger.title", defaultValue: "DANGER ZONE", comment: "Section header (uppercase) for destructive actions in settings") static let panicButton = String(localized: "app_info.settings.danger.panic_button", defaultValue: "panic wipe", comment: "Button in the settings danger zone that erases all local data after confirmation") static let panicNote = String(localized: "app_info.settings.danger.panic_note", defaultValue: "erases all messages, keys, and identity. triple-tapping the bitchat/ logo does the same, instantly.", comment: "Caption under the panic wipe button explaining what it does and the triple-tap shortcut") @@ -524,7 +528,8 @@ struct AppInfoView: View { } // Privacy: what a locked, seized, or borrowed phone gives away - // without being unlocked. + // without being unlocked — and whether background Bluetooth may + // relaunch the app when a peer returns (#1427). VStack(alignment: .leading, spacing: 12) { SectionHeader(verbatim: Strings.Settings.privacyTitle) @@ -541,6 +546,20 @@ struct AppInfoView: View { ) ) } + + settingsCard { + settingToggle( + title: Text(verbatim: Strings.Settings.proximityWakeTitle), + subtitle: Text(verbatim: Strings.Settings.proximityWakeSubtitle), + isOn: Binding( + get: { proximityWakeEnabled }, + set: { newValue in + proximityWakeEnabled = newValue + BLEProximityWakeSettings.enabled = newValue + } + ) + ) + } } // Danger zone