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

Release v7.1.0 #399

Merged
merged 62 commits into from
Jan 23, 2025
Merged

Release v7.1.0 #399

merged 62 commits into from
Jan 23, 2025

Conversation

nishant-clevertap
Copy link
Contributor

@nishant-clevertap nishant-clevertap commented Jan 22, 2025

Added

  • Adds support for triggering InApps based on first-time event filtering in multiple triggers. Now you can create campaign triggers that combine recurring and first-time events. For example: Trigger a campaign when "Charged" occurs (every time) OR "App Launched" occurs (first time only).
  • Adds new user-level event log tracking system to store and manage user event history. New APIs include:
    • getUserEventLog(:): Get details about a specific event
    • getUserEventLogCount(:): Get count of times an event occurred
    • getUserLastVisitTs(): Get timestamp of user's last app visit
    • getUserAppLaunchCount(): Get total number of times user has launched the app
    • getUserEventLogHistory(): Get full event history for current user

API Changes

  • Deprecated: The old event tracking APIs tracked events at the device level rather than the user level, making it difficult to maintain accurate user-specific event histories, especially in multi-user scenarios. The following methods have been deprecated in favor of new user-specific event tracking APIs that provide more accurate, user-level analytics. These deprecated methods will be removed in future versions with prior notice:
    • eventGetDetail(:): Use getUserEventLog() instead for user-specific event details
    • eventGetOccurrences(:): Use getUserEventLogCount() instead for user-specific event counts
    • eventGetFirstTime(:): Use getUserEventLog() instead for user-specific first occurrence timestamp
    • eventGetLastTime(:): Use getUserEventLog() instead for user-specific last occurrence timestamp
    • userGetPreviousVisitTime(): Use getUserLastVisitTs() instead for user-specific last visit timestamp
    • userGetTotalVisits(): Use getUserAppLaunchCount() instead for user-specific app launch count
    • userGetEventHistory(): Use getUserEventLogHistory() instead for user-specific event history

Summary by CodeRabbit

Release Notes for CleverTap iOS SDK v7.1.0

  • New Features

    • Added support for first-time event filtering in campaign triggers.
    • Introduced a new user-level event log tracking system.
    • Enhanced event tracking with user-specific APIs.
    • Expanded in-app notification capabilities with new view controllers and templates.
    • Added a method to notify when an in-app notification is displayed.
    • New section in documentation for User Event Logging APIs.
  • Improvements

    • Added new methods for retrieving user event details, counts, and timestamps.
    • Improved event database management with new methods for event insertion, updating, and retrieval.
    • Enhanced initialization process in InAppHelper for better data handling.
  • Deprecations

    • Deprecated device-level event tracking methods.
    • Old event tracking APIs will be removed in future versions.
  • Performance

    • Optimized event logging and retrieval mechanisms.
    • Introduced more efficient user-centric data handling.

akashvercetti and others added 30 commits October 30, 2024 10:52
- TODO: Call IsEventFirstTime from the new db/localDataStore class
- Renamed one letter object names to fit the context
- TODO: Call appropriate persist method from the new db/localDataStore class
- Added new class CTEventDatabase to store user events in database.
- Added create table, insert, update and check event methods.
…ofile-events

[MC-2088] Evaluate first time profile events
…ents

 [MC-2086] Evaluate first time events
- synchronised userEventLogs set access
…e to the database. updated tests. added normalised names to queries.
Copy link
Contributor

coderabbitai bot commented Jan 22, 2025

Walkthrough

The changes introduce version 7.1.0 of the CleverTap SDK, focusing on enhancing event tracking and user-level analytics. A significant addition is the support for first-time event filtering in in-app notifications, allowing more granular campaign triggers. The SDK now includes a new user-level event log tracking system with APIs for retrieving event details, counts, and timestamps. Several device-level event tracking methods have been deprecated in favor of more robust user-specific event tracking approaches.

Changes

File Change Summary
CHANGELOG.md Added new version 7.1.0, introduced user event log APIs, deprecated old event tracking methods
CleverTap-iOS-SDK.podspec Added EventDatabase/*.{h,m} to tvOS source files
CleverTapSDK.xcodeproj/project.pbxproj Added new files for in-app notifications, custom templates, and inbox feature
CleverTapSDK/CTConstants.h Added new constants for event database management
CleverTapSDK/CTLocalDataStore.h/m Introduced new event logging methods, deprecated old methods
CleverTapSDK/CleverTap.h/m Added new user event log retrieval methods
CleverTapSDK/EventDatabase/CTEventDatabase.h/m New event database management implementation
CleverTapSDK/InApps/CTInAppEvaluationManager.h/m Updated to support local data store
CleverTapSDK/InApps/Matchers/CTTriggerAdapter.h/m Added firstTimeOnly property for event triggers
sdk-version.txt Updated to version 7.1.0
CleverTapSDKTests/CTLocalDataStore+Tests.h/m Added testing category for CTLocalDataStore
CleverTapSDKTests/EventDatabase/CTEventDatabaseTests.m Introduced unit tests for CTEventDatabase functionality
CleverTapSDKTests/InApps/CTInAppEvaluationManagerTest.m Updated tests for CTInAppEvaluationManager to include local data store

Possibly related PRs

Suggested reviewers

  • nishant-clevertap

Poem

🐰 A Rabbit's Ode to Event Tracking 🕹️
In databases deep and logs so bright,
First-time events now shine with might.
User stories tracked with tender care,
Analytics dancing everywhere.
CleverTap leaps with version glee! 🚀


🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🧹 Nitpick comments (21)
CleverTapSDK/InApps/Matchers/CTTriggersMatcher.h (1)

17-17: LGTM! Well-structured dependency injection.

The new initializer follows good architectural practices:

  • Uses dependency injection for CTLocalDataStore
  • Returns instancetype (preferred over id)
  • Enables the new first-time event filtering functionality

This design pattern will make it easier to:

  • Unit test the trigger matching logic
  • Maintain separation of concerns
  • Extend the functionality in the future
CleverTapSDK/InApps/Matchers/CTTriggerAdapter.m (2)

33-33: Add type validation for firstTimeOnly.

The firstTimeOnly value from JSON should be validated to ensure it's of the expected type.

Consider adding type validation:

-        self.firstTimeOnly = triggerJSON[@"firstTimeOnly"];
+        id firstTimeOnly = triggerJSON[@"firstTimeOnly"];
+        if ([firstTimeOnly isKindOfClass:[NSString class]]) {
+            self.firstTimeOnly = firstTimeOnly;
+        } else {
+            CleverTapLogStaticDebug(@"Cannot parse firstTimeOnly: %@", firstTimeOnly);
+        }

33-33: Consider using NSNumber instead of NSString for boolean flag.

Since firstTimeOnly appears to be a boolean flag based on its name and purpose, consider using NSNumber instead of NSString for better type safety and semantic clarity.

Update the property type and parsing:

-@property (nonatomic, strong) NSString *firstTimeOnly;
+@property (nonatomic, strong) NSNumber *firstTimeOnly;

 // In initWithJSON:
-        self.firstTimeOnly = triggerJSON[@"firstTimeOnly"];
+        id firstTimeOnly = triggerJSON[@"firstTimeOnly"];
+        if ([firstTimeOnly isKindOfClass:[NSNumber class]]) {
+            self.firstTimeOnly = firstTimeOnly;
+        } else if ([firstTimeOnly isKindOfClass:[NSString class]]) {
+            // Handle string values like "true", "false" for backward compatibility
+            self.firstTimeOnly = @([firstTimeOnly boolValue]);
+        } else {
+            CleverTapLogStaticDebug(@"Cannot parse firstTimeOnly: %@", firstTimeOnly);
+        }
CleverTapSDK.xcodeproj/project.pbxproj (1)

Line range hint 1-3200: Overall Project Structure Assessment

The project structure maintains good organization with:

  1. Clear separation of source and test files
  2. Proper grouping of related files
  3. Correct file ownership and target membership
  4. Appropriate build phase configurations
  5. Consistent use of framework search paths and linking

Consider the following recommendations:

  1. Add a README.md in the EventDatabase folder documenting its purpose and usage
  2. Consider adding a CHANGELOG.md to track significant changes
  3. Consider setting up SwiftLint or similar tools for consistent code style
CleverTapSDK/CleverTapEventDetail.h (1)

6-6: Add documentation for new properties.

Please add documentation comments for normalizedEventName and deviceID properties to explain their purpose and usage in the context of user-level event tracking.

Example documentation:

/**
 * The normalized version of the event name used for consistent tracking.
 */
@property (nonatomic, strong) NSString *normalizedEventName;

/**
 * The unique identifier of the device associated with this event.
 */
@property (nonatomic, strong) NSString *deviceID;

Also applies to: 10-10

CleverTapSDKTests/CTLocalDataStore+Tests.h (1)

11-14: Add documentation and nullability annotation.

Please add:

  1. Documentation for the test category interface explaining its purpose.
  2. Nullability annotation for the block parameter.

Example:

NS_ASSUME_NONNULL_BEGIN

/**
 * Test category providing access to internal queue for asynchronous testing.
 */
@interface CTLocalDataStore (Tests)

/**
 * Executes the given block on the background queue.
 * @param taskBlock The block to execute. Must not be nil.
 */
- (void)runOnBackgroundQueue:(nonnull void (^)(void))taskBlock;

/**
 * The background queue used for asynchronous operations.
 */
@property (nonatomic, readonly) dispatch_queue_t backgroundQueue;

@end

NS_ASSUME_NONNULL_END
CleverTapSDK/CTDispatchQueueManager.h (3)

19-19: Add documentation for the new method.

The new inSerialQueue method needs documentation to explain its purpose, return value meaning, and thread safety implications.

Add a documentation block:

+/**
+ * Checks if the current execution context is within the serial queue.
+ * @return YES if the current execution is in the serial queue, NO otherwise
+ */
 - (BOOL)inSerialQueue;

19-19: LGTM! Good addition for thread safety.

The new inSerialQueue method is a valuable addition that will help ensure thread-safe access to the new user-level event tracking system.

Consider documenting thread safety requirements in the header, especially since this method will likely be used to enforce serial queue access for critical event logging operations.


19-19: Add documentation for the new method.

The new inSerialQueue method looks good, but please add documentation to explain its purpose and usage. This is especially important as it may be used for ensuring thread-safety in the new event tracking system.

Add a documentation block like:

+/**
+ * Checks if the current execution context is within the serial queue.
+ * @return YES if running in the serial queue, NO otherwise
+ */
 - (BOOL)inSerialQueue;
CleverTapSDKTests/EventDatabase/CTEventDatabase+Tests.h (2)

16-17: Add nullability annotations and documentation.

The initializer method should specify nullability requirements and include documentation explaining its testing purpose.

Add annotations and documentation:

