Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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
8 changes: 8 additions & 0 deletions Sentry.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -787,6 +787,8 @@
D48891CC2E98F22A00212823 /* SentryInfoPlistWrapperProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = D48891C62E98F21D00212823 /* SentryInfoPlistWrapperProvider.swift */; };
D48891CE2E98F28E00212823 /* SentryInfoPlistWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = D48891CD2E98F28E00212823 /* SentryInfoPlistWrapper.swift */; };
D48891D02E98F2E700212823 /* SentryInfoPlistError.swift in Sources */ = {isa = PBXBuildFile; fileRef = D48891CF2E98F2E600212823 /* SentryInfoPlistError.swift */; };
D48954722EF5ABE00086F240 /* SentryAttributeValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = D489546C2EF5ABD90086F240 /* SentryAttributeValue.swift */; };
D48954742EF5B00A0086F240 /* SentryAttributeValuable.swift in Sources */ = {isa = PBXBuildFile; fileRef = D48954732EF5B0060086F240 /* SentryAttributeValuable.swift */; };
D48E8B8B2D3E79610032E35E /* SentryTraceOrigin.swift in Sources */ = {isa = PBXBuildFile; fileRef = D48E8B8A2D3E79610032E35E /* SentryTraceOrigin.swift */; };
D48E8B9D2D3E82AC0032E35E /* SentrySpanOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = D48E8B9C2D3E82AC0032E35E /* SentrySpanOperation.swift */; };
D490648A2DFAE1F600555785 /* SentryScreenshotOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D49064892DFAE1F600555785 /* SentryScreenshotOptions.swift */; };
Expand Down Expand Up @@ -2163,6 +2165,8 @@
D48891C62E98F21D00212823 /* SentryInfoPlistWrapperProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryInfoPlistWrapperProvider.swift; sourceTree = "<group>"; };
D48891CD2E98F28E00212823 /* SentryInfoPlistWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryInfoPlistWrapper.swift; sourceTree = "<group>"; };
D48891CF2E98F2E600212823 /* SentryInfoPlistError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryInfoPlistError.swift; sourceTree = "<group>"; };
D489546C2EF5ABD90086F240 /* SentryAttributeValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryAttributeValue.swift; sourceTree = "<group>"; };
D48954732EF5B0060086F240 /* SentryAttributeValuable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryAttributeValuable.swift; sourceTree = "<group>"; };
D48E8B8A2D3E79610032E35E /* SentryTraceOrigin.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryTraceOrigin.swift; sourceTree = "<group>"; };
D48E8B9C2D3E82AC0032E35E /* SentrySpanOperation.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentrySpanOperation.swift; sourceTree = "<group>"; };
D49064892DFAE1F600555785 /* SentryScreenshotOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SentryScreenshotOptions.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -4906,6 +4910,8 @@
620078752D38F1110022CB67 /* Codable */,
92ECD73B2E05ACDE0063EC10 /* SentryLog.swift */,
92ECD73F2E05AD500063EC10 /* SentryAttribute.swift */,
D48954732EF5B0060086F240 /* SentryAttributeValuable.swift */,
D489546C2EF5ABD90086F240 /* SentryAttributeValue.swift */,
92ECD73D2E05AD2B0063EC10 /* SentryLogLevel.swift */,
9264E1EA2E2E385B00B077CF /* SentryLogMessage.swift */,
F458D1122E180BB00028273E /* SentryFileManagerProtocol.swift */,
Expand Down Expand Up @@ -5816,6 +5822,7 @@
D8739D142BEE5049007D2F66 /* SentryRRWebSpanEvent.swift in Sources */,
FAAB2F972E4D345800FE8B7E /* SentryUIDeviceWrapper.swift in Sources */,
7B6438AB26A70F24000D0F65 /* UIViewController+Sentry.m in Sources */,
D48954722EF5ABE00086F240 /* SentryAttributeValue.swift in Sources */,
84302A812B5767A50027A629 /* SentryLaunchProfiling.m in Sources */,
D490648A2DFAE1F600555785 /* SentryScreenshotOptions.swift in Sources */,
FA6FC0AA2E0B6B1100ED2669 /* SentrySdkInfo.swift in Sources */,
Expand Down Expand Up @@ -6008,6 +6015,7 @@
FA67DCFD2DDBD4EA00896B02 /* SentryANRTracker.swift in Sources */,
FA67DCFE2DDBD4EA00896B02 /* SentryANRTrackerV2Delegate.swift in Sources */,
FABE8E152E307A5E0040809A /* SentrySDK.swift in Sources */,
D48954742EF5B00A0086F240 /* SentryAttributeValuable.swift in Sources */,
FA67DCFF2DDBD4EA00896B02 /* SentryMXManager.swift in Sources */,
D41415A72DEEE532003B14D5 /* SentryRedactViewHelper.swift in Sources */,
FA66143A2E4B593900657755 /* SentryApplicationExtensions.swift in Sources */,
Expand Down
78 changes: 56 additions & 22 deletions Sources/Swift/Protocol/SentryAttribute.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,35 @@ public final class SentryAttribute: NSObject {
super.init()
}

