Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
165 changes: 163 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,167 @@

🤖 **AI Agent Instructions**

Please read the `AGENT_README.md` file for comprehensive project information, development workflow, and testing procedures.
## Project Overview
This is the **Iterable Swift SDK** for iOS/macOS integration. The SDK provides:
- Push notification handling
- In-app messaging
- Event tracking
- User management
- Unknown user tracking

All the information you need to work on this Iterable Swift SDK project is documented there.
## Key Architecture
- **Core SDK**: `swift-sdk/` - Main SDK implementation
- **Sample Apps**: `sample-apps/` - Example integrations
- **Tests**: `tests/` - Unit tests, UI tests, and integration tests
- **Notification Extension**: `notification-extension/` - Rich push support

## Development Workflow

### 🔨 Building the SDK
```bash
./agent_build.sh
```
- Validates compilation on iOS Simulator
- Shows build errors with context
- Requires macOS with Xcode

### Listing All Available Tests

# List all available test suites
```bash
./agent_test.sh --list
```

### 🧪 Running Tests
```bash
# Run all tests
./agent_test.sh

# Run specific test suite
./agent_test.sh IterableApiCriteriaFetchTests

# Run specific unit test (dot notation - recommended)
./agent_test.sh "IterableApiCriteriaFetchTests.testForegroundCriteriaFetchWhenConditionsMet"

# Run any specific test with path
./agent_test.sh "unit-tests/IterableApiCriteriaFetchTests/testForegroundCriteriaFetchWhenConditionsMet"
```
- Executes on iOS Simulator with accurate pass/fail reporting
- Returns exit code 0 for success, 1 for failures
- Shows detailed test counts and failure information
- `--list` shows all test suites with test counts
- Requires password for xcpretty installation (first run)

## Project Structure
```
swift-sdk/
├── swift-sdk/ # Main SDK source
│ ├── Core/ # Public APIs and models
│ ├── Internal/ # Internal implementation
│ ├── SDK/ # Main SDK entry points
│ └── ui-components/ # SwiftUI/UIKit components
├── tests/ # Test suites
│ ├── unit-tests/ # Unit tests
│ ├── ui-tests/ # UI automation tests
│ └── endpoint-tests/ # API endpoint tests
├── sample-apps/ # Example applications
└── notification-extension/ # Push notification extension
```

## Key Classes
- **IterableAPI**: Main SDK interface
- **IterableConfig**: Configuration management
- **InternalIterableAPI**: Core implementation
- **UnknownUserManager**: Unknown user tracking
- **LocalStorage**: Data persistence

## Common Tasks

### Adding New Features
1. Build first: `./agent_build.sh`
2. Implement in `swift-sdk/Internal/` or `swift-sdk/SDK/`
3. Add tests in `tests/unit-tests/`
4. Verify: `./agent_test.sh` (all tests) or `./agent_test.sh YourTestSuite` (specific suite)

### Debugging Build Issues
- Build script shows compilation errors with file paths
- Check Xcode project references in `swift-sdk.xcodeproj/project.pbxproj`
- Verify file renames are reflected in project file

### Test Failures
- Test script shows specific failures with line numbers and detailed error messages
- Run failing tests individually: `./agent_test.sh "TestSuite.testMethod"`
- Mock classes available in `tests/common/`
- Update parameter names when refactoring APIs

## Requirements
- **macOS**: Required for Xcode builds
- **Xcode**: Latest stable version
- **Ruby**: For xcpretty (auto-installed)
- **iOS Simulator**: For testing

## Quick Start for AI Agents
1. Run `./agent_build.sh` to verify project builds
2. Run `./agent_test.sh` to check test health (or `./agent_test.sh TestSuite` for specific suite)
3. Make changes to source files
4. Re-run both scripts to validate
5. Debug failing tests: `./agent_test.sh "TestSuite.testMethod"`
6. Commit when both pass ✅

## Test Filtering Examples
```bash
# Debug specific failing tests
./agent_test.sh "IterableApiCriteriaFetchTests.testForegroundCriteriaFetchWhenConditionsMet"

# Run a problematic test suite
./agent_test.sh ValidateCustomEventUserUpdateAPITest

# Check auth-related tests
./agent_test.sh AuthTests
```

## AI Agent Memory System

### 🧠 Update Instructions for AI Agents
**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.

### 📍 Code Location Map
- **Auth Logic**: `swift-sdk/Internal/AuthManager.swift` (main auth manager), `swift-sdk/Internal/Auth.swift` (auth models)
- **API Calls**: `swift-sdk/Internal/api-client/ApiClient.swift` (main client), `swift-sdk/Internal/Network/NetworkHelper.swift` (networking)
- **Models**: `swift-sdk/Core/Models/` (all data structures - CommerceItem, IterableInAppMessage, etc.)
Copy link
Member

Choose a reason for hiding this comment

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

Probably good to add Public facing methods go in IterableApi.swift class. Its implementation is in ApiClient.swift class.

- **Main Entry**: `swift-sdk/SDK/IterableAPI.swift` (public API), `swift-sdk/Internal/InternalIterableAPI.swift` (core implementation)
- **Request Handling**: `swift-sdk/Internal/api-client/Request/` (online/offline processors)

### 🛠️ Common Task Recipes

**Add New API Endpoint:**
1. Add path constant to `swift-sdk/Core/Constants.swift` in `Const.Path`
2. Add method to `ApiClientProtocol.swift` and implement in `ApiClient.swift`
3. Create request in `swift-sdk/Internal/api-client/Request/RequestCreator.swift`
4. Add to `RequestHandlerProtocol.swift` and `RequestHandler.swift`

**Modify Auth Logic:**
- Main logic: `swift-sdk/Internal/AuthManager.swift`
- Token storage: `swift-sdk/Internal/Utilities/Keychain/IterableKeychain.swift`
- Auth failures: Handle in `RequestProcessorUtil.swift`

**Add New Model:**
- Create in `swift-sdk/Core/Models/YourModel.swift`
- Make it `@objcMembers public class` for Objective-C compatibility
- Implement `Codable` if it needs JSON serialization
Comment on lines +150 to +152
Copy link
Member

Choose a reason for hiding this comment

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

This is nice! More specific the expectation, better will be the output!


### 🐛 Common Failure Solutions

**"Test X failed"** → Check test file in `tests/unit-tests/` - often parameter name mismatches after refactoring

**"Build failed: file not found"** → Update `swift-sdk.xcodeproj/project.pbxproj` to include new/renamed files

**"Auth token issues"** → Check `AuthManager.swift` and ensure JWT format is correct in tests

**"Network request fails"** → Check endpoint in `Constants.swift` and request creation in `RequestCreator.swift`

## Notes
- Always test builds after refactoring
- Parameter name changes require test file updates
- Project file (`*.pbxproj`) may need manual updates for file renames
- Sample apps demonstrate SDK usage patterns
51 changes: 51 additions & 0 deletions agent_build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/bin/bash

# This script is to be used by LLMs and AI agents to build the Iterable Swift SDK on macOS.
# It uses xcpretty to format the build output and only shows errors.
# It also checks if the build is successful and exits with the correct status.

# Check if running on macOS
if [[ "$(uname)" != "Darwin" ]]; then
echo "❌ This script requires macOS to run Xcode builds"
exit 1
fi

# Make sure xcpretty is installed
if ! command -v xcpretty &> /dev/null; then
echo "xcpretty not found, installing via gem..."
sudo gem install xcpretty
fi

echo "Building Iterable Swift SDK..."

# Create a temporary file for the build output
TEMP_OUTPUT=$(mktemp)

# Run the build and capture all output
xcodebuild \
-project swift-sdk.xcodeproj \
-scheme "swift-sdk" \
-configuration Debug \
-sdk iphonesimulator \
build > $TEMP_OUTPUT 2>&1

# Check the exit status
BUILD_STATUS=$?

# Show errors and warnings if build failed
if [ $BUILD_STATUS -eq 0 ]; then
echo "✅ Iterable SDK build succeeded!"
else
echo "❌ Iterable SDK build failed with status $BUILD_STATUS"
echo ""
echo "🔍 Build errors:"
grep -E 'error:|fatal:' $TEMP_OUTPUT | head -10
echo ""
echo "⚠️ Build warnings:"
grep -E 'warning:' $TEMP_OUTPUT | head -5
fi

# Remove the temporary file
rm $TEMP_OUTPUT

exit $BUILD_STATUS
127 changes: 127 additions & 0 deletions agent_test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
#!/bin/bash

# Check if running on macOS
if [[ "$(uname)" != "Darwin" ]]; then
echo "❌ This script requires macOS to run Xcode tests"
exit 1
fi

# Parse command line arguments
FILTER=""
LIST_TESTS=false

if [[ $# -eq 1 ]]; then
if [[ "$1" == "--list" ]]; then
LIST_TESTS=true
else
FILTER="$1"
echo "🎯 Running tests with filter: $FILTER"
fi
elif [[ $# -gt 1 ]]; then
echo "❌ Usage: $0 [filter|--list]"
echo " filter: Test suite name (e.g., 'IterableApiCriteriaFetchTests')"
echo " or specific test (e.g., 'IterableApiCriteriaFetchTests.testForegroundCriteriaFetchWhenConditionsMet')"
echo " or full path (e.g., 'unit-tests/IterableApiCriteriaFetchTests/testForegroundCriteriaFetchWhenConditionsMet')"
echo " --list: List all available test suites and tests"
exit 1
fi

# Handle test listing
if [[ "$LIST_TESTS" == true ]]; then
echo "📋 Listing available test suites..."

# Use grep to extract test class names from source files
echo "📦 Available Test Suites:"
find tests/unit-tests -name "*.swift" -exec basename {} .swift \; | sort | while read test_file; do
# Count test methods in each file
test_count=$(grep -c "func test" "tests/unit-tests/$test_file.swift" 2>/dev/null || echo "0")
echo " • $test_file ($test_count tests)"
done

echo ""
echo "🔍 Example Usage:"
echo " ./agent_test.sh AuthTests"
echo " ./agent_test.sh \"AuthTests.testAsyncAuthTokenRetrieval\""
echo ""
echo "💡 To see specific test methods in a suite, check the source file:"
echo " grep 'func test' tests/unit-tests/AuthTests.swift"

exit 0
fi

# Make sure xcpretty is installed
if ! command -v xcpretty &> /dev/null; then
echo "xcpretty not found, installing via gem..."
sudo gem install xcpretty
fi

echo "Running Iterable Swift SDK unit tests..."

# Create a temporary file for the test output
TEMP_OUTPUT=$(mktemp)

# Build the xcodebuild command
XCODEBUILD_CMD="xcodebuild test \
-project swift-sdk.xcodeproj \
-scheme swift-sdk \
-sdk iphonesimulator \
-destination 'platform=iOS Simulator,name=iPhone 16 Pro,OS=18.2' \
-enableCodeCoverage YES \
-skipPackagePluginValidation \
CODE_SIGNING_REQUIRED=NO"

# Add filter if specified
if [[ -n "$FILTER" ]]; then
# If filter contains a slash, use it as-is (already in unit-tests/TestSuite/testMethod format)
if [[ "$FILTER" == *"/"* ]]; then
XCODEBUILD_CMD="$XCODEBUILD_CMD -only-testing:$FILTER"
# If filter contains a dot, convert TestSuite.testMethod to unit-tests/TestSuite/testMethod
elif [[ "$FILTER" == *"."* ]]; then
TEST_SUITE=$(echo "$FILTER" | cut -d'.' -f1)
TEST_METHOD=$(echo "$FILTER" | cut -d'.' -f2)
XCODEBUILD_CMD="$XCODEBUILD_CMD -only-testing:unit-tests/$TEST_SUITE/$TEST_METHOD"
# Otherwise, assume it's just a test suite name and add the target
else
XCODEBUILD_CMD="$XCODEBUILD_CMD -only-testing:unit-tests/$FILTER"
fi
fi

# Run the tests with xcpretty for clean output (incremental - skips rebuild if possible)
eval $XCODEBUILD_CMD 2>&1 | tee $TEMP_OUTPUT | xcpretty

# Check the exit status
TEST_STATUS=$?

# Parse the "Executed X test(s), with Y failure(s)" line
EXECUTED_LINE=$(grep "Executed.*test.*with.*failure" $TEMP_OUTPUT | tail -1)
if [[ -n "$EXECUTED_LINE" ]]; then
TOTAL_TESTS=$(echo "$EXECUTED_LINE" | sed -n 's/.*Executed \([0-9][0-9]*\) test.*/\1/p')
FAILED_TESTS=$(echo "$EXECUTED_LINE" | sed -n 's/.*with \([0-9][0-9]*\) failure.*/\1/p')

# Ensure we have valid numbers
if [[ -z "$TOTAL_TESTS" ]]; then TOTAL_TESTS=0; fi
if [[ -z "$FAILED_TESTS" ]]; then FAILED_TESTS=0; fi

PASSED_TESTS=$(($TOTAL_TESTS - $FAILED_TESTS))
else
TOTAL_TESTS=0
FAILED_TESTS=0
PASSED_TESTS=0
fi

# Show test results
if [ "$FAILED_TESTS" -eq 0 ] && [ "$TOTAL_TESTS" -gt 0 ]; then
echo "✅ All tests passed! ($TOTAL_TESTS tests)"
FINAL_STATUS=0
elif [ "$FAILED_TESTS" -gt 0 ]; then
echo "❌ Tests failed: $FAILED_TESTS failed, $PASSED_TESTS passed ($TOTAL_TESTS total)"
FINAL_STATUS=1
else
echo "⚠️ No test results found"
FINAL_STATUS=$TEST_STATUS
fi

# Remove the temporary file
rm $TEMP_OUTPUT

exit $FINAL_STATUS
Loading