Skip to content

Commit

Permalink
chore(ios): add custom event tracking
Browse files Browse the repository at this point in the history
  • Loading branch information
Adwin Ronald Ross committed Jan 7, 2025
1 parent cd54d5e commit c25f9ce
Show file tree
Hide file tree
Showing 32 changed files with 720 additions and 39 deletions.
7 changes: 7 additions & 0 deletions ios/DemoApp/Controller/ObjcDetailViewController.m
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,13 @@ - (void)viewDidLoad {

UIView *headerView = [self createTableHeaderView];
tableView.tableHeaderView = headerView;

NSDictionary *userAttributes = @{
@"user_name": @"Alice",
@"paid_user": @YES,
@"credit_balance": @1000,
@"latitude": @30.2661403415387};
[[Measure shared] trackEvent:@"event-name" attributes:userAttributes timestamp:nil];

[self setTitle:@"Objc View Controller"];

Expand Down
9 changes: 9 additions & 0 deletions ios/DemoApp/Controller/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,15 @@ import MeasureSDK
view.addSubview(tableView)
}

override func viewDidDisappear(_ animated: Bool) {
super.viewDidDisappear(animated)
let attributes: [String: AttributeValue] = ["user_name": .string("Alice"),
"paid_user": .boolean(true),
"credit_balance": .int(1000),
"latitude": .double(30.2661403415387)]
Measure.shared.trackEvent(name: "custom_event", attributes: attributes, timestamp: nil)
}

// MARK: - Table Header View with Buttons

