Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 16 additions & 83 deletions packages/core/ios/Sources/AnyEncodable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
* Copyright 2019-2020 Datadog, Inc.
*/

import DatadogInternal
import Foundation

internal func castAttributesToSwift(_ attributes: NSDictionary) -> [String: Encodable] {
Expand All @@ -29,39 +30,39 @@ internal func castValueToSwift(_ value: Any) -> Encodable {
// Otherwise, cast by preserving its encoded value (and loosing type information)
casted = castByPreservingEncodedValue(attributeValue: value)
}

return casted
}

/// Casts `Any` value to `Encodable` by preserving its type information.
private func castByPreservingTypeInformation(attributeValue: Any) -> Encodable? {
switch attributeValue {
case let string as String: // unpacking `NSTaggedPointerString`
return string // cast to String
case let number as NSNumber: // unpacking `__NSCFNumber`
case let string as String: // unpacking `NSTaggedPointerString`
return string // cast to String
case let number as NSNumber: // unpacking `__NSCFNumber`
switch CFNumberGetType(number) {
case .charType:
return number.boolValue // cast to Bool
return number.boolValue // cast to Bool
case .sInt8Type:
return number.int8Value // cast to Int8
return number.int8Value // cast to Int8
case .sInt16Type:
return number.int16Value // cast to Int16
return number.int16Value // cast to Int16
case .sInt32Type:
return number.int32Value // cast to Int32
return number.int32Value // cast to Int32
case .sInt64Type:
return number.int64Value // cast to Int64
return number.int64Value // cast to Int64
case .shortType:
return number.uint16Value // cast to UInt 16
return number.uint16Value // cast to UInt 16
case .longType:
return number.uint32Value // cast to UInt32
return number.uint32Value // cast to UInt32
case .longLongType:
return number.uint64Value // cast to UInt64
return number.uint64Value // cast to UInt64
case .intType, .nsIntegerType, .cfIndexType:
return number.intValue // cast to Int
return number.intValue // cast to Int
case .floatType, .float32Type:
return number.floatValue // cast to Float
return number.floatValue // cast to Float
case .doubleType, .float64Type, .cgFloatType:
return number.doubleValue // cast to Double
return number.doubleValue // cast to Double
@unknown default:
return nil
}
Expand All @@ -75,71 +76,3 @@ private func castByPreservingTypeInformation(attributeValue: Any) -> Encodable?
private func castByPreservingEncodedValue(attributeValue: Any) -> Encodable {
return AnyEncodable(attributeValue)
}

/// Type erasing `Encodable` wrapper to bridge Objective-C's `Any` to Swift `Encodable`.
///
/// Inspired by `AnyCodable` by Flight-School (MIT):
/// https://github.com/Flight-School/AnyCodable/blob/master/Sources/AnyCodable/AnyEncodable.swift
internal class AnyEncodable: Encodable {
internal let value: Any

init(_ value: Any) {
self.value = value
}

func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()

switch value {
case let number as NSNumber:
try encodeNSNumber(number, into: &container)
case is NSNull, is Void:
try container.encodeNil()
case let string as String:
try container.encode(string)
case let date as Date:
try container.encode(date)
case let url as URL:
try container.encode(url)
case let array as [Any]:
try container.encode(array.map { AnyEncodable($0) })
case let dictionary as [String: Any]:
try container.encode(dictionary.mapValues { AnyEncodable($0) })
default:
let context = EncodingError.Context(
codingPath: container.codingPath,
debugDescription: "Value \(value) cannot be encoded - \(type(of: value)) is not supported by `AnyEncodable`."
)
throw EncodingError.invalidValue(value, context)
}
}
}

private func encodeNSNumber(_ nsnumber: NSNumber, into container: inout SingleValueEncodingContainer) throws {
switch CFNumberGetType(nsnumber) {
case .charType:
try container.encode(nsnumber.boolValue)
case .sInt8Type:
try container.encode(nsnumber.int8Value)
case .sInt16Type:
try container.encode(nsnumber.int16Value)
case .sInt32Type:
try container.encode(nsnumber.int32Value)
case .sInt64Type:
try container.encode(nsnumber.int64Value)
case .shortType:
try container.encode(nsnumber.uint16Value)
case .longType:
try container.encode(nsnumber.uint32Value)
case .longLongType:
try container.encode(nsnumber.uint64Value)
case .intType, .nsIntegerType, .cfIndexType:
try container.encode(nsnumber.intValue)
case .floatType, .float32Type:
try container.encode(nsnumber.floatValue)
case .doubleType, .float64Type, .cgFloatType:
try container.encode(nsnumber.doubleValue)
@unknown default:
return
}
}
1 change: 1 addition & 0 deletions packages/core/ios/Tests/AnyEncodableTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import XCTest
@testable import DatadogSDKReactNative
@testable import DatadogInternal

internal class AnyEncodableTests: XCTestCase {
// MARK: - Casting attributes
Expand Down
6 changes: 3 additions & 3 deletions packages/core/ios/Tests/DdSdkTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -601,7 +601,7 @@ class DdSdkTests: XCTestCase {
XCTAssertEqual(userInfo.extraInfo["extra-info-3"] as? Bool, true)

if let extraInfo4Encodable = userInfo.extraInfo["extra-info-4"]
as? DatadogSDKReactNative.AnyEncodable,
as? AnyEncodable,
let extraInfo4Dict = extraInfo4Encodable.value as? [String: Int]
{
XCTAssertEqual(extraInfo4Dict, ["nested-extra-info-1": 456])
Expand Down Expand Up @@ -664,7 +664,7 @@ class DdSdkTests: XCTestCase {
XCTAssertEqual(userInfo.extraInfo["extra-info-3"] as? Bool, true)

if let extraInfo4Encodable = userInfo.extraInfo["extra-info-4"]
as? DatadogSDKReactNative.AnyEncodable,
as? AnyEncodable,
let extraInfo4Dict = extraInfo4Encodable.value as? [String: Int]
{
XCTAssertEqual(extraInfo4Dict, ["nested-extra-info-1": 456])
Expand Down Expand Up @@ -718,7 +718,7 @@ class DdSdkTests: XCTestCase {
XCTAssertEqual(userInfo.extraInfo["extra-info-3"] as? Bool, true)

if let extraInfo4Encodable = userInfo.extraInfo["extra-info-4"]
as? DatadogSDKReactNative.AnyEncodable,
as? AnyEncodable,
let extraInfo4Dict = extraInfo4Encodable.value as? [String: Int]
{
XCTAssertEqual(extraInfo4Dict, ["nested-extra-info-1": 456])
Expand Down
Loading