Skip to content

Commit

Permalink
✨ Adding config.attributes to action @appcues/track
Browse files Browse the repository at this point in the history
  • Loading branch information
andretortolano committed Jan 24, 2024
1 parent b5a3f90 commit 5f0bfa0
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,68 @@ import Foundation

@available(iOS 13.0, *)
internal class AppcuesTrackAction: AppcuesExperienceAction {
struct Config: Decodable {
struct Config {
let eventName: String
let attributes: [String: Any]?
}

static let type = "@appcues/track"

private weak var appcues: Appcues?

let eventName: String
let attributes: [String: Any]?

required init?(configuration: AppcuesExperiencePluginConfiguration) {
self.appcues = configuration.appcues

guard let config = configuration.decode(Config.self) else { return nil }
self.eventName = config.eventName
self.attributes = config.attributes
}

func execute(completion: ActionRegistry.Completion) {
guard let appcues = appcues else { return completion() }

appcues.track(name: eventName)
appcues.track(name: eventName, properties: attributes)
completion()
}
}

@available(iOS 13.0, *)
extension AppcuesTrackAction.Config: Decodable {

private enum CodingKeys: String, CodingKey {
case eventName
case attributes
}

// Custom decoding for this one - we want to extract the eventName and then for the attributes
// we handle it as a nestedContainer then we trim the set to only those of supported data types
// then store in the resulting attributes dictionary
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
eventName = try container.decode(String.self, forKey: .eventName)

if let attributesContainer = try? container.nestedContainer(keyedBy: CodingKeys.self, forKey: .attributes) {
var dict: [String: Any] = [:]

attributesContainer.allKeys.forEach { key in
if let boolValue = try? container.decode(Bool.self, forKey: key) {
dict[key.stringValue] = boolValue
} else if let stringValue = try? container.decode(String.self, forKey: key) {
dict[key.stringValue] = stringValue
} else if let intValue = try? container.decode(Int.self, forKey: key) {
dict[key.stringValue] = intValue
} else if let doubleValue = try? container.decode(Double.self, forKey: key) {
dict[key.stringValue] = doubleValue
} else {
// not a supported type
}
}
self.attributes = dict
} else {
attributes = nil
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class AppcuesStepInteractionActionTests: XCTestCase {
Experience.Action(
trigger: "tap",
type: "@appcues/track",
config: AppcuesTrackAction.Config(eventName: "Some event"))
config: AppcuesTrackAction.Config(eventName: "Some event", attributes: nil))
],
level: .step,
renderContext: .modal,
Expand Down Expand Up @@ -98,7 +98,7 @@ class AppcuesStepInteractionActionTests: XCTestCase {
Experience.Action(
trigger: "tap",
type: "@appcues/track",
config: AppcuesTrackAction.Config(eventName: "Some event"))
config: AppcuesTrackAction.Config(eventName: "Some event", attributes: nil))
],
level: .step,
renderContext: .modal,
Expand Down
37 changes: 35 additions & 2 deletions Tests/AppcuesKitTests/Actions/AppcuesTrackActionTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,39 @@ class AppcuesTrackActionTests: XCTestCase {
XCTAssertEqual(completionCount, 1)
XCTAssertEqual(trackCount, 1)
}

func testExecuteWithAttributes() throws {
// Arrange
var completionCount = 0
var trackCount = 0
appcues.analyticsPublisher.onPublish = { trackingUpdate in
XCTAssertEqual(trackingUpdate.type, .event(name: "My Custom Event", interactive: true))

[
"boolean": true,
"string": "string",
"int": 10,
"double": 10.5
].verifyPropertiesMatch(trackingUpdate.properties)

trackCount += 1
}
let action = AppcuesTrackAction(appcues: appcues,
eventName: "My Custom Event",
attributes: [
"boolean": true,
"string": "string",
"int": 10,
"double": 10.5
])

// Act
action?.execute(completion: { completionCount += 1 })

// Assert
XCTAssertEqual(completionCount, 1)
XCTAssertEqual(trackCount, 1)
}

func testExecuteCompletesWithoutAppcuesInstance() throws {
// Arrange
Expand All @@ -67,7 +100,7 @@ extension AppcuesTrackAction {
convenience init?(appcues: Appcues?) {
self.init(configuration: AppcuesExperiencePluginConfiguration(nil, appcues: appcues))
}
convenience init?(appcues: Appcues?, eventName: String) {
self.init(configuration: AppcuesExperiencePluginConfiguration(AppcuesTrackAction.Config(eventName: eventName), appcues: appcues))
convenience init?(appcues: Appcues?, eventName: String, attributes: [String: Any]? = nil) {
self.init(configuration: AppcuesExperiencePluginConfiguration(AppcuesTrackAction.Config(eventName: eventName, attributes: attributes), appcues: appcues))
}
}

0 comments on commit 5f0bfa0

Please sign in to comment.