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

Implement Anonymous user tracking #709

Closed
wants to merge 9 commits into from
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
113 changes: 113 additions & 0 deletions AnonymousUserEventTracking.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# AnonymousUserManager Class

## Class Introduction

The `AnonymousUserManager` class is responsible for managing anonymous user sessions and tracking events.
The `AnonymousUserManager+Functions` class is contains util functions and `CriteriaCompletionChecker` struct which contains criteria checking logic.
It includes methods for updating sessions, tracking events (i.e custom event, update cart, update user and purchase) and create a user if criterias are met.
We call track methods of this class internally to make sure we have tracked the events even when user is NOT logged in and after certain criterias are met we create a user and logs them automatically and sync events through Iterable API.

## Class Structure

The `AnonymousUserManager` class includes the following key components:

- **Methods:**
- `updateAnonSession()`: Updates the anonymous user session.
- `trackAnonEvent(name: String, dataFields: [AnyHashable: Any]?)`: Tracks an anonymous event and store it locally.
- `trackAnonPurchaseEvent(total: NSNumber, items: [CommerceItem], dataFields: [AnyHashable: Any]?)`: Tracks an anonymous purchase event and store it locally.
- `trackAnonUpdateUser(_ dataFields: [AnyHashable: Any])`: Tracks an anonymous update user event and store it locally.
- `trackAnonUpdateCart(items: [CommerceItem])`: Tracks an anonymous cart event and store it locally.
- `trackAnonTokenRegistration(token: String)`: Tracks an anonymous token registration event and store it locally.
- `getAnonCriteria()`: Gets the anonymous criteria.
- `checkCriteriaCompletion()`: Checks if criterias are being met.
- `createKnownUser()`: Creates a user after criterias met and login the user and then sync the data through track APIs.
- `syncEvents()`: Syncs locally saved data through track APIs.
- `updateAnonSession()`: Stores an anonymous sessions locally. Update the last session time when new session is created.
- `storeEventData()`: Stores event data locally.
- `logout()`: Reset the locally saved data when user logs out to make sure no old data is left.
- `syncNonSyncedEvents()`: Syncs unsynced data which might have failed to sync when calling syncEvents for the first time after criterias met.
- `convertCommerceItems(from dictionaries: [[AnyHashable: Any]]) -> [CommerceItem]`: Convert to commerce items from dictionaries.
- `convertCommerceItemsToDictionary(_ items: [CommerceItem]) -> [[AnyHashable:Any]]`: Convert commerce items to dictionaries.
- `getUTCDateTime()`: Converts UTC Datetime from current time.


## Methods Description

### `updateAnonSession()`

This method updates the anonymous user session. It does the following:

* Retrieves the previous session data from local storage.
* Increments the session number.
* Stores the updated session data back to local storage.

### `trackAnonEvent(name: String, dataFields: [AnyHashable: Any]?)`

This method tracks an anonymous event. It does the following:

* Creates a dictionary object with event details, including the event name, timestamp, data fields, and tracking type.
* Stores the event data in local storage.
* Checks criteria completion and creates a known user if criteria are met.

### `trackAnonPurchaseEvent(total: NSNumber, items: [CommerceItem], dataFields: [AnyHashable: Any]?)`

This method tracks an anonymous purchase event. It does the following:

* Converts the list of commerce items to JSON.
* Creates a dictionary object with purchase event details, including items, total, timestamp, data fields, and tracking type.
* Stores the purchase event data in local storage.
* Checks criteria completion and creates a known user if criteria are met.

### `trackAnonUpdateUser(dataFields: [AnyHashable: Any]?)`

This method tracks an anonymous update user event. It does the following:

* Creates a dictionary object with event details, including the event name, timestamp, data fields, and tracking type.
* Stores the event data in local storage, and if data of this event already exists it replaces the data.
* Checks criteria completion and creates a known user if criteria are met.

### `trackAnonUpdateCart(items: [CommerceItem])`

This method tracks an anonymous cart update. It does the following:

* Converts the list of commerce items to dictionary.
* Creates a dictionary object with cart update details, including items, timestamp, and tracking type.
* Stores the cart update data in local storage.
* Checks criteria completion and creates a known user if criteria are met.

### `trackAnonTokenRegistration(token: String)`

This method tracks an anonymous token registration event and stores it locally.

### `getAnonCriteria()`

This method is responsible for fetching criteria data. It simulates calling an API and saving data in local storage.

### `checkCriteriaCompletion()`

This private method checks if criteria for creating a known user are met. It compares stored event data with predefined criteria and returns `criteriaId` if any of the criteria is matched.

### `createKnownUser()`

This method is responsible for creating a known user in the Iterable API. It does the following:

* Sets a random user ID using a UUID (Universally Unique Identifier).
* Retrieves user session data from local storage.
* If user session data exists, it updates the user information in the Iterable API.
* Calls the syncEvents() method to synchronize anonymous tracked events.
* Finally, it clears locally stored data after data is syncronized.

### `syncEvents()`

This method is used to synchronize anonymous tracked events stored in local storage with the Iterable API. It performs the following tasks:

