diff --git a/bitchat/Models/BitchatPeer.swift b/bitchat/Models/BitchatPeer.swift index b3ee9f46..8de8271e 100644 --- a/bitchat/Models/BitchatPeer.swift +++ b/bitchat/Models/BitchatPeer.swift @@ -143,6 +143,7 @@ class PeerManager: ObservableObject { // Build peer list var allPeers: [BitchatPeer] = [] var connectedNicknames: Set = [] + var addedPeerIDs: Set = [] // Add connected mesh peers (only if actually connected or relay connected) for (peerID, nickname) in meshPeers { @@ -180,6 +181,9 @@ class PeerManager: ObservableObject { connectedNicknames.insert(nickname) } + // Track that we've added this peer ID + addedPeerIDs.insert(peerID) + var peer = BitchatPeer( id: peerID, noisePublicKey: noiseKey, @@ -221,6 +225,13 @@ class PeerManager: ObservableObject { continue } + // Skip if we already added a peer with this ID (prevents duplicates) + if addedPeerIDs.contains(favoriteID) { + SecureLogger.log(" - Skipping '\(favorite.peerNickname)' - peer ID already added", + category: SecureLogger.session, level: .debug) + continue + } + // Only add peers that WE favorite (not just ones who favorite us) if !favorite.isFavorite { SecureLogger.log(" - Skipping '\(favorite.peerNickname)' - we don't favorite them (they favorite us: \(favorite.theyFavoritedUs))", @@ -241,6 +252,7 @@ class PeerManager: ObservableObject { // Set favorite status peer.favoriteStatus = favorite peer.nostrPublicKey = favorite.peerNostrPublicKey + addedPeerIDs.insert(favoriteID) // Track that we've added this ID allPeers.append(peer) } @@ -287,7 +299,20 @@ class PeerManager: ObservableObject { } } - self.peers = allPeers + // Final safety check: ensure no duplicate IDs + var finalPeers: [BitchatPeer] = [] + var seenIDs: Set = [] + for peer in allPeers { + if !seenIDs.contains(peer.id) { + seenIDs.insert(peer.id) + finalPeers.append(peer) + } else { + SecureLogger.log("⚠️ Removing duplicate peer ID in final check: \(peer.id) (\(peer.displayName))", + category: SecureLogger.session, level: .warning) + } + } + + self.peers = finalPeers self.favorites = favorites self.mutualFavorites = mutualFavorites diff --git a/bitchat/ViewModels/ChatViewModel.swift b/bitchat/ViewModels/ChatViewModel.swift index 5f113df6..4aafcf43 100644 --- a/bitchat/ViewModels/ChatViewModel.swift +++ b/bitchat/ViewModels/ChatViewModel.swift @@ -220,7 +220,18 @@ class ChatViewModel: ObservableObject, BitchatDelegate { // Update peers directly self?.allPeers = peers // Update peer index for O(1) lookups - self?.peerIndex = Dictionary(uniqueKeysWithValues: peers.map { ($0.id, $0) }) + // Deduplicate peers by ID to prevent crash from duplicate keys + var uniquePeers: [String: PeerData] = [:] + for peer in peers { + // Keep the first occurrence of each peer ID + if uniquePeers[peer.id] == nil { + uniquePeers[peer.id] = peer + } else { + SecureLogger.log("⚠️ Duplicate peer ID detected: \(peer.id) (\(peer.displayName))", + category: SecureLogger.session, level: .warning) + } + } + self?.peerIndex = uniquePeers // Schedule UI update if peers changed if peers.count > 0 || self?.allPeers.count ?? 0 > 0 { // UI will update automatically