func createTableHeaderView() -> UIView {
Expand Down
16 changes: 16 additions & 0 deletions ios/MeasureSDK.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
52159E2D2CC8FAA500486F54 /* TimeProviderTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52159E2C2CC8FAA500486F54 /* TimeProviderTests.swift */; };
5224ECE02C88057A00D1B1F7 /* FatalErrorUtil.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5224ECDF2C88057A00D1B1F7 /* FatalErrorUtil.swift */; };
5224ECE32C880FA400D1B1F7 /* XCTextCase+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5224ECE22C880FA300D1B1F7 /* XCTextCase+Extension.swift */; };
522532CA2D295F3F001B5D7C /* AttributeValue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 522532C92D295F3F001B5D7C /* AttributeValue.swift */; };
5229D16E2CCB533C00EFFE44 /* RecentSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5229D16D2CCB533C00EFFE44 /* RecentSession.swift */; };
523287692C85E07B000EE268 /* LifecycleObserverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 523287682C85E07B000EE268 /* LifecycleObserverTests.swift */; };
523287732C86195E000EE268 /* SessionManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 523287722C86195E000EE268 /* SessionManagerTests.swift */; };
Expand Down Expand Up @@ -106,6 +107,8 @@
52AE72032CABAE9000F2830A /* GestureEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52AE71FD2CABAE9000F2830A /* GestureEvents.swift */; };
52AE72082CABAEAB00F2830A /* UIWindow+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52AE72062CABAEAB00F2830A /* UIWindow+Extension.swift */; };
52AE72092CABAEAB00F2830A /* NSObject+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52AE72072CABAEAB00F2830A /* NSObject+Extension.swift */; };
52B2A8772D1A790200C6B5CF /* CustomEventData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52B2A8762D1A790200C6B5CF /* CustomEventData.swift */; };
52B2A8792D1A89EF00C6B5CF /* CustomEventCollector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52B2A8782D1A89EF00C6B5CF /* CustomEventCollector.swift */; };
52BCF1DC2CB42026003102DF /* MeasureModel.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 52BCF1DA2CB42026003102DF /* MeasureModel.xcdatamodeld */; };
52BCF1DD2CB42383003102DF /* MeasureModel.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 52BCF1DA2CB42026003102DF /* MeasureModel.xcdatamodeld */; };
52BCF20A2CB59FBF003102DF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52BCF2092CB59FBF003102DF /* AppDelegate.swift */; };
Expand Down Expand Up @@ -164,6 +167,7 @@
52D51ABB2CD0E5C9008F30A6 /* MsrMoniterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52D51ABA2CD0E5C9008F30A6 /* MsrMoniterView.swift */; };
52D51ABD2CD2060A008F30A6 /* LifecycleCollectorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52D51ABC2CD2060A008F30A6 /* LifecycleCollectorTests.swift */; };
52EB380C2C8C7334002D63EC /* SignPost.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52EB380B2C8C7334002D63EC /* SignPost.swift */; };
52F0C6352D2D46980060FD08 /* CustomEventCollectorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52F0C6342D2D46980060FD08 /* CustomEventCollectorTests.swift */; };
52F3773B2CB41DDF006147E8 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52F3773A2CB41DDF006147E8 /* AppDelegate.swift */; };
52F3773D2CB41DE0006147E8 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 52F3773C2CB41DDF006147E8 /* SceneDelegate.swift */; };
52F377422CB41DE0006147E8 /* Base in Resources */ = {isa = PBXBuildFile; fileRef = 52F377412CB41DE0006147E8 /* Base */; };
Expand Down Expand Up @@ -339,6 +343,7 @@
52159E2C2CC8FAA500486F54 /* TimeProviderTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimeProviderTests.swift; sourceTree = "<group>"; };
5224ECDF2C88057A00D1B1F7 /* FatalErrorUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FatalErrorUtil.swift; sourceTree = "<group>"; };
5224ECE22C880FA300D1B1F7 /* XCTextCase+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "XCTextCase+Extension.swift"; sourceTree = "<group>"; };
522532C92D295F3F001B5D7C /* AttributeValue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttributeValue.swift; sourceTree = "<group>"; };
5229D16D2CCB533C00EFFE44 /* RecentSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecentSession.swift; sourceTree = "<group>"; };
523287682C85E07B000EE268 /* LifecycleObserverTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LifecycleObserverTests.swift; sourceTree = "<group>"; };
523287722C86195E000EE268 /* SessionManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SessionManagerTests.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -408,6 +413,8 @@
52AE71FD2CABAE9000F2830A /* GestureEvents.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GestureEvents.swift; sourceTree = "<group>"; };
52AE72062CABAEAB00F2830A /* UIWindow+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIWindow+Extension.swift"; sourceTree = "<group>"; };
52AE72072CABAEAB00F2830A /* NSObject+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+Extension.swift"; sourceTree = "<group>"; };
52B2A8762D1A790200C6B5CF /* CustomEventData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomEventData.swift; sourceTree = "<group>"; };
52B2A8782D1A89EF00C6B5CF /* CustomEventCollector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomEventCollector.swift; sourceTree = "<group>"; };
52BCF1DB2CB42026003102DF /* MeasureModel.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = MeasureModel.xcdatamodel; sourceTree = "<group>"; };
52BCF2072CB59FBF003102DF /* TestApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TestApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
52BCF2092CB59FBF003102DF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -460,6 +467,7 @@
52D51ABA2CD0E5C9008F30A6 /* MsrMoniterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MsrMoniterView.swift; sourceTree = "<group>"; };
52D51ABC2CD2060A008F30A6 /* LifecycleCollectorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LifecycleCollectorTests.swift; sourceTree = "<group>"; };
52EB380B2C8C7334002D63EC /* SignPost.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignPost.swift; sourceTree = "<group>"; };
52F0C6342D2D46980060FD08 /* CustomEventCollectorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomEventCollectorTests.swift; sourceTree = "<group>"; };
52F377382CB41DDF006147E8 /* DemoApp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DemoApp.app; sourceTree = BUILT_PRODUCTS_DIR; };
52F3773A2CB41DDF006147E8 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
52F3773C2CB41DDF006147E8 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -546,6 +554,7 @@
5202BE302C895FC800A3496E /* AppAttributeProcessor.swift */,
5202BE312C895FC800A3496E /* Attribute.swift */,
5202BE322C895FC800A3496E /* AttributeProcessor.swift */,
522532C92D295F3F001B5D7C /* AttributeValue.swift */,
5202BE332C895FC800A3496E /* ComputeOnceAttributeProcessor.swift */,
5202BE342C895FC800A3496E /* DeviceAttributeProcessor.swift */,
5202BE352C895FC800A3496E /* InstallationIdAttributeProcessor.swift */,
Expand Down Expand Up @@ -591,6 +600,8 @@
children = (
5202BE732C8B117900A3496E /* Attachment.swift */,
5202BE742C8B117900A3496E /* AttachmentType.swift */,
52B2A8782D1A89EF00C6B5CF /* CustomEventCollector.swift */,
52B2A8762D1A790200C6B5CF /* CustomEventData.swift */,
5202BE752C8B117900A3496E /* Event.swift */,
5202BE762C8B117900A3496E /* EventProcessor.swift */,
5202BE772C8B117900A3496E /* EventType.swift */,
Expand Down Expand Up @@ -853,6 +864,7 @@
isa = PBXGroup;
children = (
52A1A9612CA592A100461103 /* EventProcessorTests.swift */,
52F0C6342D2D46980060FD08 /* CustomEventCollectorTests.swift */,
);
path = Event;
sourceTree = "<group>";
Expand Down Expand Up @@ -1353,10 +1365,12 @@
52AE72022CABAE9000F2830A /* GestureDetector.swift in Sources */,
52CC63C12C9C608E00F7CA0A /* CrashDataPersistence.swift in Sources */,
52EB380C2C8C7334002D63EC /* SignPost.swift in Sources */,
52B2A8792D1A89EF00C6B5CF /* CustomEventCollector.swift in Sources */,
52A8533E2C994CC200B2A39F /* ExceptionDetail.swift in Sources */,
526E30F62CE77BCB00F484B4 /* AppLaunchEvents.swift in Sources */,
52A853402C994D7900B2A39F /* Exception.swift in Sources */,
52159E272CC802A800486F54 /* EventSerializer.swift in Sources */,
522532CA2D295F3F001B5D7C /* AttributeValue.swift in Sources */,
528EAB892C804AA100CB1574 /* SessionManager.swift in Sources */,
52CD91262C7C3D90000189BA /* MeasureInitializer.swift in Sources */,
52A1A94A2CA3CF9B00461103 /* EventStore.swift in Sources */,
Expand All @@ -1375,6 +1389,7 @@
524576712CC1128600B288E5 /* BatchCreator.swift in Sources */,
52F93B9E2CAE6B3C00168AB4 /* GestureTargetFinder.swift in Sources */,
52D51ABB2CD0E5C9008F30A6 /* MsrMoniterView.swift in Sources */,
52B2A8772D1A790200C6B5CF /* CustomEventData.swift in Sources */,
524576732CC116DD00B288E5 /* BatchStore.swift in Sources */,
52816B6A2CCE399B00B160A4 /* LifecycleCollector.swift in Sources */,
528EAB962C84553500CB1574 /* LifecycleObserver.swift in Sources */,
Expand Down Expand Up @@ -1437,6 +1452,7 @@
52A1A9662CA5AC9900461103 /* Attachment+Extension.swift in Sources */,
52CC63D72C9EC5FA00F7CA0A /* CrashReportingManagerTests.swift in Sources */,
52D3D7452CC6191B004E404B /* HeartbeatTests.swift in Sources */,
52F0C6352D2D46980060FD08 /* CustomEventCollectorTests.swift in Sources */,
52BCF1DD2CB42383003102DF /* MeasureModel.xcdatamodeld in Sources */,
52FA6A8F2CE222360091F089 /* MemoryUsageCalculatorTests.swift in Sources */,
52159E292CC8091E00486F54 /* EventSerializerTests.swift in Sources */,
Expand Down
9 changes: 6 additions & 3 deletions ios/MeasureSDK/AppLaunch/AppLaunchCollector.swift
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,8 @@ final class BaseAppLaunchCollector: AppLaunchCollector {
type: .coldLaunch,
attributes: nil,
sessionId: nil,
attachments: nil)
attachments: nil,
userDefinedAttributes: nil)
}

