Skip to content

Commit b167fd6

Browse files
committed
Complete Unknown User refactoring with test fixes and agent scripts
- Fixed all test parameter/property name mismatches - Added mac_agent_build.sh and mac_agent_test.sh for development workflow - Added AI_README.md for future AI agent guidance - All 56 unit tests now passing ✅
1 parent 1920348 commit b167fd6

25 files changed

+381
-236
lines changed

AI_README.md

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# AI 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+
- Anonymous/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+
./mac_agent_build.sh
22+
```
23+
- Validates compilation on iOS Simulator
24+
- Shows build errors with context
25+
- Requires macOS with Xcode
26+
27+
### 🧪 Running Tests
28+
```bash
29+
./mac_agent_test.sh
30+
```
31+
- Runs full unit test suite
32+
- Executes on iOS Simulator
33+
- Shows test failures and summary
34+
- Requires password for xcpretty installation (first run)
35+
36+
## Project Structure
37+
```
38+
swift-sdk/
39+
├── swift-sdk/ # Main SDK source
40+
│ ├── Core/ # Public APIs and models
41+
│ ├── Internal/ # Internal implementation
42+
│ ├── SDK/ # Main SDK entry points
43+
│ └── ui-components/ # SwiftUI/UIKit components
44+
├── tests/ # Test suites
45+
│ ├── unit-tests/ # Unit tests
46+
│ ├── ui-tests/ # UI automation tests
47+
│ └── endpoint-tests/ # API endpoint tests
48+
├── sample-apps/ # Example applications
49+
└── notification-extension/ # Push notification extension
50+
```
51+
52+
## Key Classes
53+
- **IterableAPI**: Main SDK interface
54+
- **IterableConfig**: Configuration management
55+
- **InternalIterableAPI**: Core implementation
56+
- **UnknownUserManager**: Anonymous user tracking
57+
- **LocalStorage**: Data persistence
58+
59+
## Common Tasks
60+
61+
### Adding New Features
62+
1. Build first: `./mac_agent_build.sh`
63+
2. Implement in `swift-sdk/Internal/` or `swift-sdk/SDK/`
64+
3. Add tests in `tests/unit-tests/`
65+
4. Verify: `./mac_agent_test.sh`
66+
67+
### Debugging Build Issues
68+
- Build script shows compilation errors with file paths
69+
- Check Xcode project references in `swift-sdk.xcodeproj/project.pbxproj`
70+
- Verify file renames are reflected in project file
71+
72+
### Test Failures
73+
- Test script shows specific failures with line numbers
74+
- Mock classes available in `tests/common/`
75+
- Update parameter names when refactoring APIs
76+
77+
## Requirements
78+
- **macOS**: Required for Xcode builds
79+
- **Xcode**: Latest stable version
80+
- **Ruby**: For xcpretty (auto-installed)
81+
- **iOS Simulator**: For testing
82+
83+
## Quick Start for AI Agents
84+
1. Run `./mac_agent_build.sh` to verify project builds
85+
2. Run `./mac_agent_test.sh` to check test health
86+
3. Make changes to source files
87+
4. Re-run both scripts to validate
88+
5. Commit when both pass ✅
89+
90+
## Notes
91+
- Always test builds after refactoring
92+
- Parameter name changes require test file updates
93+
- Project file (`*.pbxproj`) may need manual updates for file renames
94+
- Sample apps demonstrate SDK usage patterns

mac_llm_build.sh renamed to mac_agent_build.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ fi
1313
# Make sure xcpretty is installed
1414
if ! command -v xcpretty &> /dev/null; then
1515
echo "xcpretty not found, installing via gem..."
16-
gem install xcpretty
16+
sudo gem install xcpretty
1717
fi
1818

1919
echo "Building Iterable Swift SDK..."

mac_agent_test.sh

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#!/bin/bash
2+
3+
# Check if running on macOS
4+
if [[ "$(uname)" != "Darwin" ]]; then
5+
echo "❌ This script requires macOS to run Xcode tests"
6+
exit 1
7+
fi
8+
9+
# Make sure xcpretty is installed
10+
if ! command -v xcpretty &> /dev/null; then
11+
echo "xcpretty not found, installing via gem..."
12+
sudo gem install xcpretty
13+
fi
14+
15+
echo "Running Iterable Swift SDK unit tests..."
16+
17+
# Create a temporary file for the test output
18+
TEMP_OUTPUT=$(mktemp)
19+
20+
# Run the tests and capture all output
21+
xcodebuild test \
22+
-project swift-sdk.xcodeproj \
23+
-scheme swift-sdk \
24+
-sdk iphonesimulator \
25+
-destination 'platform=iOS Simulator,name=iPhone 16 Pro,OS=18.2' \
26+
-enableCodeCoverage YES \
27+
CODE_SIGNING_REQUIRED=NO > $TEMP_OUTPUT 2>&1
28+
29+
# Check the exit status
30+
TEST_STATUS=$?
31+
32+
# Show test results
33+
if [ $TEST_STATUS -eq 0 ]; then
34+
echo "✅ All tests passed!"
35+
echo ""
36+
echo "📊 Test Summary:"
37+
grep -E 'Test Suite|tests passed|tests failed|Executed' $TEMP_OUTPUT | tail -10
38+
else
39+
echo "❌ Tests failed with status $TEST_STATUS"
40+
echo ""
41+
echo "🔍 Test failures:"
42+
grep -E 'error:|failed:|FAILED' $TEMP_OUTPUT | head -10
43+
echo ""
44+
echo "📊 Test Summary:"
45+
grep -E 'Test Suite|tests passed|tests failed|Executed' $TEMP_OUTPUT | tail -5
46+
fi
47+
48+
# Remove the temporary file
49+
rm $TEMP_OUTPUT
50+
51+
exit $TEST_STATUS

tests/offline-events-tests/RequestHandlerTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1230,7 +1230,7 @@ class RequestHandlerTests: XCTestCase {
12301230

12311231
extension RequestHandlerTests: AuthProvider {
12321232
var auth: Auth {
1233-
Auth(userId: nil, email: "[email protected]", authToken: nil, userIdAnon: nil)
1233+
Auth(userId: nil, email: "[email protected]", authToken: nil, userIdUnknownUser: nil)
12341234
}
12351235
}
12361236

tests/offline-events-tests/TaskProcessorTests.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ class TaskProcessorTests: XCTestCase {
1414
let dataFields = ["var1": "val1", "var2": "val2"]
1515

1616
let expectation1 = expectation(description: #function)
17-
let auth = Auth(userId: nil, email: email, authToken: nil, userIdAnon: nil)
17+
let auth = Auth(userId: nil, email: email, authToken: nil, userIdUnknownUser: nil)
1818
let config = IterableConfig()
1919
let networkSession = MockNetworkSession()
2020
let internalAPI = InternalIterableAPI.initializeForTesting(apiKey: apiKey, config: config, networkSession: networkSession)
@@ -221,7 +221,7 @@ class TaskProcessorTests: XCTestCase {
221221
let eventName = "CustomEvent1"
222222
let dataFields = ["var1": "val1", "var2": "val2"]
223223

224-
let auth = Auth(userId: nil, email: email, authToken: nil, userIdAnon: nil)
224+
let auth = Auth(userId: nil, email: email, authToken: nil, userIdUnknownUser: nil)
225225
let requestCreator = RequestCreator(auth: auth,
226226
deviceMetadata: deviceMetadata)
227227
guard case let Result.success(trackEventRequest) = requestCreator.createTrackEventRequest(eventName, dataFields: dataFields) else {

tests/offline-events-tests/TaskRunnerTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -421,6 +421,6 @@ class TaskRunnerTests: XCTestCase {
421421

422422
extension TaskRunnerTests: AuthProvider {
423423
var auth: Auth {
424-
Auth(userId: nil, email: "[email protected]", authToken: nil, userIdAnon: nil)
424+
Auth(userId: nil, email: "[email protected]", authToken: nil, userIdUnknownUser: nil)
425425
}
426426
}

tests/offline-events-tests/TaskSchedulerTests.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,6 @@ class TaskSchedulerTests: XCTestCase {
123123

124124
extension TaskSchedulerTests: AuthProvider {
125125
var auth: Auth {
126-
Auth(userId: nil, email: "[email protected]", authToken: nil, userIdAnon: nil)
126+
Auth(userId: nil, email: "[email protected]", authToken: nil, userIdUnknownUser: nil)
127127
}
128128
}

tests/unit-tests/AnonymousUserComplexCriteriaMatchTests.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,7 @@ class AnonymousUserComplexCriteriaMatchTests: XCTestCase {
490490
"dataType": "updateCart",
491491
]]
492492
let expectedCriteriaId = "49"
493-
let matchedCriteriaId = CriteriaCompletionChecker(anonymousCriteria: data(from: mockDataForCriteria1)!, anonymousEvents: eventItems).getMatchedCriteria()
493+
let matchedCriteriaId = CriteriaCompletionChecker(unknownUserCriteria: data(from: mockDataForCriteria1)!, unknownUserEvents: eventItems).getMatchedCriteria()
494494
XCTAssertEqual(matchedCriteriaId, expectedCriteriaId)
495495
}
496496

@@ -502,7 +502,7 @@ class AnonymousUserComplexCriteriaMatchTests: XCTestCase {
502502
"createdAt": 1699246745093,
503503
"dataType": "updateCart",
504504
]]
505-
let matchedCriteriaId = CriteriaCompletionChecker(anonymousCriteria: data(from: mockDataForCriteria1)!, anonymousEvents: eventItems).getMatchedCriteria()
505+
let matchedCriteriaId = CriteriaCompletionChecker(unknownUserCriteria: data(from: mockDataForCriteria1)!, unknownUserEvents: eventItems).getMatchedCriteria()
506506
XCTAssertEqual(matchedCriteriaId, nil)
507507
}
508508

@@ -512,7 +512,7 @@ class AnonymousUserComplexCriteriaMatchTests: XCTestCase {
512512
], ["dataType": "user", "createdAt": 1699246745093, "phone_number": "999999", "country": "USA", "dataFields": ["preferred_car_models": "Subaru"]
513513
]]
514514
let expectedCriteriaId = "51"
515-
let matchedCriteriaId = CriteriaCompletionChecker(anonymousCriteria: data(from: mockDataForCriteria2)!, anonymousEvents: eventItems).getMatchedCriteria()
515+
let matchedCriteriaId = CriteriaCompletionChecker(unknownUserCriteria: data(from: mockDataForCriteria2)!, unknownUserEvents: eventItems).getMatchedCriteria()
516516
XCTAssertEqual(matchedCriteriaId, expectedCriteriaId)
517517
}
518518

@@ -521,7 +521,7 @@ class AnonymousUserComplexCriteriaMatchTests: XCTestCase {
521521
["dataType": "customEvent", "createdAt": 1699246745093, "eventName": "button-clicked", "dataFields": ["lastPageViewed": "welcome page"]
522522
], ["dataType": "user", "createdAt": 1699246745093, "phone_number": "999999", "country": "USA", "dataFields": ["preferred_car_models": "Mazda"]
523523
]]
524-
let matchedCriteriaId = CriteriaCompletionChecker(anonymousCriteria: data(from: mockDataForCriteria2)!, anonymousEvents: eventItems).getMatchedCriteria()
524+
let matchedCriteriaId = CriteriaCompletionChecker(unknownUserCriteria: data(from: mockDataForCriteria2)!, unknownUserEvents: eventItems).getMatchedCriteria()
525525
XCTAssertEqual(matchedCriteriaId, nil)
526526
}
527527

@@ -547,7 +547,7 @@ class AnonymousUserComplexCriteriaMatchTests: XCTestCase {
547547
], ]
548548

549549
let expectedCriteriaId = "50"
550-
let matchedCriteriaId = CriteriaCompletionChecker(anonymousCriteria: data(from: mockDataForCriteria3)!, anonymousEvents: eventItems).getMatchedCriteria()
550+
let matchedCriteriaId = CriteriaCompletionChecker(unknownUserCriteria: data(from: mockDataForCriteria3)!, unknownUserEvents: eventItems).getMatchedCriteria()
551551
XCTAssertEqual(matchedCriteriaId, expectedCriteriaId)
552552

553553
}
@@ -572,7 +572,7 @@ class AnonymousUserComplexCriteriaMatchTests: XCTestCase {
572572
], [
573573
"dataType": "user", "createdAt": 1699246745093, "phone_number": "999999", "country": "US", "dataFields": ["preferred_car_models": "Subaru"]
574574
]]
575-
let matchedCriteriaId = CriteriaCompletionChecker(anonymousCriteria: data(from: mockDataForCriteria3)!, anonymousEvents: eventItems).getMatchedCriteria()
575+
let matchedCriteriaId = CriteriaCompletionChecker(unknownUserCriteria: data(from: mockDataForCriteria3)!, unknownUserEvents: eventItems).getMatchedCriteria()
576576
XCTAssertEqual(matchedCriteriaId, nil)
577577
}
578578

@@ -584,7 +584,7 @@ class AnonymousUserComplexCriteriaMatchTests: XCTestCase {
584584
"dataType": "purchase"
585585
]]
586586
let expectedCriteriaId = "48"
587-
let matchedCriteriaId = CriteriaCompletionChecker(anonymousCriteria: data(from: mockDataForCriteria4)!, anonymousEvents: eventItems).getMatchedCriteria()
587+
let matchedCriteriaId = CriteriaCompletionChecker(unknownUserCriteria: data(from: mockDataForCriteria4)!, unknownUserEvents: eventItems).getMatchedCriteria()
588588
XCTAssertEqual(matchedCriteriaId, expectedCriteriaId)
589589
}
590590

@@ -595,7 +595,7 @@ class AnonymousUserComplexCriteriaMatchTests: XCTestCase {
595595
"createdAt": 1699246745093,
596596
"dataType": "purchase"
597597
]]
598-
let matchedCriteriaId = CriteriaCompletionChecker(anonymousCriteria: data(from: mockDataForCriteria4)!, anonymousEvents: eventItems).getMatchedCriteria()
598+
let matchedCriteriaId = CriteriaCompletionChecker(unknownUserCriteria: data(from: mockDataForCriteria4)!, unknownUserEvents: eventItems).getMatchedCriteria()
599599
XCTAssertEqual(matchedCriteriaId, nil)
600600
}
601601

tests/unit-tests/AnonymousUserCriteriaIsSetTests.swift

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -284,27 +284,27 @@ class AnonymousUserCriteriaIsSetTests: XCTestCase {
284284
func testCompareDataIsSetUserPropertySuccess() {
285285
let eventItems: [[AnyHashable: Any]] = [["dataType": "user", "createdAt": 1699246745093, "phoneNumberDetails": "999999", "country": "UK", "eventTimeStamp": "1234567890", "shoppingCartItems.price": "33"]]
286286
let expectedCriteriaId = "1"
287-
let matchedCriteriaId = CriteriaCompletionChecker(anonymousCriteria: data(from: mockDataUserProperty)!, anonymousEvents: eventItems).getMatchedCriteria()
287+
let matchedCriteriaId = CriteriaCompletionChecker(unknownUserCriteria: data(from: mockDataUserProperty)!, unknownUserEvents: eventItems).getMatchedCriteria()
288288
XCTAssertEqual(matchedCriteriaId, expectedCriteriaId)
289289
}
290290

291291
func testCompareDataIsSetUserPropertyFailure() {
292292
let eventItems: [[AnyHashable: Any]] = [["dataType": "user", "createdAt": 1699246745093, "phoneNumberDetails": "999999", "country": "", "eventTimeStamp": "", "shoppingCartItems.price": "33"]]
293-
let matchedCriteriaId = CriteriaCompletionChecker(anonymousCriteria: data(from: mockDataUserProperty)!, anonymousEvents: eventItems).getMatchedCriteria()
293+
let matchedCriteriaId = CriteriaCompletionChecker(unknownUserCriteria: data(from: mockDataUserProperty)!, unknownUserEvents: eventItems).getMatchedCriteria()
294294
XCTAssertEqual(matchedCriteriaId, nil)
295295
}
296296

297297

298298
func testCompareDataIsSetCustomEventSuccess() {
299299
let eventItems: [[AnyHashable: Any]] = [["dataType": "customEvent", "eventName":"button-clicked", "dataFields": ["button-clicked":"cc", "animal": "aa", "clickCount": "1", "total": "10"]]]
300300
let expectedCriteriaId = "1"
301-
let matchedCriteriaId = CriteriaCompletionChecker(anonymousCriteria: data(from: mockDataCustomEvent)!, anonymousEvents: eventItems).getMatchedCriteria()
301+
let matchedCriteriaId = CriteriaCompletionChecker(unknownUserCriteria: data(from: mockDataCustomEvent)!, unknownUserEvents: eventItems).getMatchedCriteria()
302302
XCTAssertEqual(matchedCriteriaId, expectedCriteriaId)
303303
}
304304

305305
func testCompareDataIsSetCustomEventFailure() {
306306
let eventItems: [[AnyHashable: Any]] = [["dataType": "customEvent", "eventName":"vvv", "dataFields": ["button-clicked":"", "animal": "", "clickCount": "1", "total": "10"]]]
307-
let matchedCriteriaId = CriteriaCompletionChecker(anonymousCriteria: data(from: mockDataCustomEvent)!, anonymousEvents: eventItems).getMatchedCriteria()
307+
let matchedCriteriaId = CriteriaCompletionChecker(unknownUserCriteria: data(from: mockDataCustomEvent)!, unknownUserEvents: eventItems).getMatchedCriteria()
308308
XCTAssertEqual(matchedCriteriaId, nil)
309309
}
310310

@@ -316,7 +316,7 @@ class AnonymousUserCriteriaIsSetTests: XCTestCase {
316316
"dataType": "purchase"
317317
]]
318318
let expectedCriteriaId = "1"
319-
let matchedCriteriaId = CriteriaCompletionChecker(anonymousCriteria: data(from: mockDataPurchase)!, anonymousEvents: eventItems).getMatchedCriteria()
319+
let matchedCriteriaId = CriteriaCompletionChecker(unknownUserCriteria: data(from: mockDataPurchase)!, unknownUserEvents: eventItems).getMatchedCriteria()
320320
XCTAssertEqual(matchedCriteriaId, expectedCriteriaId)
321321
}
322322

@@ -326,7 +326,7 @@ class AnonymousUserCriteriaIsSetTests: XCTestCase {
326326
"createdAt": 1699246745093,
327327
"dataType": "purchase"
328328
]]
329-
let matchedCriteriaId = CriteriaCompletionChecker(anonymousCriteria: data(from: mockDataPurchase)!, anonymousEvents: eventItems).getMatchedCriteria()
329+
let matchedCriteriaId = CriteriaCompletionChecker(unknownUserCriteria: data(from: mockDataPurchase)!, unknownUserEvents: eventItems).getMatchedCriteria()
330330
XCTAssertEqual(matchedCriteriaId, nil)
331331
}
332332

@@ -337,7 +337,7 @@ class AnonymousUserCriteriaIsSetTests: XCTestCase {
337337
"dataType": "updateCart"
338338
]]
339339
let expectedCriteriaId = "1"
340-
let matchedCriteriaId = CriteriaCompletionChecker(anonymousCriteria: data(from: mockDataUpdateCart)!, anonymousEvents: eventItems).getMatchedCriteria()
340+
let matchedCriteriaId = CriteriaCompletionChecker(unknownUserCriteria: data(from: mockDataUpdateCart)!, unknownUserEvents: eventItems).getMatchedCriteria()
341341
XCTAssertEqual(matchedCriteriaId, expectedCriteriaId)
342342
}
343343

@@ -347,7 +347,7 @@ class AnonymousUserCriteriaIsSetTests: XCTestCase {
347347
"createdAt": 1699246745093,
348348
"dataType": "updateCart"
349349
]]
350-
let matchedCriteriaId = CriteriaCompletionChecker(anonymousCriteria: data(from: mockDataUpdateCart)!, anonymousEvents: eventItems).getMatchedCriteria()
350+
let matchedCriteriaId = CriteriaCompletionChecker(unknownUserCriteria: data(from: mockDataUpdateCart)!, unknownUserEvents: eventItems).getMatchedCriteria()
351351
XCTAssertEqual(matchedCriteriaId, nil)
352352
}
353353
}

0 commit comments

Comments
 (0)