diff --git a/Sources/AblyChat/Headers.swift b/Sources/AblyChat/Headers.swift index edf5400..a961bf2 100644 --- a/Sources/AblyChat/Headers.swift +++ b/Sources/AblyChat/Headers.swift @@ -1,10 +1,71 @@ -// TODO: https://github.com/ably-labs/ably-chat-swift/issues/13 - try to improve this type - +/// A value that can be used in ``Headers``. It is the same as ``JSONValue`` except it does not have the `object` or `array` cases. public enum HeadersValue: Sendable, Equatable { case string(String) case number(Double) case bool(Bool) case null + + // MARK: - Convenience getters for associated values + + /// If this `HeadersValue` has case `string`, this returns the associated value. Else, it returns `nil`. + public var stringValue: String? { + if case let .string(stringValue) = self { + stringValue + } else { + nil + } + } + + /// If this `HeadersValue` has case `number`, this returns the associated value. Else, it returns `nil`. + public var numberValue: Double? { + if case let .number(numberValue) = self { + numberValue + } else { + nil + } + } + + /// If this `HeadersValue` has case `bool`, this returns the associated value. Else, it returns `nil`. + public var boolValue: Bool? { + if case let .bool(boolValue) = self { + boolValue + } else { + nil + } + } + + /// Returns true if and only if this `HeadersValue` has case `null`. + public var isNull: Bool { + if case .null = self { + true + } else { + false + } + } +} + +extension HeadersValue: ExpressibleByStringLiteral { + public init(stringLiteral value: String) { + self = .string(value) + } +} + +extension HeadersValue: ExpressibleByIntegerLiteral { + public init(integerLiteral value: Int) { + self = .number(Double(value)) + } +} + +extension HeadersValue: ExpressibleByFloatLiteral { + public init(floatLiteral value: Double) { + self = .number(value) + } +} + +extension HeadersValue: ExpressibleByBooleanLiteral { + public init(booleanLiteral value: Bool) { + self = .bool(value) + } } extension HeadersValue: JSONDecodable { @@ -43,10 +104,6 @@ extension HeadersValue: JSONEncodable { } } -// The corresponding type in TypeScript is -// Record -// There may be a better way to represent it in Swift; this will do for now. Have omitted `undefined` because I don’t know how that would occur. - /** * Headers are a flat key-value map that can be attached to chat messages. * diff --git a/Tests/AblyChatTests/ChatAPITests.swift b/Tests/AblyChatTests/ChatAPITests.swift index c77602e..753aff0 100644 --- a/Tests/AblyChatTests/ChatAPITests.swift +++ b/Tests/AblyChatTests/ChatAPITests.swift @@ -66,7 +66,7 @@ struct ChatAPITests { params: .init( text: "", // arbitrary // The exact value here is arbitrary, just want to check it gets serialized - headers: ["numberKey": .number(10), "stringKey": .string("hello")] + headers: ["numberKey": 10, "stringKey": "hello"] ) ) diff --git a/Tests/AblyChatTests/DefaultRoomReactionsTests.swift b/Tests/AblyChatTests/DefaultRoomReactionsTests.swift index 084674a..af75309 100644 --- a/Tests/AblyChatTests/DefaultRoomReactionsTests.swift +++ b/Tests/AblyChatTests/DefaultRoomReactionsTests.swift @@ -31,7 +31,7 @@ struct DefaultRoomReactionsTests { let sendReactionParams = SendReactionParams( type: "like", metadata: ["someMetadataKey": "someMetadataValue"], - headers: ["someHeadersKey": HeadersValue.string("someHeadersValue")] + headers: ["someHeadersKey": "someHeadersValue"] ) // When diff --git a/Tests/AblyChatTests/IntegrationTests.swift b/Tests/AblyChatTests/IntegrationTests.swift index 655b678..2f091f9 100644 --- a/Tests/AblyChatTests/IntegrationTests.swift +++ b/Tests/AblyChatTests/IntegrationTests.swift @@ -119,7 +119,7 @@ struct IntegrationTests { params: .init( text: "Hello from txRoom, after rxRoom subscribe", metadata: ["someMetadataKey": 123, "someOtherMetadataKey": "foo"], - headers: ["someHeadersKey": .number(456), "someOtherHeadersKey": .string("bar")] + headers: ["someHeadersKey": 456, "someOtherHeadersKey": "bar"] ) ) let rxMessageFromSubscription = try #require(await rxMessageSubscription.first { _ in true }) @@ -169,7 +169,7 @@ struct IntegrationTests { params: .init( type: "heart", metadata: ["someMetadataKey": 123, "someOtherMetadataKey": "foo"], - headers: ["someHeadersKey": .number(456), "someOtherHeadersKey": .string("bar")] + headers: ["someHeadersKey": 456, "someOtherHeadersKey": "bar"] ) ) let rxReactionFromSubscription = try #require(await rxReactionSubscription.first { _ in true }) diff --git a/Tests/AblyChatTests/RoomReactionDTOTests.swift b/Tests/AblyChatTests/RoomReactionDTOTests.swift index 97b8061..d818b9b 100644 --- a/Tests/AblyChatTests/RoomReactionDTOTests.swift +++ b/Tests/AblyChatTests/RoomReactionDTOTests.swift @@ -87,7 +87,7 @@ enum RoomReactionDTOTests { ] ) - #expect(data == .init(headers: ["someStringKey": .string("someStringValue"), "someNumberKey": .number(123)])) + #expect(data == .init(headers: ["someStringKey": "someStringValue", "someNumberKey": 123])) } // MARK: - JSONCodable @@ -100,7 +100,7 @@ enum RoomReactionDTOTests { @Test func toJSONValue() { - let data = RoomReactionDTO.Extras(headers: ["someStringKey": .string("someStringValue"), "someNumberKey": .number(123)]) + let data = RoomReactionDTO.Extras(headers: ["someStringKey": "someStringValue", "someNumberKey": 123]) #expect(data.toJSONValue == [ "headers": [