func onWarmLaunchCallback(_ data: WarmLaunchData) {
Expand All @@ -59,7 +60,8 @@ final class BaseAppLaunchCollector: AppLaunchCollector {
type: .warmLaunch,
attributes: nil,
sessionId: nil,
attachments: nil)
attachments: nil,
userDefinedAttributes: nil)
}

func onHotLaunchCallback(_ data: HotLaunchData) {
Expand All @@ -68,6 +70,7 @@ final class BaseAppLaunchCollector: AppLaunchCollector {
type: .hotLaunch,
attributes: nil,
sessionId: nil,
attachments: nil)
attachments: nil,
userDefinedAttributes: nil)
}
}
68 changes: 68 additions & 0 deletions ios/MeasureSDK/Attribute/AttributeValue.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//
// AttributeValue.swift
// MeasureSDK
//
// Created by Adwin Ross on 04/01/25.
//

import Foundation

/// Represents a value of an attribute. It can be a string, boolean, integer, or double.
public enum AttributeValue {
case string(String)
case boolean(Bool)
case int(Int)
case long(Int64)
case float(Float)
case double(Double)

/// Returns the underlying value as `Any`.
var value: Any {
switch self {
case .string(let value): return value
case .boolean(let value): return value
case .int(let value): return value
case .long(let value): return value
case .float(let value): return value
case .double(let value): return value
}
}

func serialize() -> Any {
switch self {
case .string(let stringValue):
return "\"\(stringValue)\""
default:
return self.value
}
}
}

