Skip to content

Commit 77461da

Browse files
evantk91hardikmashruEvan GreerAyyanchiramegha-iterable
authored
[Feature] Unknown User Activation (#764)
Co-authored-by: hardikmashru <[email protected]> Co-authored-by: Evan Greer <[email protected]> Co-authored-by: Akshay Ayyanchira <[email protected]> Co-authored-by: Megha <[email protected]> Co-authored-by: hani <[email protected]> Co-authored-by: Joao Dordio <[email protected]> Co-authored-by: lokeshdud <[email protected]> Co-authored-by: sumeruchat <[email protected]>
1 parent efaf943 commit 77461da

File tree

66 files changed

+11837
-163
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

66 files changed

+11837
-163
lines changed

.github/workflows/build-and-test.yml

Lines changed: 87 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ jobs:
7474
total_tests: 0,
7575
passed_tests: 0,
7676
failed_tests: 0,
77-
success_rate: 0
77+
success_rate: 0,
78+
skipped_tests: 0
7879
};
7980
8081
try {
@@ -92,17 +93,93 @@ jobs:
9293
} catch (error) {
9394
core.warning(`Error reading test summary: ${error.message}`);
9495
}
95-
96-
97-
// Extract just the main content from the HTML - removing the HTML tags
96+
97+
// Clean and optimize HTML for GitHub Check Run API
9898
function stripHtml(html) {
99-
// Simple regex to extract text content from HTML
99+
if (!html) return '';
100+
100101
return html
101-
.replace(/<h2>[\s\S]*?<\/h2>/gi, '')
102+
// Remove problematic elements
103+
.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '')
104+
.replace(/<style\b[^<]*(?:(?!<\/style>)<[^<]*)*<\/style>/gi, '')
105+
// Clean up complex attributes but keep basic structure
106+
.replace(/\s*(class|id|dir|data-[^=]*|role|aria-[^=]*|tabindex)="[^"]*"/gi, '')
107+
.replace(/\s*(markdown-accessiblity-table|data-catalyst)="[^"]*"/gi, '')
108+
// Remove GitHub-specific elements that don't render in Check Runs
109+
.replace(/<g-emoji[^>]*>.*?<\/g-emoji>/gi, '⚠️')
110+
.replace(/<clipboard-copy[\s\S]*?<\/clipboard-copy>/gi, '')
111+
.replace(/<div class="zeroclipboard-container[\s\S]*?<\/div>/gi, '')
112+
.replace(/<div class="snippet-clipboard-content[\s\S]*?<\/div>/gi, '')
113+
// Clean up excessive whitespace
114+
.replace(/\s+/g, ' ')
115+
.replace(/>\s+</g, '><')
102116
.trim();
103117
}
104118
105-
// Create the check with test results as summary and coverage as details
119+
// Function to safely truncate content to fit byte limit
120+
function truncateToByteLimit(text, maxBytes) {
121+
if (!text) return '';
122+
123+
// Convert to bytes to check actual size
124+
const encoder = new TextEncoder();
125+
let bytes = encoder.encode(text);
126+
127+
if (bytes.length <= maxBytes) {
128+
return text;
129+
}
130+
131+
// Binary search to find the maximum length that fits
132+
let left = 0;
133+
let right = text.length;
134+
let result = '';
135+
136+
while (left <= right) {
137+
const mid = Math.floor((left + right) / 2);
138+
const substring = text.substring(0, mid);
139+
const substringBytes = encoder.encode(substring);
140+
141+
if (substringBytes.length <= maxBytes) {
142+
result = substring;
143+
left = mid + 1;
144+
} else {
145+
right = mid - 1;
146+
}
147+
}
148+
149+
// Add truncation indicator if content was cut off
150+
if (result.length < text.length) {
151+
const truncateMsg = '\n\n... (truncated due to size limits)';
152+
const truncateMsgBytes = encoder.encode(truncateMsg);
153+
154+
if (encoder.encode(result).length + truncateMsgBytes.length <= maxBytes) {
155+
result += truncateMsg;
156+
}
157+
}
158+
159+
return result;
160+
}
161+
162+
// Extract and clean content
163+
const cleanTestReport = stripHtml(testReport);
164+
const cleanCoverageReport = stripHtml(coverageReport);
165+
166+
// Create concise summary focusing on key information
167+
const summaryContent = `Test Results Summary:
168+
• Total Tests: ${testStats.total_tests}
169+
• Passed: ${testStats.passed_tests}
170+
• Failed: ${testStats.failed_tests}
171+
• Skipped: ${testStats.skipped_tests || 0}
172+
• Success Rate: ${(testStats.success_rate || 0).toFixed(1)}%
173+
174+
${cleanTestReport}`;
175+
176+
// Ensure summary fits within GitHub's 65535 byte limit (leaving some buffer)
177+
const truncatedSummary = truncateToByteLimit(summaryContent, 65000);
178+
179+
// Ensure coverage report fits within the text field limit
180+
const truncatedCoverage = truncateToByteLimit(cleanCoverageReport, 65000);
181+
182+
// Create the check with test results
106183
await github.rest.checks.create({
107184
owner: context.repo.owner,
108185
repo: context.repo.repo,
@@ -111,9 +188,9 @@ jobs:
111188
status: 'completed',
112189
conclusion: testStats.failed_tests > 0 ? 'failure' : 'success',
113190
output: {
114-
title: `Tests: ${testStats.passed_tests}/${testStats.passed_tests + testStats.failed_tests} passed (${(testStats.success_rate).toFixed(1)}%) Skipped: ${testStats.skipped_tests}`,
115-
summary: stripHtml(testReport.substring(0, 65000)),
116-
text: stripHtml(coverageReport.substring(0, 65000))
191+
title: `Tests: ${testStats.passed_tests}/${testStats.passed_tests + testStats.failed_tests} passed (${(testStats.success_rate || 0).toFixed(1)}%) Skipped: ${testStats.skipped_tests || 0}`,
192+
summary: truncatedSummary,
193+
text: truncatedCoverage
117194
}
118195
});
119196

AGENT_README.md

Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
# AGENT README - Iterable Swift SDK
2+
3+
## Project Overview
4+
This is the **Iterable Swift SDK** for iOS/macOS integration. The SDK provides:
5+
- Push notification handling
6+
- In-app messaging
7+
- Event tracking
8+
- User management
9+
- Unknown user tracking
10+
11+
## Key Architecture
12+
- **Core SDK**: `swift-sdk/` - Main SDK implementation
13+
- **Sample Apps**: `sample-apps/` - Example integrations
14+
- **Tests**: `tests/` - Unit tests, UI tests, and integration tests
15+
- **Notification Extension**: `notification-extension/` - Rich push support
16+
17+
## Development Workflow
18+
19+
### 🔨 Building the SDK
20+
```bash
21+
./agent_build.sh
22+
```
23+
- Validates compilation on iOS Simulator
24+
- Shows build errors with context
25+
- Requires macOS with Xcode
26+
27+
### Listing All Available Tests
28+
29+
# List all available test suites
30+
```bash
31+
./agent_test.sh --list
32+
```
33+
34+
### 🧪 Running Tests
35+
```bash
36+
# Run all tests
37+
./agent_test.sh
38+
39+
# Run specific test suite
40+
./agent_test.sh IterableApiCriteriaFetchTests
41+
42+
# Run specific unit test (dot notation - recommended)
43+
./agent_test.sh "IterableApiCriteriaFetchTests.testForegroundCriteriaFetchWhenConditionsMet"
44+
45+
# Run any specific test with path
46+
./agent_test.sh "unit-tests/IterableApiCriteriaFetchTests/testForegroundCriteriaFetchWhenConditionsMet"
47+
```
48+
- Executes on iOS Simulator with accurate pass/fail reporting
49+
- Returns exit code 0 for success, 1 for failures
50+
- Shows detailed test counts and failure information
51+
- `--list` shows all test suites with test counts
52+
- Requires password for xcpretty installation (first run)
53+
54+
## Project Structure
55+
```
56+
swift-sdk/
57+
├── swift-sdk/ # Main SDK source
58+
│ ├── Core/ # Public APIs and models
59+
│ ├── Internal/ # Internal implementation
60+
│ ├── SDK/ # Main SDK entry points
61+
│ └── ui-components/ # SwiftUI/UIKit components
62+
├── tests/ # Test suites
63+
│ ├── unit-tests/ # Unit tests
64+
│ ├── ui-tests/ # UI automation tests
65+
│ └── endpoint-tests/ # API endpoint tests
66+
├── sample-apps/ # Example applications
67+
└── notification-extension/ # Push notification extension
68+
```
69+
70+
## Key Classes
71+
- **IterableAPI**: Main SDK interface
72+
- **IterableConfig**: Configuration management
73+
- **InternalIterableAPI**: Core implementation
74+
- **UnknownUserManager**: Unknown user tracking
75+
- **LocalStorage**: Data persistence
76+
77+
## Common Tasks
78+
79+
### Adding New Features
80+
1. Build first: `./agent_build.sh`
81+
2. Implement in `swift-sdk/Internal/` or `swift-sdk/SDK/`
82+
3. Add tests in `tests/unit-tests/`
83+
4. Verify: `./agent_test.sh` (all tests) or `./agent_test.sh YourTestSuite` (specific suite)
84+
85+
### Debugging Build Issues
86+
- Build script shows compilation errors with file paths
87+
- Check Xcode project references in `swift-sdk.xcodeproj/project.pbxproj`
88+
- Verify file renames are reflected in project file
89+
90+
### Test Failures
91+
- Test script shows specific failures with line numbers and detailed error messages
92+
- Run failing tests individually: `./agent_test.sh "TestSuite.testMethod"`
93+
- Mock classes available in `tests/common/`
94+
- Update parameter names when refactoring APIs
95+
96+
## Requirements
97+
- **macOS**: Required for Xcode builds
98+
- **Xcode**: Latest stable version
99+
- **Ruby**: For xcpretty (auto-installed)
100+
- **iOS Simulator**: For testing
101+
102+
## Quick Start for AI Agents
103+
1. Run `./agent_build.sh` to verify project builds
104+
2. Run `./agent_test.sh` to check test health (or `./agent_test.sh TestSuite` for specific suite)
105+
3. Make changes to source files
106+
4. Re-run both scripts to validate
107+
5. Debug failing tests: `./agent_test.sh "TestSuite.testMethod"`
108+
6. Commit when both pass ✅
109+
110+
## Test Filtering Examples
111+
```bash
112+
# Debug specific failing tests
113+
./agent_test.sh "IterableApiCriteriaFetchTests.testForegroundCriteriaFetchWhenConditionsMet"
114+
115+
# Run a problematic test suite
116+
./agent_test.sh ValidateCustomEventUserUpdateAPITest
117+
118+
# Check auth-related tests
119+
./agent_test.sh AuthTests
120+
```
121+
122+
## AI Agent Memory System
123+
124+
### 🧠 Update Instructions for AI Agents
125+
**IMPORTANT**: When you discover something useful while working on this codebase, update this README to help future AI agents. Add learnings to the sections below.
126+
127+
### 📍 Code Location Map
128+
- **Auth Logic**: `swift-sdk/Internal/AuthManager.swift` (main auth manager), `swift-sdk/Internal/Auth.swift` (auth models)
129+
- **API Calls**: `swift-sdk/Internal/api-client/ApiClient.swift` (main client), `swift-sdk/Internal/Network/NetworkHelper.swift` (networking)
130+
- **Models**: `swift-sdk/Core/Models/` (all data structures - CommerceItem, IterableInAppMessage, etc.)
131+
- **Main Entry**: `swift-sdk/SDK/IterableAPI.swift` (public API), `swift-sdk/Internal/InternalIterableAPI.swift` (core implementation)
132+
- **Request Handling**: `swift-sdk/Internal/api-client/Request/` (online/offline processors)
133+
134+
### 🛠️ Common Task Recipes
135+
136+
**Add New API Endpoint:**
137+
1. Add path constant to `swift-sdk/Core/Constants.swift` in `Const.Path`
138+
2. Add method to `ApiClientProtocol.swift` and implement in `ApiClient.swift`
139+
3. Create request in `swift-sdk/Internal/api-client/Request/RequestCreator.swift`
140+
4. Add to `RequestHandlerProtocol.swift` and `RequestHandler.swift`
141+
142+
**Modify Auth Logic:**
143+
- Main logic: `swift-sdk/Internal/AuthManager.swift`
144+
- Token storage: `swift-sdk/Internal/Utilities/Keychain/IterableKeychain.swift`
145+
- Auth failures: Handle in `RequestProcessorUtil.swift`
146+
147+
**Add New Model:**
148+
- Create in `swift-sdk/Core/Models/YourModel.swift`
149+
- Make it `@objcMembers public class` for Objective-C compatibility
150+
- Implement `Codable` if it needs JSON serialization
151+
152+
### 🐛 Common Failure Solutions
153+
154+
**"Test X failed"** → Check test file in `tests/unit-tests/` - often parameter name mismatches after refactoring
155+
156+
**"Build failed: file not found"** → Update `swift-sdk.xcodeproj/project.pbxproj` to include new/renamed files
157+
158+
**"Auth token issues"** → Check `AuthManager.swift` and ensure JWT format is correct in tests
159+
160+
**"Network request fails"** → Check endpoint in `Constants.swift` and request creation in `RequestCreator.swift`
161+
162+
## Notes
163+
- Always test builds after refactoring
164+
- Parameter name changes require test file updates
165+
- Project file (`*.pbxproj`) may need manual updates for file renames
166+
- Sample apps demonstrate SDK usage patterns

CHANGELOG.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,27 @@
22

33
All notable changes to this project will be documented in this file.
44
This project adheres to [Semantic Versioning](http://semver.org/).
5+
6+
## [6.6.0-beta3]
7+
8+
- This release includes fixes for the Unknown user activation private beta:
9+
- Criteria is now fetched on foregrounding the app by default. This feature can be turned off setting enableForegroundCriteriaFetch flag to false.
10+
- Unknown user ids are only generated once when multiple track calls are made.
11+
- Unknown user activation is currently in private beta. If you'd like to learn more about it or discuss using it, talk to your Iterable customer success manager (who can also provide detailed documentation).
12+
13+
## [6.6.0-beta2]
14+
15+
- This release fixes beta1 release which was released from the wrong branch.
16+
17+
## [6.6.0-beta1]
18+
19+
- This release includes initial support for Unknown user activation, a feature that allows marketers to convert valuable visitors into customers. With this feature, the SDK can:
20+
- Fetch unknown user profile creation criteria from your Iterable project, and then automatically create Iterable user profiles for Unknown users who meet these criteria.
21+
- Save information about a user's previous interactions with your application to their unknown user profile, after it's created.
22+
- Display personalized messages for Unknown users (in-app, push, and embedded messages).
23+
- Merge unknown user profiles into an existing, known user profiles (when needed).
24+
- Unknown user activation is currently in private beta. If you'd like to learn more about it or discuss using it, talk to your Iterable customer success manager (who can also provide detailed documentation).
25+
526
## [Unreleased]
627

728
## [6.5.14]

CLAUDE.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# CLAUDE.md
2+
3+
🤖 **AI Agent Instructions**
4+
5+
Please read the `AGENT_README.md` file for comprehensive project information, development workflow, and testing procedures.
6+
7+
All the information you need to work on this Iterable Swift SDK project is documented there.

agent_build.sh

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#!/bin/bash
2+
3+
# This script is to be used by LLMs and AI agents to build the Iterable Swift SDK on macOS.
4+
# It uses xcpretty to format the build output and only shows errors.
5+
# It also checks if the build is successful and exits with the correct status.
6+
7+
# Check if running on macOS
8+
if [[ "$(uname)" != "Darwin" ]]; then
9+
echo "❌ This script requires macOS to run Xcode builds"
10+
exit 1
11+
fi
12+
13+
# Make sure xcpretty is installed
14+
if ! command -v xcpretty &> /dev/null; then
15+
echo "xcpretty not found, installing via gem..."
16+
sudo gem install xcpretty
17+
fi
18+
19+
echo "Building Iterable Swift SDK..."
20+
21+
# Create a temporary file for the build output
22+
TEMP_OUTPUT=$(mktemp)
23+
24+
# Run the build and capture all output
25+
xcodebuild \
26+
-project swift-sdk.xcodeproj \
27+
-scheme "swift-sdk" \
28+
-configuration Debug \
29+
-sdk iphonesimulator \
30+
build > $TEMP_OUTPUT 2>&1
31+
32+
# Check the exit status
33+
BUILD_STATUS=$?
34+
35+
# Show errors and warnings if build failed
36+
if [ $BUILD_STATUS -eq 0 ]; then
37+
echo "✅ Iterable SDK build succeeded!"
38+
else
39+
echo "❌ Iterable SDK build failed with status $BUILD_STATUS"
40+
echo ""
41+
echo "🔍 Build errors:"
42+
grep -E 'error:|fatal:' $TEMP_OUTPUT | head -10
43+
echo ""
44+
echo "⚠️ Build warnings:"
45+
grep -E 'warning:' $TEMP_OUTPUT | head -5
46+
fi
47+
48+
# Remove the temporary file
49+
rm $TEMP_OUTPUT
50+
51+
exit $BUILD_STATUS

0 commit comments

Comments
 (0)