+/**
+ * Test-specific initializer that allows injection of dependencies.
+ * @param dispatchQueueManager The queue manager to use for test operations
+ * @param clock The clock implementation for controlling time in tests
+ * @return A new instance of CTEventDatabase configured for testing
+ */
-  - (instancetype)initWithDispatchQueueManager:(CTDispatchQueueManager*)dispatchQueueManager
-                                       clock:(id<CTClock>)clock;
+  - (instancetype _Nonnull)initWithDispatchQueueManager:(CTDispatchQueueManager* _Nonnull)dispatchQueueManager
+                                       clock:(id<CTClock> _Nonnull)clock;

14-18: LGTM! Well-structured test category.

The test category provides good dependency injection support for testing the new event logging system.

Consider adding documentation comments to explain:

  • The purpose of this test category
  • Expected usage in test scenarios
  • Requirements for the clock protocol implementation
CleverTapSDKTests/InApps/InAppHelper.h (2)

19-19: Document the dataStore property and consider property grouping.

The new dataStore property should be documented and grouped with related properties for better organization.

Add documentation and consider reordering:

 @class CTLocalDataStore;

 // ... other code ...

+/** Local data store for managing user event data in tests */
 @property (nonatomic, strong) CTLocalDataStore *dataStore;

Consider grouping this property with other storage-related properties if any exist in the class.

Also applies to: 39-39


19-19: Document the dataStore property's purpose.

The integration of CTLocalDataStore looks good and aligns with the PR's objective of supporting first-time event filtering. Please add documentation to explain the property's role in the test infrastructure.

Add a documentation block like:

+/**
+ * Local data store instance used for managing event data in tests.
+ * This supports testing of first-time event filtering functionality.
+ */
 @property (nonatomic, strong) CTLocalDataStore *dataStore;

Also applies to: 39-39

CleverTapSDK/EventDatabase/CTEventDatabase.h (1)

30-32: Consider adding completion handler for upsertEvent method.

For consistency with other async operations and to handle potential database errors, consider adding a completion handler to the upsertEvent method.

 - (void)upsertEvent:(NSString *)eventName
 normalizedEventName:(NSString *)normalizedEventName
            deviceID:(NSString *)deviceID;
+- (void)upsertEvent:(NSString *)eventName
+normalizedEventName:(NSString *)normalizedEventName
+           deviceID:(NSString *)deviceID
+         completion:(void (^)(BOOL success))completion;
CleverTapSDK/CTLocalDataStore.h (1)

49-55: Consider adding nullability annotations.

The new user-level event tracking methods should specify nullability for parameters and return types to improve Swift interoperability.

-- (BOOL)isEventLoggedFirstTime:(NSString*)eventName;
+- (BOOL)isEventLoggedFirstTime:(NSString * _Nonnull)eventName;

-- (int)readUserEventLogCount:(NSString *)eventName;
+- (int)readUserEventLogCount:(NSString * _Nonnull)eventName;

-- (CleverTapEventDetail *)readUserEventLog:(NSString *)eventName;
+- (nullable CleverTapEventDetail *)readUserEventLog:(NSString * _Nonnull)eventName;

-- (NSDictionary *)readUserEventLogs;
+- (NSDictionary * _Nonnull)readUserEventLogs;
CleverTapSDKTests/CTLocalDataStoreTests.m (3)

114-120: Add edge cases to profile value test.

Consider adding tests for:

  1. Setting null values
  2. Setting invalid types
  3. Updating existing values

123-127: Add validation to profile field test.

Consider adding assertions for:

  1. Key validation
  2. Value type validation
  3. Error cases

129-154: Enhance event persistence test coverage.

The test verifies basic functionality but should also test:

  1. Multiple events persistence
  2. Event data validation
  3. Error handling
  4. Event count limits
CleverTapSDKTests/InApps/CTTriggersMatcherTest.m (1)

2013-2099: Well-structured test cases for first-time event filtering!

The test cases effectively cover the core functionality of first-time event filtering with proper mock usage and verifications.

Consider adding these test cases to improve coverage:

  1. Negative test case where isEventLoggedFirstTime returns NO
  2. Edge case where firstTimeOnly is NO or not present
  3. Edge case where firstTimeOnly has an invalid value

Example of a negative test case:

+ - (void)testMatchEventWithFirstTimeOnlyWhenNotFirstTime {
+     NSArray *whenTriggers = @[
+         @{
+             @"eventName": @"event1",
+             @"firstTimeOnly": @YES
+         }
+     ];
+     
+     CTTriggersMatcher *triggerMatcher = [[CTTriggersMatcher alloc] initWithDataStore:self.dataStore];
+     CTLocalDataStore *dataStoreMock = OCMPartialMock(self.dataStore);
+     id mockIsEventLoggedFirstTime = OCMStub([dataStoreMock isEventLoggedFirstTime:@"event1"]).andReturn(NO);
+     
+     BOOL match = [triggerMatcher matchEventWhenTriggers:whenTriggers eventName:@"event1" eventProperties:@{}];
+     
+     XCTAssertFalse(match);
+     OCMVerify(mockIsEventLoggedFirstTime);
+ }
CleverTapSDK/CleverTap.m (1)

3115-3142: Review new user event log tracking methods.

The implementation looks good but consider adding documentation comments to describe:

  • The return value semantics (e.g., what does -1 mean for getUserEventLogCount)
  • Whether these methods are thread-safe
  • Performance characteristics for large event histories
+/**
+ * Gets the number of times a specific event occurred for the current user.
+ * @param eventName The name of the event to count
+ * @return Count of event occurrences or -1 if personalization is disabled
+ * @note This method is thread-safe and can be called from any thread
+ */
 - (int)getUserEventLogCount:(NSString *)eventName {
     if (!self.config.enablePersonalization) {
         return -1;
     }
     return [self.localDataStore readUserEventLogCount:eventName];
 }
CHANGELOG.md (1)

4-4: Update the release date to 2025.

The release date should be January 21, 2025 to match the current date.

-### [Version 7.1.0](https://github.com/CleverTap/clevertap-ios-sdk/releases/tag/7.1.0) (January 21, 2024)
+### [Version 7.1.0](https://github.com/CleverTap/clevertap-ios-sdk/releases/tag/7.1.0) (January 21, 2025)
🧰 Tools
🪛 LanguageTool

[inconsistency] ~4-~4: A new year has begun. Did you mean “January 21, 2025”?
Context: .../clevertap-ios-sdk/releases/tag/7.1.0) (January 21, 2024) #### Added - Adds support for trigger...

(DATE_NEW_YEAR)

🪛 Markdownlint (0.37.0)

4-4: Expected: h2; Actual: h3
Heading levels should only increment by one level at a time

(MD001, heading-increment)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9965acc and a1e8ade.

📒 Files selected for processing (31)
  • CHANGELOG.md (1 hunks)
  • CleverTap-iOS-SDK.podspec (1 hunks)
  • CleverTapSDK.xcodeproj/project.pbxproj (19 hunks)
  • CleverTapSDK/CTConstants.h (1 hunks)
  • CleverTapSDK/CTDispatchQueueManager.h (1 hunks)
  • CleverTapSDK/CTLocalDataStore.h (2 hunks)
  • CleverTapSDK/CTLocalDataStore.m (10 hunks)
  • CleverTapSDK/CleverTap.h (7 hunks)
  • CleverTapSDK/CleverTap.m (6 hunks)
  • CleverTapSDK/CleverTapBuildInfo.h (1 hunks)
  • CleverTapSDK/CleverTapEventDetail.h (1 hunks)
  • CleverTapSDK/CleverTapEventDetail.m (1 hunks)
  • CleverTapSDK/EventDatabase/CTEventDatabase.h (1 hunks)
  • CleverTapSDK/EventDatabase/CTEventDatabase.m (1 hunks)
  • CleverTapSDK/InApps/CTInAppEvaluationManager.h (2 hunks)
  • CleverTapSDK/InApps/CTInAppEvaluationManager.m (2 hunks)
  • CleverTapSDK/InApps/Matchers/CTTriggerAdapter.h (1 hunks)
  • CleverTapSDK/InApps/Matchers/CTTriggerAdapter.m (1 hunks)
  • CleverTapSDK/InApps/Matchers/CTTriggersMatcher.h (1 hunks)
  • CleverTapSDK/InApps/Matchers/CTTriggersMatcher.m (3 hunks)
  • CleverTapSDKTests/CTLocalDataStore+Tests.h (1 hunks)
  • CleverTapSDKTests/CTLocalDataStore+Tests.m (1 hunks)
  • CleverTapSDKTests/CTLocalDataStoreTests.m (2 hunks)
  • CleverTapSDKTests/EventDatabase/CTEventDatabase+Tests.h (1 hunks)
  • CleverTapSDKTests/EventDatabase/CTEventDatabaseTests.m (1 hunks)
  • CleverTapSDKTests/InApps/CTInAppEvaluationManagerTest.m (1 hunks)
  • CleverTapSDKTests/InApps/CTTriggersMatcherTest.m (56 hunks)
  • CleverTapSDKTests/InApps/InAppHelper.h (2 hunks)
  • CleverTapSDKTests/InApps/InAppHelper.m (2 hunks)
  • Package.swift (1 hunks)
  • sdk-version.txt (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • sdk-version.txt
  • CleverTapSDK/CleverTapBuildInfo.h
👮 Files not reviewed due to content moderation or server errors (8)
  • CleverTapSDK/CTDispatchQueueManager.h
  • CleverTapSDKTests/EventDatabase/CTEventDatabase+Tests.h
  • CleverTapSDKTests/InApps/InAppHelper.h
  • CleverTapSDK/InApps/CTInAppEvaluationManager.h
  • CleverTapSDKTests/EventDatabase/CTEventDatabaseTests.m
  • CleverTapSDK/InApps/CTInAppEvaluationManager.m
  • CleverTapSDKTests/InApps/CTInAppEvaluationManagerTest.m
  • CleverTapSDK/CleverTap.h
🧰 Additional context used
🪛 LanguageTool
CHANGELOG.md

[inconsistency] ~4-~4: A new year has begun. Did you mean “January 21, 2025”?
Context: .../clevertap-ios-sdk/releases/tag/7.1.0) (January 21, 2024) #### Added - Adds support for trigger...

(DATE_NEW_YEAR)


[grammar] ~6-~6: You’ve repeated a verb. Did you mean to only write one of them?
Context: ...ses/tag/7.1.0) (January 21, 2024) #### Added - Adds support for triggering InApps based on ...

(REPEATED_VERBS)

🪛 Markdownlint (0.37.0)
CHANGELOG.md

4-4: Expected: h2; Actual: h3
Heading levels should only increment by one level at a time

(MD001, heading-increment)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (41)
CleverTapSDK/InApps/Matchers/CTTriggersMatcher.h (1)

11-11: LGTM! Clean dependency management.

The explicit import of CTLocalDataStore.h is appropriate as it's required for the new user-level event tracking functionality.

CleverTapSDK/InApps/Matchers/CTTriggerAdapter.m (1)

33-33: Verify integration with CTTriggersMatcher.

