Skip to content

Commit

Permalink
Merge pull request #92 from ably-labs/73-change-some-api-names
Browse files Browse the repository at this point in the history
[ECO-5015] Implement public API changes of CHADR-062
  • Loading branch information
lawrence-forooghian authored Nov 6, 2024
2 parents 48a3d24 + b771238 commit c6525a2
Show file tree
Hide file tree
Showing 10 changed files with 167 additions and 194 deletions.
2 changes: 1 addition & 1 deletion Example/AblyChatExample/ContentView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ struct ContentView: View {
}

func showRoomStatus() async throws {
for await status in try await room().status.onChange(bufferingPolicy: .unbounded) {
for await status in try await room().onStatusChange(bufferingPolicy: .unbounded) {
withAnimation {
if status.current.isAttaching {
statusInfo = "\(status.current)...".capitalized
Expand Down
44 changes: 15 additions & 29 deletions Example/AblyChatExample/Mocks/MockClients.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ actor MockRoom: Room {

nonisolated lazy var occupancy: any Occupancy = MockOccupancy(clientID: clientID, roomID: roomID)

nonisolated lazy var status: any RoomStatus = MockRoomStatus(clientID: clientID, roomID: roomID)
var status: RoomStatus = .initialized

private var mockSubscriptions: [MockSubscription<RoomStatusChange>] = []

func attach() async throws {
fatalError("Not yet implemented")
Expand All @@ -73,6 +75,18 @@ actor MockRoom: Room {
func detach() async throws {
fatalError("Not yet implemented")
}

private func createSubscription() -> MockSubscription<RoomStatusChange> {
let subscription = MockSubscription<RoomStatusChange>(randomElement: {
RoomStatusChange(current: [.attached, .attached, .attached, .attached, .attaching(error: nil), .attaching(error: nil), .suspended(error: .createUnknownError())].randomElement()!, previous: .attaching(error: nil))
}, interval: 8)
mockSubscriptions.append(subscription)
return subscription
}

func onStatusChange(bufferingPolicy _: BufferingPolicy) async -> Subscription<RoomStatusChange> {
.init(mockAsyncSequence: createSubscription())
}
}

actor MockMessages: Messages {
Expand Down Expand Up @@ -394,31 +408,3 @@ actor MockOccupancy: Occupancy {
fatalError("Not yet implemented")
}
}

actor MockRoomStatus: RoomStatus {
let clientID: String
let roomID: String

var current: RoomLifecycle
var error: ARTErrorInfo?

private var mockSubscriptions: [MockSubscription<RoomStatusChange>] = []

init(clientID: String, roomID: String) {
self.clientID = clientID
self.roomID = roomID
current = .initialized
}

private func createSubscription() -> MockSubscription<RoomStatusChange> {
let subscription = MockSubscription<RoomStatusChange>(randomElement: {
RoomStatusChange(current: [.attached, .attached, .attached, .attached, .attaching(error: nil), .attaching(error: nil), .suspended(error: .createUnknownError())].randomElement()!, previous: .attaching(error: nil))
}, interval: 8)
mockSubscriptions.append(subscription)
return subscription
}

func onChange(bufferingPolicy _: BufferingPolicy) async -> Subscription<RoomStatusChange> {
.init(mockAsyncSequence: createSubscription())
}
}
16 changes: 6 additions & 10 deletions Sources/AblyChat/Connection.swift
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
import Ably

public protocol Connection: AnyObject, Sendable {
var status: any ConnectionStatus { get }
}

public protocol ConnectionStatus: AnyObject, Sendable {
var current: ConnectionLifecycle { get }
var status: ConnectionStatus { get }
// TODO: (https://github.com/ably-labs/ably-chat-swift/issues/12): consider how to avoid the need for an unwrap
var error: ARTErrorInfo? { get }
func onChange(bufferingPolicy: BufferingPolicy) -> Subscription<ConnectionStatusChange>
func onStatusChange(bufferingPolicy: BufferingPolicy) -> Subscription<ConnectionStatusChange>
}

public enum ConnectionLifecycle: Sendable {
public enum ConnectionStatus: Sendable {
case initialized
case connecting
case connected
Expand All @@ -21,13 +17,13 @@ public enum ConnectionLifecycle: Sendable {
}

public struct ConnectionStatusChange: Sendable {
public var current: ConnectionLifecycle
public var previous: ConnectionLifecycle
public var current: ConnectionStatus
public var previous: ConnectionStatus
// TODO: (https://github.com/ably-labs/ably-chat-swift/issues/12): consider how to avoid the need for an unwrap
public var error: ARTErrorInfo?
public var retryIn: TimeInterval

public init(current: ConnectionLifecycle, previous: ConnectionLifecycle, error: ARTErrorInfo? = nil, retryIn: TimeInterval) {
public init(current: ConnectionStatus, previous: ConnectionStatus, error: ARTErrorInfo? = nil, retryIn: TimeInterval) {
self.current = current
self.previous = previous
self.error = error
Expand Down
45 changes: 36 additions & 9 deletions Sources/AblyChat/Room.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,24 @@ public protocol Room: AnyObject, Sendable {
var typing: any Typing { get }
// To access this property if occupancy is not enabled for the room is a programmer error, and will lead to `fatalError` being called.
var occupancy: any Occupancy { get }
var status: any RoomStatus { get }
// TODO: change to `status`
var status: RoomStatus { get async }
func onStatusChange(bufferingPolicy: BufferingPolicy) async -> Subscription<RoomStatusChange>
func attach() async throws
func detach() async throws
var options: RoomOptions { get }
}

public struct RoomStatusChange: Sendable {
public var current: RoomStatus
public var previous: RoomStatus

public init(current: RoomStatus, previous: RoomStatus) {
self.current = current
self.previous = previous
}
}

internal actor DefaultRoom: Room {
internal nonisolated let roomID: String
internal nonisolated let options: RoomOptions
Expand All @@ -33,15 +45,16 @@ internal actor DefaultRoom: Room {
}
#endif

private let _status: DefaultRoomStatus
internal private(set) var status: RoomStatus = .initialized
// TODO: clean up old subscriptions (https://github.com/ably-labs/ably-chat-swift/issues/36)
private var statusSubscriptions: [Subscription<RoomStatusChange>] = []
private let logger: InternalLogger

internal init(realtime: RealtimeClient, chatAPI: ChatAPI, roomID: String, options: RoomOptions, logger: InternalLogger) async throws {
self.realtime = realtime
self.roomID = roomID
self.options = options
self.logger = logger
_status = .init(logger: logger)
self.chatAPI = chatAPI

guard let clientId = realtime.clientId else {
Expand Down Expand Up @@ -71,10 +84,6 @@ internal actor DefaultRoom: Room {
fatalError("Not yet implemented")
}

internal nonisolated var status: any RoomStatus {
_status
}

/// Fetches the channels that contribute to this room.
private func channels() -> [any RealtimeChannelProtocol] {
[
Expand All @@ -95,7 +104,7 @@ internal actor DefaultRoom: Room {
throw error
}
}
await _status.transition(to: .attached)
transition(to: .attached)
}

public func detach() async throws {
Expand All @@ -107,6 +116,24 @@ internal actor DefaultRoom: Room {
throw error
}
}
await _status.transition(to: .detached)
transition(to: .detached)
}

// MARK: - Room status

internal func onStatusChange(bufferingPolicy: BufferingPolicy) -> Subscription<RoomStatusChange> {
let subscription: Subscription<RoomStatusChange> = .init(bufferingPolicy: bufferingPolicy)
statusSubscriptions.append(subscription)
return subscription
}

/// Sets ``status`` to the given status, and emits a status change to all subscribers added via ``onStatusChange(bufferingPolicy:)``.
internal func transition(to newStatus: RoomStatus) {
logger.log(message: "Transitioning to \(newStatus)", level: .debug)
let statusChange = RoomStatusChange(current: newStatus, previous: status)
status = newStatus
for subscription in statusSubscriptions {
subscription.emit(statusChange)
}
}
}
10 changes: 5 additions & 5 deletions Sources/AblyChat/RoomLifecycleManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,7 @@ internal actor RoomLifecycleManager<Contributor: RoomLifecycleContributor> {
case releasing(releaseOperationID: UUID)
case released

internal var toRoomLifecycle: RoomLifecycle {
internal var toRoomStatus: RoomStatus {
switch self {
case .initialized:
.initialized
Expand Down Expand Up @@ -264,8 +264,8 @@ internal actor RoomLifecycleManager<Contributor: RoomLifecycleContributor> {

// MARK: - Room status and its changes

internal var current: RoomLifecycle {
status.toRoomLifecycle
internal var roomStatus: RoomStatus {
status.toRoomStatus
}

internal func onChange(bufferingPolicy: BufferingPolicy) -> Subscription<RoomStatusChange> {
Expand All @@ -279,7 +279,7 @@ internal actor RoomLifecycleManager<Contributor: RoomLifecycleContributor> {
logger.log(message: "Transitioning from \(status) to \(new)", level: .info)
let previous = status
status = new
let statusChange = RoomStatusChange(current: status.toRoomLifecycle, previous: previous.toRoomLifecycle)
let statusChange = RoomStatusChange(current: status.toRoomStatus, previous: previous.toRoomStatus)
emitStatusChange(statusChange)
}

Expand Down Expand Up @@ -779,7 +779,7 @@ internal actor RoomLifecycleManager<Contributor: RoomLifecycleContributor> {
}

// This check is CHA-RL2h2
if !status.toRoomLifecycle.isFailed {
if !status.toRoomStatus.isFailed {
changeStatus(to: .failed(error: error))
}
default:
Expand Down
50 changes: 3 additions & 47 deletions Sources/AblyChat/RoomStatus.swift
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import Ably

public protocol RoomStatus: AnyObject, Sendable {
var current: RoomLifecycle { get async }
func onChange(bufferingPolicy: BufferingPolicy) async -> Subscription<RoomStatusChange>
}

public enum RoomLifecycle: Sendable, Equatable {
// TODO: rename
public enum RoomStatus: Sendable, Equatable {
case initialized
case attaching(error: ARTErrorInfo?)
case attached
Expand All @@ -16,7 +12,7 @@ public enum RoomLifecycle: Sendable, Equatable {
case releasing
case released

// Helpers to allow us to test whether a `RoomLifecycle` value has a certain case, without caring about the associated value. These are useful for in contexts where we want to use a `Bool` to communicate a case. For example:
// Helpers to allow us to test whether a `RoomStatus` value has a certain case, without caring about the associated value. These are useful for in contexts where we want to use a `Bool` to communicate a case. For example:
//
// 1. testing (e.g. `#expect(status.isFailed)`)
// 2. testing that a status does _not_ have a particular case (e.g. if !status.isFailed), which a `case` statement cannot succinctly express
Expand Down Expand Up @@ -45,43 +41,3 @@ public enum RoomLifecycle: Sendable, Equatable {
}
}
}

public struct RoomStatusChange: Sendable {
public var current: RoomLifecycle
public var previous: RoomLifecycle

public init(current: RoomLifecycle, previous: RoomLifecycle) {
self.current = current
self.previous = previous
}
}

internal actor DefaultRoomStatus: RoomStatus {
internal private(set) var current: RoomLifecycle = .initialized
internal private(set) var error: ARTErrorInfo?

private let logger: InternalLogger

internal init(logger: InternalLogger) {
self.logger = logger
}

// TODO: clean up old subscriptions (https://github.com/ably-labs/ably-chat-swift/issues/36)
private var subscriptions: [Subscription<RoomStatusChange>] = []

internal func onChange(bufferingPolicy: BufferingPolicy) -> Subscription<RoomStatusChange> {
let subscription: Subscription<RoomStatusChange> = .init(bufferingPolicy: bufferingPolicy)
subscriptions.append(subscription)
return subscription
}

/// Sets ``current`` to the given state, and emits a status change to all subscribers added via ``onChange(bufferingPolicy:)``.
internal func transition(to newState: RoomLifecycle) {
logger.log(message: "Transitioning to \(newState)", level: .debug)
let statusChange = RoomStatusChange(current: newState, previous: current)
current = newState
for subscription in subscriptions {
subscription.emit(statusChange)
}
}
}
41 changes: 0 additions & 41 deletions Tests/AblyChatTests/DefaultRoomStatusTests.swift

This file was deleted.

Loading

0 comments on commit c6525a2

Please sign in to comment.