diff --git a/bitchat.xcodeproj/project.pbxproj b/bitchat.xcodeproj/project.pbxproj index c5f1e7dc..5eaf6abc 100644 --- a/bitchat.xcodeproj/project.pbxproj +++ b/bitchat.xcodeproj/project.pbxproj @@ -255,6 +255,8 @@ DEVELOPMENT_TEAM = L3N5LHJD5Y; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = bitchat/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = bitchat; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking"; IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -264,9 +266,12 @@ PRODUCT_BUNDLE_IDENTIFIER = chat.bitchat; PRODUCT_NAME = bitchat; SDKROOT = iphoneos; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Debug; }; @@ -282,6 +287,8 @@ DEVELOPMENT_TEAM = L3N5LHJD5Y; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = bitchat/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = bitchat; + INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.social-networking"; IPHONEOS_DEPLOYMENT_TARGET = 16.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", @@ -291,9 +298,12 @@ PRODUCT_BUNDLE_IDENTIFIER = chat.bitchat; PRODUCT_NAME = bitchat; SDKROOT = iphoneos; - SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = YES; + SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; + SUPPORTS_MACCATALYST = NO; + SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; + SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO; SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; + TARGETED_DEVICE_FAMILY = 1; }; name = Release; }; diff --git a/bitchat/Info.plist b/bitchat/Info.plist index e33081c5..4943de82 100644 --- a/bitchat/Info.plist +++ b/bitchat/Info.plist @@ -39,9 +39,13 @@ UISupportedInterfaceOrientations UIInterfaceOrientationPortrait - UIInterfaceOrientationPortraitUpsideDown + + UISupportedInterfaceOrientations~ipad + UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown diff --git a/bitchat/Services/BluetoothMeshService.swift b/bitchat/Services/BluetoothMeshService.swift index 174359c8..9eab05bf 100644 --- a/bitchat/Services/BluetoothMeshService.swift +++ b/bitchat/Services/BluetoothMeshService.swift @@ -54,6 +54,7 @@ class BluetoothMeshService: NSObject { private var announcedToPeers = Set() // Track which peers we've announced to private var announcedPeers = Set() // Track peers who have already been announced private var processedKeyExchanges = Set() // Track processed key exchanges to prevent duplicates + private var intentionalDisconnects = Set() // Track peripherals we're disconnecting intentionally // Store-and-forward message cache private struct StoredMessage { @@ -1322,6 +1323,16 @@ class BluetoothMeshService: NSObject { if senderID != "unknown" && senderID != myPeerID { // Check if we need to update peripheral mapping from the specific peripheral that sent this if let peripheral = peripheral { + // Check if we already have a different peripheral connected for this peer + if let existingPeripheral = self.connectedPeripherals[senderID], + existingPeripheral != peripheral { + // We have a duplicate connection - disconnect the newer one + // print("[DEBUG] Duplicate connection detected for \(senderID), keeping existing") + intentionalDisconnects.insert(peripheral.identifier.uuidString) + centralManager.cancelPeripheralConnection(peripheral) + return + } + // Find if this peripheral is currently mapped with a temp ID if let tempID = self.connectedPeripherals.first(where: { $0.value == peripheral })?.key, tempID.count > 8 { // It's a temp ID @@ -1748,6 +1759,8 @@ extension BluetoothMeshService: CBCentralManagerDelegate { let tempID = peripheral.identifier.uuidString connectedPeripherals[tempID] = peripheral + // Don't show connected message yet - wait for key exchange + // This prevents the connect/disconnect/connect pattern // Request RSSI reading peripheral.readRSSI() @@ -1762,6 +1775,13 @@ extension BluetoothMeshService: CBCentralManagerDelegate { func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) { let peripheralID = peripheral.identifier.uuidString + // Check if this was an intentional disconnect + if intentionalDisconnects.contains(peripheralID) { + intentionalDisconnects.remove(peripheralID) + // Don't process this disconnect further + return + } + // Implement exponential backoff for failed connections if error != nil { let attempts = connectionAttempts[peripheralID] ?? 0 diff --git a/connection_fixes.md b/connection_fixes.md new file mode 100644 index 00000000..27753399 --- /dev/null +++ b/connection_fixes.md @@ -0,0 +1,41 @@ +# Connection Stability Fixes for BluetoothMeshService + +## Issues Causing Disconnect/Reconnect + +1. **Simultaneous Connection Attempts**: Both devices acting as central and peripheral can create duplicate connections +2. **Temporary ID Management**: Using peripheral UUIDs as temporary IDs causes confusion +3. **Premature State Updates**: Peer list updates happen before connections are fully established +4. **Aggressive Disconnect Handling**: Peers are removed immediately on disconnect without grace period + +## Recommended Fixes + +### 1. Implement Connection Deduplication +- Track ongoing connection attempts by peer ID +- Reject duplicate connections from same peer +- Use deterministic logic to decide which side maintains connection (e.g., compare peer IDs) + +### 2. Improve Peer State Management +- Add connection states: CONNECTING, CONNECTED, DISCONNECTING +- Only show "joined" after key exchange AND announce received +- Add grace period before showing "left" messages (e.g., 5 seconds) + +### 3. Fix Temporary ID Handling +- Don't add temp IDs to activePeers +- Only update peer lists after receiving real peer ID +- Use a separate pendingPeripherals dictionary + +### 4. Consolidate Key Exchange and Announce +- Send key exchange and announce as atomic operation +- Wait for both before considering peer "connected" +- Add sequence numbers to prevent duplicate processing + +### 5. Add Connection Stability Timer +- Don't show "joined" until connection stable for 1 second +- Buffer rapid connect/disconnect cycles +- Implement exponential backoff for reconnection attempts + +## Implementation Priority +1. Fix duplicate connection handling (highest impact) +2. Add connection grace period +3. Improve state management +4. Consolidate initialization messages \ No newline at end of file diff --git a/project.yml b/project.yml index b24b1cc5..d5dcdade 100644 --- a/project.yml +++ b/project.yml @@ -34,11 +34,8 @@ targets: UIColorName: Black UISupportedInterfaceOrientations: - UIInterfaceOrientationPortrait - - UIInterfaceOrientationPortraitUpsideDown - - UIInterfaceOrientationLandscapeLeft - - UIInterfaceOrientationLandscapeRight settings: - PRODUCT_BUNDLE_IDENTIFIER: com.bitchat.app + PRODUCT_BUNDLE_IDENTIFIER: chat.bitchat INFOPLIST_FILE: bitchat/Info.plist ENABLE_PREVIEWS: YES SWIFT_VERSION: 5.0 @@ -49,4 +46,4 @@ targets: CODE_SIGNING_REQUIRED: YES CODE_SIGNING_ALLOWED: YES ASSETCATALOG_COMPILER_APPICON_NAME: AppIcon - ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS: YES \ No newline at end of file + ASSETCATALOG_COMPILER_INCLUDE_ALL_APPICON_ASSETS: YES