Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions .github/workflows/check.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -139,20 +139,18 @@ jobs:
run: swift run BuildTool test-library --platform ${{ matrix.platform }}

code-coverage:
name: Generate code coverage (Xcode ${{ matrix.tooling.xcodeVersion }})
name: Generate code coverage
runs-on: macos-15
needs: generate-matrices
strategy:
fail-fast: false
matrix: ${{ fromJson(needs.generate-matrices.outputs.matrix).withoutPlatform }}

steps:
- uses: actions/checkout@v4
with:
submodules: true

# This step can be removed once the runners’ default version of Xcode is 16 or above
- uses: maxim-lobanov/setup-xcode@v1
with:
xcode-version: ${{ matrix.tooling.xcodeVersion }}
xcode-version: 16.2

- run: swift run BuildTool generate-code-coverage --result-bundle-path CodeCoverage.xcresult

Expand Down
20 changes: 13 additions & 7 deletions Sources/AblyChat/ChatAPI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ internal final class ChatAPI: Sendable {
// (CHA-M6) Messages should be queryable from a paginated REST API.
internal func getMessages(roomId: String, params: QueryOptions) async throws(InternalError) -> any PaginatedResult<Message> {
let endpoint = "\(apiVersionV2)/rooms/\(roomId)/messages"
return try await makePaginatedRequest(endpoint, params: params.asQueryItems())
let result: Result<PaginatedResultWrapper<Message>, InternalError> = await makePaginatedRequest(endpoint, params: params.asQueryItems())
return try result.get()
}

internal struct SendMessageResponse: JSONObjectDecodable {
Expand Down Expand Up @@ -193,16 +194,21 @@ internal final class ChatAPI: Sendable {
return try Response(jsonValue: jsonValue)
}

// TODO: (https://github.com/ably/ably-chat-swift/issues/267) switch this back to use `throws` once Xcode 16.3 typed throw crashes are fixed
private func makePaginatedRequest<Response: JSONDecodable & Sendable & Equatable>(
_ url: String,
params: [String: String]? = nil
) async throws(InternalError) -> any PaginatedResult<Response> {
let paginatedResponse = try await realtime.request("GET", path: url, params: params, body: nil, headers: [:])
let jsonValues = paginatedResponse.items.map { JSONValue(ablyCocoaData: $0) }
let items = try jsonValues.map { jsonValue throws(InternalError) in
try Response(jsonValue: jsonValue)
) async -> Result<PaginatedResultWrapper<Response>, InternalError> {
do {
let paginatedResponse = try await realtime.request("GET", path: url, params: params, body: nil, headers: [:])
let jsonValues = paginatedResponse.items.map { JSONValue(ablyCocoaData: $0) }
let items = try jsonValues.map { jsonValue throws(InternalError) in
try Response(jsonValue: jsonValue)
}
return .success(paginatedResponse.toPaginatedResult(items: items))
} catch {
return .failure(error)
}
return paginatedResponse.toPaginatedResult(items: items)
}

internal enum ChatError: Error {
Expand Down
11 changes: 6 additions & 5 deletions Sources/AblyChat/PaginatedResult.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ public protocol PaginatedResult<T>: AnyObject, Sendable, Equatable {
var isLast: Bool { get }
// TODO: (https://github.com/ably-labs/ably-chat-swift/issues/11): consider how to avoid the need for an unwrap
// Note that there seems to be a compiler bug (https://github.com/swiftlang/swift/issues/79992) that means that the compiler does not enforce the access level of the error type for property getters. I accidentally originally wrote these as throws(InternalError), which the compiler should have rejected since InternalError is internal and this protocol is public, but it did not reject it and this mistake was only noticed in code review.
var next: (any PaginatedResult<T>)? { get async throws(ARTErrorInfo) }
var first: any PaginatedResult<T> { get async throws(ARTErrorInfo) }
var current: any PaginatedResult<T> { get async throws(ARTErrorInfo) }
// TODO: (https://github.com/ably/ably-chat-swift/issues/267) restore typed throws here once Xcode 16.3 compiler bug fixed
var next: (any PaginatedResult<T>)? { get async throws }
var first: any PaginatedResult<T> { get async throws }
var current: any PaginatedResult<T> { get async throws }
}

/// Used internally to reduce the amount of duplicate code when interacting with `ARTHTTPPaginatedCallback`'s. The wrapper takes in the callback result from the caller e.g. `realtime.request` and either throws the appropriate error, or decodes and returns the response.
Expand Down Expand Up @@ -63,7 +64,7 @@ internal final class PaginatedResultWrapper<T: JSONDecodable & Sendable & Equata

/// Asynchronously fetch the next page if available
internal var next: (any PaginatedResult<T>)? {
get async throws(ARTErrorInfo) {
get async throws {
do {
return try await withCheckedContinuation { continuation in
paginatedResponse.next { paginatedResponse, error in
Expand All @@ -78,7 +79,7 @@ internal final class PaginatedResultWrapper<T: JSONDecodable & Sendable & Equata

/// Asynchronously fetch the first page
internal var first: any PaginatedResult<T> {
get async throws(ARTErrorInfo) {
get async throws {
do {
return try await withCheckedContinuation { continuation in
paginatedResponse.first { paginatedResponse, error in
Expand Down
9 changes: 5 additions & 4 deletions Sources/AblyChat/Rooms.swift
Original file line number Diff line number Diff line change
Expand Up @@ -102,13 +102,14 @@ internal class DefaultRooms<RoomFactory: AblyChat.RoomFactory>: Rooms {
}
}

// TODO: (https://github.com/ably/ably-chat-swift/issues/267) switch this back to use `throws` once Xcode 16.3 typed throw crashes are fixed
/// Returns the room which this room map entry corresponds to. If the room map entry represents a pending request, it will return or throw with the result of this request.
func waitForRoom() async throws(InternalError) -> RoomFactory.Room {
func waitForRoom() async -> Result<RoomFactory.Room, InternalError> {
switch self {
case let .requestAwaitingRelease(_, _, creationTask: creationTask, _):
try await creationTask.value.get()
await creationTask.value
case let .created(room):
room
.success(room)
}
}
}
Expand Down Expand Up @@ -172,7 +173,7 @@ internal class DefaultRooms<RoomFactory: AblyChat.RoomFactory>: Rooms {
#endif

do {
let room = try await existingRoomMapEntry.waitForRoom()
let room = try await existingRoomMapEntry.waitForRoom().get()
logger.log(message: "Completed waiting for room from existing room map entry \(existingRoomMapEntry)", level: .debug)
return room
} catch {
Expand Down
3 changes: 3 additions & 0 deletions Sources/BuildTool/BuildTool.swift
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ struct GenerateMatrices: ParsableCommand {
[
"xcodeVersion": "16.2",
],
[
"xcodeVersion": "16.3",
],
]

let matrix: [String: Any] = [
Expand Down