Skip to content

Commit c525ad5

Browse files
authored
PubNub Swift Chat SDK 0.30.0 release (#26)
fix(files): fix the issue with the invalid download URL for files feat(chat): deprecate `getUnreadMessagesCount(limit:page:filter:sort:)` method in Chat interface feat(chat): introduce `fetchUnreadMessagesCounts(limit:page:filter:sort:)` method in Chat interface refactor(chat): remove code duplication from the `ChatImpl` constructors refactor(chat): add thread safety to chat mapping logic
1 parent 0c4e0fe commit c525ad5

File tree

15 files changed

+365
-50
lines changed

15 files changed

+365
-50
lines changed

.github/workflows/run-tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ jobs:
3232
strategy:
3333
matrix:
3434
environment: [iOS]
35-
timeout-minutes: 21
35+
timeout-minutes: 22
3636
steps:
3737
- name: Checkout repository
3838
uses: actions/checkout@v4

.pubnub.yml

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,21 @@
11
name: swift-chat-sdk
22
scm: github.com/pubnub/swift-chat-sdk
3-
version: 0.20.0
3+
version: 0.30.0
44
schema: 1
55
changelog:
6+
- date: 2025-05-14
7+
version: 0.30.0
8+
changes:
9+
- type: feature
10+
text: "Deprecate `getUnreadMessagesCount(limit:page:filter:sort:)` method in Chat interface."
11+
- type: feature
12+
text: "Introduce `fetchUnreadMessagesCounts(limit:page:filter:sort:)` method in Chat interface."
13+
- type: bug
14+
text: "Fix the issue with the invalid download URL for files."
15+
- type: improvement
16+
text: "Remove code duplication from the `ChatImpl` constructors."
17+
- type: improvement
18+
text: "Add thread safety to chat mapping logic."
619
- date: 2025-03-25
720
version: 0.20.0
821
changes:
@@ -150,7 +163,7 @@ sdks:
150163
- distribution-type: source
151164
distribution-repository: GitHub release
152165
package-name: PubNubSwiftChatSDK
153-
location: https://github.com/pubnub/swift-chat-sdk/archive/refs/tags/0.20.0.zip
166+
location: https://github.com/pubnub/swift-chat-sdk/archive/refs/tags/0.30.0.zip
154167
supported-platforms:
155168
supported-operating-systems:
156169
iOS:

Package.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ let package = Package(
1818
)
1919
],
2020
dependencies: [
21-
.package(url: "https://github.com/pubnub/kmp-chat", exact: "0.12.1-swift"),
22-
.package(url: "https://github.com/pubnub/swift", exact: "9.0.1")
21+
.package(url: "https://github.com/pubnub/kmp-chat", exact: "0.13.1-swift"),
22+
.package(url: "https://github.com/pubnub/swift", exact: "9.1.0")
2323
],
2424
targets: [
2525
// Targets are the basic building blocks of a package, defining a module or a test suite.

PubNubSwiftChatSDK.xcodeproj/project.pbxproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -951,7 +951,7 @@
951951
"@loader_path/Frameworks",
952952
);
953953
MACOSX_DEPLOYMENT_TARGET = 11.0;
954-
MARKETING_VERSION = 0.20.0;
954+
MARKETING_VERSION = 0.30.0;
955955
MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
956956
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20";
957957
OTHER_SWIFT_FLAGS = "$(inherited) -D COCOAPODS";
@@ -1000,7 +1000,7 @@
10001000
"@loader_path/Frameworks",
10011001
);
10021002
MACOSX_DEPLOYMENT_TARGET = 11.0;
1003-
MARKETING_VERSION = 0.20.0;
1003+
MARKETING_VERSION = 0.30.0;
10041004
MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++";
10051005
MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu17 gnu++20";
10061006
OTHER_SWIFT_FLAGS = "$(inherited) -D COCOAPODS";
@@ -1113,15 +1113,15 @@
11131113
repositoryURL = "https://github.com/pubnub/kmp-chat";
11141114
requirement = {
11151115
kind = exactVersion;
1116-
version = "0.12.1-swift";
1116+
version = "0.13.1-swift";
11171117
};
11181118
};
11191119
3DCF7DFA2CD0FFCC00889326 /* XCRemoteSwiftPackageReference "swift" */ = {
11201120
isa = XCRemoteSwiftPackageReference;
11211121
repositoryURL = "https://github.com/pubnub/swift";
11221122
requirement = {
11231123
kind = exactVersion;
1124-
version = 9.0.1;
1124+
version = 9.1.0;
11251125
};
11261126
};
11271127
/* End XCRemoteSwiftPackageReference section */

