Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace CodableValue with AnyCodable #1081

Merged
merged 4 commits into from
Dec 16, 2022

Conversation

maxep
Copy link
Member

@maxep maxep commented Dec 8, 2022

What and why?

Share AnyCodable logic in DatadogInternal.

While migrating the WebView tracking to v2, I realise that we were doing such heavy transformation:

let jsonData = try JSONSerialization.data(withJSONObject: event, options: [])
let encodableEvent = try JSONDecoder().decode(AnyCodable.self, from: jsonData)
writer.write(value: encodableEvent)

The Flight-School/AnyCodable, which was already inspiring our DatadogObjc.AnyEncodable, is capable of serialising Any and so we could use that implementation to write [String: Any] directly.

Moreover, CodableValue is a similar type-erasure than Flight-School/AnyCodable that can be use to decode JSON into [String: Encodable].

For those 2 reasons, I would like to consider the AnyCodable as a solution for [String: Any] serialisation and deserialisation.

How?

Replace CodableValue and DatadogObjc.AnyEncodable with implementation from Flight-School/AnyCodable.

Extensions for ExpressibleBy*Literal and Hashable have been stripped out, but the Equatable extensions remains as it can be useful for data comparison on the message bus.

Model Generator

The generator now uses the shared AnyCodable and DynamicCodingKey.

Review checklist

  • Feature or bugfix MUST have appropriate tests (unit, integration)
  • Make sure each commit and the PR mention the Issue number or JIRA reference
  • Add CHANGELOG entry for user facing changes

Custom CI job configuration (optional)

  • Run unit tests
  • Run integration tests
  • Run smoke tests

@maxep maxep self-assigned this Dec 8, 2022
@maxep maxep force-pushed the maxep/RUMM-2745/crash-reporting-context branch 2 times, most recently from 153e0e8 to ee76468 Compare December 9, 2022 09:52
@maxep maxep force-pushed the maxep/RUMM-2745/any-codable-erasure branch 2 times, most recently from 01d7afc to 59bd2c9 Compare December 9, 2022 10:00
@datadog-datadog-prod-us1
Copy link

datadog-datadog-prod-us1 bot commented Dec 9, 2022

Datadog Report

Branch report: maxep/RUMM-2745/any-codable-erasure
Commit report: 362fa40

dd-sdk-ios 0 Failed, 0 New Flaky, 58 Passed, 0 Skipped, 6.85s Wall Time

Base automatically changed from maxep/RUMM-2745/crash-reporting-context to develop December 9, 2022 13:18
Copy link
Member

@ncreated ncreated left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@maxep could you provide more context on "Why?" I get the idea, but don't fully understand why we need this new vs existing code.

@maxep
Copy link
Member Author

maxep commented Dec 12, 2022

@ncreated Indeed, it was missing! I've updated the description.
I'm currently testing this code in the web-view tracking migration to v2, it's ready for review but not yet validated on my side. I might make further modification, or even close it if that doesn't fir our need.

// The list of NSNumber encoding value can be found in the following links:
// - https://developer.apple.com/documentation/foundation/nsnumber
// - https://github.com/gnustep/libs-base/blob/master/Source/NSNumber.m
switch Character(Unicode.Scalar(UInt8(nsnumber.objCType.pointee))) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works best than CFNumberGetType(nsnumber) when using Swift value:

let nsnumber = UInt64.max as NSNumber
CFNumberGetType(nsnumber) == .sInt64Type // should be .longLongType

Character(Unicode.Scalar(UInt8(nsnumber.objCType.pointee))) == "Q" // correct

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And this relates to your other mention @maxep, right?

The reason being that the webview bridge returns a JSON as objc dictionary with NSNumber, in Swift a NSNumber with value 1 or 0 can be cast to Bool which would be encoded as true or false and lead to a type mismatch

If so, could we annotate this nsnumber.objCType.pointee code with relevant comment? It seems more important than it looks.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, so an unsigned int does not get encoded as signed! I can improve the comment 👍

@maxep maxep marked this pull request as ready for review December 13, 2022 10:16
@maxep maxep requested a review from a team as a code owner December 13, 2022 10:16
@maxep
Copy link
Member Author

maxep commented Dec 13, 2022

This is ready for review, it works well for our different use-cases 👍 We can now encode dictionary coming from WebView tracking directly. The models have been updated to use the interface shared in DatadogInternal.

Some changes in AnyEncodable were necessary to be usable in both Swift and Objc: In both cases, a value is first cast to a NSNumber so it can infer the right type using the objCType. The reason being that the webview bridge returns a JSON as objc dictionary with NSNumber, in Swift a NSNumber with value 1 or 0 can be cast to Bool which would be encoded as true or false and lead to a type mismatch:

let value: Any = NSNumber(value: 1)
switch value {
case let bool as Bool:
    encode(bool) // will encode 'true'
case let nsnumber as NSNumber:
    encode(nsnumber) // won't reach :(
default:
    break
}

@maxep maxep force-pushed the maxep/RUMM-2745/any-codable-erasure branch 2 times, most recently from 907fda8 to 618ec72 Compare December 13, 2022 14:05
Co-Authored-By: Maciek Grzybowski <maciek.grzybowski@datadoghq.com>
@maxep maxep force-pushed the maxep/RUMM-2745/any-codable-erasure branch from 618ec72 to cf8c6ae Compare December 15, 2022 09:54
Copy link
Member

@ncreated ncreated left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All looks good with only minor suggestions. Yes, it makes total sense to have more versatile support for any en-, de-, and codable 👍. Well done 💪.

Sources/Datadog/DatadogInternal/Codable/AnyEncodable.swift Outdated Show resolved Hide resolved
internal let swiftModel: Foo.Bar
internal var swiftModel: Foo.Bar
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks unrelated - does it mean rum-models-generator tests were red 😥?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.. It's not being tested in CI, which is ok imo, or maybe we could have a conditional workflow!

Copy link
Member

@ncreated ncreated Dec 15, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have the run_tools_tests lane which looks perfect candidate for it and can be easily conditioned. How about creating separate task to tackle it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ncreated I'm adding this to CI in this PR: 362fa40

// The list of NSNumber encoding value can be found in the following links:
// - https://developer.apple.com/documentation/foundation/nsnumber
// - https://github.com/gnustep/libs-base/blob/master/Source/NSNumber.m
switch Character(Unicode.Scalar(UInt8(nsnumber.objCType.pointee))) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And this relates to your other mention @maxep, right?

The reason being that the webview bridge returns a JSON as objc dictionary with NSNumber, in Swift a NSNumber with value 1 or 0 can be cast to Bool which would be encoded as true or false and lead to a type mismatch

If so, could we annotate this nsnumber.objCType.pointee code with relevant comment? It seems more important than it looks.

@maxep maxep force-pushed the maxep/RUMM-2745/any-codable-erasure branch from a561d21 to d81bc1b Compare December 15, 2022 17:23
@datadog-datadog-prod-us1
Copy link

Datadog Report

Branch report: maxep/RUMM-2745/any-codable-erasure
Commit report: 362fa40

dd-sdk-ios 0 Failed, 0 New Flaky, 58 Passed, 0 Skipped, 6.85s Wall Time

@maxep maxep merged commit 8a7c25a into develop Dec 16, 2022
@maxep maxep deleted the maxep/RUMM-2745/any-codable-erasure branch December 16, 2022 11:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants