diff --git a/.pubnub.yml b/.pubnub.yml index 25401336..20546edb 100644 --- a/.pubnub.yml +++ b/.pubnub.yml @@ -1,9 +1,14 @@ --- name: swift scm: github.com/pubnub/swift -version: "7.3.2" +version: "7.3.3" schema: 1 changelog: + - date: 2024-09-13 + version: 7.3.3 + changes: + - type: bug + text: "Added synchronized access inside `WeakSet` and for global subscriptions." - date: 2024-07-22 version: 7.3.2 changes: @@ -564,7 +569,7 @@ sdks: - distribution-type: source distribution-repository: GitHub release package-name: PubNub - location: https://github.com/pubnub/swift/archive/refs/tags/7.3.2.zip + location: https://github.com/pubnub/swift/archive/refs/tags/7.3.3.zip supported-platforms: supported-operating-systems: macOS: diff --git a/PubNub.xcodeproj/project.pbxproj b/PubNub.xcodeproj/project.pbxproj index 6f8f3f80..01ee7ccc 100644 --- a/PubNub.xcodeproj/project.pbxproj +++ b/PubNub.xcodeproj/project.pbxproj @@ -3847,7 +3847,7 @@ "@loader_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 7.3.2; + MARKETING_VERSION = 7.3.3; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++17"; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; @@ -3898,7 +3898,7 @@ "@loader_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 7.3.2; + MARKETING_VERSION = 7.3.3; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++17"; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; @@ -4006,7 +4006,7 @@ "@loader_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 7.3.2; + MARKETING_VERSION = 7.3.3; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++17"; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; @@ -4059,7 +4059,7 @@ "@loader_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 7.3.2; + MARKETING_VERSION = 7.3.3; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++17"; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; @@ -4180,7 +4180,7 @@ "@loader_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 7.3.2; + MARKETING_VERSION = 7.3.3; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++17"; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; @@ -4232,7 +4232,7 @@ "@loader_path/Frameworks", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 7.3.2; + MARKETING_VERSION = 7.3.3; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++17"; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; @@ -4712,7 +4712,7 @@ "$(TOOLCHAIN_DIR)/usr/lib/swift/macosx", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 7.3.2; + MARKETING_VERSION = 7.3.3; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++14"; OTHER_CFLAGS = "$(inherited)"; OTHER_LDFLAGS = "$(inherited)"; @@ -4754,7 +4754,7 @@ "$(TOOLCHAIN_DIR)/usr/lib/swift/macosx", ); MACOSX_DEPLOYMENT_TARGET = 10.15; - MARKETING_VERSION = 7.3.2; + MARKETING_VERSION = 7.3.3; MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++14"; OTHER_CFLAGS = "$(inherited)"; OTHER_LDFLAGS = "$(inherited)"; diff --git a/PubNubSwift.podspec b/PubNubSwift.podspec index ad5da0dc..9b7321d6 100644 --- a/PubNubSwift.podspec +++ b/PubNubSwift.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'PubNubSwift' - s.version = '7.3.2' + s.version = '7.3.3' s.homepage = 'https://github.com/pubnub/swift' s.documentation_url = 'https://www.pubnub.com/docs/swift-native/pubnub-swift-sdk' s.authors = { 'PubNub, Inc.' => 'support@pubnub.com' } diff --git a/Sources/PubNub/Helpers/Constants.swift b/Sources/PubNub/Helpers/Constants.swift index a34ef409..93efe562 100644 --- a/Sources/PubNub/Helpers/Constants.swift +++ b/Sources/PubNub/Helpers/Constants.swift @@ -57,7 +57,7 @@ public enum Constant { static let pubnubSwiftSDKName: String = "PubNubSwift" - static let pubnubSwiftSDKVersion: String = "7.3.2" + static let pubnubSwiftSDKVersion: String = "7.3.3" static let appBundleId: String = { if let info = Bundle.main.infoDictionary, diff --git a/Sources/PubNub/Helpers/WeakBox.swift b/Sources/PubNub/Helpers/WeakBox.swift index ffba2013..21f3f120 100644 --- a/Sources/PubNub/Helpers/WeakBox.swift +++ b/Sources/PubNub/Helpers/WeakBox.swift @@ -29,43 +29,51 @@ final class WeakBox: Hashable where Element: AnyObject, Element: Hashab } struct WeakSet where Element: AnyObject, Element: Hashable { - private var elements: Set> = [] + private var elements: Atomic>> = Atomic([]) init(_ elements: [Element]) { - elements.forEach { self.elements.update(with: WeakBox($0)) } + self.elements.lockedWrite { [elements] currentValue in + elements.forEach { element in + currentValue.update(with: WeakBox(element)) + } + } } // NSSet Operations var allObjects: [Element] { - return elements.compactMap { $0.underlying } + return elements.lockedRead { $0.compactMap { $0.underlying } } } var count: Int { - return self.elements.count + elements.lockedRead { $0.count } } mutating func update(_ element: Element) { - elements.update(with: WeakBox(element)) + elements.lockedWrite { [element] in + $0.update(with: WeakBox(element)) + } } mutating func remove(_ element: Element) { - elements.remove(WeakBox(element)) + elements.lockedWrite { [element] in + $0.remove(WeakBox(element)) + } } mutating func removeAll() { - elements.removeAll() + elements.lockedWrite { $0 = Set>() } } } extension WeakSet: Collection { - var startIndex: Set>.Index { return elements.startIndex } - var endIndex: Set>.Index { return elements.endIndex } + var startIndex: Set>.Index { return elements.lockedRead { $0.startIndex } } + var endIndex: Set>.Index { return elements.lockedRead { $0.endIndex } } subscript(position: Set>.Index) -> Element? { - return elements[position].underlying + elements.lockedRead { $0[position].underlying } } func index(after index: Set>.Index) -> Set>.Index { - return elements.index(after: index) + elements.lockedRead { $0.index(after: index) } } } diff --git a/Sources/PubNub/Subscription/SubscriptionSession.swift b/Sources/PubNub/Subscription/SubscriptionSession.swift index 0b3fe32a..4f1789c3 100644 --- a/Sources/PubNub/Subscription/SubscriptionSession.swift +++ b/Sources/PubNub/Subscription/SubscriptionSession.swift @@ -64,8 +64,8 @@ class SubscriptionSession: EventEmitter, StatusEmitter { return statusListener }() - private var globalChannelSubscriptions: [String: Subscription] = [:] - private var globalGroupSubscriptions: [String: Subscription] = [:] + private var globalChannelSubscriptions: Atomic<[String: Subscription]> = Atomic([:]) + private var globalGroupSubscriptions: Atomic<[String: Subscription]> = Atomic([:]) private let strategy: any SubscriptionSessionStrategy init( @@ -125,16 +125,29 @@ class SubscriptionSession: EventEmitter, StatusEmitter { and: channelGroupSubscriptions, at: cursor?.timetoken ) - for subscription in channelSubscriptions { - subscription.subscriptionNames.compactMap { $0 }.forEach { - globalChannelSubscriptions[$0] = subscription + + let channelSubsToMerge = channelSubscriptions.reduce( + into: [String: Subscription]() + ) { accumulatedValue, subscription in + subscription.subscriptionNames.forEach { + accumulatedValue[$0] = subscription } } - for subscription in channelGroupSubscriptions { - subscription.subscriptionNames.compactMap { $0 }.forEach { - globalGroupSubscriptions[$0] = subscription + + let channelGroupSubsToMerge = channelGroupSubscriptions.reduce( + into: [String: Subscription]() + ) { accumulatedValue, subscription in + subscription.subscriptionNames.forEach { + accumulatedValue[$0] = subscription } } + + globalChannelSubscriptions.lockedWrite { + $0.merge(channelSubsToMerge) { _, new in new } + } + globalGroupSubscriptions.lockedWrite { + $0.merge(channelGroupSubsToMerge) { _, new in new } + } } // MARK: - Reconnect @@ -163,15 +176,25 @@ class SubscriptionSession: EventEmitter, StatusEmitter { presenceOnly ? [$0.presenceChannelName] : [$0, $0.presenceChannelName] } internalUnsubscribe( - from: globalChannelSubscriptions.compactMap { channelNamesToUnsubscribe.contains($0.key) ? $0.value : nil }, - and: globalGroupSubscriptions.compactMap { groupNamesToUnsubscribe.contains($0.key) ? $0.value : nil }, + from: globalChannelSubscriptions.lockedRead { $0.compactMap { + channelNamesToUnsubscribe.contains($0.key) ? $0.value : nil + } }, + and: globalGroupSubscriptions.lockedRead { $0.compactMap { + groupNamesToUnsubscribe.contains($0.key) ? $0.value : nil + } }, presenceOnly: presenceOnly ) - channelNamesToUnsubscribe.forEach { - globalChannelSubscriptions.removeValue(forKey: $0) + + globalChannelSubscriptions.lockedWrite { currentContainer in + channelNamesToUnsubscribe.forEach { + currentContainer.removeValue(forKey: $0) + } } - groupNamesToUnsubscribe.forEach { - globalGroupSubscriptions.removeValue(forKey: $0) + + globalGroupSubscriptions.lockedWrite { currentContainer in + groupNamesToUnsubscribe.forEach { + currentContainer.removeValue(forKey: $0) + } } }