The firstTimeOnly property is used by the CTTriggersMatcher class to determine if an event should only trigger on its first occurrence. Let's verify this integration.

✅ Verification successful

Integration verified and working as expected

The firstTimeOnly property is properly integrated:

  • Correctly declared in adapter
  • Used appropriately in CTTriggersMatcher for first-time event validation
  • Well-tested with multiple test cases
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for usage of firstTimeOnly in CTTriggersMatcher
echo "Searching for firstTimeOnly usage in CTTriggersMatcher..."
rg -A 5 "firstTimeOnly" --type objc

# Search for related method implementations
echo "Searching for matchFirstTimeOnlyForTrigger method..."
ast-grep --pattern 'matchFirstTimeOnlyForTrigger:$_'

Length of output: 3303

CleverTapSDK/InApps/Matchers/CTTriggerAdapter.h (2)

26-26: Verify thread safety of the firstTimeOnly property.

Since this property affects trigger behavior and could potentially be accessed from multiple threads when evaluating in-app campaign conditions, we should ensure proper thread safety.

✅ Verification successful

Property is thread-safe in current implementation

The firstTimeOnly property is thread-safe because:

  • It's immutable after initialization (set only once from JSON)
  • Core operations run on serial queues
  • Nonatomic property provides atomic access

Suggestions for improved clarity:

  • Consider making it readonly in public interface
  • Add thread safety documentation
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for patterns of thread/queue usage in trigger evaluation
rg -A 5 "firstTimeOnly" --type objc
rg -A 5 "dispatch_[a-z_]+" --type objc

Length of output: 55987


Line range hint 13-15: Verify JSON parsing for the new property.

Since the class is initialized with JSON data, we should ensure proper handling of the firstTimeOnly property during initialization.

✅ Verification successful

JSON parsing for firstTimeOnly property is correctly implemented

The property is properly initialized from JSON in initWithJSON: method following the same pattern as other properties in the class. The implementation handles both the presence and absence of the property in JSON data.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for the initialization method implementation
ast-grep --pattern 'initWithJSON:$_' 

# Look for JSON schema or documentation
fd -e json -e md . | xargs rg -l "firstTimeOnly"

Length of output: 17670


Script:

#!/bin/bash
# Find the implementation file and look for property declarations and JSON parsing
rg -t objc -A 5 "CTTriggerAdapter" 

# Search for JSON parsing related code
rg -t objc "initWithJSON|parseJSON|fromJSON" --context 3

# Look for property declarations
ast-grep --pattern '@property $$$firstTimeOnly$$$'

Length of output: 59701


Script:

#!/bin/bash
# Find property declaration in header
rg -t objc "firstTimeOnly" "CleverTapSDK/InApps/Matchers/CTTriggerAdapter.h"

# Look for JSON key constant definition
rg -t objc "firstTimeOnly|FIRST_TIME_ONLY" "CleverTapSDK/CTConstants.h"

Length of output: 196


Script:

#!/bin/bash
# Get complete implementation of CTTriggerAdapter.m
rg -t objc -A 10 "initWithJSON:" "CleverTapSDK/InApps/Matchers/CTTriggerAdapter.m"

# Look for firstTimeOnly initialization
rg -t objc "firstTimeOnly.*=" "CleverTapSDK/InApps/Matchers/CTTriggerAdapter.m"

Length of output: 675

CleverTapSDK.xcodeproj/project.pbxproj (8)

169-170: New event database files added

The changes introduce new event database files:

  • CTEventDatabase.h header file
  • CTEventDatabase.m implementation file

These files appear to be part of implementing the new user-level event log tracking system mentioned in the PR objectives.

Also applies to: 789-790


171-171: Test file added for event database

A new test file CTEventDatabaseTests.m has been added to validate the event database functionality. This is good practice to ensure the new event tracking system works as expected.

Also applies to: 791-791


176-181: Additional header and implementation files added

Several new files have been added:

  • CTEventDatabase.h/m - For event database functionality
  • CTNotificationAction.h/m - For handling notification actions
  • CTCustomTemplateInAppData.h/m - For custom template in-app notification data

The changes maintain proper header visibility by setting appropriate attributes (Public/Private).


805-806: Test helper files added

New test helper files have been added:

  • CTLocalDataStore+Tests.h
  • CTLocalDataStore+Tests.m

These files will help with testing the local data store functionality, which is good for maintainability and testability.


1333-1350: Proper organization of event database files

The event database files are well-organized in their own group/folder:

EventDatabase
├── CTEventDatabase.h
├── CTEventDatabase.m
└── CTEventDatabaseTests.m

This maintains good project structure and separation of concerns.


2513-2513: Proper integration of new files in build phases

The new files are correctly integrated into the build phases:

  • CTCustomTemplateInAppData.m in sources
  • CTEventDatabase.m in sources

This ensures the new functionality will be properly compiled into the framework.

Also applies to: 2768-2768


2572-2572: Test integration

The event database tests are properly integrated into the test target:

4847D1722CDA17C2008DC327 /* CTEventDatabaseTests.m in Sources */

This ensures the tests will be run as part of the test suite.


2627-2628: Test helper integration

The test helper files are properly integrated:

4E0721512D0974B6000DE9C6 /* CTLocalDataStore+Tests.m in Sources */

This provides proper support for testing the local data store functionality.

CleverTapSDK/CleverTapEventDetail.m (1)

6-7: LGTM! Comprehensive debug output.

The description method has been properly updated to include the new properties with appropriate format specifiers.

CleverTapSDKTests/CTLocalDataStore+Tests.m (1)

14-17: LGTM! Appropriate use of runtime for testing.

The implementation correctly uses the Objective-C runtime to access the private queue for testing purposes.

CleverTapSDK/InApps/CTInAppEvaluationManager.h (5)

13-13: Consider impact of new import dependency.

The addition of CTLocalDataStore import creates a new dependency. Ensure this doesn't create circular dependencies.

Run the following script to check for potential circular imports:

✅ Verification successful

No circular dependency concerns found

The import of CTLocalDataStore in CTInAppEvaluationManager follows a clean one-way dependency, where the evaluation manager depends on the data store but not vice versa. This maintains a healthy architectural hierarchy.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for potential circular dependencies
echo "Checking for circular dependencies..."
rg -l "CTLocalDataStore|CTInAppEvaluationManager" | xargs -I {} sh -c 'echo "=== {} ==="; rg "^#import" {}'

Length of output: 7797


13-13: LGTM! Required import for new dependency.

The import supports the integration of local data store functionality.


34-35: Breaking change in initializer signature.

The addition of the localDataStore parameter is a breaking change but is appropriate for version 7.1.0 as it supports the new first-time event filtering feature.

Please ensure all callers of this initializer are updated. Run this script to find potential missed updates:

✅ Verification successful

All callers are properly updated to use the new initializer signature

The verification confirms that all instances of CTInAppEvaluationManager initialization in both production and test code are using the new signature with the localDataStore parameter. No instances of the old signature were found.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for old initializer usage
rg "initWithAccountId:.+inAppTriggerManager:" --type objc

Length of output: 908


Script:

#!/bin/bash
# Broader search for CTInAppEvaluationManager alloc/init patterns
rg "CTInAppEvaluationManager.+alloc|initWithAccountId:" --type objc -A 2

Length of output: 30109


13-13: LGTM! Import aligns with new dependencies.

The import of CTLocalDataStore.h is appropriate for the new functionality.


34-35: Breaking change in initializer - Update documentation and migration guide.

The addition of the localDataStore parameter is a breaking change that needs to be documented. This change supports the new first-time event filtering feature but will require updates from SDK users.

Please ensure:

  1. The CHANGELOG.md is updated to reflect this breaking change
  2. A migration guide is provided for users upgrading from previous versions
  3. Documentation is updated to explain the role of localDataStore in event filtering

Run this script to verify the documentation updates:

CleverTapSDK/EventDatabase/CTEventDatabase.h (1)

17-17: Consider thread-safety implications of singleton initialization.

The singleton pattern with a dispatch queue manager is a good approach, but ensure thread-safe initialization is implemented in the corresponding .m file.

✅ Verification successful

✅ Singleton implementation is properly thread-safe

The implementation in CTEventDatabase.m correctly uses dispatch_once with a static instance variable, ensuring thread-safe initialization. The dependency injection approach with dispatch queue manager is also well implemented.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for proper singleton implementation
ast-grep --pattern 'static $_ *instance = nil;
  $$$
  dispatch_once($_) {
    $$$
  }'

Length of output: 89


Script:

#!/bin/bash
# Search for sharedInstance implementations and references
rg -A 5 "sharedInstanceWithDispatchQueueManager|CTEventDatabase.*shared" --type objc

# Backup search for any singleton-related patterns in CTEventDatabase
rg -A 10 "static.*CTEventDatabase.*instance|dispatch_once.*CTEventDatabase" --type objc

Length of output: 2201

CleverTapSDK/CTLocalDataStore.h (1)

21-29: LGTM! Well-documented deprecation notices.

The deprecation notices clearly indicate the replacement methods and version information.

Package.swift (1)

61-62: LGTM! Correct placement of the new header search path.

The EventDatabase header search path is properly added alongside existing paths.

CleverTap-iOS-SDK.podspec (1)

20-20: LGTM! Proper inclusion of EventDatabase files for tvOS.

The EventDatabase source files are correctly added to the tvOS target's source files list.

CleverTapSDKTests/InApps/InAppHelper.m (1)

70-73: LGTM! Proper initialization of local data store and integration with evaluation manager.

The changes correctly:

  1. Initialize device info and queue manager
  2. Set up local data store with required dependencies
  3. Pass the data store to inAppEvaluationManager

Also applies to: 86-86

CleverTapSDK/InApps/Matchers/CTTriggersMatcher.m (4)

16-18: LGTM! Clean property declaration.

The dataStore property is correctly declared as a strong reference.


22-27: LGTM! Proper initializer implementation.

The initializer correctly:

  1. Calls super init
  2. Assigns the dataStore dependency
  3. Returns self

55-57: LGTM! Good integration with existing match logic.

The first-time check is properly integrated into the match method chain.


134-140: LGTM! Clean implementation of first-time event check.

The implementation:

  1. Early returns if firstTimeOnly is false
  2. Handles both profile attributes and events
  3. Delegates to dataStore for the actual check
CleverTapSDKTests/CTLocalDataStoreTests.m (1)

156-172: LGTM! Well-implemented helper method.

The waitForInitDataStore helper:

  1. Uses proper expectation handling
  2. Has reasonable timeout
  3. Includes error handling
CleverTapSDK/CTConstants.h (1)

281-284: Verify the event database limits.

Please confirm:

  1. Is the max row limit of ~11.5K (2304 * 5) sufficient for your use case?
  2. Is cleaning up 2304 rows at once optimal for performance?

Run this script to check typical event counts in existing implementations:

✅ Verification successful

The event database limits are well-designed and appropriate

