Skip to content

Commit

Permalink
Implement DefaultChatClient.rooms
Browse files Browse the repository at this point in the history
The @preconcurrency import of ably-cocoa is temporary and will be
removed once [2] is done; created #31 for tracking.

Part of #19.
  • Loading branch information
lawrence-forooghian committed Aug 29, 2024
1 parent bc06747 commit 202161b
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 30 deletions.
2 changes: 1 addition & 1 deletion Example/AblyChatExample/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import SwiftUI
struct ContentView: View {
/// Just used to check that we can successfully import and use the AblyChat library. TODO remove this once we start building the library
@State private var ablyChatClient = DefaultChatClient(
realtime: MockRealtime(key: ""),
realtime: MockRealtime.create(),
clientOptions: ClientOptions()
)

Expand Down
16 changes: 14 additions & 2 deletions Example/AblyChatExample/Mocks/MockRealtime.swift
Original file line number Diff line number Diff line change
@@ -1,19 +1,31 @@
import Ably
import Foundation

/// A mock implementation of `ARTRealtimeProtocol`. It only exists so that we can construct an instance of `DefaultChatClient` without needing to create a proper `ARTRealtime` instance (which we can’t yet do because we don’t have a method for inserting an API key into the example app). TODO remove this once we start building the example app
class MockRealtime: NSObject, ARTRealtimeProtocol {
final class MockRealtime: NSObject, ARTRealtimeProtocol, Sendable {
var device: ARTLocalDevice {
fatalError("Not implemented")
}

var clientId: String?
var clientId: String? {
fatalError("Not implemented")
}

required init(options _: ARTClientOptions) {}

required init(key _: String) {}

required init(token _: String) {}

/**
Creates an instance of MockRealtime.

This exists to give a convenient way to create an instance, because `init` is marked as unavailable in `ARTRealtimeProtocol`.
*/
static func create() -> MockRealtime {
MockRealtime(key: "")
}

func time(_: @escaping ARTDateTimeCallback) {
fatalError("Not implemented")
}
Expand Down
33 changes: 16 additions & 17 deletions Sources/AblyChat/ChatClient.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Ably
@preconcurrency import Ably

public protocol ChatClient: AnyObject, Sendable {
var rooms: any Rooms { get }
Expand All @@ -8,28 +8,22 @@ public protocol ChatClient: AnyObject, Sendable {
var clientOptions: ClientOptions { get }
}

public final class DefaultChatClient: ChatClient {
public init(realtime _: ARTRealtimeProtocol, clientOptions _: ClientOptions?) {
// This one doesn’t do `fatalError`, so that I can call it in the example app
}

public var rooms: any Rooms {
fatalError("Not yet implemented")
}

public var connection: any Connection {
fatalError("Not yet implemented")
}
public actor DefaultChatClient: ChatClient {
public let realtime: ARTRealtimeProtocol
public nonisolated let clientOptions: ClientOptions
public nonisolated let rooms: Rooms

public var clientID: String {
fatalError("Not yet implemented")
public init(realtime: ARTRealtimeProtocol, clientOptions: ClientOptions?) {
self.realtime = realtime
self.clientOptions = clientOptions ?? .init()
rooms = DefaultRooms(realtime: realtime, clientOptions: self.clientOptions)
}

public var realtime: any ARTRealtimeProtocol {
public nonisolated var connection: any Connection {
fatalError("Not yet implemented")
}

public var clientOptions: ClientOptions {
public nonisolated var clientID: String {
fatalError("Not yet implemented")
}
}
Expand All @@ -42,4 +36,9 @@ public struct ClientOptions: Sendable {
self.logHandler = logHandler
self.logLevel = logLevel
}

/// Used for comparing these instances in tests without having to make this Equatable, which I’m not yet sure makes sense (we’ll decide in https://github.com/ably-labs/ably-chat-swift/issues/10)
internal func isEqualForTestPurposes(_ other: ClientOptions) -> Bool {
logHandler === other.logHandler && logLevel == other.logLevel
}
}
21 changes: 21 additions & 0 deletions Sources/AblyChat/Rooms.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,26 @@
import Ably

public protocol Rooms: AnyObject, Sendable {
func get(roomID: String, options: RoomOptions) throws -> any Room
func release(roomID: String) async throws
var clientOptions: ClientOptions { get }
}

internal actor DefaultRooms: Rooms {
/// Exposed so that we can test it.
internal nonisolated let realtime: ARTRealtimeProtocol
internal nonisolated let clientOptions: ClientOptions

internal init(realtime: ARTRealtimeProtocol, clientOptions: ClientOptions) {
self.realtime = realtime
self.clientOptions = clientOptions
}

internal nonisolated func get(roomID _: String, options _: RoomOptions) throws -> any Room {
fatalError("Not yet implemented")
}

internal func release(roomID _: String) async throws {
fatalError("Not yet implemented")
}
}
8 changes: 0 additions & 8 deletions Tests/AblyChatTests/AblyChatTests.swift

This file was deleted.

27 changes: 27 additions & 0 deletions Tests/AblyChatTests/DefaultChatClientTests.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
@testable import AblyChat
import XCTest

class DefaultChatClientTests: XCTestCase {
func test_init_withoutClientOptions() {
// Given: An instance of DefaultChatClient is created with nil clientOptions
let client = DefaultChatClient(realtime: MockRealtime.create(), clientOptions: nil)

// Then: It uses the default client options
let defaultOptions = ClientOptions()
XCTAssertTrue(client.clientOptions.isEqualForTestPurposes(defaultOptions))
}

func test_rooms() throws {
// Given: An instance of DefaultChatClient
let realtime = MockRealtime.create()
let options = ClientOptions()
let client = DefaultChatClient(realtime: realtime, clientOptions: options)

// Then: Its `rooms` property returns an instance of DefaultRooms with the same realtime client and client options
let rooms = client.rooms

let defaultRooms = try XCTUnwrap(rooms as? DefaultRooms)
XCTAssertIdentical(defaultRooms.realtime, realtime)
XCTAssertTrue(defaultRooms.clientOptions.isEqualForTestPurposes(options))
}
}
15 changes: 13 additions & 2 deletions Tests/AblyChatTests/Mocks/MockRealtime.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,30 @@ import Ably
import Foundation

/// A mock implementation of `ARTRealtimeProtocol`. Copied from the class of the same name in the example app. We’ll figure out how to do mocking in tests properly in https://github.com/ably-labs/ably-chat-swift/issues/5.
class MockRealtime: NSObject, ARTRealtimeProtocol {
final class MockRealtime: NSObject, ARTRealtimeProtocol, Sendable {
var device: ARTLocalDevice {
fatalError("Not implemented")
}

var clientId: String?
var clientId: String? {
fatalError("Not implemented")
}

required init(options _: ARTClientOptions) {}

required init(key _: String) {}

required init(token _: String) {}

/**
Creates an instance of MockRealtime.

This exists to give a convenient way to create an instance, because `init` is marked as unavailable in `ARTRealtimeProtocol`.
*/
static func create() -> MockRealtime {
MockRealtime(key: "")
}

func time(_: @escaping ARTDateTimeCallback) {
fatalError("Not implemented")
}
Expand Down

0 comments on commit 202161b

Please sign in to comment.