diff --git a/bitchat.xcodeproj/project.pbxproj b/bitchat.xcodeproj/project.pbxproj index 2feb9835..3b59b2f9 100644 --- a/bitchat.xcodeproj/project.pbxproj +++ b/bitchat.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 54; + objectVersion = 63; objects = { /* Begin PBXBuildFile section */ @@ -86,7 +86,7 @@ 763E0DBA9492A654FC0CDCB9 /* AppInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppInfoView.swift; sourceTree = ""; }; 7EEBDA723E1CFD88758DA4AC /* bitchat.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = bitchat.app; sourceTree = BUILT_PRODUCTS_DIR; }; 8DE9CDF66D4E52D268851048 /* MessagePaddingTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessagePaddingTests.swift; sourceTree = ""; }; - 997D512074C64904D75DDD40 /* bitchat.app */ = {isa = PBXFileReference; includeInIndex = 0; lastKnownFileType = wrapper.application; path = bitchat.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 997D512074C64904D75DDD40 /* bitchat.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = bitchat.app; sourceTree = BUILT_PRODUCTS_DIR; }; A08E03AA0C63E97C91749AEC /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; A2136C3E22D02D4A8DBE7EAB /* BinaryProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BinaryProtocol.swift; sourceTree = ""; }; AA4D7595A613F7ED3B386132 /* MessageRetryService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageRetryService.swift; sourceTree = ""; }; @@ -302,7 +302,6 @@ ); mainGroup = 18198ED912AAF495D8AF7763; minimizedProjectReferenceProxies = 1; - preferredProjectObjectVersion = 54; projectDirPath = ""; projectRoot = ""; targets = ( @@ -486,6 +485,7 @@ CODE_SIGNING_REQUIRED = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = L3N5LHJD5Y; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = bitchat/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 16.0; @@ -531,6 +531,7 @@ CODE_SIGNING_REQUIRED = YES; CODE_SIGN_IDENTITY = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = L3N5LHJD5Y; ENABLE_PREVIEWS = YES; INFOPLIST_FILE = bitchat/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 16.0; diff --git a/bitchat/Info.plist b/bitchat/Info.plist index d619eacb..6b60e722 100644 --- a/bitchat/Info.plist +++ b/bitchat/Info.plist @@ -26,7 +26,19 @@ bitchat uses Bluetooth to create a secure mesh network for chatting with nearby users. NSBluetoothPeripheralUsageDescription bitchat uses Bluetooth to discover and connect with other bitchat users nearby. - NSUserNotificationAlertStyle - alert + UIBackgroundModes + + bluetooth-central + bluetooth-peripheral + + UILaunchScreen + + UIColorName + Black + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + diff --git a/bitchat/Services/DeliveryTracker.swift b/bitchat/Services/DeliveryTracker.swift index 79f23acc..ca87e691 100644 --- a/bitchat/Services/DeliveryTracker.swift +++ b/bitchat/Services/DeliveryTracker.swift @@ -68,9 +68,6 @@ class DeliveryTracker { // MARK: - Public Methods func trackMessage(_ message: BitchatMessage, recipientID: String, recipientNickname: String, isFavorite: Bool = false, expectedRecipients: Int = 1) { - pendingLock.lock() - defer { pendingLock.unlock() } - // Don't track broadcasts or certain message types guard message.isPrivate || message.room != nil else { return } @@ -86,14 +83,17 @@ class DeliveryTracker { timeoutTimer: nil ) + // Store the delivery with lock + pendingLock.lock() pendingDeliveries[message.id] = delivery + pendingLock.unlock() // Update status to sent DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) { [weak self] in self?.updateDeliveryStatus(message.id, status: .sent) } - // Schedule timeout + // Schedule timeout (outside of lock) scheduleTimeout(for: message.id) } @@ -175,10 +175,18 @@ class DeliveryTracker { } private func scheduleTimeout(for messageID: String) { - guard let delivery = pendingDeliveries[messageID] else { return } + // Get delivery info with lock + pendingLock.lock() + guard let delivery = pendingDeliveries[messageID] else { + pendingLock.unlock() + return + } + let isFavorite = delivery.isFavorite + let isRoomMessage = delivery.isRoomMessage + pendingLock.unlock() - let timeout = delivery.isFavorite ? favoriteTimeout : - (delivery.isRoomMessage ? roomMessageTimeout : privateMessageTimeout) + let timeout = isFavorite ? favoriteTimeout : + (isRoomMessage ? roomMessageTimeout : privateMessageTimeout) let timer = Timer.scheduledTimer(withTimeInterval: timeout, repeats: false) { [weak self] _ in self?.handleTimeout(messageID: messageID) @@ -194,23 +202,33 @@ class DeliveryTracker { private func handleTimeout(messageID: String) { pendingLock.lock() - defer { pendingLock.unlock() } + guard let delivery = pendingDeliveries[messageID] else { + pendingLock.unlock() + return + } - guard let delivery = pendingDeliveries[messageID] else { return } + let shouldRetry = delivery.shouldRetry + let isRoomMessage = delivery.isRoomMessage - if delivery.shouldRetry { - // Retry for favorites + if shouldRetry { + pendingLock.unlock() + // Retry for favorites (outside of lock) retryDelivery(messageID: messageID) } else { // Mark as failed - let reason = delivery.isRoomMessage ? "No response from room members" : "Message not delivered" - updateDeliveryStatus(messageID, status: .failed(reason: reason)) + let reason = isRoomMessage ? "No response from room members" : "Message not delivered" pendingDeliveries.removeValue(forKey: messageID) + pendingLock.unlock() + updateDeliveryStatus(messageID, status: .failed(reason: reason)) } } private func retryDelivery(messageID: String) { - guard let delivery = pendingDeliveries[messageID] else { return } + pendingLock.lock() + guard let delivery = pendingDeliveries[messageID] else { + pendingLock.unlock() + return + } // Increment retry count let newDelivery = PendingDelivery( @@ -227,9 +245,11 @@ class DeliveryTracker { ) pendingDeliveries[messageID] = newDelivery + let retryCount = delivery.retryCount + pendingLock.unlock() // Exponential backoff for retry - let delay = retryDelay * pow(2, Double(delivery.retryCount)) + let delay = retryDelay * pow(2, Double(retryCount)) DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [weak self] in // Trigger resend through delegate or notification