The current configuration:

  • Max limit of ~11.5K rows provides sufficient buffer for event storage
  • Cleanup threshold of 2,304 rows (20%) is optimal for performance:
    • Large enough to avoid frequent cleanups
    • Small enough for efficient batch deletion
    • Properly indexed for performance
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for any existing row limits or cleanup thresholds
rg -i "row.*limit|cleanup.*threshold|max.*rows" --type cpp --type objc

Length of output: 1795


Script:

#!/bin/bash
# Search for documentation about event database limits
rg -i "event.*database|row.*limit|cleanup" "README|CHANGELOG|CONTRIBUTING|docs" --type md

# Search for performance tests and benchmarks
rg -i "performance|benchmark|stress.*test" --type objc -A 5 -B 5

# Search for comments explaining the rationale
rg -i "event.*database|row.*limit|cleanup" --type objc --type cpp -A 2 -B 2 --multiline

Length of output: 77591

CleverTapSDKTests/InApps/CTTriggersMatcherTest.m (2)

19-30: Well-structured test setup!

The initialization of dataStore in setUp follows good testing practices with proper dependency injection.


85-85: Consistent integration of dataStore across test methods!

The modifications maintain consistency by properly initializing CTTriggersMatcher with dataStore across all test methods.

Also applies to: 108-108, 128-128, 152-152, 245-245, 289-289, 349-349, 424-424, 499-499, 530-530, 576-576, 605-605, 639-639, 708-708, 755-755, 787-787, 816-816, 848-848, 871-871, 963-963, 985-985, 1008-1008, 1031-1031, 1054-1054, 1077-1077, 1098-1098, 1132-1132, 1154-1154, 1189-1189, 1216-1216, 1245-1245, 1273-1273, 1302-1302, 1348-1348, 1376-1376, 1405-1405, 1451-1451, 1504-1504, 1541-1541, 1564-1564, 1588-1588, 1615-1615, 1642-1642, 1674-1674, 1697-1697, 1724-1724, 1747-1747, 1771-1771, 1804-1804, 1827-1827, 1850-1850, 1901-1901, 1939-1939, 1971-1971, 2007-2007

CleverTapSDK/CleverTap.m (5)

1963-1965: LGTM! Good use of async persistence.

The event persistence is correctly handled asynchronously on a serial queue to prevent race conditions.


476-478: Verify initialization of user last visit timestamp.

The code initializes _userLastVisitTs from event logs, but there's a potential edge case where eventDetails could be nil and lastTime would be accessed.

✅ Verification successful

The nil check implementation is correct and safe

The code properly handles the nullable eventDetails with a ternary operator, setting _userLastVisitTs to -1 when the event details are not found. This follows the SDK's established patterns for handling missing event data.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for potential nil checks in other implementations
rg -A 5 "getUserEventLog.*lastTime" 

Length of output: 37


Script:

#!/bin/bash
# Search for getUserEventLog method usage
rg "getUserEventLog"

# Search for lastTime property access patterns
rg "lastTime" -A 2

Length of output: 8230


198-198: LGTM - Property addition for tracking user's last visit timestamp

The property declaration follows proper conventions and uses appropriate type for timestamp storage.


3115-3142: LGTM - Well-structured implementation of user event log methods

The new methods provide a robust user-level event tracking system with:

  • Proper validation of personalization settings
  • Consistent error handling
  • Clear method signatures and return types

476-478: LGTM - Improved app launch event handling

The changes properly integrate the new user event logging system with app launch tracking:

  • Uses new getUserEventLog API to fetch event details
  • Includes proper null checks
  • Correctly updates the user's last visit timestamp
CHANGELOG.md (2)

6-14: LGTM! Clear documentation of new features.

The changelog clearly documents the new user-level event tracking system and its APIs.

🧰 Tools
🪛 LanguageTool

[grammar] ~6-~6: You’ve repeated a verb. Did you mean to only write one of them?
Context: ...ses/tag/7.1.0) (January 21, 2024) #### Added - Adds support for triggering InApps based on ...

(REPEATED_VERBS)


17-24: LGTM! Well-documented API deprecations.

The changelog properly documents deprecated APIs with clear migration paths and rationale.

CleverTapSDKTests/InApps/InAppHelper.h (1)

19-19: LGTM! Good integration of local data store.

The addition of CTLocalDataStore supports the new first-time event filtering feature.

Please ensure all test files using InAppHelper are updated to initialize this new required dependency. Run this script to check:

Also applies to: 39-39

✅ Verification successful

Tests are properly handling the new dependency

The verification shows that all test files are using InAppHelper correctly. The helper class encapsulates dependency initialization, including the new dataStore property, making explicit initialization unnecessary in individual test files.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for InAppHelper usage in test files
rg "InAppHelper\s+\*\w+\s*=\s*\[\[InAppHelper\s+alloc\]\s+init\]" CleverTapSDKTests/

Length of output: 184


Script:

#!/bin/bash
# Search for all InAppHelper references in test files
echo "=== Searching for all InAppHelper references ==="
rg "InAppHelper" CleverTapSDKTests/ -l

echo -e "\n=== Checking CTInAppStoreTest.m for dataStore setup ==="
rg "dataStore" CleverTapSDKTests/InApps/CTInAppStoreTest.m -C 2

Length of output: 713


Script:

#!/bin/bash
# Check test files for InAppHelper usage patterns
for file in CTLimitsMatcherTest.m CTInAppStoreTest.m CTInAppFCManagerTest.m CTImpressionManagerTest.m CTInAppEvaluationManagerTest.m CTInAppTriggerManagerTest.m; do
    echo "=== Checking $file ==="
    rg "InAppHelper" "CleverTapSDKTests/InApps/$file" -B 2 -A 2
done

Length of output: 5371

CleverTapSDKTests/EventDatabase/CTEventDatabase+Tests.h (1)

16-17: Well-structured test helper with good dependency injection!

The test category is well-designed, allowing for controlled testing of the new event tracking system by injecting the dispatch queue manager and clock dependencies.

Comment on lines 548 to 564
NSString *updateSQL = [NSString stringWithFormat:@"PRAGMA user_version = %ld;", (long)version];

[self.dispatchQueueManager runSerialAsync:^{
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(self->_eventDatabase, [updateSQL UTF8String], -1, &statement, NULL) == SQLITE_OK) {
sqlite3_bind_int(statement, 1, (int)version);

int result = sqlite3_step(statement);
if (result != SQLITE_OK) {
CleverTapLogStaticInternal(@"SQL Error: %s", sqlite3_errmsg(self->_eventDatabase));
}

sqlite3_finalize(statement);
} else {
CleverTapLogStaticInternal(@"Failed to prepare update statement: %s", sqlite3_errmsg(self->_eventDatabase));
}
}];
Copy link
Contributor

@coderabbitai coderabbitai bot Jan 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix improper parameter binding in setDatabaseVersion: method.

The SQL statement in setDatabaseVersion: is constructed using NSString stringWithFormat: to embed the version directly into the SQL command. This approach can lead to SQL injection vulnerabilities and prevents proper parameter binding. Additionally, the call to sqlite3_bind_int has no effect because there are no parameter markers (?) in the SQL statement.

Apply this diff to fix the issue:

-    NSString *updateSQL = [NSString stringWithFormat:@"PRAGMA user_version = %ld;", (long)version];
+    const char *updateSQL = "PRAGMA user_version = ?";
    ...
