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

Add Navigation events #161

Merged
merged 5 commits into from
Jun 20, 2024
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
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,9 @@ config.defaultUser = "[email protected]"

You can update the configuration after TelemetryDeck is already initialized.

## Payload
## Parameters

You can also send additional payload data with each signal:
You can also send additional parameters with each signal:

```swift
TelemetryDeck.signal("Database.updated", parameters: ["numberOfDatabaseEntries": "3831"])
Expand Down
11 changes: 6 additions & 5 deletions Sources/TelemetryClient/Presets/TelemetryDeck+Errors.swift
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Foundation

extension TelemetryDeck {
public extension TelemetryDeck {
/// Sends a telemetry signal indicating that an error has occurred.
///
/// - Parameters:
Expand All @@ -10,7 +10,7 @@ extension TelemetryDeck {
/// - parameters: Additional parameters to include with the signal. Default is an empty dictionary.
/// - floatValue: An optional floating-point value to include with the signal. Default is `nil`.
/// - customUserID: An optional custom user identifier. If provided, it overrides the default user identifier from the configuration. Default is `nil`.
public static func errorOccurred(
static func errorOccurred(
id: String,
category: ErrorCategory? = nil,
message: String? = nil,
Expand Down Expand Up @@ -44,7 +44,7 @@ extension TelemetryDeck {
/// - parameters: Additional parameters to include with the signal. Default is an empty dictionary.
/// - floatValue: An optional floating-point value to include with the signal. Default is `nil`.
/// - customUserID: An optional custom user identifier. If provided, it overrides the default user identifier from the configuration. Default is `nil`.
public static func errorOccurred(
static func errorOccurred(
identifiableError: IdentifiableError,
category: ErrorCategory = .thrownException,
parameters: [String: String] = [:],
Expand All @@ -71,9 +71,10 @@ extension TelemetryDeck {
/// - floatValue: An optional floating-point value to include with the signal. Default is `nil`.
/// - customUserID: An optional custom user identifier. If provided, it overrides the default user identifier from the configuration. Default is `nil`.
///
/// - Note: Use this overload if you want to provide a custom `message` parameter. Prefer ``errorOccurred(identifiableError:category:parameters:floatValue:customUserID:)`` to send `error.localizedDescription` as the `message` automatically.
/// - Note: Use this overload if you want to provide a custom `message` parameter. Prefer ``errorOccurred(identifiableError:category:parameters:floatValue:customUserID:)`` to send
/// `error.localizedDescription` as the `message` automatically.
@_disfavoredOverload
public static func errorOccurred(
static func errorOccurred(
identifiableError: IdentifiableError,
category: ErrorCategory = .thrownException,
message: String? = nil,
Expand Down
1 change: 1 addition & 0 deletions Sources/TelemetryClient/Signal.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import Foundation
import TVUIKit
#endif

/// Note: only use this when posting to the deprecated V1 ingest API
internal struct SignalPostBody: Codable, Equatable {
/// When was this signal generated
let receivedAt: Date
Expand Down
2 changes: 1 addition & 1 deletion Sources/TelemetryClient/TelemetryClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@ public class TelemetryManager {

/// Send a Signal to TelemetryDeck, to record that an event has occurred.
///
/// If you specify a payload, it will be sent in addition to the default payload which includes OS Version, App Version, and more.
/// If you specify parameters, they will be sent in addition to the default parameters which include OS Version, App Version, and more.
@available(*, deprecated, renamed: "TelemetryDeck.signal(_:parameters:)", message: "This call was renamed to `TelemetryDeck.signal(_:parameters:)`. Please migrate – a fix-it is available.")
public static func send(_ signalName: String, with parameters: [String: String] = [:]) {
send(signalName, for: nil, floatValue: nil, with: parameters)
Expand Down
82 changes: 78 additions & 4 deletions Sources/TelemetryClient/TelemetryDeck.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import Foundation

/// This internal singleton keeps track of the last used navigation path so
/// that the `navigate(to:)` function has a source to work off of.
class TelemetryDeckNavigationStatus {
var previousNavigationPath: String?
static let shared = TelemetryDeckNavigationStatus()
}

/// A namespace for TelemetryDeck related functionalities.
public enum TelemetryDeck {
/// This alias makes it easier to migrate the configuration type into the TelemetryDeck namespace in future versions when deprecated code is fully removed.
Expand Down Expand Up @@ -33,12 +40,79 @@ public enum TelemetryDeck {
TelemetryManager.send(signalName, for: customUserID, floatValue: floatValue, with: parameters)
}

/// Do not call this method unless you really know what you're doing. The signals will automatically sync with the server at appropriate times, there's no need to call this.
/// Send a signal that represents a navigation event with a source and a destination
///
/// This is a convenience method that will internally send a completely normal TelemetryDeck signals with the type
/// `TelemetryDeck.Route.Transition.navigation` and the necessary parameters.
///
/// Since TelemetryDeck navigation signals need a source and a destination, this method will store the last
/// used destination for use in the `navigate(to:)` method.
///
/// ## Navigation Paths
/// Navigation Paths are strings that describe a location or view in your application or website. They must be
/// delineated by either `.` or `/` characters. Delineation characters at the beginning and end of the string are
/// ignored. Use the empty string `""` for navigation from outside the app. Examples are `index`,
/// `settings.user.changePassword`, or `/blog/ios-market-share`.
///
/// - Parameters:
/// - from: The navigation path at the beginning of the navigation event, identifying the view the user is leaving
/// - to: The navigation path at the end of the navigation event, identifying the view the user is arriving at
/// - customUserID: An optional string specifying a custom user identifier. If provided, it will override the default user identifier from the configuration. Default is `nil`.
public static func navigate(from source: String, to destination: String, customUserID: String? = nil) {
TelemetryDeckNavigationStatus.shared.previousNavigationPath = destination

TelemetryManager.send(
"TelemetryDeck.Navigation.pathChanged",
for: customUserID,
with: [
"TelemetryDeck.Navigation.schemaVersion": "1",
"TelemetryDeck.Navigation.identifier": "\(source) -> \(destination)",
"TelemetryDeck.Navigation.sourcePath": source,
"TelemetryDeck.Navigation.destinationPath": destination
]
)
}

/// Send a signal that represents a navigation event with a destination and a default source.
///
/// This is a convenience method that will internally send a completely normal TelemetryDeck signals with the type
/// `TelemetryDeck.Route.Transition.navigation` and the necessary parameters.
///
/// ## Navigation Paths
/// Navigation Paths are strings that describe a location or view in your application or website. They must be
/// delineated by either `.` or `/` characters. Delineation characters at the beginning and end of the string are
/// ignored. Use the empty string `""` for navigation from outside the app. Examples are `index`,
/// `settings.user.changePassword`, or `/blog/ios-market-share`.
///
/// ## Automatic Navigation Tracking
/// Since TelemetryDeck navigation signals need a source and a destination, this method will keep track of the last
/// used destination and will automatically insert it as a source the next time you call this method.
///
/// This is very convenient, but will produce incorrect graphs if you don't call it from every screen in your app.
/// Suppose you have 3 tabs "Home", "User" and "Settings", but only set up navigation in "Home" and "Settings". If
/// a user taps "Home", "User" and "Settings" in that order, that'll produce an incorrect navigation signal with
/// source "Home" and destination "Settings", a path that the user did not take.
///
/// - Parameters:
/// - to: The navigation path representing the view the user is arriving at.
/// - customUserID: An optional string specifying a custom user identifier. If provided, it will override the default user identifier from the configuration. Default is `nil`.
public static func navigate(to destination: String, customUserID: String? = nil) {
let source = TelemetryDeckNavigationStatus.shared.previousNavigationPath ?? ""
Self.navigate(from: source, to: destination, customUserID: customUserID)
}

/// Do not call this method unless you really know what you're doing. The signals will automatically sync with
/// the server at appropriate times, there's no need to call this.
///
/// Use this sparingly and only to indicate a time in your app where a signal was just sent but the user is likely
/// to leave your app and not return again for a long time.
///
/// Use this sparingly and only to indicate a time in your app where a signal was just sent but the user is likely to leave your app and not return again for a long time.
/// This function does not guarantee that the signal cache will be sent right away. Calling this after every
/// ``signal(_:parameters:floatValue:customUserID:)`` will not make data reach our servers faster, so avoid
/// doing that.
///
/// This function does not guarantee that the signal cache will be sent right away. Calling this after every ``signal(_:parameters:floatValue:customUserID:)`` will not make data reach our servers faster, so avoid doing that.
/// But if called at the right time (sparingly), it can help ensure the server doesn't miss important churn data because a user closes your app and doesn't reopen it anytime soon (if at all).
/// But if called at the right time (sparingly), it can help ensure the server doesn't miss important churn
/// data because a user closes your app and doesn't reopen it anytime soon (if at all).
public static func requestImmediateSync() {
TelemetryManager.requestImmediateSync()
}
Expand Down