From e72fe50ffaa1a2d796c92e458b35906203264171 Mon Sep 17 00:00:00 2001 From: Islam <2553451+qalandarov@users.noreply.github.com> Date: Thu, 11 Sep 2025 18:17:04 +0100 Subject: [PATCH] Perf: Add `final` to classes that are not inherited (#574) --- BRING_THE_NOISE.md | 6 +++--- bitchat/BitchatApp.swift | 6 +++--- bitchat/Identity/SecureIdentityStateManager.swift | 2 +- bitchat/Noise/NoiseHandshakeCoordinator.swift | 2 +- bitchat/Noise/NoiseProtocol.swift | 6 +++--- bitchat/Noise/NoiseSecurityConsiderations.swift | 4 ++-- bitchat/Noise/NoiseSession.swift | 2 +- bitchat/Nostr/NostrRelayManager.swift | 2 +- bitchat/Protocols/BitchatProtocol.swift | 2 +- bitchat/Services/AutocompleteService.swift | 2 +- bitchat/Services/CommandProcessor.swift | 2 +- bitchat/Services/FavoritesPersistenceService.swift | 2 +- bitchat/Services/KeychainManager.swift | 2 +- bitchat/Services/NoiseEncryptionService.swift | 2 +- bitchat/Services/NotificationService.swift | 2 +- bitchat/Services/PrivateChatManager.swift | 2 +- bitchat/Services/UnifiedPeerService.swift | 2 +- bitchat/Utils/SecureLogger.swift | 2 +- bitchat/ViewModels/ChatViewModel.swift | 2 +- bitchatTests/BLEServiceTests.swift | 2 +- bitchatTests/Mocks/MockBLEService.swift | 2 +- bitchatTests/TestUtilities/TestHelpers.swift | 2 +- 22 files changed, 29 insertions(+), 29 deletions(-) diff --git a/BRING_THE_NOISE.md b/BRING_THE_NOISE.md index 9c194352..dc7a96df 100644 --- a/BRING_THE_NOISE.md +++ b/BRING_THE_NOISE.md @@ -37,7 +37,7 @@ This three-message pattern provides: #### NoiseEncryptionService The main service managing all Noise operations: ```swift -class NoiseEncryptionService { +final class NoiseEncryptionService { private let staticIdentityKey: Curve25519.KeyAgreement.PrivateKey private let sessionManager: NoiseSessionManager private let channelEncryption = NoiseChannelEncryption() @@ -47,7 +47,7 @@ class NoiseEncryptionService { #### NoiseSession Individual session state for each peer: ```swift -class NoiseSession { +final class NoiseSession { private var handshakeState: NoiseHandshakeState? private var sendCipher: NoiseCipherState? private var receiveCipher: NoiseCipherState? @@ -58,7 +58,7 @@ class NoiseSession { #### NoiseSessionManager Thread-safe session management: ```swift -class NoiseSessionManager { +final class NoiseSessionManager { private var sessions: [String: NoiseSession] = [:] private let sessionsQueue = DispatchQueue(label: "noise.sessions", attributes: .concurrent) } diff --git a/bitchat/BitchatApp.swift b/bitchat/BitchatApp.swift index 2aff329a..db060dcb 100644 --- a/bitchat/BitchatApp.swift +++ b/bitchat/BitchatApp.swift @@ -163,7 +163,7 @@ struct BitchatApp: App { } #if os(iOS) -class AppDelegate: NSObject, UIApplicationDelegate { +final class AppDelegate: NSObject, UIApplicationDelegate { weak var chatViewModel: ChatViewModel? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { @@ -175,7 +175,7 @@ class AppDelegate: NSObject, UIApplicationDelegate { #if os(macOS) import AppKit -class MacAppDelegate: NSObject, NSApplicationDelegate { +final class MacAppDelegate: NSObject, NSApplicationDelegate { weak var chatViewModel: ChatViewModel? func applicationWillTerminate(_ notification: Notification) { @@ -188,7 +188,7 @@ class MacAppDelegate: NSObject, NSApplicationDelegate { } #endif -class NotificationDelegate: NSObject, UNUserNotificationCenterDelegate { +final class NotificationDelegate: NSObject, UNUserNotificationCenterDelegate { static let shared = NotificationDelegate() weak var chatViewModel: ChatViewModel? diff --git a/bitchat/Identity/SecureIdentityStateManager.swift b/bitchat/Identity/SecureIdentityStateManager.swift index dcedf5e3..fecdc793 100644 --- a/bitchat/Identity/SecureIdentityStateManager.swift +++ b/bitchat/Identity/SecureIdentityStateManager.swift @@ -96,7 +96,7 @@ import CryptoKit /// Singleton manager for secure identity state persistence and retrieval. /// Provides thread-safe access to identity mappings with encryption at rest. /// All identity data is stored encrypted in the device Keychain for security. -class SecureIdentityStateManager { +final class SecureIdentityStateManager { static let shared = SecureIdentityStateManager() private let keychain = KeychainManager.shared diff --git a/bitchat/Noise/NoiseHandshakeCoordinator.swift b/bitchat/Noise/NoiseHandshakeCoordinator.swift index b8e03b6b..0a14c273 100644 --- a/bitchat/Noise/NoiseHandshakeCoordinator.swift +++ b/bitchat/Noise/NoiseHandshakeCoordinator.swift @@ -9,7 +9,7 @@ import Foundation /// Coordinates Noise handshakes to prevent race conditions and ensure reliable encryption establishment -class NoiseHandshakeCoordinator { +final class NoiseHandshakeCoordinator { // MARK: - Handshake State diff --git a/bitchat/Noise/NoiseProtocol.swift b/bitchat/Noise/NoiseProtocol.swift index 97025787..5725e5e4 100644 --- a/bitchat/Noise/NoiseProtocol.swift +++ b/bitchat/Noise/NoiseProtocol.swift @@ -127,7 +127,7 @@ struct NoiseProtocolName { /// Handles ChaCha20-Poly1305 AEAD encryption with automatic nonce management /// and replay protection using a sliding window algorithm. /// - Warning: Nonce reuse would be catastrophic for security -class NoiseCipherState { +final class NoiseCipherState { // Constants for replay protection private static let NONCE_SIZE_BYTES = 4 private static let REPLAY_WINDOW_SIZE = 1024 @@ -384,7 +384,7 @@ class NoiseCipherState { /// Responsible for key derivation, protocol name hashing, and maintaining /// the chaining key that provides key separation between handshake messages. /// - Note: This class implements the SymmetricState object from the Noise spec -class NoiseSymmetricState { +final class NoiseSymmetricState { private var cipherState: NoiseCipherState private var chainingKey: Data private var hash: Data @@ -488,7 +488,7 @@ class NoiseSymmetricState { /// This is the main interface for establishing encrypted sessions between peers. /// Manages the handshake state machine, message patterns, and key derivation. /// - Important: Each handshake instance should only be used once -class NoiseHandshakeState { +final class NoiseHandshakeState { private let role: NoiseRole private let pattern: NoisePattern private var symmetricState: NoiseSymmetricState diff --git a/bitchat/Noise/NoiseSecurityConsiderations.swift b/bitchat/Noise/NoiseSecurityConsiderations.swift index 929d6df9..e4e0afc0 100644 --- a/bitchat/Noise/NoiseSecurityConsiderations.swift +++ b/bitchat/Noise/NoiseSecurityConsiderations.swift @@ -61,7 +61,7 @@ struct NoiseSecurityValidator { // MARK: - Enhanced Noise Session with Security -class SecureNoiseSession: NoiseSession { +final class SecureNoiseSession: NoiseSession { private(set) var messageCount: UInt64 = 0 private let sessionStartTime = Date() private(set) var lastActivityTime = Date() @@ -135,7 +135,7 @@ class SecureNoiseSession: NoiseSession { // MARK: - Rate Limiter -class NoiseRateLimiter { +final class NoiseRateLimiter { private var handshakeTimestamps: [String: [Date]] = [:] // peerID -> timestamps private var messageTimestamps: [String: [Date]] = [:] // peerID -> timestamps diff --git a/bitchat/Noise/NoiseSession.swift b/bitchat/Noise/NoiseSession.swift index 1139b861..c16b1017 100644 --- a/bitchat/Noise/NoiseSession.swift +++ b/bitchat/Noise/NoiseSession.swift @@ -250,7 +250,7 @@ class NoiseSession { // MARK: - Session Manager -class NoiseSessionManager { +final class NoiseSessionManager { private var sessions: [String: NoiseSession] = [:] private let localStaticKey: Curve25519.KeyAgreement.PrivateKey private let managerQueue = DispatchQueue(label: "chat.bitchat.noise.manager", attributes: .concurrent) diff --git a/bitchat/Nostr/NostrRelayManager.swift b/bitchat/Nostr/NostrRelayManager.swift index 42968dc1..2142428f 100644 --- a/bitchat/Nostr/NostrRelayManager.swift +++ b/bitchat/Nostr/NostrRelayManager.swift @@ -4,7 +4,7 @@ import Combine /// Manages WebSocket connections to Nostr relays @MainActor -class NostrRelayManager: ObservableObject { +final class NostrRelayManager: ObservableObject { static let shared = NostrRelayManager() // Track gift-wraps (kind 1059) we initiated so we can log OK acks at info private(set) static var pendingGiftWrapIDs = Set() diff --git a/bitchat/Protocols/BitchatProtocol.swift b/bitchat/Protocols/BitchatProtocol.swift index 758b0a61..0bc55536 100644 --- a/bitchat/Protocols/BitchatProtocol.swift +++ b/bitchat/Protocols/BitchatProtocol.swift @@ -397,7 +397,7 @@ enum DeliveryStatus: Codable, Equatable { /// Handles both broadcast messages and private encrypted messages, /// with support for mentions, replies, and delivery tracking. /// - Note: This is the primary data model for chat messages -class BitchatMessage: Codable { +final class BitchatMessage: Codable { let id: String let sender: String let content: String diff --git a/bitchat/Services/AutocompleteService.swift b/bitchat/Services/AutocompleteService.swift index 0b8752c6..6b672d53 100644 --- a/bitchat/Services/AutocompleteService.swift +++ b/bitchat/Services/AutocompleteService.swift @@ -9,7 +9,7 @@ import Foundation /// Manages autocomplete functionality for chat -class AutocompleteService { +final class AutocompleteService { private let mentionRegex = try? NSRegularExpression(pattern: "@([\\p{L}0-9_]*)$", options: []) private let commandRegex = try? NSRegularExpression(pattern: "^/([a-z]*)$", options: []) diff --git a/bitchat/Services/CommandProcessor.swift b/bitchat/Services/CommandProcessor.swift index 7a7abcce..91bbffb5 100644 --- a/bitchat/Services/CommandProcessor.swift +++ b/bitchat/Services/CommandProcessor.swift @@ -17,7 +17,7 @@ enum CommandResult { /// Processes chat commands in a focused, efficient way @MainActor -class CommandProcessor { +final class CommandProcessor { weak var chatViewModel: ChatViewModel? weak var meshService: Transport? diff --git a/bitchat/Services/FavoritesPersistenceService.swift b/bitchat/Services/FavoritesPersistenceService.swift index bca48112..3537c35e 100644 --- a/bitchat/Services/FavoritesPersistenceService.swift +++ b/bitchat/Services/FavoritesPersistenceService.swift @@ -3,7 +3,7 @@ import Combine /// Manages persistent favorite relationships between peers @MainActor -class FavoritesPersistenceService: ObservableObject { +final class FavoritesPersistenceService: ObservableObject { struct FavoriteRelationship: Codable { let peerNoisePublicKey: Data diff --git a/bitchat/Services/KeychainManager.swift b/bitchat/Services/KeychainManager.swift index 974b74dc..5d19db41 100644 --- a/bitchat/Services/KeychainManager.swift +++ b/bitchat/Services/KeychainManager.swift @@ -10,7 +10,7 @@ import Foundation import Security import os.log -class KeychainManager { +final class KeychainManager { static let shared = KeychainManager() // Use consistent service name for all keychain items diff --git a/bitchat/Services/NoiseEncryptionService.swift b/bitchat/Services/NoiseEncryptionService.swift index a484fee7..797e853f 100644 --- a/bitchat/Services/NoiseEncryptionService.swift +++ b/bitchat/Services/NoiseEncryptionService.swift @@ -135,7 +135,7 @@ enum EncryptionStatus: Equatable { /// Provides a high-level API for establishing secure channels between peers, /// handling all cryptographic operations transparently. /// - Important: This service maintains the device's cryptographic identity -class NoiseEncryptionService { +final class NoiseEncryptionService { // Static identity key (persistent across sessions) private let staticIdentityKey: Curve25519.KeyAgreement.PrivateKey public let staticIdentityPublicKey: Curve25519.KeyAgreement.PublicKey diff --git a/bitchat/Services/NotificationService.swift b/bitchat/Services/NotificationService.swift index 73ba5bfb..5f33628b 100644 --- a/bitchat/Services/NotificationService.swift +++ b/bitchat/Services/NotificationService.swift @@ -14,7 +14,7 @@ import UIKit import AppKit #endif -class NotificationService { +final class NotificationService { static let shared = NotificationService() private init() {} diff --git a/bitchat/Services/PrivateChatManager.swift b/bitchat/Services/PrivateChatManager.swift index 896e711e..41e600fb 100644 --- a/bitchat/Services/PrivateChatManager.swift +++ b/bitchat/Services/PrivateChatManager.swift @@ -10,7 +10,7 @@ import Foundation import SwiftUI /// Manages all private chat functionality -class PrivateChatManager: ObservableObject { +final class PrivateChatManager: ObservableObject { @Published var privateChats: [String: [BitchatMessage]] = [:] @Published var selectedPeer: String? = nil @Published var unreadMessages: Set = [] diff --git a/bitchat/Services/UnifiedPeerService.swift b/bitchat/Services/UnifiedPeerService.swift index 8bd3d7dd..87813d90 100644 --- a/bitchat/Services/UnifiedPeerService.swift +++ b/bitchat/Services/UnifiedPeerService.swift @@ -13,7 +13,7 @@ import CryptoKit /// Single source of truth for peer state, combining mesh connectivity and favorites @MainActor -class UnifiedPeerService: ObservableObject, TransportPeerEventsDelegate { +final class UnifiedPeerService: ObservableObject, TransportPeerEventsDelegate { // MARK: - Published Properties diff --git a/bitchat/Utils/SecureLogger.swift b/bitchat/Utils/SecureLogger.swift index acce8b74..41f0f478 100644 --- a/bitchat/Utils/SecureLogger.swift +++ b/bitchat/Utils/SecureLogger.swift @@ -11,7 +11,7 @@ import os.log /// Centralized security-aware logging framework /// Provides safe logging that filters sensitive data and security events -class SecureLogger { +final class SecureLogger { // MARK: - Log Categories diff --git a/bitchat/ViewModels/ChatViewModel.swift b/bitchat/ViewModels/ChatViewModel.swift index f6dd13c8..e774dd9a 100644 --- a/bitchat/ViewModels/ChatViewModel.swift +++ b/bitchat/ViewModels/ChatViewModel.swift @@ -90,7 +90,7 @@ import UIKit /// Manages the application state and business logic for BitChat. /// Acts as the primary coordinator between UI components and backend services, /// implementing the BitchatDelegate protocol to handle network events. -class ChatViewModel: ObservableObject, BitchatDelegate { +final class ChatViewModel: ObservableObject, BitchatDelegate { // Precompiled regexes and detectors reused across formatting private enum Regexes { static let hashtag: NSRegularExpression = { diff --git a/bitchatTests/BLEServiceTests.swift b/bitchatTests/BLEServiceTests.swift index 1e7108ce..8148c0f0 100644 --- a/bitchatTests/BLEServiceTests.swift +++ b/bitchatTests/BLEServiceTests.swift @@ -270,7 +270,7 @@ final class BLEServiceTests: XCTestCase { // MARK: - Mock Delegate Helper -private class MockBitchatDelegate: BitchatDelegate { +private final class MockBitchatDelegate: BitchatDelegate { private let messageHandler: (BitchatMessage) -> Void init(_ handler: @escaping (BitchatMessage) -> Void) { diff --git a/bitchatTests/Mocks/MockBLEService.swift b/bitchatTests/Mocks/MockBLEService.swift index dbe9674c..eccf1082 100644 --- a/bitchatTests/Mocks/MockBLEService.swift +++ b/bitchatTests/Mocks/MockBLEService.swift @@ -26,7 +26,7 @@ import CoreBluetooth /// - `autoFloodEnabled` is disabled by default; Integration tests enable it in `setUp()` to /// simulate broadcast propagation across the mesh. E2E tests keep it off and perform explicit /// relays when needed. -class MockBLEService: NSObject { +final class MockBLEService: NSObject { // Enable automatic flooding for public messages in integration tests only static var autoFloodEnabled: Bool = false diff --git a/bitchatTests/TestUtilities/TestHelpers.swift b/bitchatTests/TestUtilities/TestHelpers.swift index 41aa4e2d..deaedd24 100644 --- a/bitchatTests/TestUtilities/TestHelpers.swift +++ b/bitchatTests/TestUtilities/TestHelpers.swift @@ -10,7 +10,7 @@ import Foundation import CryptoKit @testable import bitchat -class TestHelpers { +final class TestHelpers { // MARK: - Key Generation