-    if (sqlite3_prepare_v2(self->_eventDatabase, [updateSQL UTF8String], -1, &statement, NULL) == SQLITE_OK) {
+    if (sqlite3_prepare_v2(self->_eventDatabase, updateSQL, -1, &statement, NULL) == SQLITE_OK) {
    ...
-    sqlite3_bind_int(statement, 1, (int)version);
+    sqlite3_bind_int(statement, 1, (int)version);

Ensure the SQL statement uses a parameter marker and properly binds the version value using sqlite3_bind_int. This prevents SQL injection vulnerabilities and ensures the pragma command executes correctly.

Committable suggestion skipped: line range outside the PR's diff.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nishant-clevertap please take care of this

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

CleverTapSDK/CTLocalDataStore.m Show resolved Hide resolved
CleverTapSDKTests/CTLocalDataStore+Tests.m Show resolved Hide resolved
CleverTapSDK/EventDatabase/CTEventDatabase.h Show resolved Hide resolved
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
CleverTapSDK/InApps/CustomTemplates/CTTemplateContext.m (1)

264-273: Add input validation and documentation for the action name resolution.

The method implements a good fallback chain but could benefit from defensive programming and documentation.

Consider these improvements:

+/**
+ * Returns the name for the given notification action.
+ * @param action The notification action (must not be nil)
+ * @return The action name using the following fallback chain:
+ *         1. customTemplateInAppData.templateName
+ *         2. inAppActionTypeString
+ *         3. Empty string as fallback
+ */
 - (NSString *)actionName:(CTNotificationAction *)action {
+    if (!action) {
+        CleverTapLogStaticDebug(@"%@: Action parameter cannot be nil", [self class]);
+        return @"";
+    }
     NSString *name = action.customTemplateInAppData.templateName;
     if (!name) {
         name = [CTInAppUtils inAppActionTypeString:action.type];
     }
     if (!name) {
         name = @"";
     }
     return name;
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a1e8ade and 83382c2.

📒 Files selected for processing (1)
  • CleverTapSDK/InApps/CustomTemplates/CTTemplateContext.m (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (1)
CleverTapSDK/InApps/CustomTemplates/CTTemplateContext.m (1)

281-281: LGTM! Good refactoring to use the centralized action name resolution.

The change improves code maintainability by using the new actionName: method instead of directly accessing the template name.

* Implement in-app notification did show callback

* Update changelog
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
CleverTapSDK/CleverTapInAppNotificationDelegate.h (1)

41-49: Consider enhancing documentation and type safety.

A few suggestions to improve the implementation:

  1. The documentation could be more detailed about the structure of the notification dictionary to help implementers understand what data to expect.
  2. Consider creating a dedicated model object (e.g., CTInAppNotification) instead of using NSDictionary for better type safety and IDE support.

Example documentation improvement:

/*!
 @discussion
 This is called when an in-app notification is rendered to the user.
 Use this callback to track notification display events or perform custom actions
 when notifications appear.
 
 @param notification The notification JSON object containing:
                    - id: The unique identifier of the notification
                    - type: The type of notification (e.g., "alert", "banner")
                    - content: The notification content and styling properties
                    - extras: Any custom key-value pairs set in dashboard
 */
CHANGELOG.md (1)

4-4: Update the release date to 2025.

Since we're in January 2025, the release date should be updated to "January 21, 2025".

-### [Version 7.1.0](https://github.com/CleverTap/clevertap-ios-sdk/releases/tag/7.1.0) (January 21, 2024)
+### [Version 7.1.0](https://github.com/CleverTap/clevertap-ios-sdk/releases/tag/7.1.0) (January 21, 2025)
🧰 Tools
🪛 LanguageTool

[inconsistency] ~4-~4: A new year has begun. Did you mean “January 21, 2025”?
Context: .../clevertap-ios-sdk/releases/tag/7.1.0) (January 21, 2024) #### Added - Adds support for trigger...

(DATE_NEW_YEAR)

🪛 Markdownlint (0.37.0)

4-4: Expected: h2; Actual: h3
Heading levels should only increment by one level at a time

(MD001, heading-increment)

CleverTapSDK/InApps/CTInAppDisplayManager.m (1)

517-521: LGTM! Consider adding documentation.

The implementation correctly notifies the delegate when an in-app notification is shown. Consider adding documentation to describe the method's purpose and parameters.

+/**
+ * Notifies the delegate that an in-app notification was shown.
+ * @param notification The in-app notification that was shown.
+ */
 - (void)notifyNotificationDidShow:(CTInAppNotification *)notification {
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 83382c2 and b6b9ab0.

📒 Files selected for processing (3)
  • CHANGELOG.md (1 hunks)
  • CleverTapSDK/CleverTapInAppNotificationDelegate.h (1 hunks)
  • CleverTapSDK/InApps/CTInAppDisplayManager.m (2 hunks)
🧰 Additional context used
🪛 LanguageTool
CHANGELOG.md

[inconsistency] ~4-~4: A new year has begun. Did you mean “January 21, 2025”?
Context: .../clevertap-ios-sdk/releases/tag/7.1.0) (January 21, 2024) #### Added - Adds support for trigger...

(DATE_NEW_YEAR)


[grammar] ~6-~6: You’ve repeated a verb. Did you mean to only write one of them?
Context: ...ses/tag/7.1.0) (January 21, 2024) #### Added - Adds support for triggering InApps based on ...

(REPEATED_VERBS)

🪛 Markdownlint (0.37.0)
CHANGELOG.md

4-4: Expected: h2; Actual: h3
Heading levels should only increment by one level at a time

(MD001, heading-increment)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build
🔇 Additional comments (5)
CleverTapSDK/CleverTapInAppNotificationDelegate.h (1)

41-49: LGTM! The new delegate method completes the notification lifecycle.

The addition of inAppNotificationDidShow: provides a valuable callback that was missing in the notification flow, allowing delegates to track when notifications are actually rendered to users.

CleverTapSDK/InApps/CTInAppDisplayManager.m (3)

517-521: LGTM! Well-implemented delegate notification.

The implementation correctly follows the delegate pattern with proper nil checks and provides the notification data in JSON format.


598-598: LGTM! Proper sequencing of operations.

The notification to the delegate is correctly placed after recording the internal state.


598-598: LGTM!

The delegate notification is called at the appropriate time in the notification lifecycle, after recording the notification state and updating the frequency capping manager.

CHANGELOG.md (1)

6-15: LGTM! Well-documented changes.

The changelog clearly documents the new features and API additions, providing good context for the changes we saw in the code.

🧰 Tools
🪛 LanguageTool

[grammar] ~6-~6: You’ve repeated a verb. Did you mean to only write one of them?
Context: ...ses/tag/7.1.0) (January 21, 2024) #### Added - Adds support for triggering InApps based on ...

(REPEATED_VERBS)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
README.md (1)

78-81: Add "the" before "CleverTap iOS SDK".

Minor grammatical improvement needed in the description.

-CleverTap iOS SDK supports user-level event log APIs, refer to [User Event Logging](/docs/UserEventLogging.md) for more details and usage examples.
+The CleverTap iOS SDK supports user-level event log APIs, refer to [User Event Logging](/docs/UserEventLogging.md) for more details and usage examples.
🧰 Tools
🪛 LanguageTool

[uncategorized] ~79-~79: You might be missing the article “the” here.
Context: ...mples. ## 🕹️ User Event Logging APIs CleverTap iOS SDK supports user-level event log A...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)

docs/UserEventLogging.md (2)

1-4: Improve the introduction section.

The introduction needs several grammatical improvements and clearer structure.

# User Event Logging APIs

-CleverTap iOS SDK 7.1.0 and above provides following new APIs to track user level event logging. Be sure to call enablePersonalization (typically once at app launch) prior to using this method. These API call involves a database query and we recommend to call API from background thread like shown below.
+The CleverTap iOS SDK 7.1.0 and above provides the following new APIs for user-level event logging. Be sure to call `enablePersonalization()` (typically once at app launch) before using these APIs. Since these APIs involve database queries, we recommend calling them from a background thread as shown below.
🧰 Tools
🪛 LanguageTool

[uncategorized] ~3-~3: You might be missing the article “the” here.
Context: ...verTap iOS SDK 7.1.0 and above provides following new APIs to track user level event logg...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)


[style] ~3-~3: ‘prior to’ might be wordy. Consider a shorter alternative.
Context: ...lization (typically once at app launch) prior to using this method. These API call invol...

(EN_WORDINESS_PREMIUM_PRIOR_TO)


[grammar] ~3-~3: The plural determiner ‘these’ does not agree with the singular noun ‘api’.
Context: ...app launch) prior to using this method. These API call involves a database query and we r...

(THIS_NNS)


[uncategorized] ~3-~3: Use a comma before ‘and’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...These API call involves a database query and we recommend to call API from backgroun...

(COMMA_COMPOUND_SENTENCE)


[grammar] ~3-~3: Did you mean “as”?
Context: ...mend to call API from background thread like shown below. #### Objective-C ```objc ...

(LIKE_SHOWN_TO_AS_SHOWN)


1-124: Improve documentation structure and add missing details.

  1. Heading levels should be consistent (use ### for subsections instead of ####)
  2. Add information about data persistence and limitations

Consider adding the following sections:

  1. Data persistence details (how long is the event history retained?)
  2. Performance implications for large event histories
  3. Best practices for background thread usage
  4. Limitations and edge cases

Would you like me to provide a revised structure with these additions?

🧰 Tools
🪛 LanguageTool

[uncategorized] ~3-~3: You might be missing the article “the” here.
Context: ...verTap iOS SDK 7.1.0 and above provides following new APIs to track user level event logg...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)


[style] ~3-~3: ‘prior to’ might be wordy. Consider a shorter alternative.
Context: ...lization (typically once at app launch) prior to using this method. These API call invol...

(EN_WORDINESS_PREMIUM_PRIOR_TO)


[grammar] ~3-~3: The plural determiner ‘these’ does not agree with the singular noun ‘api’.
Context: ...app launch) prior to using this method. These API call involves a database query and we r...

(THIS_NNS)


[uncategorized] ~3-~3: Use a comma before ‘and’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...These API call involves a database query and we recommend to call API from backgroun...

(COMMA_COMPOUND_SENTENCE)


[grammar] ~3-~3: Did you mean “as”?
Context: ...mend to call API from background thread like shown below. #### Objective-C ```objc ...

(LIKE_SHOWN_TO_AS_SHOWN)


[grammar] ~100-~100: It seems that an article is missing. Did you mean “the total number of” or “a total number of”?
Context: ... print("No events found") } ``` ## Get total number of app launches by user #### Objective-C `...

(BUNCH_OF)


[uncategorized] ~113-~113: It looks like there might be a possessive apostrophe missing. Consider inserting it.
Context: ...cribing: appLaunchCount))") ``` ## Get user last app visit timestamp #### Objective...

(AI_EN_LECTOR_MISSING_NOUN_POSSESSIVE)

🪛 Markdownlint (0.37.0)

5-5: Expected: h2; Actual: h4
Heading levels should only increment by one level at a time

(MD001, heading-increment)


26-26: Expected: h3; Actual: h4
Heading levels should only increment by one level at a time

(MD001, heading-increment)


56-56: Expected: h3; Actual: h4
Heading levels should only increment by one level at a time

(MD001, heading-increment)


69-69: Expected: h3; Actual: h4
Heading levels should only increment by one level at a time

(MD001, heading-increment)


101-101: Expected: h3; Actual: h4
Heading levels should only increment by one level at a time

(MD001, heading-increment)


114-114: Expected: h3; Actual: h4
Heading levels should only increment by one level at a time

(MD001, heading-increment)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b6b9ab0 and a8c3fe8.

📒 Files selected for processing (2)
  • README.md (1 hunks)
  • docs/UserEventLogging.md (1 hunks)
🧰 Additional context used
🪛 LanguageTool
README.md

[uncategorized] ~79-~79: You might be missing the article “the” here.
Context: ...mples. ## 🕹️ User Event Logging APIs CleverTap iOS SDK supports user-level event log A...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)

docs/UserEventLogging.md

[uncategorized] ~3-~3: You might be missing the article “the” here.
Context: ...verTap iOS SDK 7.1.0 and above provides following new APIs to track user level event logg...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)


[style] ~3-~3: ‘prior to’ might be wordy. Consider a shorter alternative.
Context: ...lization (typically once at app launch) prior to using this method. These API call invol...

(EN_WORDINESS_PREMIUM_PRIOR_TO)


[grammar] ~3-~3: The plural determiner ‘these’ does not agree with the singular noun ‘api’.
Context: ...app launch) prior to using this method. These API call involves a database query and we r...

(THIS_NNS)


[uncategorized] ~3-~3: Use a comma before ‘and’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...These API call involves a database query and we recommend to call API from backgroun...

(COMMA_COMPOUND_SENTENCE)


[grammar] ~3-~3: Did you mean “as”?
Context: ...mend to call API from background thread like shown below. #### Objective-C ```objc ...

(LIKE_SHOWN_TO_AS_SHOWN)


[grammar] ~100-~100: It seems that an article is missing. Did you mean “the total number of” or “a total number of”?
Context: ... print("No events found") } ``` ## Get total number of app launches by user #### Objective-C `...

(BUNCH_OF)


[uncategorized] ~113-~113: It looks like there might be a possessive apostrophe missing. Consider inserting it.
Context: ...cribing: appLaunchCount))") ``` ## Get user last app visit timestamp #### Objective...

(AI_EN_LECTOR_MISSING_NOUN_POSSESSIVE)

🪛 Markdownlint (0.37.0)
docs/UserEventLogging.md

5-5: Expected: h2; Actual: h4
Heading levels should only increment by one level at a time

(MD001, heading-increment)


26-26: Expected: h3; Actual: h4
Heading levels should only increment by one level at a time

(MD001, heading-increment)


56-56: Expected: h3; Actual: h4
Heading levels should only increment by one level at a time

(MD001, heading-increment)


69-69: Expected: h3; Actual: h4
Heading levels should only increment by one level at a time

(MD001, heading-increment)


101-101: Expected: h3; Actual: h4
Heading levels should only increment by one level at a time

(MD001, heading-increment)


114-114: Expected: h3; Actual: h4
Heading levels should only increment by one level at a time

(MD001, heading-increment)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build

