Skip to content

Commit

Permalink
Add Navigation events (#161)
Browse files Browse the repository at this point in the history
* Add Navigation events

Add convenience methods to SwiftSDK that allow the easier sending of navigational signals.

Fix #156

* Fix a documentation line that was a bit too wide

* Fix a documentation line that was a bit too wide

* Rename payload to parameters in a few more places

* Update naming from payload keys to parameters
  • Loading branch information
winsmith authored Jun 20, 2024
1 parent 81d3b79 commit a02eddd
Show file tree
Hide file tree
Showing 5 changed files with 88 additions and 12 deletions.
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 = "myuser@example.com"

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

0 comments on commit a02eddd

Please sign in to comment.