* Retrieves the list of tracked events from local storage.
* Iterates through the list of events and processes each event based on its type.
* Supported event types include regular event tracking, purchase event tracking, and cart update tracking.
* For each event, it extracts relevant data, including event name, data fields, items (for purchase and cart update events), and timestamps.
* It then calls the Iterable API to sync these events.
* After processing all the events, it clears locally stored event data.

### `updateAnonSession()`

This method is responsible for storing/updating anonymous sessions locally. It updates the last session time each time when new session is created.
38 changes: 38 additions & 0 deletions AnonymousUserMerge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# AnonymousUserMerge Class

## Class Introduction

The `AnonymousUserMerge` class is responsible for merging anonymous user with logged-in one.
It includes methods for merge user by userId and emailId.
We call methods of this class internally to merge user when setUserId or setEmail method call. After merge we sync events through Iterable API.

## Class Structure

The `AnonymousUserMerge` class includes the following key components:

- **Methods:**
- `mergeUserUsingUserId(apiClient: IterableApiClient, destinationUserId: String)`: Merge user using userID if anonymous user exists and sync events
- `mergeUserUsingEmail(apiClient: IterableApiClient, destinationEmail: String)`: Merge user using emailId if anonymous user exists and sync events
- `callMergeApi(apiClient: IterableApiClient, sourceEmail: String, sourceUserId: String, destinationEmail: String, destinationUserId: String)`: Call API to merge user and sync remaining events.

## Methods Description

### `mergeUserUsingUserId(apiClient: IterableApiClient, destinationUserId: String)`

This method merge the anonymous user with the logged-in one. It does the following:

* Check for user exists using userId.
* If user exists then call the merge user API.

### `mergeUserUsingEmail(apiClient: IterableApiClient, destinationEmail: String)`

This method merge the anonymous user with the logged-in one. It does the following:

* Check for user exists using emailId.
* If user exists then call the merge user API.

### `callMergeApi(apiClient: IterableApiClient, sourceEmail: String, sourceUserId: String, destinationEmail: String, destinationUserId: String)`

This method call API to merge user. It does the following:

* Call the Iterable API and sync remaining events.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
objects = {

/* Begin PBXBuildFile section */
37088F332B3C38250000B218 /* IterableAppExtensions in Frameworks */ = {isa = PBXBuildFile; productRef = 37088F322B3C38250000B218 /* IterableAppExtensions */; };
37088F352B3C38250000B218 /* IterableSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 37088F342B3C38250000B218 /* IterableSDK */; };
551A5FF1251AB1950004C9A0 /* IterableSDK in Frameworks */ = {isa = PBXBuildFile; productRef = 551A5FF0251AB1950004C9A0 /* IterableSDK */; };
551A5FF3251AB19B0004C9A0 /* IterableAppExtensions in Frameworks */ = {isa = PBXBuildFile; productRef = 551A5FF2251AB19B0004C9A0 /* IterableAppExtensions */; };
AC1BDF5820E304BC000010CA /* CoffeeListTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = AC5ECD9E20E304000081E1DA /* CoffeeListTableViewController.swift */; };
Expand Down Expand Up @@ -73,13 +75,15 @@
buildActionMask = 2147483647;
files = (
551A5FF1251AB1950004C9A0 /* IterableSDK in Frameworks */,
37088F352B3C38250000B218 /* IterableSDK in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
ACA3A14B20E2F83D00FEF74F /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
37088F332B3C38250000B218 /* IterableAppExtensions in Frameworks */,
551A5FF3251AB19B0004C9A0 /* IterableAppExtensions in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down Expand Up @@ -202,6 +206,7 @@
name = "swift-sample-app";
packageProductDependencies = (
551A5FF0251AB1950004C9A0 /* IterableSDK */,
37088F342B3C38250000B218 /* IterableSDK */,
);
productName = "swift-sample-app";
productReference = ACA3A13520E2F6AF00FEF74F /* swift-sample-app.app */;
Expand All @@ -222,6 +227,7 @@
name = "swift-sample-app-notification-extension";
packageProductDependencies = (
551A5FF2251AB19B0004C9A0 /* IterableAppExtensions */,
37088F322B3C38250000B218 /* IterableAppExtensions */,
);
productName = "swift-sample-app-notification-extension";
productReference = ACA3A14E20E2F83D00FEF74F /* swift-sample-app-notification-extension.appex */;
Expand Down Expand Up @@ -264,6 +270,8 @@
Base,
);
mainGroup = ACA3A12C20E2F6AF00FEF74F;
packageReferences = (
);
productRefGroup = ACA3A13620E2F6AF00FEF74F /* Products */;
projectDirPath = "";
projectRoot = "";
Expand Down Expand Up @@ -572,6 +580,14 @@
/* End XCConfigurationList section */

/* Begin XCSwiftPackageProductDependency section */
37088F322B3C38250000B218 /* IterableAppExtensions */ = {
isa = XCSwiftPackageProductDependency;
productName = IterableAppExtensions;
};
37088F342B3C38250000B218 /* IterableSDK */ = {
isa = XCSwiftPackageProductDependency;
productName = IterableSDK;
};
551A5FF0251AB1950004C9A0 /* IterableSDK */ = {
isa = XCSwiftPackageProductDependency;
productName = IterableSDK;
Expand Down
Loading