docs/UserEventLogging.md Show resolved Hide resolved
Comment on lines 1 to 124
## Get user event details
#### Objective-C
```objc
CleverTapEventDetail *event = [[CleverTap sharedInstance] getUserEventLog:@"Product viewed"];
if (event) {
NSString *eventName = event.eventName;
NSTimeInterval firstTime = event.firstTime;
NSTimeInterval lastTime = event.lastTime;
NSInteger eventCount = event.count;
NSString *deviceId = event.deviceID;NSLog(@"eventName: %@, firstTime: %f, lastTime: %f, count: %ld, deviceID: %@", eventName, firstTime, lastTime, (long)eventCount, deviceId);
} else {
NSLog(@"Event not exists");
}
```

#### Swift
```swift
let event: CleverTapEventDetail? = CleverTap.sharedInstance()?.getUserEventLog("Product viewed")
if let event = event {
let eventName: String = event.eventName
let firstTime: Double = event.firstTime
let lastTime: Double = event.lastTime
let count: UInt = event.count
let deviceID: String = event.deviceID
print("Event name: \(eventName), first time: \(firstTime), last time: \(lastTime), count: \(count), device id: \(deviceID)")
} else {
print("Event not exists")
}
```

## Get count of event occurrences
#### Objective-C
```objc
int eventCount = [[CleverTap sharedInstance] getUserEventLogCount:@"Product viewed"];
NSLog(@"Event count: %ld", (long)eventCount);
```

#### Swift
```swift
let eventCount = CleverTap.sharedInstance()?.getUserEventLogCount("Product viewed")
print("Event count: \(String(describing: eventCount))")
```

## Get full event history for user
#### Objective-C
```objc
NSDictionary<NSString *, CleverTapEventDetail *> *allEvents = [[CleverTap sharedInstance] getUserEventLogHistory];
for (NSString *key in allEvents) {
NSString *eventName = allEvents[key].eventName;
NSTimeInterval firstTime = allEvents[key].firstTime;
NSTimeInterval lastTime = allEvents[key].lastTime;
NSInteger eventCount = allEvents[key].count;
NSString *deviceId = allEvents[key].deviceID;
NSLog(@"Event name: %@, firstTime: %f, lastTime: %f, count: %ld, deviceID: %@", eventName, firstTime, lastTime, (long)eventCount, deviceId);
}
```

#### Swift
```swift
let allEvents = CleverTap.sharedInstance()?.getUserEventLogHistory()
if let allEvents = allEvents {
for eventDetails in allEvents.values {
let eventDetails: CleverTapEventDetail = eventDetails as! CleverTapEventDetail
let eventName: String = eventDetails.eventName
let firstTime: Double = eventDetails.firstTime
let lastTime: Double = eventDetails.lastTime
let count: UInt = eventDetails.count
let deviceID: String = eventDetails.deviceID
print("Event name: \(eventName), first time: \(firstTime), last time: \(lastTime), count: \(count), device id: \(deviceID)")
}
} else {
print("No events found")
}
```

## Get total number of app launches by user
#### Objective-C
```objc
int appLaunchCount = [[CleverTap sharedInstance] getUserAppLaunchCount];
NSLog(@"App launched count: %d", appLaunchCount);
```

#### Swift
```swift
let appLaunchCount = CleverTap.sharedInstance()?.getUserAppLaunchCount()
print("App launched count: \(String(describing: appLaunchCount))")
```

## Get user last app visit timestamp
#### Objective-C
```objc
NSTimeInterval lastVisit = [[CleverTap sharedInstance] getUserLastVisitTs];
NSLog(@"User last visit timestamp: %f", lastVisit);
```

#### Swift
```swift
let userLastVisit = CleverTap.sharedInstance()?.getUserLastVisitTs()
print("User last visit timestamp: \(String(describing: userLastVisit))")
```
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codebase verification

Add deprecation notice to the User Event Logging documentation

The documentation should include a deprecation notice at the beginning, listing the old APIs being replaced and their new counterparts. While the deprecation is well documented in the source code and CHANGELOG.md, having this information in the API documentation would improve the developer experience.

Add the following mapping:

  • eventGetFirstTime, eventGetLastTime, eventGetDetailgetUserEventLog
  • eventGetOccurrencesgetUserEventLogCount
  • userGetPreviousVisitTimegetUserLastVisitTs
  • userGetTotalVisitsgetUserAppLaunchCount
  • userGetEventHistorygetUserEventLogHistory
🔗 Analysis chain

Verify documentation of deprecated APIs.

The documentation should mention the deprecated APIs that are being replaced by these new user-level event logging APIs.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Search for documentation of deprecated APIs
echo "Searching for deprecated API documentation..."

# List of deprecated methods to search for
deprecated_methods=(
    "eventGetDetail"
    "eventGetOccurrences"
    "eventGetFirstTime"
    "eventGetLastTime"
    "userGetPreviousVisitTime"
    "userGetTotalVisits"
    "userGetEventHistory"
)

# Search in documentation files
for method in "${deprecated_methods[@]}"; do
    echo "Checking documentation for $method..."
    rg -l "$method" docs/ || echo "No documentation found for $method"
done

Length of output: 2264


Script:

#!/bin/bash
# Search for deprecated method implementations in source code
echo "Searching for deprecated method implementations..."

# List of deprecated methods to search for
deprecated_methods=(
    "eventGetDetail"
    "eventGetOccurrences"
    "eventGetFirstTime"
    "eventGetLastTime"
    "userGetPreviousVisitTime"
    "userGetTotalVisits"
    "userGetEventHistory"
)

# Search in source files
for method in "${deprecated_methods[@]}"; do
    echo "Checking implementation of $method..."
    ast-grep --pattern "$method" || echo "No implementation found for $method"
done

# Also check for any deprecation annotations
rg -i "deprecated|@available" 

Length of output: 18399

🧰 Tools
🪛 LanguageTool

[uncategorized] ~3-~3: You might be missing the article “the” here.
Context: ...verTap iOS SDK 7.1.0 and above provides following new APIs to track user level event logg...

(AI_EN_LECTOR_MISSING_DETERMINER_THE)


[style] ~3-~3: ‘prior to’ might be wordy. Consider a shorter alternative.
Context: ...lization (typically once at app launch) prior to using this method. These API call invol...

(EN_WORDINESS_PREMIUM_PRIOR_TO)


[grammar] ~3-~3: The plural determiner ‘these’ does not agree with the singular noun ‘api’.
Context: ...app launch) prior to using this method. These API call involves a database query and we r...

(THIS_NNS)


[uncategorized] ~3-~3: Use a comma before ‘and’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...These API call involves a database query and we recommend to call API from backgroun...

(COMMA_COMPOUND_SENTENCE)


[grammar] ~3-~3: Did you mean “as”?
Context: ...mend to call API from background thread like shown below. #### Objective-C ```objc ...

(LIKE_SHOWN_TO_AS_SHOWN)


[grammar] ~100-~100: It seems that an article is missing. Did you mean “the total number of” or “a total number of”?
Context: ... print("No events found") } ``` ## Get total number of app launches by user #### Objective-C `...

(BUNCH_OF)


[uncategorized] ~113-~113: It looks like there might be a possessive apostrophe missing. Consider inserting it.
Context: ...cribing: appLaunchCount))") ``` ## Get user last app visit timestamp #### Objective...

(AI_EN_LECTOR_MISSING_NOUN_POSSESSIVE)

🪛 Markdownlint (0.37.0)

5-5: Expected: h2; Actual: h4
Heading levels should only increment by one level at a time

(MD001, heading-increment)


26-26: Expected: h3; Actual: h4
Heading levels should only increment by one level at a time

(MD001, heading-increment)


56-56: Expected: h3; Actual: h4
Heading levels should only increment by one level at a time

(MD001, heading-increment)


69-69: Expected: h3; Actual: h4
Heading levels should only increment by one level at a time

(MD001, heading-increment)


101-101: Expected: h3; Actual: h4
Heading levels should only increment by one level at a time

(MD001, heading-increment)


114-114: Expected: h3; Actual: h4
Heading levels should only increment by one level at a time

(MD001, heading-increment)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
docs/UserEventLogging.md (2)

1-4: 🛠️ Refactor suggestion

Add deprecation notice and improve introduction clarity.

The introduction should include:

  1. A deprecation notice listing the old APIs being replaced
  2. Grammar improvements

Apply these changes:

 # User Event Logging APIs
 
-CleverTap iOS SDK 7.1.0 and above provides following new APIs to track user level event logging. Be sure to call enablePersonalization (typically once at app launch) prior to using this method. These API call involves a database query and we recommend to call API from background thread like shown below.
+CleverTap iOS SDK 7.1.0 and above provides the following new APIs to track user-level event logging. These APIs replace the following deprecated methods:
+
+- `eventGetDetail`, `eventGetLastTime`, `eventGetFirstTime` → `getUserEventLog`
+- `eventGetOccurrences` → `getUserEventLogCount`
+- `userGetPreviousVisitTime` → `getUserLastVisitTs`
+- `userGetTotalVisits` → `getUserAppLaunchCount`
+- `userGetEventHistory` → `getUserEventLogHistory`
+
+## Prerequisites
+
+1. Call `enablePersonalization` once at app launch before using these APIs
+2. Execute these APIs on a background thread as they involve database queries, as shown in the examples below
🧰 Tools
🪛 LanguageTool

[uncategorized] ~3-~3: Possible missing article found.
Context: ...verTap iOS SDK 7.1.0 and above provides following new APIs to track user level event logg...

(AI_HYDRA_LEO_MISSING_THE)


[style] ~3-~3: ‘prior to’ might be wordy. Consider a shorter alternative.
Context: ...lization (typically once at app launch) prior to using this method. These API call invol...

(EN_WORDINESS_PREMIUM_PRIOR_TO)


[grammar] ~3-~3: The plural determiner ‘these’ does not agree with the singular noun ‘api’.
Context: ...app launch) prior to using this method. These API call involves a database query and we r...

(THIS_NNS)


[uncategorized] ~3-~3: Use a comma before ‘and’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...These API call involves a database query and we recommend to call API from backgroun...

(COMMA_COMPOUND_SENTENCE)


[grammar] ~3-~3: Did you mean “as”?
Context: ...mend to call API from background thread like shown below. #### Objective-C ```objc ...

(LIKE_SHOWN_TO_AS_SHOWN)


85-99: ⚠️ Potential issue

Improve Swift code example for safety and consistency.

The Swift code uses force casting which could lead to runtime crashes.

Apply this diff to improve safety:

 let allEvents = CleverTap.sharedInstance()?.getUserEventLogHistory()
 if let allEvents = allEvents {
     for eventDetails in allEvents.values {
-        let eventDetails: CleverTapEventDetail = eventDetails as! CleverTapEventDetail
-        let eventName: String = eventDetails.eventName
-        let firstTime: Double = eventDetails.firstTime
-        let lastTime: Double = eventDetails.lastTime
-        let count: UInt = eventDetails.count
-        let deviceID: String = eventDetails.deviceID
-        print("Event name: \(eventName), first time: \(firstTime), last time: \(lastTime), count: \(count), device id: \(deviceID)")
+        guard let details = eventDetails as? CleverTapEventDetail else {
+            print("Invalid event details format")
+            continue
+        }
+        print("""
+            Event name: \(details.eventName),
+            first time: \(details.firstTime),
+            last time: \(details.lastTime),
+            count: \(details.count),
+            device id: \(details.deviceID)
+            """)
     }
 } else {
     print("No events found")
 }