internal init(attributableValue: SentryAttributeValue) {
switch attributableValue {
case .boolean(let value):
self.type = "boolean"
self.value = value
case .string(let value):
self.type = "string"
self.value = value
case .integer(let value):
self.type = "integer"
self.value = value
case .double(let value):
self.type = "double"
self.value = value
case .booleanArray(let array):
self.type = "boolean[]"
self.value = array
case .stringArray(let array):
self.type = "string[]"
self.value = array
case .integerArray(let array):
self.type = "integer[]"
self.value = array
case .doubleArray(let array):
self.type = "double[]"
self.value = array
}
}

internal init(value: Any) {
switch value {
case let stringValue as String:
Expand All @@ -57,6 +86,16 @@ public final class SentryAttribute: NSObject {
case let floatValue as Float:
self.type = "double"
self.value = Double(floatValue)
case let attributable as SentryAttributeValuable:
let value = attributable.asAttributeValue
self.type = value.type
self.value = value.anyValue
case let attribute as SentryAttributeValue:
self.type = attribute.type
self.value = attribute.anyValue
case let attribute as SentryAttribute:
self.type = attribute.type
self.value = attribute.value
default:
// For any other type, convert to string representation
self.type = "string"
Expand All @@ -67,40 +106,35 @@ public final class SentryAttribute: NSObject {
}

// MARK: - Internal Encodable Support
@_spi(Private) extension SentryAttribute: Encodable {
private enum CodingKeys: String, CodingKey {
case value
case type
}

@_spi(Private) extension SentryAttribute: Encodable {
@_spi(Private) public func encode(to encoder: any Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)

try container.encode(type, forKey: .type)
try self.asAttributeValue.encode(to: encoder)
}
}

switch type {
@_spi(Private) extension SentryAttribute: SentryAttributeValuable {
@_spi(Private) public var asAttributeValue: SentryAttributeValue {
switch self.type {
case "string":
guard let stringValue = value as? String else {
throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: encoder.codingPath, debugDescription: "Expected String but got \(Swift.type(of: value))"))
if let val = self.value as? String {
return .string(val)
}
try container.encode(stringValue, forKey: .value)
case "boolean":
guard let boolValue = value as? Bool else {
throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: encoder.codingPath, debugDescription: "Expected Bool but got \(Swift.type(of: value))"))
if let val = self.value as? Bool {
return .boolean(val)
}
try container.encode(boolValue, forKey: .value)
case "integer":
guard let intValue = value as? Int else {
throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: encoder.codingPath, debugDescription: "Expected Int but got \(Swift.type(of: value))"))
if let val = self.value as? Int {
return .integer(val)
}
try container.encode(intValue, forKey: .value)
case "double":
guard let doubleValue = value as? Double else {
throw EncodingError.invalidValue(value, EncodingError.Context(codingPath: encoder.codingPath, debugDescription: "Expected Double but got \(Swift.type(of: value))"))
if let val = self.value as? Double {
return .double(val)
}
try container.encode(doubleValue, forKey: .value)
default:
try container.encode(String(describing: value), forKey: .value)
break
}
return .string(String(describing: value))
}
}
33 changes: 33 additions & 0 deletions Sources/Swift/Protocol/SentryAttributeValuable.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
public protocol SentryAttributeValuable {
var asAttributeValue: SentryAttributeValue { get }
Copy link
Member

Choose a reason for hiding this comment

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

m: We have sentry in the naming everywhere. I think this would make it a bit clearer that this returns a SentryAttributeValue.

Suggested change
var asAttributeValue: SentryAttributeValue { get }
var asSentryAttributeValue: SentryAttributeValue { get }

}

extension String: SentryAttributeValuable {
public var asAttributeValue: SentryAttributeValue {
return .string(self)
}
}

extension Bool: SentryAttributeValuable {
public var asAttributeValue: SentryAttributeValue {
return .boolean(self)
}
}

extension Int: SentryAttributeValuable {
public var asAttributeValue: SentryAttributeValue {
return .integer(self)
}
}

extension Double: SentryAttributeValuable {
public var asAttributeValue: SentryAttributeValue {
return .double(self)
}
}

extension Float: SentryAttributeValuable {
public var asAttributeValue: SentryAttributeValue {
return .double(Double(self))
}
}
134 changes: 134 additions & 0 deletions Sources/Swift/Protocol/SentryAttributeValue.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
public enum SentryAttributeValue: Equatable, Hashable {
case string(String)
case boolean(Bool)
case integer(Int)
case double(Double)
case stringArray([String])
case booleanArray([Bool])
case integerArray([Int])
case doubleArray([Double])

var type: String {
switch self {
case .string:
return "string"
case .boolean:
return "boolean"
case .integer:
return "integer"
case .double:
return "double"
case .stringArray:
return "string[]"
case .booleanArray:
return "boolean[]"
case .integerArray:
return "integer[]"
case .doubleArray:
return "double[]"
}
}
}

extension SentryAttributeValue: Encodable {
private enum CodingKeys: String, CodingKey {
case type
case value
}

public func encode(to encoder: any Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(type, forKey: .type)

switch self {
case .string(let value):
try container.encode(value, forKey: .value)
case .boolean(let value):
try container.encode(value, forKey: .value)
case .integer(let value):
try container.encode(value, forKey: .value)
case .double(let value):
try container.encode(value, forKey: .value)
case .stringArray(let value):
try container.encode(value, forKey: .value)
case .booleanArray(let value):
try container.encode(value, forKey: .value)
case .integerArray(let value):
try container.encode(value, forKey: .value)
case .doubleArray(let value):
try container.encode(value, forKey: .value)
}
}
}

extension SentryAttributeValue {
static func from(anyValue value: Any) -> Self {
if let val = value as? String {
return .string(val)
}
if let val = value as? Bool {
return .boolean(val)
}
if let val = value as? Int {
return .integer(val)
}
if let val = value as? Double {
return .double(val)
}
if let val = value as? Float {
return .double(Double(val))
}
if let val = value as? SentryAttributeValue {
return val
}
if let val = value as? SentryAttributeValuable {
return val.asAttributeValue
}
return .string(String(describing: value))
}

var anyValue: Any {
switch self {
case .string(let value):
return value
case .boolean(let value):
return value
case .integer(let value):
return value
case .double(let value):
return value
case .stringArray(let value):
return value
case .booleanArray(let value):
return value
case .integerArray(let value):
return value
case .doubleArray(let value):
return value
}
}
}

extension SentryAttributeValue: ExpressibleByStringLiteral {
public init(stringLiteral value: StringLiteralType) {
self = .string(value)
}
}

extension SentryAttributeValue: ExpressibleByBooleanLiteral {
public init(booleanLiteral value: BooleanLiteralType) {
self = .boolean(value)
}
}

extension SentryAttributeValue: ExpressibleByFloatLiteral {
public init(floatLiteral value: FloatLiteralType) {
self = .double(value)
}
}

extension SentryAttributeValue: ExpressibleByIntegerLiteral {
public init(integerLiteral value: IntegerLiteralType) {
self = .integer(value)
}
}
2 changes: 1 addition & 1 deletion Sources/Swift/Tools/Batcher/BatcherItem.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
protocol BatcherItem: Encodable {
var attributes: [String: SentryAttribute] { get set }
var attributesMap: [String: SentryAttributeValue] { get set }
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 was renamed because it would otherwise collide with the type of SentryAttribute.attributes

var traceId: SentryId { get set }
var body: String { get }
}
Loading
Loading