PubNubSwiftChatSDK/PubNubSwiftChatSDK.docc/Chat.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,8 @@
7474
- ``GetUnreadMessagesCount``
7575
- ``getUnreadMessagesCount(limit:page:filter:sort:)``
7676
- ``getUnreadMessagesCount(limit:page:filter:sort:completion:)``
77+
- ``fetchUnreadMessagesCounts(limit:page:filter:sort:)``
78+
- ``fetchUnreadMessagesCounts(limit:page:filter:sort:completion:)``
7779
- ``markAllMessagesAsRead(limit:page:filter:sort:)``
7880
- ``markAllMessagesAsRead(limit:page:filter:sort:completion:)``
7981

PubNubSwiftChatSDK/PubNubSwiftChatSDK.docc/ChatImpl.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@
7575
- ``GetUnreadMessagesCount``
7676
- ``getUnreadMessagesCount(limit:page:filter:sort:)``
7777
- ``getUnreadMessagesCount(limit:page:filter:sort:completion:)``
78+
- ``fetchUnreadMessagesCounts(limit:page:filter:sort:)``
79+
- ``fetchUnreadMessagesCounts(limit:page:filter:sort:completion:)``
7880
- ``markAllMessagesAsRead(limit:page:filter:sort:)``
7981
- ``markAllMessagesAsRead(limit:page:filter:sort:completion:)``
8082