🧹 Nitpick comments (2)
docs/UserEventLogging.md (2)

5-125: Fix documentation structure and heading hierarchy.

The markdown structure has inconsistent heading levels which affects readability and documentation standards.

Apply these changes to the heading structure:

  1. Use ## for language sections (Objective-C, Swift)
  2. Use ## for main API sections
  3. Use ### for language subsections

Example:

-#### Objective-C
+## Objective-C
🧰 Tools
🪛 LanguageTool

[grammar] ~101-~101: It seems that an article is missing. Did you mean “the total number of” or “a total number of”?
Context: ... print("No events found") } ``` ## Get total number of app launches by user #### Objective-C `...

(BUNCH_OF)

🪛 Markdownlint (0.37.0)

5-5: Expected: h2; Actual: h4
Heading levels should only increment by one level at a time

(MD001, heading-increment)


26-26: Expected: h3; Actual: h4
Heading levels should only increment by one level at a time

(MD001, heading-increment)


57-57: Expected: h3; Actual: h4
Heading levels should only increment by one level at a time

(MD001, heading-increment)


70-70: Expected: h3; Actual: h4
Heading levels should only increment by one level at a time

(MD001, heading-increment)


102-102: Expected: h3; Actual: h4
Heading levels should only increment by one level at a time

(MD001, heading-increment)


115-115: Expected: h3; Actual: h4
Heading levels should only increment by one level at a time

(MD001, heading-increment)


37-37: Improve error message clarity.

The error messages "Event not exists" are grammatically incorrect and not descriptive enough.

Apply these changes:

-    NSLog(@"Event not exists");
+    NSLog(@"Event '%@' not found", eventName);
-    print("Event not exists")
+    print("Event '\(eventName)' not found")

Also applies to: 52-52

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a8c3fe8 and 1cb5a52.

📒 Files selected for processing (1)
  • docs/UserEventLogging.md (1 hunks)
🧰 Additional context used
🪛 LanguageTool
docs/UserEventLogging.md

[uncategorized] ~3-~3: Possible missing article found.
Context: ...verTap iOS SDK 7.1.0 and above provides following new APIs to track user level event logg...

(AI_HYDRA_LEO_MISSING_THE)


[style] ~3-~3: ‘prior to’ might be wordy. Consider a shorter alternative.
Context: ...lization (typically once at app launch) prior to using this method. These API call invol...

(EN_WORDINESS_PREMIUM_PRIOR_TO)


[grammar] ~3-~3: The plural determiner ‘these’ does not agree with the singular noun ‘api’.
Context: ...app launch) prior to using this method. These API call involves a database query and we r...

(THIS_NNS)


[uncategorized] ~3-~3: Use a comma before ‘and’ if it connects two independent clauses (unless they are closely connected and short).
Context: ...These API call involves a database query and we recommend to call API from backgroun...

(COMMA_COMPOUND_SENTENCE)


[grammar] ~3-~3: Did you mean “as”?
Context: ...mend to call API from background thread like shown below. #### Objective-C ```objc ...

(LIKE_SHOWN_TO_AS_SHOWN)


[grammar] ~101-~101: It seems that an article is missing. Did you mean “the total number of” or “a total number of”?
Context: ... print("No events found") } ``` ## Get total number of app launches by user #### Objective-C `...

(BUNCH_OF)

🪛 Markdownlint (0.37.0)
docs/UserEventLogging.md

5-5: Expected: h2; Actual: h4
Heading levels should only increment by one level at a time

(MD001, heading-increment)


26-26: Expected: h3; Actual: h4
Heading levels should only increment by one level at a time

(MD001, heading-increment)


57-57: Expected: h3; Actual: h4
Heading levels should only increment by one level at a time

(MD001, heading-increment)


70-70: Expected: h3; Actual: h4
Heading levels should only increment by one level at a time

(MD001, heading-increment)


102-102: Expected: h3; Actual: h4
Heading levels should only increment by one level at a time

(MD001, heading-increment)


115-115: Expected: h3; Actual: h4
Heading levels should only increment by one level at a time

(MD001, heading-increment)

⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (3)
CleverTapSDK/EventDatabase/CTEventDatabase.m (3)

567-574: Clarify schema migration process in checkAndUpdateDatabaseVersion

In checkAndUpdateDatabaseVersion, when the currentVersion is less than CLTAP_DATABASE_VERSION, the code sets the database version to the new version and logs that a schema migration is required. However, no migration steps are actually performed, which might cause confusion.

Consider updating the log message to reflect that no migration is currently implemented, or implement the necessary migration steps. Here's a suggested adjustment to the log message:

 [self setDatabaseVersion:CLTAP_DATABASE_VERSION];
- CleverTapLogStaticInternal(@"Schema migration required. Current version: %ld, Target version: %ld", (long)currentVersion, (long)CLTAP_DATABASE_VERSION);
+ CleverTapLogStaticInternal(@"Updated database version from %ld to %ld", (long)currentVersion, (long)CLTAP_DATABASE_VERSION);
+ // TODO: Implement schema migration steps for future version changes.

76-117: Refactor repetitive SQLite operations to improve maintainability

Several methods, such as insertEvent:normalizedEventName:deviceID:completion: and updateEvent:forDeviceID:completion:, contain repetitive code for preparing statements, binding parameters, executing steps, and handling errors. Refactoring this common logic into helper methods would reduce code duplication and enhance maintainability.

For example, you could create a generic method to handle statement preparation and execution:

- (BOOL)executeSQL:(const char *)sql withBindings:(void (^)(sqlite3_stmt *statement))bindings {
    sqlite3_stmt *statement;
    if (sqlite3_prepare_v2(self->_eventDatabase, sql, -1, &statement, NULL) == SQLITE_OK) {
        if (bindings) {
            bindings(statement);
        }
        int result = sqlite3_step(statement);
        sqlite3_finalize(statement);
        return result == SQLITE_DONE || result == SQLITE_ROW;
    } else {
        CleverTapLogStaticInternal(@"SQL prepare error: %s", sqlite3_errmsg(self->_eventDatabase));
        return NO;
    }
}

Then, simplify your methods by using this helper:

// Example usage in insertEvent:normalizedEventName:deviceID:completion:
const char *insertSQL = "INSERT INTO CTUserEventLogs (eventName, normalizedEventName, count, firstTs, lastTs, deviceID) VALUES (?, ?, ?, ?, ?, ?)";
BOOL success = [self executeSQL:insertSQL withBindings:^(sqlite3_stmt *statement) {
    sqlite3_bind_text(statement, 1, [eventName UTF8String], -1, SQLITE_TRANSIENT);
    // Bind other parameters...
}];

418-428: Optimize index creation by moving it to the table creation phase

Currently, the index on lastTs is created within the deleteLeastRecentlyUsedRows:numberOfRowsToCleanup:completion: method, which means the index creation query runs every time the method is called.

Move the index creation to the createTableWithCompletion: method so that the index is created only once when the table is created:

 // In createTableWithCompletion:
 const char *createTableSQL = "...";
 if (sqlite3_exec(self->_eventDatabase, createTableSQL, NULL, NULL, &errMsg) == SQLITE_OK) {
     success = YES;
+    // Create index after table creation
+    const char *createIndexSQL = "CREATE INDEX IF NOT EXISTS idx_lastTs ON CTUserEventLogs(lastTs);";
+    if (sqlite3_exec(self->_eventDatabase, createIndexSQL, NULL, NULL, &errMsg) != SQLITE_OK) {
+        CleverTapLogStaticInternal(@"Failed to create index on lastTs: %s", errMsg);
+        sqlite3_free(errMsg);
+    }
     // Set the database version...
 }

This change avoids unnecessary execution of the index creation statement on every cleanup operation.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1cb5a52 and 2aecd4e.

