Skip to content
Closed
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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@

## Unreleased

### Features

- Expose metrics API to Objective-C ([#7339](https://github.com/getsentry/sentry-cocoa/pull/7339))

### Fixes

- Fix mismatch of `in_foreground` app context (#7188) The app context `in_foreground` for handled and unhandled events was sometimes different. This is fixed now by aligning the implementation and adding a new `is_active` app context field.
Expand Down
16 changes: 5 additions & 11 deletions Sources/Swift/Helper/SentrySDK.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@ import Foundation
/// using their individual attributes.
///
/// The `metrics` namespace exposes three methods to capture different types of metric information:
/// - ``SentryMetricsApiProtocol/count(key:value:unit:attributes:)``: Track discrete occurrence counts
/// - ``SentryMetrics/count(key:value:unit:attributes:)``: Track discrete occurrence counts
/// (e.g., button clicks, API requests, errors).
/// - ``SentryMetricsApiProtocol/gauge(key:value:unit:attributes:)``: Track values that can go up and down
/// - ``SentryMetrics/gauge(key:value:unit:attributes:)``: Track values that can go up and down
/// (e.g., memory usage, queue depth, active connections).
/// - ``SentryMetricsApiProtocol/distribution(key:value:unit:attributes:)``: Track the distribution of a value
/// - ``SentryMetrics/distribution(key:value:unit:attributes:)``: Track the distribution of a value
/// over time for statistical analysis like percentiles (e.g., response times, request durations).
///
/// Each method supports optional units (via ``SentryUnit``) and attributes for filtering and grouping.
Expand All @@ -72,20 +72,14 @@ import Foundation
/// ## Requirements
///
/// Metrics are enabled by default even though it is an experimental feature, because you must still
/// manually call the API methods (``SentryMetricsApiProtocol/count(key:value:unit:attributes:)``,
/// ``SentryMetricsApiProtocol/gauge(key:value:unit:attributes:)``, or
/// ``SentryMetricsApiProtocol/distribution(key:value:unit:attributes:)``) to use it.
/// manually call the API methods to use it.
///
/// To disable metrics, set ``Options/experimental`` ``SentryExperimentalOptions/enableMetrics`` to `false`.
///
/// - Note: This feature is currently in open beta.
///
/// - Important: The Metrics API has been designed and optimized for Swift. Objective-C support is not
/// currently available. If you need Objective-C support, please open an issue at
/// https://github.com/getsentry/sentry-cocoa/issues to show demand for this feature.
///
/// - SeeAlso: For complete documentation, visit https://docs.sentry.io/platforms/apple/metrics/
public static var metrics: SentryMetricsApiProtocol = SentryMetricsApi(dependencies: SentryDependencyContainer.sharedInstance())
@objc public static var metrics: SentryMetrics = SentryMetrics(api: SentryMetricsApi(dependencies: SentryDependencyContainer.sharedInstance()))

/// Inits and configures Sentry (`SentryHub`, `SentryClient`) and sets up all integrations. Make sure to
/// set a valid DSN.
Expand Down
118 changes: 118 additions & 0 deletions Sources/Swift/Integrations/Metrics/SentryMetrics.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import Foundation

/// Objective-C compatible wrapper for the Sentry Metrics API.
///
/// This class provides an Objective-C accessible interface to the underlying Swift metrics API.
/// It accepts `String` for units (matching the raw value of ``SentryUnit``) and `[String: Any]`
/// for attributes, converting them to the Swift-native types internally.
///
/// Swift callers can use the type-safe ``SentryUnit`` and ``SentryAttributeValue`` parameters
/// directly through the ``SentryMetricsApiProtocol`` conformance on this class.
///
/// - Important: From Objective-C, pass unit names as plain strings (e.g., `@"millisecond"`,
/// `@"byte"`) and attributes as `NSDictionary<NSString *, id>`. The wrapper converts these
/// to the Swift-native types internally.
@objc
public final class SentryMetrics: NSObject {
private let api: SentryMetricsApiProtocol

init(api: SentryMetricsApiProtocol) {
self.api = api
super.init()
}

// MARK: - Count

/// Records a count metric, incrementing the specified key by 1.
@objc(countWithKey:)
public func count(key: String) {
api.count(key: key, value: 1)
}

/// Records a count metric with the specified key and value.
@objc(countWithKey:value:)
public func count(key: String, value: UInt) {
api.count(key: key, value: value)
}

/// Records a count metric with the specified key, value, and unit.
@objc(countWithKey:value:unit:)
public func count(key: String, value: UInt, unit: String?) {
api.count(key: key, value: value, unit: sentryUnit(from: unit))
}

/// Records a count metric with the specified key, value, unit, and attributes.
@objc(countWithKey:value:unit:attributes:)
public func count(key: String, value: UInt, unit: String?, attributes: [String: Any]) {
api.count(key: key, value: value, unit: sentryUnit(from: unit), attributes: convertAttributes(attributes))
}

// MARK: - Distribution

/// Records a distribution metric with the specified key and value.
@objc(distributionWithKey:value:)
public func distribution(key: String, value: Double) {
api.distribution(key: key, value: value)
}

/// Records a distribution metric with the specified key, value, and unit.
@objc(distributionWithKey:value:unit:)
public func distribution(key: String, value: Double, unit: String?) {
api.distribution(key: key, value: value, unit: sentryUnit(from: unit))
}

/// Records a distribution metric with the specified key, value, unit, and attributes.
@objc(distributionWithKey:value:unit:attributes:)
public func distribution(key: String, value: Double, unit: String?, attributes: [String: Any]) {
api.distribution(key: key, value: value, unit: sentryUnit(from: unit), attributes: convertAttributes(attributes))
}

// MARK: - Gauge

/// Records a gauge metric with the specified key and value.
@objc(gaugeWithKey:value:)
public func gauge(key: String, value: Double) {
api.gauge(key: key, value: value)
}

/// Records a gauge metric with the specified key, value, and unit.
@objc(gaugeWithKey:value:unit:)
public func gauge(key: String, value: Double, unit: String?) {
api.gauge(key: key, value: value, unit: sentryUnit(from: unit))
}

/// Records a gauge metric with the specified key, value, unit, and attributes.
@objc(gaugeWithKey:value:unit:attributes:)
public func gauge(key: String, value: Double, unit: String?, attributes: [String: Any]) {
api.gauge(key: key, value: value, unit: sentryUnit(from: unit), attributes: convertAttributes(attributes))
}

// MARK: - Private

private func sentryUnit(from string: String?) -> SentryUnit? {
guard let string, !string.isEmpty else { return nil }
return SentryUnit(rawValue: string)
}

private func convertAttributes(_ attrs: [String: Any]) -> [String: SentryAttributeValue] {
attrs.mapValues { SentryAttribute(value: $0) }
}
}

// Conformance needed so SentryMetrics can also be used through the protocol-based Swift API.
extension SentryMetrics: SentryMetricsApiProtocol {
/// Records a count metric with type-safe Swift parameters.
public func count(key: String, value: UInt, unit: SentryUnit?, attributes: [String: SentryAttributeValue]) {
api.count(key: key, value: value, unit: unit, attributes: attributes)
}

/// Records a distribution metric with type-safe Swift parameters.
public func distribution(key: String, value: Double, unit: SentryUnit?, attributes: [String: SentryAttributeValue]) {
api.distribution(key: key, value: value, unit: unit, attributes: attributes)
}

/// Records a gauge metric with type-safe Swift parameters.
public func gauge(key: String, value: Double, unit: SentryUnit?, attributes: [String: SentryAttributeValue]) {
api.gauge(key: key, value: value, unit: unit, attributes: attributes)
}
}
Loading