Fix crash from duplicate peer IDs in Dictionary initialization

- Added deduplication logic in ChatViewModel to handle duplicate peer IDs gracefully
- Enhanced PeerManager to prevent duplicate peers by tracking both nicknames and IDs
- Added final safety check to ensure no duplicates in peers array
- This fixes crash when Dictionary(uniqueKeysWithValues:) encounters duplicate keys
This commit is contained in:
jack
2025-08-01 19:04:36 +02:00
parent 1d698c2006
commit d605a0b6db
2 changed files with 38 additions and 2 deletions
+26 -1
View File
@@ -143,6 +143,7 @@ class PeerManager: ObservableObject {
// Build peer list // Build peer list
var allPeers: [BitchatPeer] = [] var allPeers: [BitchatPeer] = []
var connectedNicknames: Set<String> = [] var connectedNicknames: Set<String> = []
var addedPeerIDs: Set<String> = []
// Add connected mesh peers (only if actually connected or relay connected) // Add connected mesh peers (only if actually connected or relay connected)
for (peerID, nickname) in meshPeers { for (peerID, nickname) in meshPeers {
@@ -180,6 +181,9 @@ class PeerManager: ObservableObject {
connectedNicknames.insert(nickname) connectedNicknames.insert(nickname)
} }
// Track that we've added this peer ID
addedPeerIDs.insert(peerID)
var peer = BitchatPeer( var peer = BitchatPeer(
id: peerID, id: peerID,
noisePublicKey: noiseKey, noisePublicKey: noiseKey,
@@ -221,6 +225,13 @@ class PeerManager: ObservableObject {
continue 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) // Only add peers that WE favorite (not just ones who favorite us)
if !favorite.isFavorite { if !favorite.isFavorite {
SecureLogger.log(" - Skipping '\(favorite.peerNickname)' - we don't favorite them (they favorite us: \(favorite.theyFavoritedUs))", 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 // Set favorite status
peer.favoriteStatus = favorite peer.favoriteStatus = favorite
peer.nostrPublicKey = favorite.peerNostrPublicKey peer.nostrPublicKey = favorite.peerNostrPublicKey
addedPeerIDs.insert(favoriteID) // Track that we've added this ID
allPeers.append(peer) 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<String> = []
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.favorites = favorites
self.mutualFavorites = mutualFavorites self.mutualFavorites = mutualFavorites
+12 -1
View File
@@ -220,7 +220,18 @@ class ChatViewModel: ObservableObject, BitchatDelegate {
// Update peers directly // Update peers directly
self?.allPeers = peers self?.allPeers = peers
// Update peer index for O(1) lookups // 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 // Schedule UI update if peers changed
if peers.count > 0 || self?.allPeers.count ?? 0 > 0 { if peers.count > 0 || self?.allPeers.count ?? 0 > 0 {
// UI will update automatically // UI will update automatically