diff --git a/Sources/AblyLiveObjects/Internal/InternalDefaultLiveMap.swift b/Sources/AblyLiveObjects/Internal/InternalDefaultLiveMap.swift index 53321db..312affa 100644 --- a/Sources/AblyLiveObjects/Internal/InternalDefaultLiveMap.swift +++ b/Sources/AblyLiveObjects/Internal/InternalDefaultLiveMap.swift @@ -887,31 +887,31 @@ internal final class InternalDefaultLiveMap: Sendable { // RTLM5d2b: If ObjectsMapEntry.data.boolean exists, return it if let boolean = entry.data?.boolean { - return .primitive(.bool(boolean)) + return .bool(boolean) } // RTLM5d2c: If ObjectsMapEntry.data.bytes exists, return it if let bytes = entry.data?.bytes { - return .primitive(.data(bytes)) + return .data(bytes) } // RTLM5d2d: If ObjectsMapEntry.data.number exists, return it if let number = entry.data?.number { - return .primitive(.number(number.doubleValue)) + return .number(number.doubleValue) } // RTLM5d2e: If ObjectsMapEntry.data.string exists, return it if let string = entry.data?.string { - return .primitive(.string(string)) + return .string(string) } // TODO: Needs specification (see https://github.com/ably/ably-cocoa-liveobjects-plugin/issues/46) if let json = entry.data?.json { switch json { case let .array(array): - return .primitive(.jsonArray(array)) + return .jsonArray(array) case let .object(object): - return .primitive(.jsonObject(object)) + return .jsonObject(object) } } diff --git a/Sources/AblyLiveObjects/Internal/InternalLiveMapValue.swift b/Sources/AblyLiveObjects/Internal/InternalLiveMapValue.swift index 657a37f..9ebeca8 100644 --- a/Sources/AblyLiveObjects/Internal/InternalLiveMapValue.swift +++ b/Sources/AblyLiveObjects/Internal/InternalLiveMapValue.swift @@ -2,7 +2,12 @@ import Foundation /// Same as the public ``LiveMapValue`` type but with associated values of internal type. internal enum InternalLiveMapValue: Sendable, Equatable { - case primitive(PrimitiveObjectValue) + case string(String) + case number(Double) + case bool(Bool) + case data(Data) + case jsonArray([JSONValue]) + case jsonObject([String: JSONValue]) case liveMap(InternalDefaultLiveMap) case liveCounter(InternalDefaultLiveCounter) @@ -13,8 +18,18 @@ internal enum InternalLiveMapValue: Sendable, Equatable { /// Needed in order to access the internals of user-provided LiveObject-valued LiveMap entries to extract their object ID. internal init(liveMapValue: LiveMapValue) { switch liveMapValue { - case let .primitive(primitiveValue): - self = .primitive(primitiveValue) + case let .string(value): + self = .string(value) + case let .number(value): + self = .number(value) + case let .bool(value): + self = .bool(value) + case let .data(value): + self = .data(value) + case let .jsonArray(value): + self = .jsonArray(value) + case let .jsonObject(value): + self = .jsonObject(value) case let .liveMap(publicLiveMap): guard let publicDefaultLiveMap = publicLiveMap as? PublicDefaultLiveMap else { // TODO: Try and remove this runtime check and know this type statically, see https://github.com/ably/ably-cocoa-liveobjects-plugin/issues/37 @@ -36,21 +51,18 @@ internal enum InternalLiveMapValue: Sendable, Equatable { internal var toObjectData: ObjectData { // RTO11f4c1: Create an ObjectsMapEntry for the current value switch self { - case let .primitive(primitiveValue): - switch primitiveValue { - case let .bool(value): - .init(boolean: value) - case let .data(value): - .init(bytes: value) - case let .number(value): - .init(number: NSNumber(value: value)) - case let .string(value): - .init(string: value) - case let .jsonArray(value): - .init(json: .array(value)) - case let .jsonObject(value): - .init(json: .object(value)) - } + case let .bool(value): + .init(boolean: value) + case let .data(value): + .init(bytes: value) + case let .number(value): + .init(number: NSNumber(value: value)) + case let .string(value): + .init(string: value) + case let .jsonArray(value): + .init(json: .array(value)) + case let .jsonObject(value): + .init(json: .object(value)) case let .liveMap(liveMap): // RTO11f4c1a: If the value is of type LiveMap, set ObjectsMapEntry.data.objectId to the objectId of that object .init(objectId: liveMap.objectID) @@ -62,14 +74,6 @@ internal enum InternalLiveMapValue: Sendable, Equatable { // MARK: - Convenience getters for associated values - /// If this `InternalLiveMapValue` has case `primitive`, this returns the associated value. Else, it returns `nil`. - internal var primitiveValue: PrimitiveObjectValue? { - if case let .primitive(value) = self { - return value - } - return nil - } - /// If this `InternalLiveMapValue` has case `liveMap`, this returns the associated value. Else, it returns `nil`. internal var liveMapValue: InternalDefaultLiveMap? { if case let .liveMap(value) = self { @@ -86,54 +90,76 @@ internal enum InternalLiveMapValue: Sendable, Equatable { return nil } - /// If this `InternalLiveMapValue` has case `primitive` with a string value, this returns that value. Else, it returns `nil`. + /// If this `InternalLiveMapValue` has case `string`, this returns that value. Else, it returns `nil`. internal var stringValue: String? { - primitiveValue?.stringValue + if case let .string(value) = self { + return value + } + return nil } - /// If this `InternalLiveMapValue` has case `primitive` with a number value, this returns that value. Else, it returns `nil`. + /// If this `InternalLiveMapValue` has case `number`, this returns that value. Else, it returns `nil`. internal var numberValue: Double? { - primitiveValue?.numberValue + if case let .number(value) = self { + return value + } + return nil } - /// If this `InternalLiveMapValue` has case `primitive` with a boolean value, this returns that value. Else, it returns `nil`. + /// If this `InternalLiveMapValue` has case `bool`, this returns that value. Else, it returns `nil`. internal var boolValue: Bool? { - primitiveValue?.boolValue + if case let .bool(value) = self { + return value + } + return nil } - /// If this `InternalLiveMapValue` has case `primitive` with a data value, this returns that value. Else, it returns `nil`. + /// If this `InternalLiveMapValue` has case `data`, this returns that value. Else, it returns `nil`. internal var dataValue: Data? { - primitiveValue?.dataValue + if case let .data(value) = self { + return value + } + return nil } - /// If this `InternalLiveMapValue` has case `primitive` with a JSON array value, this returns that value. Else, it returns `nil`. + /// If this `InternalLiveMapValue` has case `jsonArray`, this returns that value. Else, it returns `nil`. internal var jsonArrayValue: [JSONValue]? { - primitiveValue?.jsonArrayValue + if case let .jsonArray(value) = self { + return value + } + return nil } - /// If this `InternalLiveMapValue` has case `primitive` with a JSON object value, this returns that value. Else, it returns `nil`. + /// If this `InternalLiveMapValue` has case `jsonObject`, this returns that value. Else, it returns `nil`. internal var jsonObjectValue: [String: JSONValue]? { - primitiveValue?.jsonObjectValue + if case let .jsonObject(value) = self { + return value + } + return nil } // MARK: - Equatable Implementation internal static func == (lhs: InternalLiveMapValue, rhs: InternalLiveMapValue) -> Bool { - switch lhs { - case let .primitive(lhsValue): - if case let .primitive(rhsValue) = rhs, lhsValue == rhsValue { - return true - } - case let .liveMap(lhsMap): - if case let .liveMap(rhsMap) = rhs, lhsMap === rhsMap { - return true - } - case let .liveCounter(lhsCounter): - if case let .liveCounter(rhsCounter) = rhs, lhsCounter === rhsCounter { - return true - } + switch (lhs, rhs) { + case let (.string(lhsValue), .string(rhsValue)): + lhsValue == rhsValue + case let (.number(lhsValue), .number(rhsValue)): + lhsValue == rhsValue + case let (.bool(lhsValue), .bool(rhsValue)): + lhsValue == rhsValue + case let (.data(lhsValue), .data(rhsValue)): + lhsValue == rhsValue + case let (.jsonArray(lhsValue), .jsonArray(rhsValue)): + lhsValue == rhsValue + case let (.jsonObject(lhsValue), .jsonObject(rhsValue)): + lhsValue == rhsValue + case let (.liveMap(lhsMap), .liveMap(rhsMap)): + lhsMap === rhsMap + case let (.liveCounter(lhsCounter), .liveCounter(rhsCounter)): + lhsCounter === rhsCounter + default: + false } - - return false } } diff --git a/Sources/AblyLiveObjects/Public/Public Proxy Objects/InternalLiveMapValue+ToPublic.swift b/Sources/AblyLiveObjects/Public/Public Proxy Objects/InternalLiveMapValue+ToPublic.swift index e5b599d..bd88d2f 100644 --- a/Sources/AblyLiveObjects/Public/Public Proxy Objects/InternalLiveMapValue+ToPublic.swift +++ b/Sources/AblyLiveObjects/Public/Public Proxy Objects/InternalLiveMapValue+ToPublic.swift @@ -20,8 +20,18 @@ internal extension InternalLiveMapValue { /// Fetches the cached public object that wraps this `InternalLiveMapValue`'s associated value, creating a new public object if there isn't already one. func toPublic(creationArgs: PublicValueCreationArgs) -> LiveMapValue { switch self { - case let .primitive(primitive): - .primitive(primitive) + case let .string(value): + .string(value) + case let .number(value): + .number(value) + case let .bool(value): + .bool(value) + case let .data(value): + .data(value) + case let .jsonArray(value): + .jsonArray(value) + case let .jsonObject(value): + .jsonObject(value) case let .liveMap(internalLiveMap): .liveMap( PublicObjectsStore.shared.getOrCreateMap( diff --git a/Sources/AblyLiveObjects/Public/PublicTypes.swift b/Sources/AblyLiveObjects/Public/PublicTypes.swift index be64bca..6e79b29 100644 --- a/Sources/AblyLiveObjects/Public/PublicTypes.swift +++ b/Sources/AblyLiveObjects/Public/PublicTypes.swift @@ -82,22 +82,40 @@ public protocol RealtimeObjects: Sendable { } /// Represents the type of data stored for a given key in a ``LiveMap``. -/// It may be a primitive value (``PrimitiveObjectValue``), or another ``LiveObject``. +/// It may be a primitive value (string, number, boolean, binary data, JSON array, or JSON object), or another ``LiveObject``. +/// +/// `LiveMapValue` implements Swift's `ExpressibleBy*Literal` protocols. This, in combination with `JSONValue`'s conformance to these protocols, allows you to write type-safe map values using familiar syntax. For example: +/// +/// ```swift +/// let map = try await channel.objects.createMap(entries: [ +/// "someStringKey": "someString", +/// "someIntegerKey": 123, +/// "someFloatKey": 123.456, +/// "someTrueKey": true, +/// "someFalseKey": false, +/// "someJSONObjectKey": [ +/// "someNestedJSONObjectKey": [ +/// "someOtherKey": "someOtherValue", +/// ], +/// ], +/// "someJSONArrayKey": [ +/// "foo", +/// 42, +/// ], +/// ]) +/// ``` public enum LiveMapValue: Sendable, Equatable { - case primitive(PrimitiveObjectValue) + case string(String) + case number(Double) + case bool(Bool) + case data(Data) + case jsonArray([JSONValue]) + case jsonObject([String: JSONValue]) case liveMap(any LiveMap) case liveCounter(any LiveCounter) // MARK: - Convenience getters for associated values - /// If this `LiveMapValue` has case `primitive`, this returns the associated value. Else, it returns `nil`. - public var primitiveValue: PrimitiveObjectValue? { - if case let .primitive(value) = self { - return value - } - return nil - } - /// If this `LiveMapValue` has case `liveMap`, this returns the associated value. Else, it returns `nil`. public var liveMapValue: (any LiveMap)? { if case let .liveMap(value) = self { @@ -114,45 +132,115 @@ public enum LiveMapValue: Sendable, Equatable { return nil } - /// If this `LiveMapValue` has case `primitive` with a string value, this returns that value. Else, it returns `nil`. + /// If this `LiveMapValue` has case `string`, this returns the associated value. Else, it returns `nil`. public var stringValue: String? { - primitiveValue?.stringValue + if case let .string(value) = self { + return value + } + return nil } - /// If this `LiveMapValue` has case `primitive` with a number value, this returns that value. Else, it returns `nil`. + /// If this `LiveMapValue` has case `number`, this returns the associated value. Else, it returns `nil`. public var numberValue: Double? { - primitiveValue?.numberValue + if case let .number(value) = self { + return value + } + return nil } - /// If this `LiveMapValue` has case `primitive` with a boolean value, this returns that value. Else, it returns `nil`. + /// If this `LiveMapValue` has case `bool`, this returns the associated value. Else, it returns `nil`. public var boolValue: Bool? { - primitiveValue?.boolValue + if case let .bool(value) = self { + return value + } + return nil } - /// If this `LiveMapValue` has case `primitive` with a data value, this returns that value. Else, it returns `nil`. + /// If this `LiveMapValue` has case `data`, this returns the associated value. Else, it returns `nil`. public var dataValue: Data? { - primitiveValue?.dataValue + if case let .data(value) = self { + return value + } + return nil + } + + /// If this `LiveMapValue` has case `jsonArray`, this returns the associated value. Else, it returns `nil`. + public var jsonArrayValue: [JSONValue]? { + if case let .jsonArray(value) = self { + return value + } + return nil + } + + /// If this `LiveMapValue` has case `jsonObject`, this returns the associated value. Else, it returns `nil`. + public var jsonObjectValue: [String: JSONValue]? { + if case let .jsonObject(value) = self { + return value + } + return nil } // MARK: - Equatable Implementation public static func == (lhs: LiveMapValue, rhs: LiveMapValue) -> Bool { - switch lhs { - case let .primitive(lhsValue): - if case let .primitive(rhsValue) = rhs, lhsValue == rhsValue { - return true - } - case let .liveMap(lhsMap): - if case let .liveMap(rhsMap) = rhs, lhsMap === rhsMap { - return true - } - case let .liveCounter(lhsCounter): - if case let .liveCounter(rhsCounter) = rhs, lhsCounter === rhsCounter { - return true - } + switch (lhs, rhs) { + case let (.string(lhsValue), .string(rhsValue)): + lhsValue == rhsValue + case let (.number(lhsValue), .number(rhsValue)): + lhsValue == rhsValue + case let (.bool(lhsValue), .bool(rhsValue)): + lhsValue == rhsValue + case let (.data(lhsValue), .data(rhsValue)): + lhsValue == rhsValue + case let (.jsonArray(lhsValue), .jsonArray(rhsValue)): + lhsValue == rhsValue + case let (.jsonObject(lhsValue), .jsonObject(rhsValue)): + lhsValue == rhsValue + case let (.liveMap(lhsMap), .liveMap(rhsMap)): + lhsMap === rhsMap + case let (.liveCounter(lhsCounter), .liveCounter(rhsCounter)): + lhsCounter === rhsCounter + default: + false } + } +} + +// MARK: - ExpressibleBy*Literal conformances + +extension LiveMapValue: ExpressibleByDictionaryLiteral { + public init(dictionaryLiteral elements: (String, JSONValue)...) { + self = .jsonObject(.init(uniqueKeysWithValues: elements)) + } +} + +extension LiveMapValue: ExpressibleByArrayLiteral { + public init(arrayLiteral elements: JSONValue...) { + self = .jsonArray(elements) + } +} - return false +extension LiveMapValue: ExpressibleByStringLiteral { + public init(stringLiteral value: String) { + self = .string(value) + } +} + +extension LiveMapValue: ExpressibleByIntegerLiteral { + public init(integerLiteral value: Int) { + self = .number(Double(value)) + } +} + +extension LiveMapValue: ExpressibleByFloatLiteral { + public init(floatLiteral value: Double) { + self = .number(value) + } +} + +extension LiveMapValue: ExpressibleByBooleanLiteral { + public init(booleanLiteral value: Bool) { + self = .bool(value) } } @@ -226,7 +314,7 @@ public protocol BatchContextLiveCounter: AnyObject, Sendable { /// Conflicts in a LiveMap are automatically resolved with last-write-wins (LWW) semantics, /// meaning that if two clients update the same key in the map, the update with the most recent timestamp wins. /// -/// Keys must be strings. Values can be another ``LiveObject``, or a primitive type, such as a string, number, boolean, JSON-serializable object or array, or binary data (see ``PrimitiveObjectValue``). +/// Keys must be strings. Values can be another ``LiveObject``, or a primitive type, such as a string, number, boolean, JSON-serializable object or array, or binary data. public protocol LiveMap: LiveObject where Update == LiveMapUpdate { /// Returns the value associated with a given key. Returns `nil` if the key doesn't exist in a map or if the associated ``LiveObject`` has been deleted. /// @@ -285,66 +373,6 @@ public protocol LiveMapUpdate: Sendable { var update: [String: LiveMapUpdateAction] { get } } -/// Represents a primitive value that can be stored in a ``LiveMap``. -public enum PrimitiveObjectValue: Sendable, Equatable { - case string(String) - case number(Double) - case bool(Bool) - case data(Data) - case jsonArray([JSONValue]) - case jsonObject([String: JSONValue]) - - // MARK: - Convenience getters for associated values - - /// If this `PrimitiveObjectValue` has case `string`, this returns the associated value. Else, it returns `nil`. - public var stringValue: String? { - if case let .string(value) = self { - return value - } - return nil - } - - /// If this `PrimitiveObjectValue` has case `number`, this returns the associated value. Else, it returns `nil`. - public var numberValue: Double? { - if case let .number(value) = self { - return value - } - return nil - } - - /// If this `PrimitiveObjectValue` has case `bool`, this returns the associated value. Else, it returns `nil`. - public var boolValue: Bool? { - if case let .bool(value) = self { - return value - } - return nil - } - - /// If this `PrimitiveObjectValue` has case `data`, this returns the associated value. Else, it returns `nil`. - public var dataValue: Data? { - if case let .data(value) = self { - return value - } - return nil - } - - /// If this `PrimitiveObjectValue` has case `jsonArray`, this returns the associated value. Else, it returns `nil`. - public var jsonArrayValue: [JSONValue]? { - if case let .jsonArray(value) = self { - return value - } - return nil - } - - /// If this `PrimitiveObjectValue` has case `jsonObject`, this returns the associated value. Else, it returns `nil`. - public var jsonObjectValue: [String: JSONValue]? { - if case let .jsonObject(value) = self { - return value - } - return nil - } -} - /// The `LiveCounter` class represents a counter that can be incremented or decremented and is synchronized across clients in realtime. public protocol LiveCounter: LiveObject where Update == LiveCounterUpdate { /// Returns the current value of the counter. diff --git a/Tests/AblyLiveObjectsTests/AblyLiveObjectsTests.swift b/Tests/AblyLiveObjectsTests/AblyLiveObjectsTests.swift index c9cb540..e71959d 100644 --- a/Tests/AblyLiveObjectsTests/AblyLiveObjectsTests.swift +++ b/Tests/AblyLiveObjectsTests/AblyLiveObjectsTests.swift @@ -139,13 +139,13 @@ struct AblyLiveObjectsTests { // Create a map and check its initial entries let map = try await channel.objects.createMap(entries: [ - "boolKey": .primitive(.bool(true)), - "numberKey": .primitive(.number(10)), + "boolKey": true, + "numberKey": 10, ]) #expect( try Dictionary(uniqueKeysWithValues: map.entries) == [ - "boolKey": .primitive(.bool(true)), - "numberKey": .primitive(.number(10)), + "boolKey": true, + "numberKey": 10, ], ) let mapSubscription = try map.updates() @@ -162,8 +162,8 @@ struct AblyLiveObjectsTests { #expect(mapUpdate.update == ["counterKey": .updated]) #expect( try Dictionary(uniqueKeysWithValues: map.entries) == [ - "boolKey": .primitive(.bool(true)), - "numberKey": .primitive(.number(10)), + "boolKey": true, + "numberKey": 10, "counterKey": .liveCounter(counter), ], ) @@ -180,7 +180,7 @@ struct AblyLiveObjectsTests { #expect(mapRemoveUpdate.update == ["boolKey": .removed]) #expect( try Dictionary(uniqueKeysWithValues: map.entries) == [ - "numberKey": .primitive(.number(10)), + "numberKey": 10, "counterKey": .liveCounter(counter), ], ) diff --git a/Tests/AblyLiveObjectsTests/InternalDefaultLiveMapTests.swift b/Tests/AblyLiveObjectsTests/InternalDefaultLiveMapTests.swift index daa2f50..9d982e9 100644 --- a/Tests/AblyLiveObjectsTests/InternalDefaultLiveMapTests.swift +++ b/Tests/AblyLiveObjectsTests/InternalDefaultLiveMapTests.swift @@ -1237,7 +1237,7 @@ struct InternalDefaultLiveMapTests { let coreSDK = MockCoreSDK(channelState: channelState) await #expect { - try await map.set(key: "test", value: .primitive(.string("value")), coreSDK: coreSDK) + try await map.set(key: "test", value: .string("value"), coreSDK: coreSDK) } throws: { error in guard let errorInfo = error as? ARTErrorInfo else { return false @@ -1264,16 +1264,16 @@ struct InternalDefaultLiveMapTests { (value: .liveMap(.createZeroValued(objectID: "map:test@123", logger: TestLogger(), userCallbackQueue: .main, clock: MockSimpleClock())), expectedData: .init(objectId: "map:test@123")), (value: .liveCounter(.createZeroValued(objectID: "map:test@123", logger: TestLogger(), userCallbackQueue: .main, clock: MockSimpleClock())), expectedData: .init(objectId: "map:test@123")), // RTLM20e5b - (value: .primitive(.jsonArray(["test"])), expectedData: .init(json: .array(["test"]))), - (value: .primitive(.jsonObject(["foo": "bar"])), expectedData: .init(json: .object(["foo": "bar"]))), + (value: .jsonArray(["test"]), expectedData: .init(json: .array(["test"]))), + (value: .jsonObject(["foo": "bar"]), expectedData: .init(json: .object(["foo": "bar"]))), // RTLM20e5c - (value: .primitive(.string("test")), expectedData: .init(string: "test")), + (value: .string("test"), expectedData: .init(string: "test")), // RTLM20e5d - (value: .primitive(.number(42.5)), expectedData: .init(number: NSNumber(value: 42.5))), + (value: .number(42.5), expectedData: .init(number: NSNumber(value: 42.5))), // RTLM20e5e - (value: .primitive(.bool(true)), expectedData: .init(boolean: true)), + (value: .bool(true), expectedData: .init(boolean: true)), // RTLM20e5f - (value: .primitive(.data(Data([0x01, 0x02]))), expectedData: .init(bytes: Data([0x01, 0x02]))), + (value: .data(Data([0x01, 0x02])), expectedData: .init(bytes: Data([0x01, 0x02]))), ] as [(value: InternalLiveMapValue, expectedData: ObjectData)]) func publishesCorrectObjectMessageForDifferentValueTypes(value: InternalLiveMapValue, expectedData: ObjectData) async throws { let logger = TestLogger() @@ -1317,7 +1317,7 @@ struct InternalDefaultLiveMapTests { } await #expect { - try await map.set(key: "testKey", value: .primitive(.string("testValue")), coreSDK: coreSDK) + try await map.set(key: "testKey", value: .string("testValue"), coreSDK: coreSDK) } throws: { error in guard let errorInfo = error as? ARTErrorInfo else { return false diff --git a/Tests/AblyLiveObjectsTests/InternalDefaultRealtimeObjectsTests.swift b/Tests/AblyLiveObjectsTests/InternalDefaultRealtimeObjectsTests.swift index fee10e3..b4bedee 100644 --- a/Tests/AblyLiveObjectsTests/InternalDefaultRealtimeObjectsTests.swift +++ b/Tests/AblyLiveObjectsTests/InternalDefaultRealtimeObjectsTests.swift @@ -1048,7 +1048,7 @@ struct InternalDefaultRealtimeObjectsTests { func throwsIfChannelIsInInvalidState(channelState: ARTRealtimeChannelState) async throws { let realtimeObjects = InternalDefaultRealtimeObjectsTests.createDefaultRealtimeObjects() let coreSDK = MockCoreSDK(channelState: channelState) - let entries: [String: InternalLiveMapValue] = ["testKey": .primitive(.string("testValue"))] + let entries: [String: InternalLiveMapValue] = ["testKey": .string("testValue")] await #expect { _ = try await realtimeObjects.createMap(entries: entries, coreSDK: coreSDK) @@ -1078,7 +1078,7 @@ struct InternalDefaultRealtimeObjectsTests { // Call createMap let returnedMap = try await realtimeObjects.createMap( entries: [ - "stringKey": .primitive(.string("stringValue")), + "stringKey": .string("stringValue"), ], coreSDK: coreSDK, ) @@ -1098,7 +1098,7 @@ struct InternalDefaultRealtimeObjectsTests { ]) // Verify initial value was merged per RTO11h3a - #expect(returnedMap.testsOnly_data == ["stringKey": .init(data: .init(string: "stringValue"))]) + #expect(returnedMap.testsOnly_data == ["stringKey": InternalObjectsMapEntry(data: ObjectData(string: "stringValue"))]) // Verify object was added to pool per RTO11h3b #expect(realtimeObjects.testsOnly_objectsPool.entries[objectID]?.mapValue === returnedMap) @@ -1156,7 +1156,7 @@ struct InternalDefaultRealtimeObjectsTests { } // Call createMap - the publishHandler will create the object with the generated ID - let result = try await realtimeObjects.createMap(entries: ["testKey": .primitive(.string("testValue"))], coreSDK: coreSDK) + let result = try await realtimeObjects.createMap(entries: ["testKey": .string("testValue")], coreSDK: coreSDK) // Verify ObjectMessage was published #expect(publishedMessages.count == 1) diff --git a/Tests/AblyLiveObjectsTests/JS Integration Tests/ObjectsIntegrationTests.swift b/Tests/AblyLiveObjectsTests/JS Integration Tests/ObjectsIntegrationTests.swift index 7f0e984..016f06c 100644 --- a/Tests/AblyLiveObjectsTests/JS Integration Tests/ObjectsIntegrationTests.swift +++ b/Tests/AblyLiveObjectsTests/JS Integration Tests/ObjectsIntegrationTests.swift @@ -137,52 +137,52 @@ private let primitiveKeyData: [(key: String, data: [String: JSONValue], liveMapV ( key: "stringKey", data: ["string": .string("stringValue")], - liveMapValue: .primitive(.string("stringValue")) + liveMapValue: "stringValue" ), ( key: "emptyStringKey", data: ["string": .string("")], - liveMapValue: .primitive(.string("")) + liveMapValue: "" ), ( key: "bytesKey", data: ["bytes": .string("eyJwcm9kdWN0SWQiOiAiMDAxIiwgInByb2R1Y3ROYW1lIjogImNhciJ9")], - liveMapValue: .primitive(.data(Data(base64Encoded: "eyJwcm9kdWN0SWQiOiAiMDAxIiwgInByb2R1Y3ROYW1lIjogImNhciJ9")!)) + liveMapValue: .data(Data(base64Encoded: "eyJwcm9kdWN0SWQiOiAiMDAxIiwgInByb2R1Y3ROYW1lIjogImNhciJ9")!) ), ( key: "emptyBytesKey", data: ["bytes": .string("")], - liveMapValue: .primitive(.data(Data(base64Encoded: "")!)) + liveMapValue: .data(Data(base64Encoded: "")!) ), ( key: "maxSafeIntegerKey", data: ["number": .number(.init(value: Int.max))], - liveMapValue: .primitive(.number(Double(Int.max))) + liveMapValue: .number(Double(Int.max)) ), ( key: "negativeMaxSafeIntegerKey", data: ["number": .number(.init(value: -Int.max))], - liveMapValue: .primitive(.number(-Double(Int.max))) + liveMapValue: .number(-Double(Int.max)) ), ( key: "numberKey", data: ["number": .number(1)], - liveMapValue: .primitive(.number(1)) + liveMapValue: 1 ), ( key: "zeroKey", data: ["number": .number(0)], - liveMapValue: .primitive(.number(0)) + liveMapValue: 0 ), ( key: "trueKey", data: ["boolean": .bool(true)], - liveMapValue: .primitive(.bool(true)) + liveMapValue: true ), ( key: "falseKey", data: ["boolean": .bool(false)], - liveMapValue: .primitive(.bool(false)) + liveMapValue: false ), ] @@ -422,7 +422,7 @@ private struct ObjectsIntegrationTests { } // MAP_CREATE - let map = try await objects.createMap(entries: ["shouldStay": .primitive(.string("foo")), "shouldDelete": .primitive(.string("bar"))]) + let map = try await objects.createMap(entries: ["shouldStay": "foo", "shouldDelete": "bar"]) // COUNTER_CREATE let counter = try await objects.createCounter(count: 1) @@ -449,7 +449,7 @@ private struct ObjectsIntegrationTests { } // Perform the operations and await the promise - async let setAnotherKeyPromise: Void = map.set(key: "anotherKey", value: .primitive(.string("baz"))) + async let setAnotherKeyPromise: Void = map.set(key: "anotherKey", value: "baz") async let removeKeyPromise: Void = map.remove(key: "shouldDelete") async let incrementPromise: Void = counter.increment(amount: 10) _ = try await (setAnotherKeyPromise, removeKeyPromise, incrementPromise, operationsAppliedPromise) @@ -2555,16 +2555,16 @@ private struct ObjectsIntegrationTests { let actualValue = try #require(try root.get(key: keyData.key)) switch keyData.liveMapValue { - case let .primitive(.data(expectedData)): + case let .data(expectedData): let actualData = try #require(actualValue.dataValue) #expect(actualData == expectedData, "Check root has correct value for \"\(keyData.key)\" key after LiveMap.set call") - case let .primitive(.string(expectedString)): + case let .string(expectedString): let actualString = try #require(actualValue.stringValue) #expect(actualString == expectedString, "Check root has correct value for \"\(keyData.key)\" key after LiveMap.set call") - case let .primitive(.number(expectedNumber)): + case let .number(expectedNumber): let actualNumber = try #require(actualValue.numberValue) #expect(actualNumber == expectedNumber, "Check root has correct value for \"\(keyData.key)\" key after LiveMap.set call") - case let .primitive(.bool(expectedBool)): + case let .bool(expectedBool): let actualBool = try #require(actualValue.boolValue as Bool?) #expect(actualBool == expectedBool, "Check root has correct value for \"\(keyData.key)\" key after LiveMap.set call") default: @@ -2947,19 +2947,19 @@ private struct ObjectsIntegrationTests { let actualValue = try map.get(key: key) switch expectedValue { - case let .primitive(.data(expectedData)): + case let .data(expectedData): let actualData = try #require(actualValue?.dataValue) #expect(actualData == expectedData, "Check map #\(i + 1) has correct value for \"\(key)\" key") - case let .primitive(.string(expectedString)): + case let .string(expectedString): let actualString = try #require(actualValue?.stringValue) #expect(actualString == expectedString, "Check map #\(i + 1) has correct value for \"\(key)\" key") - case let .primitive(.number(expectedNumber)): + case let .number(expectedNumber): let actualNumber = try #require(actualValue?.numberValue) #expect(actualNumber == expectedNumber, "Check map #\(i + 1) has correct value for \"\(key)\" key") - case let .primitive(.bool(expectedBool)): + case let .bool(expectedBool): let actualBool = try #require(actualValue?.boolValue as Bool?) #expect(actualBool == expectedBool, "Check map #\(i + 1) has correct value for \"\(key)\" key") - case .primitive(.jsonArray), .primitive(.jsonObject): + case .jsonArray, .jsonObject: Issue.record("JSON array/object primitives not expected in test data") case .liveCounter, .liveMap: Issue.record("Nested objects not expected in primitive test data") @@ -3032,7 +3032,7 @@ private struct ObjectsIntegrationTests { async let mapCreatedPromise: Void = waitForMapKeyUpdate(mapCreatedPromiseUpdates, "map") let counter = try await objects.createCounter() - let map = try await objects.createMap(entries: ["foo": .primitive(.string("bar")), "baz": .liveCounter(counter)]) + let map = try await objects.createMap(entries: ["foo": "bar", "baz": .liveCounter(counter)]) try await root.set(key: "map", value: .liveMap(map)) _ = await mapCreatedPromise @@ -3058,7 +3058,7 @@ private struct ObjectsIntegrationTests { internallyTypedObjects.testsOnly_overridePublish(with: { _ in }) // prevent publishing of ops to realtime so we guarantee that the initial value doesn't come from a CREATE op - let map = try await objects.createMap(entries: ["foo": .primitive(.string("bar"))]) + let map = try await objects.createMap(entries: ["foo": "bar"]) #expect(try #require(map.get(key: "foo")?.stringValue) == "bar", "Check map has expected initial value") }, ), @@ -3103,7 +3103,7 @@ private struct ObjectsIntegrationTests { } }) - let map = try await objects.createMap(entries: ["foo": .primitive(.string("bar"))]) + let map = try await objects.createMap(entries: ["foo": "bar"]) // Map should be created with forged initial value instead of the actual one #expect(try map.get(key: "foo") == nil, "Check key \"foo\" was not set on a map client-side") @@ -3127,7 +3127,7 @@ private struct ObjectsIntegrationTests { }) // Create map locally, should have an initial value set - let map = try await objects.createMap(entries: ["foo": .primitive(.string("bar"))]) + let map = try await objects.createMap(entries: ["foo": "bar"]) let internalMap = try #require(map as? PublicDefaultLiveMap) let mapId = internalMap.proxied.objectID diff --git a/Tests/AblyLiveObjectsTests/ObjectCreationHelpersTests.swift b/Tests/AblyLiveObjectsTests/ObjectCreationHelpersTests.swift index 126b942..d51efec 100644 --- a/Tests/AblyLiveObjectsTests/ObjectCreationHelpersTests.swift +++ b/Tests/AblyLiveObjectsTests/ObjectCreationHelpersTests.swift @@ -36,16 +36,16 @@ struct ObjectCreationHelpersTests { // RTO11f4c1a "counterRef": .liveCounter(referencedCounter), // RTO11f4c1b - "jsonArrayKey": .primitive(.jsonArray([.string("arrayItem1"), .string("arrayItem2")])), - "jsonObjectKey": .primitive(.jsonObject(["nestedKey": .string("nestedValue")])), + "jsonArrayKey": .jsonArray([.string("arrayItem1"), .string("arrayItem2")]), + "jsonObjectKey": .jsonObject(["nestedKey": .string("nestedValue")]), // RTO11f4c1c - "stringKey": .primitive(.string("stringValue")), + "stringKey": .string("stringValue"), // RTO11f4c1d - "numberKey": .primitive(.number(42.5)), + "numberKey": .number(42.5), // RTO11f4c1e - "booleanKey": .primitive(.bool(true)), + "booleanKey": .bool(true), // RTO11f4c1f - "dataKey": .primitive(.data(Data([0x01, 0x02, 0x03]))), + "dataKey": .data(Data([0x01, 0x02, 0x03])), ], timestamp: timestamp, )