Sources/Chat+AsyncAwait.swift

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -571,6 +571,7 @@ public extension Chat {
571571
/// - filter: Expression used to filter the results. Returns only these channels whose properties satisfy the given expression are returned
572572
/// - sort: A collection to specify the sort order
573573
/// - Returns: An array of ``GetUnreadMessagesCount`` representing unread messages for the current user in a given channel
574+
@available(*, deprecated, message: "Use `fetchUnreadMessagesCounts(limit:page:filter:sort:)` instead")
574575
func getUnreadMessagesCount(
575576
limit: Int? = nil,
576577
page: PubNubHashedPage? = nil,
@@ -594,6 +595,38 @@ public extension Chat {
594595
}
595596
}
596597

598+
/// Returns info on all messages you didn't read on all joined channels. You can display this number on UI in the channel list of your chat app.
599+
///
600+
/// - Parameters:
601+
/// - limit: Number of objects to return in response. The maximum value is 100
602+
/// - page: Object used for pagination to define which previous or next result page you want to fetch
603+
/// - filter: Expression used to filter the results. Returns only these channels whose properties satisfy the given expression are returned
604+
/// - sort: A collection to specify the sort order
605+
/// - completion: The async `Result` of the method call
606+
/// - Returns: A Tuple containing an `Array` of unread messages for the current user across all joined channels, and the next pagination `PubNubHashedPage` (if one exists)
607+
func fetchUnreadMessagesCounts(
608+
limit: Int? = nil,
609+
page: PubNubHashedPage? = nil,
610+
filter: String? = nil,
611+
sort: [PubNub.MembershipSortField] = []
612+
) async throws -> (countsByChannel: [GetUnreadMessagesCount<ChannelImpl, MembershipImpl>], page: PubNubHashedPage?) {
613+
try await withCheckedThrowingContinuation { continuation in
614+
fetchUnreadMessagesCounts(
615+
limit: limit,
616+
page: page,
617+
filter: filter,
618+
sort: sort
619+
) {
620+
switch $0 {
621+
case let .success(unreadMessagesCount):
622+
continuation.resume(returning: unreadMessagesCount)
623+
case let .failure(error):
624+
continuation.resume(throwing: error)
625+
}
626+
}
627+
}
628+
}
629+
597630
/// Allows you to mark as read all messages you didn't read on all joined channels.
598631
///
599632
/// - Parameters:

Sources/Chat.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,7 @@ public protocol Chat: AnyObject {
414414
/// - completion: The async `Result` of the method call
415415
/// - **Success**: An array of ``GetUnreadMessagesCount`` representing unread messages for the current user in a given channel
416416
/// - **Failure**: An `Error` describing the failure
417+
@available(*, deprecated, message: "Use `fetchUnreadMessagesCounts(limit:page:filter:sort:completion:)` instead")
417418
func getUnreadMessagesCount(
418419
limit: Int?,
419420
page: PubNubHashedPage?,
@@ -422,6 +423,24 @@ public protocol Chat: AnyObject {
422423
completion: ((Swift.Result<[GetUnreadMessagesCount<ChannelImpl, MembershipImpl>], Error>) -> Void)?
423424
)
424425

426+
/// Returns info on all messages you didn't read on all joined channels. You can display this number on UI in the channel list of your chat app.
427+
///
428+
/// - Parameters:
429+
/// - limit: Number of objects to return in response. The maximum value is 100
430+
/// - page: Object used for pagination to define which previous or next result page you want to fetch
431+
/// - filter: Expression used to filter the results. Returns only these channels whose properties satisfy the given expression are returned
432+
/// - sort: A collection to specify the sort order
433+
/// - completion: The async `Result` of the method call
434+
/// - **Success**: A Tuple containing an `Array` of unread messages for the current user across all joined channels, and the next pagination `PubNubHashedPage` (if one exists)
435+
/// - **Failure**: An `Error` describing the failure
436+
func fetchUnreadMessagesCounts(
437+
limit: Int?,
438+
page: PubNubHashedPage?,
439+
filter: String?,
440+
sort: [PubNub.MembershipSortField],
441+
completion: ((Swift.Result<(countsByChannel: [GetUnreadMessagesCount<ChannelImpl, MembershipImpl>], page: PubNubHashedPage?), Error>) -> Void)?
442+
)
443+
425444
/// Allows you to mark as read all messages you didn't read on all joined channels.
426445
///
427446
/// - Parameters:

Sources/ChatImpl.swift

Lines changed: 43 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,8 @@ public final class ChatImpl {
3434
/// - Parameters:
3535
/// - chatConfiguration: A configuration object of type ``ChatConfiguration`` that defines the chat settings
3636
/// - pubNubConfiguration: A configuration object of type `PubNubConfiguration` that defines the `PubNub` settings
37-
public init(
38-
chatConfiguration: ChatConfiguration = ChatConfiguration(),
39-
pubNubConfiguration: PubNubConfiguration
40-
) {
41-
pubNub = PubNub(configuration: pubNubConfiguration)
42-
config = chatConfiguration
43-
chat = ChatImpl.createKMPChat(from: pubNub, config: chatConfiguration)
44-
mutedUsersManager = MutedUsersManagerImpl(underlying: chat.mutedUsersManager)
45-
46-
pubNub.setConsumer(identifier: "chat-sdk", value: "CA-SWIFT/\(pubNubSwiftChatSDKVersion)")
47-
// Creates an association between KMP chat and the current instance
48-
ChatAdapter.associate(chat: self, rawChat: chat)
37+
convenience public init(chatConfiguration: ChatConfiguration = ChatConfiguration(), pubNubConfiguration: PubNubConfiguration) {
38+
self.init(pubNub: PubNub(configuration: pubNubConfiguration), configuration: chatConfiguration)
4939
}
5040

5141
init(pubNub: PubNub, configuration: ChatConfiguration) {
@@ -55,8 +45,8 @@ public final class ChatImpl {
5545
mutedUsersManager = MutedUsersManagerImpl(underlying: chat.mutedUsersManager)
5646

5747
pubNub.setConsumer(identifier: "chat-sdk", value: "CA-SWIFT/\(pubNubSwiftChatSDKVersion)")
58-
// Creates an association between KMP chat and the current instance
59-
ChatAdapter.associate(chat: self, rawChat: chat)
48+
// Creates an association between Kotlin Multiplatform chat and the current instance
49+
ChatAdapter.associate(chat: self, with: chat)
6050
}
6151

6252
deinit {
@@ -631,6 +621,45 @@ extension ChatImpl: Chat {
631621
}
632622
}
633623

624+
public func fetchUnreadMessagesCounts(
625+
limit: Int? = nil,
626+
page: PubNubHashedPage? = nil,
627+
filter: String? = nil,
628+
sort: [PubNub.MembershipSortField] = [],
629+
// swiftlint:disable:next line_length
630+
completion: ((Swift.Result<(countsByChannel: [GetUnreadMessagesCount<ChannelImpl, MembershipImpl>], page: PubNubHashedPage?), Error>) -> Void)? = nil
631+
) {
632+
chat.fetchUnreadMessagesCounts(
633+
limit: limit?.asKotlinInt,
634+
page: page?.transform(),
635+
filter: filter,
636+
sort: sort.compactMap { $0.transform() }
637+
).async(caller: self) { (result: FutureResult<ChatImpl, PubNubChat.UnreadMessagesCounts>) in
638+
switch result.result {
639+
case let .success(response):
640+
completion?(
641+
.success(
642+
(
643+
countsByChannel: response.countsByChannel.map {
644+
GetUnreadMessagesCount(
645+
channel: ChannelImpl(channel: $0.channel),
646+
membership: MembershipImpl(membership: $0.membership),
647+
count: UInt64($0.count)
648+
)
649+
},
650+
page: PubNubHashedPageBase(
651+
start: response.next?.pageHash,
652+
end: response.prev?.pageHash
653+
)
654+
)
655+
)
656+
)
657+
case let .failure(error):
658+
completion?(.failure(error))
659+
}
660+
}
661+
}
662+
634663
public func markAllMessagesAsRead(
635664
limit: Int? = nil,
636665
page: PubNubHashedPage? = nil,

Sources/Miscellaneous/ChatAdapter.swift

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -12,46 +12,52 @@ import Foundation
1212
import PubNubChat
1313

1414
class ChatAdapter {
15-
static var associations: [Association] = []
16-
15+
private static var associations: [Association] = []
16+
private static let queue = DispatchQueue(label: "ChatAdapter.associations", attributes: .concurrent)
1717
private init() {}
1818

1919
static func map(chat: PubNubChat.Chat) -> Association {
20-
if let association = associations.first(where: { !$0.isEmpty() && $0.rawChat === chat }) {
21-
association
22-
} else {
23-
preconditionFailure("Cannot find Chat object matching \(chat)")
20+
queue.sync {
21+
if let association = associations.first(where: { !$0.isEmpty() && $0.kotlinChat === chat }) {
22+
return association
23+
} else {
24+
preconditionFailure("Cannot find Chat object matching \(chat)")
25+
}
2426
}
2527
}
2628

27-
static func associate(chat: ChatImpl, rawChat: PubNubChat.ChatImpl) {
28-
if !associations.contains(where: { !$0.isEmpty() && $0.chat !== chat && $0.rawChat !== rawChat }) {
29-
associations.append(.init(chat: chat, rawChat: rawChat))
29+
static func associate(chat: ChatImpl, with kotlinChat: PubNubChat.ChatImpl) {
30+
queue.async(flags: .barrier) {
31+
if !associations.contains(where: { !$0.isEmpty() && $0.chat !== chat && $0.kotlinChat !== kotlinChat }) {
32+
associations.append(.init(chat: chat, kotlinChat: kotlinChat))
33+
}
3034
}
3135
}
3236

3337
static func clean() {
34-
associations.removeAll {
35-
$0.isEmpty()
38+
queue.async(flags: .barrier) {
39+
associations.removeAll {
40+
$0.isEmpty()
41+
}
3642
}
3743
}
3844

3945
class Association {
4046
// swiftlint:disable:next force_unwrapping
4147
var chat: ChatImpl { _chat! }
4248
// swiftlint:disable:next force_unwrapping
43-
var rawChat: PubNubChat.ChatImpl { _rawChat! }
49+
var kotlinChat: PubNubChat.ChatImpl { _kotlinChat! }
4450

4551
private weak var _chat: ChatImpl?
46-
private weak var _rawChat: PubNubChat.ChatImpl?
52+
private weak var _kotlinChat: PubNubChat.ChatImpl?
4753

48-
init(chat: ChatImpl, rawChat: PubNubChat.ChatImpl) {
54+
init(chat: ChatImpl, kotlinChat: PubNubChat.ChatImpl) {
4955
_chat = chat
50-
_rawChat = rawChat
56+
_kotlinChat = kotlinChat
5157
}
5258

5359
func isEmpty() -> Bool {
54-
_chat == nil || _rawChat == nil
60+
_chat == nil || _kotlinChat == nil
5561
}
5662
}
5763
}

0 commit comments

Comments
 (0)