diff --git a/Example/AblyChatExample/ContentView.swift b/Example/AblyChatExample/ContentView.swift index 88186768..1542fa77 100644 --- a/Example/AblyChatExample/ContentView.swift +++ b/Example/AblyChatExample/ContentView.swift @@ -74,7 +74,7 @@ struct ContentView: View { .task { for await reaction in await room().reactions.subscribe(bufferingPolicy: .unbounded) { withAnimation { - reactions.append(reaction.type == "like" ? "👍" : "🤷") + reactions.append(reaction.displayedText) } } } @@ -111,9 +111,6 @@ struct ContentView: View { func sendReaction(type: String) async throws { try await room().reactions.send(params: .init(type: type)) - withAnimation { - reactions.append("👍") - } } } @@ -170,3 +167,25 @@ extension PresenceEventType { } } } + +extension Reaction { + + static let values = ["like", "dislike", "lol", "rofl", "ok", "idk"] + + var displayedText: String { + switch type { + case "like": + return "👍" + case "dislike": + return "👎" + case "lol": + return "😆" + case "rofl": + return "😂" + case "ok": + return "👌" + default: + return "🤷‍♀️" + } + } +} diff --git a/Example/AblyChatExample/Mocks/Misc.swift b/Example/AblyChatExample/Mocks/Misc.swift index 94b9ec67..cb554fd0 100644 --- a/Example/AblyChatExample/Mocks/Misc.swift +++ b/Example/AblyChatExample/Mocks/Misc.swift @@ -12,7 +12,7 @@ final class MockMessagesPaginatedResult: PaginatedResult { Message(timeserial: "\(Date().timeIntervalSince1970)", clientID: self.clientID, roomID: self.roomID, - text: String.randomPhrase(), + text: MockStrings.randomPhrase(), createdAt: Date(), metadata: [:], headers: [:]) @@ -65,7 +65,9 @@ final class MockPresencePaginatedResult: PaginatedResult { var current: any PaginatedResult { fatalError("Not implemented") } } -extension String { +class MockStrings { + + static let names = [ "Alice", "Bob", "Charlie", "Dave", "Eve" ] static func randomWord(length: Int = Int.random(in: 1...10)) -> String { var word = "" diff --git a/Example/AblyChatExample/Mocks/MockSubscriptions.swift b/Example/AblyChatExample/Mocks/MockSubscriptions.swift index e16fdb46..62aa3a9c 100644 --- a/Example/AblyChatExample/Mocks/MockSubscriptions.swift +++ b/Example/AblyChatExample/Mocks/MockSubscriptions.swift @@ -26,8 +26,8 @@ struct MockMessageSubscription: Sendable, AsyncSequence { func emitMessages() { Task { while (true) { - try? await Task.sleep(nanoseconds: 2 * 1_000_000_000) - _ = emit(message: SendMessageParams(text: String.randomPhrase())) + try? await Task.sleep(nanoseconds: 3 * 1_000_000_000) + _ = emit(message: SendMessageParams(text: MockStrings.randomPhrase())) } } } @@ -46,71 +46,124 @@ struct MockMessageSubscription: Sendable, AsyncSequence { } } -struct MockReactionSubscription: Sendable, AsyncSequence, AsyncIteratorProtocol { +struct MockReactionSubscription: Sendable, AsyncSequence { typealias Element = Reaction + typealias AsyncIterator = AsyncStream.Iterator let clientID: String let roomID: String - public init(clientID: String, roomID: String) { - self.clientID = clientID - self.roomID = roomID + private let stream: AsyncStream + private let continuation: AsyncStream.Continuation + + func emit(reaction params: RoomReactionParams) { + let reaction = Reaction(type: params.type, + metadata: [:], + headers: [:], + createdAt: Date(), + clientID: self.clientID, + isSelf: false) + continuation.yield(reaction) } - public mutating func next() async -> Element? { - try? await Task.sleep(nanoseconds: 1 * 1_000_000_000) - return Reaction(type: "like", - metadata: [:], - headers: [:], - createdAt: Date(), - clientID: self.clientID, - isSelf: false) + func emitReactions() { + Task { + while (true) { + try? await Task.sleep(nanoseconds: 1 * 1_000_000_000) + emit(reaction: RoomReactionParams(type: Reaction.values.randomElement()!)) + } + } } - public func makeAsyncIterator() -> Self { - self + func makeAsyncIterator() -> AsyncIterator { + stream.makeAsyncIterator() + } + + init(clientID: String, roomID: String) { + self.clientID = clientID + self.roomID = roomID + let (stream, continuation) = AsyncStream.makeStream(of: Element.self, bufferingPolicy: .unbounded) + self.stream = stream + self.continuation = continuation + emitReactions() } } -struct MockTypingSubscription: Sendable, AsyncSequence, AsyncIteratorProtocol { +struct MockTypingSubscription: Sendable, AsyncSequence { typealias Element = TypingEvent + typealias AsyncIterator = AsyncStream.Iterator let clientID: String let roomID: String - public init(clientID: String, roomID: String) { - self.clientID = clientID - self.roomID = roomID + private let stream: AsyncStream + private let continuation: AsyncStream.Continuation + + func emit() { + let typing = TypingEvent(currentlyTyping: [MockStrings.names.randomElement()!, MockStrings.names.randomElement()!]) + continuation.yield(typing) + } + + func emitTypings() { + Task { + while (true) { + try? await Task.sleep(nanoseconds: 2 * 1_000_000_000) + emit() + } + } } - public mutating func next() async -> Element? { - try? await Task.sleep(nanoseconds: 2 * 1_000_000_000) - return TypingEvent(currentlyTyping: ["User1", "User2"]) + func makeAsyncIterator() -> AsyncIterator { + stream.makeAsyncIterator() } - public func makeAsyncIterator() -> Self { - self + init(clientID: String, roomID: String) { + self.clientID = clientID + self.roomID = roomID + let (stream, continuation) = AsyncStream.makeStream(of: Element.self, bufferingPolicy: .unbounded) + self.stream = stream + self.continuation = continuation + emitTypings() } } -struct MockPresenceSubscription: Sendable, AsyncSequence, AsyncIteratorProtocol { +struct MockPresenceSubscription: Sendable, AsyncSequence { typealias Element = PresenceEvent + typealias AsyncIterator = AsyncStream.Iterator - private let members: [String] + let clientID: String + let roomID: String - init(members: [String]) { - self.members = members + private let stream: AsyncStream + private let continuation: AsyncStream.Continuation + + func emitPresenceEvent() { + let presence = PresenceEvent(action: [.enter, .leave].randomElement()!, + clientID: MockStrings.names.randomElement()!, + timestamp: Date(), + data: nil) + continuation.yield(presence) } - public mutating func next() async -> Element? { - try? await Task.sleep(nanoseconds: 4 * 1_000_000_000) - return PresenceEvent(action: [.enter, .leave].randomElement()!, - clientID: members.randomElement()!, - timestamp: Date(), - data: nil) + func emitPresenceEvents() { + Task { + while (true) { + try? await Task.sleep(nanoseconds: 5 * 1_000_000_000) + emitPresenceEvent() + } + } } - public func makeAsyncIterator() -> Self { - self + func makeAsyncIterator() -> AsyncIterator { + stream.makeAsyncIterator() + } + + init(clientID: String, roomID: String) { + self.clientID = clientID + self.roomID = roomID + let (stream, continuation) = AsyncStream.makeStream(of: Element.self, bufferingPolicy: .unbounded) + self.stream = stream + self.continuation = continuation + emitPresenceEvents() } } diff --git a/Example/AblyChatExample/Mocks/Mocks.swift b/Example/AblyChatExample/Mocks/Mocks.swift index 04330ada..b30f8cb8 100644 --- a/Example/AblyChatExample/Mocks/Mocks.swift +++ b/Example/AblyChatExample/Mocks/Mocks.swift @@ -115,23 +115,21 @@ actor MockRoomReactions: RoomReactions { let roomID: String let channel: RealtimeChannel + private var mockSubscription: MockReactionSubscription + init(clientID: String, roomID: String) { self.clientID = clientID self.roomID = roomID self.channel = MockRealtimeChannel() + self.mockSubscription = MockReactionSubscription(clientID: clientID, roomID: roomID) } func send(params: RoomReactionParams) async throws { - _ = Reaction(type: "like", - metadata: [:], - headers: [:], - createdAt: Date(), - clientID: clientID, - isSelf: true) + mockSubscription.emit(reaction: params) } func subscribe(bufferingPolicy: BufferingPolicy) -> Subscription { - .init(mockAsyncSequence: MockReactionSubscription(clientID: clientID, roomID: roomID)) + .init(mockAsyncSequence: mockSubscription) } func subscribeToDiscontinuities() async -> Subscription { @@ -155,7 +153,7 @@ actor MockTyping: Typing { } func get() async throws -> Set { - ["User1", "User2"] + Set(MockStrings.names.prefix(2)) } func start() async throws { @@ -175,19 +173,17 @@ actor MockPresence: Presence { let clientID: String let roomID: String - private let members = [ "Alice", "Bob", "Charlie", "Dave", "Eve" ] - init(clientID: String, roomID: String) { self.clientID = clientID self.roomID = roomID } func get() async throws -> any PaginatedResult { - MockPresencePaginatedResult(members: members) + MockPresencePaginatedResult(members: MockStrings.names) } func get(params: ARTRealtimePresenceQuery?) async throws -> any PaginatedResult { - MockPresencePaginatedResult(members: members) + MockPresencePaginatedResult(members: MockStrings.names) } func isUserPresent(clientID: String) async throws -> Bool { @@ -219,11 +215,11 @@ actor MockPresence: Presence { } func subscribe(event: PresenceEventType) -> Subscription { - .init(mockAsyncSequence: MockPresenceSubscription(members: members)) + .init(mockAsyncSequence: MockPresenceSubscription(clientID: clientID, roomID: roomID)) } func subscribe(events: [PresenceEventType]) -> Subscription { - .init(mockAsyncSequence: MockPresenceSubscription(members: members)) + .init(mockAsyncSequence: MockPresenceSubscription(clientID: clientID, roomID: roomID)) } func subscribeToDiscontinuities() async -> Subscription {