This is the Optimizely Swift SDK for Feature Experimentation and Full Stack. It provides A/B testing and feature management capabilities for iOS, tvOS, and watchOS platforms.
- iOS 10.0+
- tvOS 10.0+
- watchOS 3.0+
- Swift 5+
- Swift Package Manager (preferred)
- CocoaPods
- SwiftLint (development)
# Install dependencies
pod install
# Build the SDK
swift build
# Verify setup (see "Testing" section for detailed commands)
swift test-
Sources/Optimizely/: Main SDK entry point and client implementation
OptimizelyClient.swift: Primary SDK interfaceOptimizelyConfig.swift: Configuration managementVuidManager.swift: Visitor unique ID management
-
Sources/Optimizely+Decide/: Decision-making and user context
OptimizelyUserContext.swift: User context for decision-makingOptimizelyDecision.swift: Decision resultsOptimizelyDecideOption.swift: Decision options and flags
-
Sources/Data Model/: Data structures for experiments, features, and events
- Core entities: Experiment, FeatureFlag, Variation, Event, Audience
- CMAB (Contextual Multi-Armed Bandit) models
- Holdout configurations
-
Sources/Implementation/: Core business logic
DefaultDecisionService.swift: Decision-making engineDefaultBucketer.swift: User bucketing logic- Event handling and batch processing
-
Sources/CMAB/: Contextual Multi-Armed Bandit implementation
CmabClient.swift: Client for CMAB predictionsCmabConfig.swift: Configuration for CMABCmabService.swift: Service layer for CMAB operations
-
Sources/ODP/: Optimizely Data Platform integration
- Event and segment management
- API managers for ODP communication
-
Sources/Customization/: Extensibility points
- Protocol definitions for custom handlers
- Default implementations (logger, event dispatcher, datafile handler)
-
Sources/Utils/: Shared utilities
- Atomic properties and thread-safe collections
- Hashing (MurmurHash3)
- Network reachability
- Tests/OptimizelyTests-Common/: Common utility and core functionality tests
- Tests/OptimizelyTests-APIs/: Public API tests
- Tests/OptimizelyTests-Batch/: Event batching and dispatching tests
- Tests/OptimizelyTests-DataModel/: Data model tests
- Tests/TestData/: JSON fixture files for test data
- Test naming convention:
{FeatureName}Tests.swift - Test data fixtures: Predefined JSON files with sample configurations
We follow the Ray Wenderlich Swift Style Guide for readability and consistency.
SwiftLint is enforced. Before committing:
swiftlintFix all warnings and errors. Configuration in .swiftlint.yml.
The SDK uses protocols for extensibility:
OPTLogger: Custom loggingOPTEventDispatcher: Custom event dispatchingOPTDatafileHandler: Custom datafile managementOPTUserProfileService: Custom user profile persistence
- Use
AtomicProperty,AtomicArray,AtomicDictionaryfor thread-safe state - All atomic utilities are located in
Sources/Utils/ - Ensure event dispatchers and managers are thread-safe
- Use
OptimizelyErrorenum for SDK-specific errors - Use
OptimizelyResult<T>for result types - Handle errors gracefully with meaningful messages
- Use
ThreadSafeLoggeror custom logger implementingOPTLogger - Log levels: debug, info, warning, error
- Use appropriate log levels for different message types
# Run all tests
swift test
# Run with verbose output
swift test --verbose# Run all tests for iOS
xcodebuild test \
-workspace OptimizelySwiftSDK.xcworkspace \
-scheme OptimizelySwiftSDK-iOS \
-destination 'platform=iOS Simulator,name=iPhone 16'
# Run a specific test target
xcodebuild test \
-workspace OptimizelySwiftSDK.xcworkspace \
-scheme OptimizelySwiftSDK-iOS \
-destination 'platform=iOS Simulator,name=iPhone 16' \
-only-testing:TestTarget/TestClass
# Run a specific test method
xcodebuild test \
-workspace OptimizelySwiftSDK.xcworkspace \
-scheme OptimizelySwiftSDK-iOS \
-destination 'platform=iOS Simulator,name=iPhone 16' \
-only-testing:TestTarget/TestClass/testMethodNameOptimizelyTests-Common-iOS: Common utilities and core functionalityOptimizelyTests-APIs-iOS: Public API testsOptimizelyTests-Batch-iOS: Event batching and dispatchingOptimizelyTests-DataModel-iOS: Data modelsOptimizelyTests-Legacy-iOS: Legacy compatibilityOptimizelyTests-MultiClients-iOS: Multi-client scenariosOptimizelyTests-Others-iOS: Miscellaneous testsOptimizelyTests-iOS: Main test suite
Similar test targets exist for tvOS and other platforms.
- All code must have test coverage
- Use XCTest framework
- Use
.sortedKeysfor JSONEncoder in tests to ensure deterministic JSON output - Override network calls in test mocks to avoid timeouts
- Use JSON fixtures from
Tests/TestData/for consistent test data - Each test should use unique file names for persistent storage
CRITICAL: New test files must be manually added to OptimizelySwiftSDK.xcodeproj/project.pbxproj - file creation alone is insufficient.
Steps:
- Create test file in appropriate directory (
Tests/OptimizelyTests-Common/, etc.) - Generate unique IDs:
uuidgen | tr 'A-F' 'a-f' | tr -d '-' | cut -c1-24 | awk '{print toupper($0)}' - Edit
project.pbxprojfollowing pattern of similar files (e.g.,DecisionServiceTests_LocalHoldouts.swift):- Add PBXBuildFile entries (~line 2120) - one per target (iOS, tvOS)
- Add PBXFileReference entry (~line 2640)
- Add to file group listing (~line 3180)
- Add to PBXSourcesBuildPhase for each target (~lines 5200, 5510)
- Verify:
swift build && swift test --filter YourTestClass
Pattern: Common tests need 2 targets (iOS + tvOS); base classes may need 4 targets.
- Main branch:
master - Create feature branches:
YOUR_NAME/branch_name - Don't commit on master branch, create new branch before committing any changes
- Create a branch (see "Helpful Commands > Git Commands")
- Make your changes following coding standards
- Write or update tests (see "Testing" section)
- Run linting (see "Coding Standards > Linting")
- Run tests to verify changes (see "Testing" section)
When creating a pull request, follow this checklist:
-
Ensure all tests pass (see "Testing" section)
-
Run SwiftLint and fix all issues (see "Coding Standards > Linting")
-
Verify no merge conflicts with
mastergit fetch origin git merge origin/master
-
Follow the PR template (located at
pull_request_template.md)Your PR description MUST include:
## Summary
- Bullet points describing "what" changed (each logical change)
- Context explaining "why" the changes were made
## Test plan
- Describe how the changes were tested
- List specific test cases added or modified
- Include manual testing steps if applicable
## Issues
- Reference related issues: "THING-1234" or "Fixes #123"
- If no issue exists, explain why the change is needed
Example:
## Summary - Fixed flaky tests by replacing asyncAfter with DispatchGroup.wait() - Updated MockCmabService to override both sync and async methods Recent async retry refactoring introduced timing issues in tests. Tests were using unreliable asyncAfter delays instead of proper synchronization. ## Test plan - Ran EventDispatcherRetryTests suite 20 times, all passed - Verified on GitHub CI across multiple Xcode versions - Added capturedOptions array to MockCmabService for thread-safe tracking ## Issues - Fixes #456
-
Get review from maintainer
- Request review from code owners
- Address all feedback and comments
-
Don't update SDK version
- Version updates are handled by maintainers during release process
When preparing a new version release, follow these steps:
- Major (X.0.0): Breaking changes, major new features
- Minor (5.X.0): New features, enhancements, backwards compatible
- Patch (5.2.X): Bug fixes only, no new features
# Create branch from master
git checkout master
git pull origin master
git checkout -b prepare-release-X.Y.ZUpdate exactly 3 files:
File 1: .github/workflows/swift.yml (Line ~20)
env:
- VERSION: 5.2.0
+ VERSION: 5.2.1File 2: CHANGELOG.md (Add new section at top)
## X.Y.Z
Month Day, Year
### New Features (if minor/major release)
- **Feature Name**: Description ([#PR](link))
### Bug Fixes (if patch release)
- **Component**: Fix description ([#PR](link))
### API Changes (if applicable)
- **Breaking/New API**: Description ([#PR](link))Important: Only include user-facing changes:
- ✅ Include: New features, bug fixes, API changes, breaking changes
- ❌ Exclude: Internal refactoring, test improvements, CI updates, documentation changes
File 3: README.md (Line ~42)
-```pod 'OptimizelySwiftSDK', '~> 5.2.0'```
+```pod 'OptimizelySwiftSDK', '~> 5.2.1'```# Stage all changes
git add .github/workflows/swift.yml CHANGELOG.md README.md
# Create release commit
git commit -m "chore: prepare release X.Y.Z
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>"# Push branch
git push origin prepare-release-X.Y.Z
# Create PR (adjust ticket number)
gh pr create --title "[FSSDK-XXXXX] chore: prepare release X.Y.Z" --body "## Summary
Prepare for Release X.Y.Z: [Brief description of changes]
## Test plan
- All tests passing on CI
- No new functionality, version bump only
## Issues
- FSSDK-XXXXX"After the release PR is merged to master:
- Maintainers will tag the release
- GitHub Actions will build and publish to CocoaPods
- Release notes will be published automatically
- 5.2.0 Release: PR #613 (minor release with new features)
- 5.2.1 Release: PR #XXX (patch release with bug fixes)
- Created release branch:
prepare-release-X.Y.Z - Updated VERSION in
.github/workflows/swift.yml - Added release notes to
CHANGELOG.md(user-facing changes only) - Updated version in
README.md - Created commit with message:
chore: prepare release X.Y.Z - Created PR with proper title and description
- All CI tests passing
- PR approved and merged
Initialize the SDK with an SDK key and start fetching the datafile:
let optimizely = OptimizelyClient(sdkKey: "YOUR_SDK_KEY")
optimizely.start { result in
switch result {
case .success:
// SDK ready
case .failure(let error):
// Handle error
}
}Create a user context and make feature flag decisions:
let user = optimizely.createUserContext(userId: "user123")
let decision = user.decide(key: "feature_key")
if decision.enabled {
// Feature is enabled
}Track custom events for analytics:
try optimizely.track(eventKey: "purchase", userId: "user123")# Find implementation files by pattern
find Sources -name "*ClassName*.swift"
# Find test files by pattern
find Tests -name "*TestName*.swift"
# List all files in a specific module
find Sources/ModuleName -name "*.swift"# Find protocol definitions
grep -r "^protocol" Sources/ --include="*.swift"
# Search for specific functions or classes
grep -r "class ClassName" Sources/ --include="*.swift"
grep -r "func functionName" Sources/ --include="*.swift"
# Find TODO or FIXME comments
grep -r "TODO\|FIXME" Sources/ --include="*.swift"# Create a new branch
git checkout -b YOUR_NAME/feature-name
# View recent commits
git log --oneline -10
# Check what changed in a specific commit
git show <commit-hash>
# View file changes
git diff <file-path>
# Fetch and merge latest from master
git fetch origin
git merge origin/master# List available simulators
xcrun simctl list devices available
# List schemes and build targets
xcodebuild -workspace OptimizelySwiftSDK.xcworkspace -list
# Show build settings for a scheme
xcodebuild -workspace OptimizelySwiftSDK.xcworkspace \
-scheme OptimizelySwiftSDK-iOS -showBuildSettings