[FSSDK-12409] feat: add Swift Package Manager (SPM) support for iOS - #116
[FSSDK-12409] feat: add Swift Package Manager (SPM) support for iOS#116muzahidul-opti wants to merge 10 commits into
Conversation
Feature specification for migrating the Flutter SDK and test app from CocoaPods to Swift Package Manager, including clarifications on iOS deployment targets, version pinning strategy, and non-regression requirements. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Research, data model, quickstart validation guide, and full implementation plan covering Flutter SPM plugin structure, source file migration, CI updates, and constitution compliance. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Clarification session resolved 4 ambiguities: test app scope (CI + SPM config), mixed CocoaPods/SPM state (Flutter handles automatically), version drift enforcement (CI check via FR-014), and no deprecation notices. Updated all design artifacts to reflect these decisions. Generated tasks.md with 39 tasks across 7 phases organized by user story. Cross-artifact analysis found no critical issues; remediated 3 medium findings (plan/CI inconsistency, missing script path, ambiguous CI decision). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move iOS source files from ios/Classes/ to SPM-standard layout at ios/optimizely-flutter-sdk/Sources/optimizely_flutter_sdk/. Create Package.swift declaring OptimizelySwiftSDK dependency via SPM with exact version pinning matching the podspec. Use a symlink (ios/optimizely_flutter_sdk → ios/optimizely-flutter-sdk) to work around Flutter/SPM identity normalization: SPM converts underscores to hyphens in package identity, but Flutter looks for the underscored directory name. Exclude ObjC bridge files from the SPM target (mixed-language not supported in SPM 5.9) — they remain for CocoaPods builds. Change pubspec.yaml pluginClass to SwiftOptimizelyFlutterSdkPlugin so both SPM and CocoaPods register the plugin via the Swift class directly. Update podspec source_files to point to the new directory layout. Both SPM and CocoaPods builds verified on iOS simulator. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Move sources from Sources/optimizely_flutter_sdk/ to Classes/ for simpler, Flutter-conventional directory structure - Add path: "Classes" to Package.swift target (SPM supports custom paths) - Update podspec source_files glob to match new layout - Add version drift CI job to enforce OptimizelySwiftSDK version parity between podspec and Package.swift - Update CLAUDE.md file paths and README.md with SPM support note Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Pin all jobs to Flutter 3.44.0 for consistent caching - Remove duplicate flutter-action call and stale 3.0.5 pin from build_test_android - Add version_drift_check dependency to build_test_ios - Enable SPM only in build_test_ios (not needed for Dart-only unit tests) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Document SPM identity normalization (SE-0292) and symlink workaround - Document mixed Swift/ObjC limitation and exclude approach - Document pluginClass change to SwiftOptimizelyFlutterSdkPlugin - Update directory paths from Sources/optimizely_flutter_sdk/ to Classes/ - Update Package.swift template with path override and exclude - Update CI section with Flutter version pinning (3.44.0 SDK, 3.16.0 testapp) - Update key risks with resolved/mitigated status Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
pvcraven
left a comment
There was a problem hiding this comment.
Kimi complaints. Are these valid?
1. Integration tests are failing
Per the project constitution, all five CI jobs must pass before merge. The two downstream integration checks are currently red:
| Job | Result | Failure |
|---|---|---|
optimizely/optimizely-flutter-testapp (ios) |
❌ fail | 124 passed, 2 failed |
optimizely/optimizely-flutter-testapp (android) |
❌ fail | Emulator boot timeout |
iOS failures (both in CMAB cache tests):
❌ CMAB Integration Tests 2. Cache Hit - Same user and attributes should use cache
Expected: true
Actual: <false>
First call should be a cache miss
❌ CMAB Integration Tests 3. Cache Miss - Changing attributes should trigger new decision
Expected: true
Actual: <false>
Should log cache attributes mismatch when attributes change
These failures must be understood before merge. If they are pre-existing on master, provide evidence; if they are caused or exposed by the SPM migration, fix the root cause.
2. Missing CHANGELOG entry
SPM support is a user-facing feature. Add an entry in CHANGELOG.md under ## Unreleased (or the target release version).
⚠️ Required changes
3. Update .specify/memory/constitution.md Principle VIII
Principle VIII currently names only ios/optimizely_flutter_sdk.podspec as the iOS native SDK source-of-truth. Because ios/optimizely-flutter-sdk/Package.swift now also declares the pinned OptimizelySwiftSDK version, the constitution should list both files.
4. Fix stale layout in specs/001-cocoapods-to-spm-migration/data-model.md
data-model.md describes a Sources/optimizely_flutter_sdk/ layout and a podspec path of optimizely_flutter_sdk/Sources/optimizely_flutter_sdk/**/*. The actual implementation uses Classes/ and optimizely-flutter-sdk/Classes/**/*. Sync the spec artifact with the real file layout.
5. Add @objc(SwiftOptimizelyFlutterSdkPlugin)
pubspec.yaml now registers the Swift class directly:
ios:
pluginClass: SwiftOptimizelyFlutterSdkPluginTo ensure a stable Objective-C symbol across CocoaPods and SPM module headers, annotate the class explicitly:
@objc(SwiftOptimizelyFlutterSdkPlugin)
public class SwiftOptimizelyFlutterSdkPlugin: NSObject, FlutterPlugin { ... }This is a standard Flutter SPM plugin convention and removes ambiguity in how the generated plugin registrant resolves the class.
📝 Optional / follow-up
- Confirm
Package.swiftproduct naming. The library product is namedoptimizely-flutter-sdk(hyphenated) while the target isoptimizely_flutter_sdk(underscored). Verify that Flutter’s generatedFlutterGeneratedPluginSwiftPackagelooks up the hyphenated product name; otherwise consumers will fail to link the plugin. - Dead Objective-C wrapper. Since
pluginClasspoints directly at the Swift class,OptimizelyFlutterSdkPlugin.m/.hare no longer the registration point for either SPM or CocoaPods. Consider removing them in a follow-up cleanup or explicitly documenting that they are kept as a transitional artifact. - CocoaPods fallback in example app. The regenerated
example/ios/Runner.xcodeproj/project.pbxprojremoved the[CP] Embed Pods Frameworksbuild phase while still linkingPods_Runner.framework. Confirm thatflutter build ios --no-enable-swift-package-managerstill embeds CocoaPods frameworks correctly. - Flutter 3.44.0 pinning. Pinning all jobs to 3.44.0 is good for reproducibility on the iOS SPM job, but unnecessary for unit tests and Android builds. Consider using
channel: stablefor non-iOS jobs to avoid breakage if 3.44.0 has any availability issues.
Merge checklist
- iOS CMAB integration test failures resolved (not just re-run).
- Android integration test re-run green or confirmed flaky/infrastructure-only.
-
CHANGELOG.mdupdated with SPM support note. -
.specify/memory/constitution.mdPrinciple VIII updated to includePackage.swift. -
specs/001-cocoapods-to-spm-migration/data-model.mdsyncs with actualClasses/layout. -
@objc(SwiftOptimizelyFlutterSdkPlugin)added to the plugin class. - All five CI jobs green.
Move iOS source files from Classes/ to Sources/optimizely_flutter_sdk/ per the official Flutter SPM plugin guide. Remove the hyphenated directory and symlink workaround — Flutter 3.44+ discovers the package natively. Pre-3.44 users resolve via CocoaPods and are unaffected. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Thanks for the thorough review! Here's the breakdown: Addressed
Not applicable
Separate work
Optional items — no action needed
|
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Summary
Adds Swift Package Manager (SPM) support for the iOS plugin while maintaining full CocoaPods backward compatibility. Flutter 3.44+ uses SPM by default for iOS dependency resolution — this change enables that path.
What changed
iOS directory layout (canonical SPM structure)
ios/optimizely_flutter_sdk/Sources/optimizely_flutter_sdk/ios/optimizely-flutter-sdk/Classes/layout and theios/optimizely_flutter_sdksymlinkPackage.swiftdeclaringOptimizelySwiftSDK 5.4.2as an SPM dependency.m/.h) are excluded from the SPM target (SPM doesn't support mixed Swift/ObjC in a single target) but remain on disk for CocoaPods buildsPlugin registration
pluginClassinpubspec.yamlfromOptimizelyFlutterSdkPlugin(ObjC) toSwiftOptimizelyFlutterSdkPlugin(Swift) for direct SPM registrationCocoaPods backward compatibility
source_filesglob to'optimizely_flutter_sdk/Sources/optimizely_flutter_sdk/**/*'flutter build ioswith SPM disabled still resolves via podspecCI: version drift check (FR-014)
.github/scripts/check-version-drift.shextractsOptimizelySwiftSDKversion from bothpodspecandPackage.swift, exits non-zero if they divergeversion_drift_checkjob toflutter.ymlthat runs beforeunit_test_coverageDocs
CLAUDE.mdwith new file paths, version management for dual SPM/CocoaPodsREADME.mdnoting SPM and CocoaPods dual supportHow dependency resolution works
Package.swift→ SPM fetchesswift-sdk.gitpodspec→ CocoaPods fetchesOptimizelySwiftSDKpodspec→ CocoaPods (SPM not available)Test plan
flutter test— 140 tests passflutter analyze— 0 issuesflutter build ios --simulator --no-codesignwith SPM enabledversion_drift_check,unit_test_coverage,build_test_android,build_test_ios, integration tests)Ticket:
🤖 Generated with Claude Code