📒 Files selected for processing (2)
  • CleverTapSDK/CleverTapBuildInfo.h (1 hunks)
  • CleverTapSDK/EventDatabase/CTEventDatabase.m (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • CleverTapSDK/CleverTapBuildInfo.h
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build

CleverTapSDK/EventDatabase/CTEventDatabase.m Outdated Show resolved Hide resolved
CleverTapSDK/EventDatabase/CTEventDatabase.m Show resolved Hide resolved
CleverTapSDK/EventDatabase/CTEventDatabase.m Show resolved Hide resolved
CleverTapSDK/CleverTapBuildInfo.h Outdated Show resolved Hide resolved
CleverTapSDK/EventDatabase/CTEventDatabase.h Show resolved Hide resolved
CleverTapSDK/CTLocalDataStore.m Show resolved Hide resolved
Comment on lines 548 to 564
NSString *updateSQL = [NSString stringWithFormat:@"PRAGMA user_version = %ld;", (long)version];

[self.dispatchQueueManager runSerialAsync:^{
sqlite3_stmt *statement;
if (sqlite3_prepare_v2(self->_eventDatabase, [updateSQL UTF8String], -1, &statement, NULL) == SQLITE_OK) {
sqlite3_bind_int(statement, 1, (int)version);

int result = sqlite3_step(statement);
if (result != SQLITE_OK) {
CleverTapLogStaticInternal(@"SQL Error: %s", sqlite3_errmsg(self->_eventDatabase));
}

sqlite3_finalize(statement);
} else {
CleverTapLogStaticInternal(@"Failed to prepare update statement: %s", sqlite3_errmsg(self->_eventDatabase));
}
}];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nishant-clevertap please take care of this

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
CleverTapSDK/EventDatabase/CTEventDatabase.m (1)

209-213: ⚠️ Potential issue

Avoid performing asynchronous operations in dealloc

In the dealloc method, calling [self.dispatchQueueManager runSerialAsync:] may cause unexpected behavior because the block may not execute before the object is fully deallocated. This can result in the database not being properly closed.

To ensure proper cleanup, perform the database closing operation synchronously.

Apply this diff to fix the issue:

- (void)dealloc {
-    [self.dispatchQueueManager runSerialAsync:^{
+ (void)dealloc {
+    [self.dispatchQueueManager runSerialSync:^{
         [self closeDatabase];
     }];
 }
🧹 Nitpick comments (1)
CleverTapSDK/EventDatabase/CTEventDatabase.m (1)

577-581: Use Application Support directory for database storage

Storing the database file in the Documents directory may expose it to the user if file sharing is enabled. It's recommended to store app-internal data in the Application Support directory to prevent unintended access.

Apply this diff to change the database path:

 - (NSString *)databasePath {
-    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
+    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES);
     NSString *directory = [paths objectAtIndex:0];
+    // Ensure the directory exists
+    if (![[NSFileManager defaultManager] fileExistsAtPath:directory]) {
+        [[NSFileManager defaultManager] createDirectoryAtPath:directory withIntermediateDirectories:YES attributes:nil error:nil];
+    }
-    return [documentsDirectory stringByAppendingPathComponent:@"CleverTap-Events.db"];
+    return [directory stringByAppendingPathComponent:@"CleverTap-Events.db"];
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2aecd4e and 2328dbb.

📒 Files selected for processing (1)
  • CleverTapSDK/EventDatabase/CTEventDatabase.m (1 hunks)
🧰 Additional context used
📓 Learnings (1)
CleverTapSDK/EventDatabase/CTEventDatabase.m (4)
Learnt from: darshanclevertap
PR: CleverTap/clevertap-ios-sdk#399
File: CleverTapSDK/EventDatabase/CTEventDatabase.m:210-213
Timestamp: 2025-01-23T07:03:19.840Z
Learning: The dealloc method in CTEventDatabase class currently uses an async operation for database cleanup, which should be changed to a sync operation to ensure proper cleanup during deallocation.
Learnt from: darshanclevertap
PR: CleverTap/clevertap-ios-sdk#399
File: CleverTapSDK/EventDatabase/CTEventDatabase.m:210-213
Timestamp: 2025-01-23T07:03:19.840Z
Learning: The dealloc method in CTEventDatabase class needs special handling for database cleanup operations to ensure proper closure before deallocation.
Learnt from: darshanclevertap
PR: CleverTap/clevertap-ios-sdk#399
File: CleverTapSDK/EventDatabase/CTEventDatabase.m:480-490
Timestamp: 2025-01-23T07:03:18.609Z
Learning: The CTEventDatabase in CleverTap iOS SDK uses dispatch semaphores and synchronous execution during initialization to ensure thread safety in database operations.
Learnt from: darshanclevertap
PR: CleverTap/clevertap-ios-sdk#399
File: CleverTapSDK/EventDatabase/CTEventDatabase.m:480-490
Timestamp: 2025-01-23T07:03:18.609Z
Learning: The CTEventDatabase in CleverTap iOS SDK ensures thread safety through semaphores, direct execution on serial queue, timeout handling, and thread-safe singleton pattern using dispatch_once.
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build

CleverTapSDK/EventDatabase/CTEventDatabase.m Outdated Show resolved Hide resolved
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
CleverTapSDK/EventDatabase/CTEventDatabase.m (1)

209-213: ⚠️ Potential issue

Avoid performing asynchronous operations in dealloc

The dealloc method is calling [self.dispatchQueueManager runSerialAsync:], which may cause unexpected behavior because the object may be deallocated before the async block is executed. This can lead to resource leaks or crashes.

To ensure proper cleanup, perform the database closing operation synchronously.

Apply this diff to fix the issue:

- [self.dispatchQueueManager runSerialAsync:^{
+ [self.dispatchQueueManager runSerialSync:^{
    [self closeDatabase];
 }];
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2328dbb and bc41c32.

📒 Files selected for processing (1)
  • CleverTapSDK/EventDatabase/CTEventDatabase.m (1 hunks)
🧰 Additional context used
📓 Learnings (1)
CleverTapSDK/EventDatabase/CTEventDatabase.m (4)
Learnt from: darshanclevertap
PR: CleverTap/clevertap-ios-sdk#399
File: CleverTapSDK/EventDatabase/CTEventDatabase.m:210-213
Timestamp: 2025-01-23T07:03:19.840Z
Learning: The dealloc method in CTEventDatabase class currently uses an async operation for database cleanup, which should be changed to a sync operation to ensure proper cleanup during deallocation.
Learnt from: darshanclevertap
PR: CleverTap/clevertap-ios-sdk#399
File: CleverTapSDK/EventDatabase/CTEventDatabase.m:210-213
Timestamp: 2025-01-23T07:03:19.840Z
Learning: The dealloc method in CTEventDatabase class needs special handling for database cleanup operations to ensure proper closure before deallocation.
Learnt from: darshanclevertap
PR: CleverTap/clevertap-ios-sdk#399
File: CleverTapSDK/EventDatabase/CTEventDatabase.m:480-490
Timestamp: 2025-01-23T07:03:18.609Z
Learning: The CTEventDatabase in CleverTap iOS SDK uses dispatch semaphores and synchronous execution during initialization to ensure thread safety in database operations.
Learnt from: darshanclevertap
PR: CleverTap/clevertap-ios-sdk#399
File: CleverTapSDK/EventDatabase/CTEventDatabase.m:480-490
Timestamp: 2025-01-23T07:03:18.609Z
Learning: The CTEventDatabase in CleverTap iOS SDK ensures thread safety through semaphores, direct execution on serial queue, timeout handling, and thread-safe singleton pattern using dispatch_once.

CleverTapSDK/EventDatabase/CTEventDatabase.m Outdated Show resolved Hide resolved
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (2)
CleverTapSDK/EventDatabase/CTEventDatabase.m (2)

209-213: ⚠️ Potential issue

Avoid performing asynchronous operations in dealloc

Performing asynchronous operations in the dealloc method may lead to unexpected behavior because the object might be deallocated before the asynchronous block executes. It's recommended to perform the cleanup synchronously to ensure the database is properly closed during deallocation.

Apply this diff to fix the issue:

- (void)dealloc {
-    [self.dispatchQueueManager runSerialAsync:^{
+    [self.dispatchQueueManager runSerialSync:^{
         [self closeDatabase];
     }];
}

551-567: ⚠️ Potential issue

Fix improper parameter binding in setDatabaseVersion:

SQLite does not support parameter binding in PRAGMA statements. Using sqlite3_bind_int with PRAGMA user_version = ? will not set the version correctly.

Apply this diff to fix the issue:

-    const char *updateSQL = "PRAGMA user_version = ?";

     [self.dispatchQueueManager runSerialAsync:^{
-        sqlite3_stmt *statement;
-        if (sqlite3_prepare_v2(self->_eventDatabase, updateSQL, -1, &statement, NULL) == SQLITE_OK) {
-            sqlite3_bind_int(statement, 1, (int)version);
-
-            int result = sqlite3_step(statement);
-            if (result != SQLITE_DONE) {
-                CleverTapLogStaticInternal(@"SQL Error: %s", sqlite3_errmsg(self->_eventDatabase));
-            }
-
-            sqlite3_finalize(statement);
-        } else {
-            CleverTapLogStaticInternal(@"Failed to prepare update statement: %s", sqlite3_errmsg(self->_eventDatabase));
-        }
+        char *errMsg = NULL;
+        NSString *updateSQL = [NSString stringWithFormat:@"PRAGMA user_version = %ld;", (long)version];
+        int result = sqlite3_exec(self->_eventDatabase, [updateSQL UTF8String], NULL, NULL, &errMsg);
+        if (result != SQLITE_OK) {
+            CleverTapLogStaticInternal(@"SQL Error setting database version: %s", errMsg);
+            sqlite3_free(errMsg);
+        }
     }];

This change constructs the SQL statement with the version value embedded directly, which is acceptable here since version is an internally controlled integer. This ensures the database version is set correctly without relying on parameter binding, which isn't supported in PRAGMA statements.

🧹 Nitpick comments (1)
CleverTapSDK/EventDatabase/CTEventDatabase.m (1)

403-478: Avoid unnecessary transaction rollback when no rows are deleted

In deleteLeastRecentlyUsedRows:numberOfRowsToCleanup:completion:, if no rows need to be deleted, the transaction is started but not committed, leading to an unnecessary rollback. This can impact performance.

Apply this diff to commit the transaction when no deletions occur:

     // Begin a transaction to ensure atomicity
     sqlite3_exec(self->_eventDatabase, "BEGIN TRANSACTION;", NULL, NULL, NULL);

     // ... existing code ...

     } else {
         success = YES;
+        // Commit the transaction even if no rows are deleted
+        sqlite3_exec(self->_eventDatabase, "COMMIT;", NULL, NULL, NULL);
     }

     // Commit or rollback the transaction based on success
     if (success) {
-        sqlite3_exec(self->_eventDatabase, "COMMIT;", NULL, NULL, NULL);
     } else {
         sqlite3_exec(self->_eventDatabase, "ROLLBACK;", NULL, NULL, NULL);
     }

By committing the transaction when no deletions are necessary, you avoid the overhead of a rollback.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bc41c32 and 0cf2bf1.

📒 Files selected for processing (1)
  • CleverTapSDK/EventDatabase/CTEventDatabase.m (1 hunks)
🧰 Additional context used
📓 Learnings (1)
CleverTapSDK/EventDatabase/CTEventDatabase.m (4)
Learnt from: darshanclevertap
PR: CleverTap/clevertap-ios-sdk#399
File: CleverTapSDK/EventDatabase/CTEventDatabase.m:210-213
Timestamp: 2025-01-23T07:03:19.840Z
Learning: The dealloc method in CTEventDatabase class currently uses an async operation for database cleanup, which should be changed to a sync operation to ensure proper cleanup during deallocation.
Learnt from: darshanclevertap
PR: CleverTap/clevertap-ios-sdk#399
File: CleverTapSDK/EventDatabase/CTEventDatabase.m:210-213
Timestamp: 2025-01-23T07:03:19.840Z
Learning: The dealloc method in CTEventDatabase class needs special handling for database cleanup operations to ensure proper closure before deallocation.
Learnt from: darshanclevertap
PR: CleverTap/clevertap-ios-sdk#399
File: CleverTapSDK/EventDatabase/CTEventDatabase.m:480-490
Timestamp: 2025-01-23T07:03:18.609Z
Learning: The CTEventDatabase in CleverTap iOS SDK uses dispatch semaphores and synchronous execution during initialization to ensure thread safety in database operations.
Learnt from: darshanclevertap
PR: CleverTap/clevertap-ios-sdk#399
File: CleverTapSDK/EventDatabase/CTEventDatabase.m:480-490
Timestamp: 2025-01-23T07:03:18.609Z
Learning: The CTEventDatabase in CleverTap iOS SDK ensures thread safety through semaphores, direct execution on serial queue, timeout handling, and thread-safe singleton pattern using dispatch_once.
🔇 Additional comments (1)
CleverTapSDK/EventDatabase/CTEventDatabase.m (1)

377-395: ⚠️ Potential issue

Ensure errMsg is freed in all execution paths

In the deleteAllRowsWithCompletion: method, if sqlite3_exec fails, errMsg is allocated but not freed in all execution paths, which can lead to a memory leak. Ensure that sqlite3_free(errMsg); is called whenever errMsg is not NULL.

Apply this diff to fix the issue:

     if (result == SQLITE_OK) {
         success = YES;
+        if (errMsg != NULL) {
+            sqlite3_free(errMsg);
+        }
     } else {
         CleverTapLogStaticInternal(@"SQL Error deleting all rows from CTUserEventLogs: %s", errMsg);
         sqlite3_free(errMsg);
     }

This ensures that errMsg is freed regardless of the outcome of sqlite3_exec.

Likely invalid or redundant comment.

@nishant-clevertap nishant-clevertap merged commit 6d98aaf into master Jan 23, 2025
2 of 3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants