diff --git a/CLAUDE.md b/CLAUDE.md index 954a9d56a..cb31e4113 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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. \ No newline at end of file +## 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.) +- **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 + +### ๐Ÿ› 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 \ No newline at end of file diff --git a/agent_build.sh b/agent_build.sh new file mode 100755 index 000000000..9eaf90e7f --- /dev/null +++ b/agent_build.sh @@ -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 \ No newline at end of file diff --git a/agent_test.sh b/agent_test.sh new file mode 100755 index 000000000..10151054a --- /dev/null +++ b/agent_test.sh @@ -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 \ No newline at end of file