/// Serializer for `AttributeValue` to handle encoding and decoding.
enum AttributeValueSerializer {
static func serialize(_ value: AttributeValue) -> Any {
switch value {
case .string(let stringValue):
return "\"\(value.value)\""
default:
return value.value
}
}

static func deserialize(from value: Any) -> AttributeValue? {
if let stringValue = value as? String {
return .string(stringValue)
} else if let boolValue = value as? Bool {
return .boolean(boolValue)
} else if let intValue = value as? Int {
return .int(intValue)
} else if let longValue = value as? Int64 {
return .long(longValue)
} else if let floatValue = value as? Float {
return .float(floatValue)
} else if let doubleValue = value as? Double {
return .double(doubleValue)
}
return nil
}
}
20 changes: 20 additions & 0 deletions ios/MeasureSDK/Config/BaseConfigProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,26 @@ final class BaseConfigProvider: ConfigProvider {
self.cachedConfig = configLoader.getCachedConfig()
}

var maxUserDefinedAttributesPerEvent: Int {
return getMergedConfig(\.maxUserDefinedAttributesPerEvent)
}

var maxUserDefinedAttributeKeyLength: Int {
return getMergedConfig(\.maxUserDefinedAttributeKeyLength)
}

var maxUserDefinedAttributeValueLength: Int {
return getMergedConfig(\.maxUserDefinedAttributeValueLength)
}

var maxEventNameLength: Int {
return getMergedConfig(\.maxEventNameLength)
}

var customEventNameRegex: String {
return getMergedConfig(\.customEventNameRegex)
}

var maxSessionDurationMs: Number {
return getMergedConfig(\.maxSessionDurationMs)
}
Expand Down
10 changes: 10 additions & 0 deletions ios/MeasureSDK/Config/Config.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@ struct Config: InternalConfig, MeasureConfig {
let maxSessionDurationMs: Number
let cpuTrackingIntervalMs: UnsignedNumber
let memoryTrackingIntervalMs: UnsignedNumber
let customEventNameRegex: String
let maxEventNameLength: Int
let maxUserDefinedAttributeKeyLength: Int
let maxUserDefinedAttributeValueLength: Int
let maxUserDefinedAttributesPerEvent: Int

internal init(enableLogging: Bool = DefaultConfig.enableLogging,
trackScreenshotOnCrash: Bool = DefaultConfig.trackScreenshotOnCrash,
Expand All @@ -45,5 +50,10 @@ struct Config: InternalConfig, MeasureConfig {
self.maxSessionDurationMs = 6 * 60 * 60 * 1000 // 6 hours
self.cpuTrackingIntervalMs = 3 * 1000 // 3 seconds
self.memoryTrackingIntervalMs = 2 * 1000 // 2 seconds
self.customEventNameRegex = "^[a-zA-Z0-9_-]+$"
self.maxEventNameLength = 64 // 64 chars
self.maxUserDefinedAttributeKeyLength = 256 // 256 chars
self.maxUserDefinedAttributeValueLength = 256 // 256 chars
self.maxUserDefinedAttributesPerEvent = 100
}
}
15 changes: 15 additions & 0 deletions ios/MeasureSDK/Config/InternalConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,19 @@ protocol InternalConfig {

/// The interval at which memory related data is collected. Defaults to 2 seconds.
var memoryTrackingIntervalMs: UnsignedNumber { get }

/// The maximum length of a custom event. Defaults to 64 chars.
var maxEventNameLength: Int { get }

/// The regex to validate a custom event name.
var customEventNameRegex: String { get }

/// The maximum length of user defined attribute key. Defaults to 256 chars.
var maxUserDefinedAttributeKeyLength: Int { get }

/// The maximum length of a user defined attribute value. Defaults to 256 chars.
var maxUserDefinedAttributeValueLength: Int { get }

/// The maximum number of user defined attributes for an event. Defaults to 100.
var maxUserDefinedAttributesPerEvent: Int { get }
}
Loading

0 comments on commit c25f9ce

Please sign in to comment.