From 909af186b368867828c6e1c8730a2927ef5574c4 Mon Sep 17 00:00:00 2001 From: Muzahidul Islam Date: Mon, 12 Oct 2026 21:12:18 +0600 Subject: [PATCH 1/9] docs: add spec for removing CocoaPods support and raising minimum iOS to 15.0 Co-Authored-By: Claude Opus 5.5 --- specs/003-remove-cocoapods-ios15/spec.md | 128 +++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 specs/003-remove-cocoapods-ios15/spec.md diff --git a/specs/003-remove-cocoapods-ios15/spec.md b/specs/003-remove-cocoapods-ios15/spec.md new file mode 100644 index 0000000..69fd3dd --- /dev/null +++ b/specs/003-remove-cocoapods-ios15/spec.md @@ -0,0 +1,128 @@ +# Feature Specification: Remove CocoaPods Support and Raise Minimum iOS to 15.0 + +**Feature Branch**: `003-remove-cocoapods-ios15` + +**Created**: 2026-10-12 + +**Status**: Draft + +**Input**: User description: "specify the ticket 1" — [Flutter] Remove CocoaPods support and raise minimum iOS to 15.0. OptimizelySwiftSDK 6.0.0 (Oct 7, 2026) dropped CocoaPods distribution and raised its minimum to iOS 15.0. The Flutter SDK must become SPM-only before it can adopt 6.0.0. Includes updating the `optimizely-flutter-testapp` repo. + +## User Scenarios & Testing *(mandatory)* + +### User Story 1 - SDK Consumers Build iOS via SPM Only (Priority: P1) + +A developer integrating `optimizely_flutter_sdk` into a Flutter app on an SPM-capable Flutter version builds and runs the iOS target exactly as today (`flutter pub get`, `flutter build ios`). The plugin ships only an SPM manifest; no CocoaPods artifacts remain in the published package. All SDK features behave identically to the previous release. + +**Why this priority**: This is the prerequisite for adopting OptimizelySwiftSDK 6.0.0, which has no CocoaPods distribution. Without it the Flutter SDK is stuck on the 5.x native line. + +**Independent Test**: Create a fresh Flutter app on the minimum supported Flutter version with iOS deployment target 15.0, add the SDK as a dependency, run `flutter build ios`, and verify the build succeeds and the native SDK is resolved through SPM. + +**Acceptance Scenarios**: + +1. **Given** a Flutter app on the minimum supported Flutter version with iOS target 15.0, **When** the developer runs `flutter build ios`, **Then** the build succeeds with the native Optimizely dependency resolved via SPM. +2. **Given** the published SDK package, **When** its iOS directory is inspected, **Then** it contains no CocoaPods spec and no source files used only by CocoaPods builds. +3. **Given** an app that previously worked with the SDK, **When** it upgrades to this version, **Then** initialize, decide (sync and async), track, user context, ODP, notification listeners, and logger forwarding behave identically on iOS. + +--- + +### User Story 2 - Developers Below the New Minimums Get a Clear Signal (Priority: P1) + +A developer whose app targets iOS below 15.0, or whose Flutter version cannot consume SPM-only plugins, is told clearly what to change — through version constraints, a build-time error that names the requirement, and a migration note in the README and CHANGELOG — rather than hitting an obscure native build failure. + +**Why this priority**: This is a breaking change. Constitution (Platform Compatibility Standards) requires raised minimums to ship as a major version, and consumers need a clear upgrade path. + +**Independent Test**: Attempt to depend on the SDK from (a) an app with iOS target 14.0 and (b) a project on a Flutter version below the new minimum; verify each fails with a message that identifies the unmet requirement, and that the README migration note explains the fix. + +**Acceptance Scenarios**: + +1. **Given** an app with iOS deployment target below 15.0, **When** it builds with the SDK, **Then** the build fails with an error identifying the iOS 15.0 minimum. +2. **Given** a project on a Flutter version below the declared minimum, **When** the developer runs `flutter pub get`, **Then** dependency resolution fails, naming the Flutter version constraint. +3. **Given** the README and CHANGELOG, **When** a developer reads the upgrade section, **Then** it lists the new iOS and Flutter minimums and the steps to migrate an app from CocoaPods to SPM. + +--- + +### User Story 3 - Test App Validates the SPM-Only SDK (Priority: P2) + +The `optimizely-flutter-testapp` repository builds its iOS target via SPM with deployment target 15.0, and its integration suite passes against the SPM-only SDK. Because SDK CI pulls the test app's `master` branch, test-app changes land before (or are coordinated with) the SDK change. + +**Why this priority**: The test app is the end-to-end validation layer for `integration_ios_tests`. If it still depends on CocoaPods or iOS 13, SDK CI breaks. + +**Independent Test**: In the test-app repo, run the iOS build and integration suite against the SDK feature branch; verify all tests pass with no CocoaPods artifacts present. + +**Acceptance Scenarios**: + +1. **Given** the test app with CocoaPods artifacts removed and iOS target 15.0, **When** its iOS CI runs, **Then** the build and all integration tests pass. +2. **Given** the SDK feature branch and the updated test app, **When** SDK CI triggers `integration_ios_tests`, **Then** the job passes. +3. **Given** the test app's Android target, **When** its Android CI runs, **Then** it passes unchanged. + +--- + +### User Story 4 - Maintainers Have One iOS Dependency Path (Priority: P3) + +SDK maintainers maintain a single iOS dependency manifest. CI no longer runs a podspec/SPM version-parity check, the in-repo example app builds via SPM at iOS 15.0, and project guidance (README, CLAUDE.md, constitution) no longer references CocoaPods for iOS. + +**Why this priority**: Reduces maintenance and removes a CI gate that has no purpose once only one manifest exists. Lower priority because it does not affect consumers. + +**Independent Test**: Search the repo for iOS CocoaPods references and run full CI; verify none remain outside historical records (CHANGELOG, prior specs) and all CI jobs pass. + +**Acceptance Scenarios**: + +1. **Given** the SDK repo after the change, **When** CI runs, **Then** there is no version-parity job and all remaining jobs pass. +2. **Given** the in-repo example app, **When** a developer runs it on iOS, **Then** it builds via SPM with deployment target 15.0. +3. **Given** README, CLAUDE.md, and the constitution, **When** reviewed, **Then** they describe SPM as the only iOS integration path and iOS 15.0 as the minimum. + +--- + +### Edge Cases + +- **App has an existing Podfile with other pods**: Other plugins or pods may still use CocoaPods; Flutter's tooling supports mixed apps where SPM-capable plugins resolve via SPM. The SDK makes no assumptions about the app's other dependencies. +- **SPM disabled in the developer's Flutter config**: With no CocoaPods spec, the plugin cannot be integrated. The migration note must tell developers to ensure SPM is enabled. +- **macOS plugin platform**: macOS has its own separate CocoaPods spec and is experimental (constitution, Platform Compatibility Standards). It is out of scope and remains unchanged. +- **Objective-C registration shim**: The iOS Objective-C plugin files exist only for CocoaPods builds (already excluded from the SPM target). They are removed with CocoaPods; registration continues through the Swift plugin class already declared in `pubspec.yaml`. +- **Test app on `master` lags the SDK**: If the SDK change merges first, `integration_ios_tests` fails. Mitigated by merging the test-app change first or temporarily pointing SDK CI at the test-app feature branch, then reverting. +- **Native SDK version**: This change keeps OptimizelySwiftSDK at its current version (5.4.2); the 6.0.0 bump is a separate ticket (Ticket 2) and must not be bundled (constitution VIII.1). + +## Requirements *(mandatory)* + +### Functional Requirements + +- **FR-001**: The SDK MUST NOT ship a CocoaPods spec for iOS; SPM MUST be the only iOS dependency mechanism. +- **FR-002**: The SDK MUST remove iOS source files used only by CocoaPods builds (the Objective-C registration shim and its header). +- **FR-003**: The SDK MUST declare iOS 15.0 as its minimum iOS platform. +- **FR-004**: The SDK MUST declare a minimum Flutter version that resolves SPM-only plugins by default (Flutter 3.44, the version CI already uses with SPM). +- **FR-005**: The SDK MUST keep OptimizelySwiftSDK pinned at its current exact version (5.4.2) in this change. +- **FR-006**: The SDK MUST preserve all existing iOS behaviour (MethodChannel handlers, logger bridge, type encoding, main-thread result dispatch) with no Dart public API changes. +- **FR-007**: CI MUST remove the podspec/SPM version-parity check and any job dependencies on it; all remaining CI jobs MUST pass. +- **FR-008**: The in-repo example app MUST build on iOS via SPM with deployment target 15.0 and no CocoaPods artifacts. +- **FR-009**: The `optimizely-flutter-testapp` iOS target MUST build via SPM with deployment target 15.0, with CocoaPods artifacts removed, and its iOS and Android CI MUST pass. +- **FR-010**: README MUST state the new iOS and Flutter minimums and include a CocoaPods-to-SPM migration note for app developers. +- **FR-011**: CLAUDE.md and the constitution (Principle VIII and the Minimum Platform Versions table) MUST be updated to remove iOS CocoaPods references and state iOS 15.0; the constitution amendment MUST follow its amendment procedure. +- **FR-012**: The change MUST be released only as part of a new major SDK version (4.0.0), together with Ticket 2; no intermediate release may ship this change alone. +- **FR-013**: All existing unit tests MUST pass and `flutter analyze` MUST report zero issues. + +### Key Entities + +- **iOS SPM manifest**: The single remaining declaration of the plugin's iOS target, its minimum iOS platform, and the pinned native Optimizely dependency. +- **CocoaPods spec (iOS)**: Removed. Previously a parallel declaration of the same dependency. +- **Version-parity check**: CI gate comparing the two manifests. Removed, as only one manifest remains. +- **Test app**: External repository consumed by SDK CI from its `master` branch; must be migrated in lockstep. + +## Success Criteria *(mandatory)* + +### Measurable Outcomes + +- **SC-001**: A fresh app on the minimum supported Flutter version with iOS target 15.0 builds for iOS on the first attempt with no manual steps beyond `flutter pub get` and `flutter build ios`. +- **SC-002**: Zero iOS CocoaPods references remain in the SDK and test-app repos outside historical records (CHANGELOG, prior specs). +- **SC-003**: 100% of existing unit tests and test-app integration tests pass on both platforms. +- **SC-004**: All SDK CI jobs pass on the feature branch. +- **SC-005**: A developer below the new minimums can identify what to change from the error message or README within 5 minutes. + +## Assumptions + +- Flutter 3.44+ resolves SPM-capable plugins by default (README already states this; CI already uses 3.44.0). +- Existing SPM integration from spec 001 (Package.swift, directory layout, Swift plugin registration) is correct and needs no structural change beyond the platform minimum. +- The macOS and Windows plugin platforms are out of scope. +- Android is unaffected except for test-app CI continuing to pass. +- The release (version files, CHANGELOG, 4.0.0) is handled by Ticket 3, not this change; this change only adds README/migration content. +- Xcode 27 runner migration belongs to Ticket 2 (required by OptimizelySwiftSDK 6.0.0), not this change. From 8a9e75276fdec2bf81e92e92dfa72ce0d9ad0b38 Mon Sep 17 00:00:00 2001 From: Muzahidul Islam Date: Mon, 12 Oct 2026 21:44:32 +0600 Subject: [PATCH 2/9] docs: amend constitution to v1.2.0 (SPM-only iOS, iOS 15 minimum) Co-Authored-By: Claude Opus 5.5 --- .specify/memory/constitution.md | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/.specify/memory/constitution.md b/.specify/memory/constitution.md index b5f391e..f3cb67b 100644 --- a/.specify/memory/constitution.md +++ b/.specify/memory/constitution.md @@ -1,19 +1,17 @@ For additional context about technologies to be used, project structure, diff --git a/README.md b/README.md index 6627bb3..7c3f812 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,14 @@ See the [pubspec.yaml](https://github.com/optimizely/optimizely-flutter-sdk/blob On the Android platform, the SDK requires a minimum SDK version of 21 or higher and compile SDK version of 32. -On the iOS platform, the SDK requires a minimum version of 10.0. The SDK supports both Swift Package Manager (SPM) and CocoaPods for iOS dependency resolution. Flutter 3.44+ uses SPM by default; older versions use CocoaPods. +On the iOS platform, the SDK requires a minimum version of 15.0 and Flutter 3.44 or later. The SDK is integrated through Swift Package Manager (SPM) only; CocoaPods is no longer supported. + +#### Migrating from CocoaPods + +1. Use Flutter 3.44 or later (SPM is enabled by default; otherwise run `flutter config --enable-swift-package-manager`). +2. Set your app's iOS deployment target to 15.0 or higher (Xcode Runner target, and `platform :ios` in your Podfile if you still have one). +3. Run `flutter pub get` and `flutter build ios`. Flutter migrates your Xcode project to SPM automatically. +4. If no other CocoaPods plugins remain, you may run `pod deintegrate` and delete your Podfile. Other Flutter platforms are not currently supported by this SDK. diff --git a/example/ios/Flutter/Debug.xcconfig b/example/ios/Flutter/Debug.xcconfig index ec97fc6..592ceee 100644 --- a/example/ios/Flutter/Debug.xcconfig +++ b/example/ios/Flutter/Debug.xcconfig @@ -1,2 +1 @@ -#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "Generated.xcconfig" diff --git a/example/ios/Flutter/Release.xcconfig b/example/ios/Flutter/Release.xcconfig index c4855bf..592ceee 100644 --- a/example/ios/Flutter/Release.xcconfig +++ b/example/ios/Flutter/Release.xcconfig @@ -1,2 +1 @@ -#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "Generated.xcconfig" diff --git a/example/ios/Podfile b/example/ios/Podfile deleted file mode 100644 index 313ea4a..0000000 --- a/example/ios/Podfile +++ /dev/null @@ -1,41 +0,0 @@ -# Uncomment this line to define a global platform for your project -platform :ios, '11.0' - -# CocoaPods analytics sends network stats synchronously affecting flutter build latency. -ENV['COCOAPODS_DISABLE_STATS'] = 'true' - -project 'Runner', { - 'Debug' => :debug, - 'Profile' => :release, - 'Release' => :release, -} - -def flutter_root - generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) - unless File.exist?(generated_xcode_build_settings_path) - raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" - end - - File.foreach(generated_xcode_build_settings_path) do |line| - matches = line.match(/FLUTTER_ROOT\=(.*)/) - return matches[1].strip if matches - end - raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" -end - -require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) - -flutter_ios_podfile_setup - -target 'Runner' do - use_frameworks! - use_modular_headers! - - flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) -end - -post_install do |installer| - installer.pods_project.targets.each do |target| - flutter_additional_ios_build_settings(target) - end -end diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 7a54079..b8fa33a 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -14,7 +14,6 @@ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; - E7471FE6544841B0C836D129 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DC1798EC3215B94C8A840B51 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -33,7 +32,6 @@ /* Begin PBXFileReference section */ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 2407820457C3954D6394333D /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; @@ -48,9 +46,6 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - ACF0C93900CB1B99B5E9C0CE /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; - D74274CA5486F97165BE309E /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; - DC1798EC3215B94C8A840B51 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -59,31 +54,12 @@ buildActionMask = 2147483647; files = ( 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, - E7471FE6544841B0C836D129 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - 3B54408533A9D1A272B285B5 /* Pods */ = { - isa = PBXGroup; - children = ( - D74274CA5486F97165BE309E /* Pods-Runner.debug.xcconfig */, - 2407820457C3954D6394333D /* Pods-Runner.release.xcconfig */, - ACF0C93900CB1B99B5E9C0CE /* Pods-Runner.profile.xcconfig */, - ); - path = Pods; - sourceTree = ""; - }; - 5922E7F1B4E66D8046FD9D1E /* Frameworks */ = { - isa = PBXGroup; - children = ( - DC1798EC3215B94C8A840B51 /* Pods_Runner.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( @@ -104,8 +80,6 @@ 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, - 3B54408533A9D1A272B285B5 /* Pods */, - 5922E7F1B4E66D8046FD9D1E /* Frameworks */, ); sourceTree = ""; }; @@ -139,7 +113,6 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - 73C63109C125F11478860EB1 /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, @@ -226,28 +199,6 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; }; - 73C63109C125F11478860EB1 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -338,7 +289,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -357,7 +308,7 @@ DEVELOPMENT_TEAM = BDMC9C2X5M; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -417,7 +368,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -466,7 +417,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -487,7 +438,7 @@ DEVELOPMENT_TEAM = BDMC9C2X5M; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -511,7 +462,7 @@ DEVELOPMENT_TEAM = BDMC9C2X5M; ENABLE_BITCODE = NO; INFOPLIST_FILE = Runner/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcworkspace/contents.xcworkspacedata index 21a3cc1..1d526a1 100644 --- a/example/ios/Runner.xcworkspace/contents.xcworkspacedata +++ b/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -4,7 +4,4 @@ - - diff --git a/ios/optimizely_flutter_sdk.podspec b/ios/optimizely_flutter_sdk.podspec deleted file mode 100644 index 01a56df..0000000 --- a/ios/optimizely_flutter_sdk.podspec +++ /dev/null @@ -1,21 +0,0 @@ -# -# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. -# Run `pod lib lint optimizely_flutter_sdk.podspec` to validate before publishing. -# - -Pod::Spec.new do |s| - s.name = 'optimizely_flutter_sdk' - s.version = '0.0.1' - s.summary = 'Optimizely experiment framework for iOS' - s.homepage = "https://docs.developers.optimizely.com/experimentation/v4.0.0-full-stack/docs" - s.license = { :type => "Apache License, Version 2.0", :file => "../LICENSE" } - s.author = { "Optimizely" => "support@optimizely.com" } - s.source = { :path => '.' } - s.source_files = 'optimizely_flutter_sdk/Sources/optimizely_flutter_sdk/**/*' - s.dependency 'Flutter' - s.dependency 'OptimizelySwiftSDK', '5.5.0' - s.platform = :ios, '10.0' - # Flutter.framework does not contain a i386 slice. - s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } - s.swift_version = '5.0' -end diff --git a/ios/optimizely_flutter_sdk/Package.swift b/ios/optimizely_flutter_sdk/Package.swift index c493fac..1bdcc47 100644 --- a/ios/optimizely_flutter_sdk/Package.swift +++ b/ios/optimizely_flutter_sdk/Package.swift @@ -1,13 +1,10 @@ // swift-tools-version: 5.9 -// ObjC files (OptimizelyFlutterSdkPlugin.m/.h) are excluded from the SPM -// target because SPM does not support mixed Swift/ObjC in a single target. -// They remain on disk for CocoaPods builds, which still use them. import PackageDescription let package = Package( name: "optimizely_flutter_sdk", - platforms: [.iOS("10.0")], + platforms: [.iOS("15.0")], products: [ .library(name: "optimizely-flutter-sdk", targets: ["optimizely_flutter_sdk"]) ], @@ -21,8 +18,7 @@ let package = Package( dependencies: [ .product(name: "FlutterFramework", package: "FlutterFramework"), .product(name: "Optimizely", package: "swift-sdk") - ], - exclude: ["OptimizelyFlutterSdkPlugin.m", "include"] + ] ) ] ) diff --git a/ios/optimizely_flutter_sdk/Sources/optimizely_flutter_sdk/OptimizelyFlutterSdkPlugin.m b/ios/optimizely_flutter_sdk/Sources/optimizely_flutter_sdk/OptimizelyFlutterSdkPlugin.m deleted file mode 100644 index 7bb833f..0000000 --- a/ios/optimizely_flutter_sdk/Sources/optimizely_flutter_sdk/OptimizelyFlutterSdkPlugin.m +++ /dev/null @@ -1,31 +0,0 @@ -/**************************************************************************** - * Copyright 2022, Optimizely, Inc. and contributors * - * * - * Licensed under the Apache License, Version 2.0 (the "License"); * - * you may not use this file except in compliance with the License. * - * You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, software * - * distributed under the License is distributed on an "AS IS" BASIS, * - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * - * See the License for the specific language governing permissions and * - * limitations under the License. * - ***************************************************************************/ - -#import "./include/optimizely_flutter_sdk/OptimizelyFlutterSdkPlugin.h" -#if __has_include() -#import -#else -// Support project import fallback if the generated compatibility header -// is not copied when this plugin is created as a library. -// https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816 -#import "optimizely_flutter_sdk-Swift.h" -#endif - -@implementation OptimizelyFlutterSdkPlugin -+ (void)registerWithRegistrar:(NSObject*)registrar { - [SwiftOptimizelyFlutterSdkPlugin registerWithRegistrar:registrar]; -} -@end diff --git a/ios/optimizely_flutter_sdk/Sources/optimizely_flutter_sdk/include/optimizely_flutter_sdk/OptimizelyFlutterSdkPlugin.h b/ios/optimizely_flutter_sdk/Sources/optimizely_flutter_sdk/include/optimizely_flutter_sdk/OptimizelyFlutterSdkPlugin.h deleted file mode 100644 index 87294b8..0000000 --- a/ios/optimizely_flutter_sdk/Sources/optimizely_flutter_sdk/include/optimizely_flutter_sdk/OptimizelyFlutterSdkPlugin.h +++ /dev/null @@ -1,20 +0,0 @@ -/**************************************************************************** - * Copyright 2022, Optimizely, Inc. and contributors * - * * - * Licensed under the Apache License, Version 2.0 (the "License"); * - * you may not use this file except in compliance with the License. * - * You may obtain a copy of the License at * - * * - * http://www.apache.org/licenses/LICENSE-2.0 * - * * - * Unless required by applicable law or agreed to in writing, software * - * distributed under the License is distributed on an "AS IS" BASIS, * - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * - * See the License for the specific language governing permissions and * - * limitations under the License. * - ***************************************************************************/ - -#import - -@interface OptimizelyFlutterSdkPlugin : NSObject -@end diff --git a/pubspec.yaml b/pubspec.yaml index 7abd615..cf731a6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -5,7 +5,7 @@ homepage: https://github.com/optimizely/optimizely-flutter-sdk environment: sdk: ">=2.16.2 <4.0.0" - flutter: ">=2.5.0" + flutter: ">=3.44.0" dependencies: flutter: diff --git a/specs/003-remove-cocoapods-ios15/checklists/requirements.md b/specs/003-remove-cocoapods-ios15/checklists/requirements.md new file mode 100644 index 0000000..97a2c97 --- /dev/null +++ b/specs/003-remove-cocoapods-ios15/checklists/requirements.md @@ -0,0 +1,35 @@ +# Specification Quality Checklist: Remove CocoaPods Support and Raise Minimum iOS to 15.0 + +**Purpose**: Validate specification completeness and quality before proceeding to planning +**Created**: 2026-10-12 +**Feature**: [spec.md](../spec.md) + +## Content Quality + +- [x] No implementation details (languages, frameworks, APIs) +- [x] Focused on user value and business needs +- [x] Written for non-technical stakeholders +- [x] All mandatory sections completed + +## Requirement Completeness + +- [x] No [NEEDS CLARIFICATION] markers remain +- [x] Requirements are testable and unambiguous +- [x] Success criteria are measurable +- [x] Success criteria are technology-agnostic (no implementation details) +- [x] All acceptance scenarios are defined +- [x] Edge cases are identified +- [x] Scope is clearly bounded +- [x] Dependencies and assumptions identified + +## Feature Readiness + +- [x] All functional requirements have clear acceptance criteria +- [x] User scenarios cover primary flows +- [x] Feature meets measurable outcomes defined in Success Criteria +- [x] No implementation details leak into specification + +## Notes + +- This feature is about the dependency mechanism itself, so naming SPM, CocoaPods, iOS, and Flutter versions is the subject of the spec, not leaked implementation. File-level details (which files to edit) are left to `/speckit-plan`. +- No clarification markers were needed: the Flutter minimum (3.44) follows the README and CI, macOS is out of scope per the constitution, and the release and Xcode 27 work are assigned to Tickets 3 and 2. diff --git a/specs/003-remove-cocoapods-ios15/contracts/platform-requirements.md b/specs/003-remove-cocoapods-ios15/contracts/platform-requirements.md new file mode 100644 index 0000000..7dec0e3 --- /dev/null +++ b/specs/003-remove-cocoapods-ios15/contracts/platform-requirements.md @@ -0,0 +1,18 @@ +# Contract: Consumer Platform Requirements + +The SDK's public Dart API is **unchanged**. The contract that changes is what an app must provide to integrate the plugin on iOS. + +| Requirement | Before (3.x) | After (4.0.0) | Enforced by | Failure signal | +|---|---|---|---|---| +| Flutter SDK | ≥2.5.0 | ≥3.44.0 | `pubspec.yaml` `environment.flutter` | `flutter pub get` fails, naming the Flutter constraint | +| iOS deployment target | ≥10.0 | ≥15.0 | `Package.swift` `platforms` | Flutter raises the app target to 15.0 automatically and prints "Updating minimum iOS deployment target to 15.0" | +| iOS dependency manager | CocoaPods or SPM | SPM only | No podspec shipped | Flutter reports that the plugin has no CocoaPods support when SPM is disabled | +| Android | API 21+ | API 21+ (unchanged) | — | — | +| Dart API surface | — | Unchanged | — | — | + +## Migration steps for app developers (source for README note) + +1. Use Flutter 3.44 or later (SPM is on by default; otherwise run `flutter config --enable-swift-package-manager`). +2. Set the iOS deployment target to 15.0 or higher (Xcode Runner target, plus `platform :ios` in the Podfile if one remains). +3. Run `flutter pub get` and `flutter build ios`. Flutter migrates the Xcode project to SPM automatically. +4. If no other CocoaPods plugins remain, you may run `pod deintegrate` and delete the Podfile. diff --git a/specs/003-remove-cocoapods-ios15/data-model.md b/specs/003-remove-cocoapods-ios15/data-model.md new file mode 100644 index 0000000..92e62ff --- /dev/null +++ b/specs/003-remove-cocoapods-ios15/data-model.md @@ -0,0 +1,18 @@ +# Data Model: Remove CocoaPods Support and Raise Minimum iOS to 15.0 + +No runtime data, persistence or Dart types change. The "entities" are build-time declarations. + +| Entity | Location | Before | After | +|---|---|---|---| +| iOS SPM manifest | `ios/optimizely_flutter_sdk/Package.swift` | `.iOS("10.0")`; excludes ObjC files | `.iOS("15.0")`; no `exclude` | +| iOS CocoaPods spec | `ios/optimizely_flutter_sdk.podspec` | `OptimizelySwiftSDK 5.4.2`, `:ios, '10.0'` | Deleted | +| ObjC registration shim | `ios/.../OptimizelyFlutterSdkPlugin.m`, `include/` | Used by CocoaPods only | Deleted | +| Plugin registration | `pubspec.yaml` `flutter.plugin.platforms.ios.pluginClass` | `SwiftOptimizelyFlutterSdkPlugin` | Unchanged | +| Flutter constraint | `pubspec.yaml` `environment.flutter` | `>=2.5.0` | `>=3.44.0` | +| Native SDK pin | `Package.swift` `exact:` | `5.4.2` | `5.4.2` (Ticket 2 moves to 6.0.0) | +| Version-parity gate | `.github/scripts/check-version-drift.sh` + `flutter.yml` job | Compares podspec vs SPM | Deleted | +| Example app iOS project | `example/ios` | CocoaPods + SPM ref, target 10.0 | SPM only, target 15.0 | +| Testapp iOS project | testapp `ios/` | CocoaPods only, Podfile 13.0, target 10.0 | SPM only, target 15.0 | +| Testapp CI Flutter | testapp `ios.yml`, `android.yml` | `3.16.0` | `3.44.0` | + +**Invariant after change**: exactly one place declares the iOS native dependency (`Package.swift`). diff --git a/specs/003-remove-cocoapods-ios15/plan.md b/specs/003-remove-cocoapods-ios15/plan.md new file mode 100644 index 0000000..072e641 --- /dev/null +++ b/specs/003-remove-cocoapods-ios15/plan.md @@ -0,0 +1,119 @@ +# Implementation Plan: Remove CocoaPods Support and Raise Minimum iOS to 15.0 + +**Branch**: `003-remove-cocoapods-ios15` | **Date**: 2026-10-12 | **Spec**: [spec.md](spec.md) + +**Input**: Feature specification from `/specs/003-remove-cocoapods-ios15/spec.md` + +## Summary + +Make the plugin's iOS integration SPM-only, so a later change (Ticket 2) can adopt OptimizelySwiftSDK 6.0.0, which has no CocoaPods distribution. The work: +- delete the iOS podspec and the Objective-C shim used only by CocoaPods +- raise the iOS minimum to 15.0 and the Flutter minimum to 3.44.0 +- de-CocoaPod the in-repo example app +- drop the now-pointless podspec/SPM version-parity CI job +- update docs and the constitution + +The `optimizely-flutter-testapp` repo gets the same iOS migration plus a Flutter 3.16 → 3.44 CI upgrade, and merges first. No Dart, Swift or Java logic changes. + +## Technical Context + +**Language/Version**: Dart (Flutter ≥3.44.0), Swift 5.0 (swift-tools-version 5.9), build config (SPM manifest, Xcode project, GitHub Actions YAML) + +**Primary Dependencies**: OptimizelySwiftSDK 5.4.2 (unchanged, via SPM), Flutter SPM plugin integration + +**Storage**: N/A + +**Testing**: `flutter test`, `flutter analyze`, example app iOS build, testapp integration suite (`integration_test/app_test.dart`) on iOS and Android + +**Target Platform**: iOS 15.0+ (raised from 10.0); Android unchanged (API 21+) + +**Project Type**: Flutter plugin (library) + external test app repo + +**Performance Goals**: N/A (no runtime change) + +**Constraints**: +- No public Dart API changes +- Native SDK version unchanged (constitution VIII.1) +- Ships only in 4.0.0 together with Ticket 2 + +**Scale/Scope**: About 15 files in the SDK repo and about 8 in the testapp repo. Two PRs, testapp first. + +## Constitution Check + +*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.* + +| Principle / Standard | Status | Notes | +|---|---|---| +| I. Bridge Pattern Integrity | ✅ N/A | No feature added; the bridge is untouched. | +| II. Response Object Pattern | ✅ N/A | No method changes. | +| III. Platform Parity | ✅ Pass | Only the iOS build mechanism changes; runtime behaviour is identical on both platforms. Android is untouched. | +| IV. Type Safety | ✅ N/A | | +| V. Thread Safety | ✅ Pass | `mainThreadResult` lives in the Swift plugin, which stays. Only the ObjC shim is removed. | +| VI. Multi-Instance | ✅ N/A | | +| VII. Version Synchronisation | ✅ Pass | No SDK version bump in this change; Ticket 3 bumps all three locations to 4.0.0. | +| VIII. Native SDK Version Pinning | ⚠️ Amend | The swift-sdk version stays at 5.4.2 (no upgrade bundled). The principle text names the podspec as a source of truth, so it's amended in this change (research R7). | +| Platform Compatibility: raising iOS minimum = major bump | ✅ Pass | Spec FR-012: released only as 4.0.0. | +| Testing: tests accompany code changes | ✅ Pass (justified) | No Dart/native logic changes, so there are no new unit tests. Verification is the existing `flutter test`, the example app build, and the testapp integration suite on both platforms (quickstart.md). | +| CI: all jobs pass | ✅ Pass | One job (`version_drift_check`) is removed because it has no remaining purpose. The five constitution-listed jobs remain. | +| Licensing | ✅ N/A | No new source files. | +| Branching | ✅ Pass | Feature branch; PR targets `master`. Suggested git branch: `feature/remove-cocoapods-ios15`. | + +**Post-design re-check**: unchanged. The Principle VIII amendment is part of the deliverable (FR-011), not a violation. + +## Project Structure + +### Documentation (this feature) + +```text +specs/003-remove-cocoapods-ios15/ +├── plan.md +├── research.md +├── data-model.md +├── quickstart.md +├── contracts/ +│ └── platform-requirements.md +└── tasks.md # /speckit-tasks +``` + +### Source Code + +```text +# optimizely-flutter-sdk (PR 2 — after testapp) +pubspec.yaml # flutter: ">=3.44.0" +ios/ +├── optimizely_flutter_sdk.podspec # DELETE +└── optimizely_flutter_sdk/ + ├── Package.swift # .iOS("15.0"); drop exclude + ObjC comment + └── Sources/optimizely_flutter_sdk/ + ├── OptimizelyFlutterSdkPlugin.m # DELETE + └── include/ # DELETE +example/ios/ +├── Podfile # DELETE +├── Flutter/Debug.xcconfig, Release.xcconfig # drop Pods #include +└── Runner.xcodeproj/project.pbxproj # pod deintegrate; target 15.0 +.github/ +├── scripts/check-version-drift.sh # DELETE +├── workflows/flutter.yml # drop version_drift_check job + needs +└── copilot-instructions.md # iOS 15.0+ +README.md, CLAUDE.md # SPM-only, iOS 15, Flutter 3.44, migration note +.specify/memory/constitution.md # v1.2.0 amendment + +# optimizely-flutter-testapp (PR 1 — merges first) +.github/workflows/ios.yml # Flutter 3.44.0 + enable SPM +.github/workflows/android.yml # Flutter 3.44.0 +ios/Podfile # DELETE +ios/Flutter/Debug.xcconfig, Release.xcconfig # drop Pods #include +ios/Runner.xcodeproj/project.pbxproj # pod deintegrate; SPM ref; target 15.0 +``` + +**Structure Decision**: Existing layout from spec 001 is kept; this change only deletes files and edits config. Not touched: macOS (`macos/`, `example/macos/Podfile`), Android, and `lib/`. + +## Delivery Order + +1. **Testapp PR**: works against current SDK `master` (SPM resolves its existing `Package.swift`), so it merges independently. +2. **SDK PR**: after the testapp merges, `integration_ios_tests` exercises the SPM-only SDK. +3. No release until Ticket 2 merges; Ticket 3 cuts 4.0.0. + +## Complexity Tracking + +No violations to justify. diff --git a/specs/003-remove-cocoapods-ios15/quickstart.md b/specs/003-remove-cocoapods-ios15/quickstart.md new file mode 100644 index 0000000..e8f6b7b --- /dev/null +++ b/specs/003-remove-cocoapods-ios15/quickstart.md @@ -0,0 +1,60 @@ +# Quickstart Validation: Remove CocoaPods Support and Raise Minimum iOS to 15.0 + +**Prerequisites**: Flutter ≥3.44 with SPM enabled (`flutter config --enable-swift-package-manager`), Xcode with an iOS 15+ simulator, Android emulator for testapp Android checks. + +## 1. Testapp (PR 1): validate against current SDK master + +```bash +cd optimizely-flutter-testapp +# place SDK checkout at ./home/runner/optimizely_flutter_sdk (master) +flutter pub get +flutter analyze +flutter build ios --simulator --target=integration_test/app_test.dart +flutter test integration_test/app_test.dart -d +flutter test integration_test/app_test.dart -d +``` + +**Expected**: build succeeds with no `Pods/` generated; all integration tests pass on both platforms. `git grep -i "pod"` in `ios/` shows only Flutter-generated ignores. + +## 2. SDK (PR 2): unit and static checks + +```bash +cd optimizely-flutter-sdk +flutter pub get +flutter analyze # zero issues +flutter test # zero failures +``` + +## 3. SDK: example app builds SPM-only at iOS 15 + +```bash +cd example +flutter build ios --simulator +``` + +**Expected**: build succeeds; no `ios/Pods`, no `ios/Podfile`; the build log shows the swift-sdk package resolved. + +## 4. Testapp against the SDK feature branch + +Repeat step 1 with the SDK checkout on the feature branch. **Expected**: same results (spec US1-AS3, US3-AS2). + +## 5. Negative checks (spec US2) + +| Check | How | Expected | +|---|---|---| +| iOS target too low | In `example`, set the Runner target to 14.0 and run `flutter build ios --simulator` | Flutter bumps the target to 15.0 ("Updating minimum iOS deployment target to 15.0") and the build succeeds | +| Flutter too old | Use Flutter <3.44 (e.g., via fvm), run `flutter pub get` in `example` | Resolution fails, naming the Flutter constraint | + +Revert the target change afterwards. + +## 6. Leftover references (spec SC-002) + +```bash +git grep -n -i -E "cocoapod|podspec|Podfile|iOS 10|ios, '10" -- ':!CHANGELOG.md' ':!specs/' ':!macos/' ':!example/macos/' +``` + +**Expected**: no hits in either repo. + +## 7. CI + +Push both branches. **Expected**: testapp `iOS` and `Android` workflows are green. All SDK jobs are green, with no `version_drift_check` job listed. diff --git a/specs/003-remove-cocoapods-ios15/research.md b/specs/003-remove-cocoapods-ios15/research.md new file mode 100644 index 0000000..e2f8bad --- /dev/null +++ b/specs/003-remove-cocoapods-ios15/research.md @@ -0,0 +1,62 @@ +# Research: Remove CocoaPods Support and Raise Minimum iOS to 15.0 + +**Feature**: 003-remove-cocoapods-ios15 | **Date**: 2026-10-12 + +## R1. Minimum Flutter version + +- **Decision**: `pubspec.yaml` → `environment.flutter: ">=3.44.0"`. Leave the Dart constraint (`>=2.16.2 <4.0.0`) unchanged. +- **Rationale**: Flutter 3.44 resolves SPM plugins by default (README line 24 already says so), and SDK CI already pins `flutter-version: '3.44.0'` in all three jobs. Without a podspec, older Flutter versions (or SPM disabled) cannot integrate the plugin on iOS, so the constraint makes `flutter pub get` fail early with a clear message (spec US2-AS2). The Flutter constraint already implies a Dart 3 SDK, so changing the Dart constraint adds diff with no effect. +- **Alternatives considered**: Leave `>=2.5.0` and rely on the iOS build failing. Rejected because the failure is obscure (spec US2). Pinning a newer Flutter (e.g., the local 3.47.1) was also rejected: 3.44 is the lowest version that works and the one CI already proves. + +## R2. Objective-C registration shim + +- **Decision**: Delete `ios/optimizely_flutter_sdk/Sources/optimizely_flutter_sdk/OptimizelyFlutterSdkPlugin.m` and `.../include/` (the header). Remove the `exclude:` argument and the ObjC comment from `Package.swift`. +- **Rationale**: `Package.swift` already excludes these files, and its comment says they "remain on disk for CocoaPods builds". The iOS `pluginClass` in `pubspec.yaml` is already `SwiftOptimizelyFlutterSdkPlugin`, so registration doesn't change. macOS has its own `pluginClass` (`OptimizelyFlutterSdkPlugin`) backed by `macos/Classes/`, which is a separate directory, so macOS is unaffected. +- **Alternatives considered**: Keep the files but leave them unused. Rejected as dead code that FR-002 explicitly removes. + +## R3. Behaviour when the app targets iOS < 15.0 + +- **Decision**: No custom code. Flutter reads `Package.swift` `platforms: [.iOS("15.0")]` and raises the app's deployment target to 15.0 automatically ("Updating minimum iOS deployment target to 15.0"). Verified 2026-10-12: an example app at 14.0 was bumped and built successfully. +- **Rationale**: Flutter's SPM integration migrates the app's deployment target up to the highest plugin minimum, so developers get a working build plus a printed notice instead of an error. That satisfies spec US2-AS1 at no cost. +- **Alternatives considered**: A build-phase script that checks the deployment target. Rejected because it duplicates what SPM already does. + +## R4. In-repo example app (`example/ios`) + +- **Decision**: + 1. Run `pod deintegrate` in `example/ios`. This strips `Pods_Runner.framework`, the `Pods` group, the `Pods-Runner.*.xcconfig` refs and the `[CP]` build phases from `project.pbxproj`. + 2. Delete `example/ios/Podfile`. `Podfile.lock` and `Pods/` are untracked. + 3. Remove the `#include? "Pods/..."` line from `Flutter/Debug.xcconfig` and `Flutter/Release.xcconfig`. + 4. Change `IPHONEOS_DEPLOYMENT_TARGET` from 10.0 to 15.0 (3 occurrences in `project.pbxproj`). + 5. Run `flutter build ios --simulator` with SPM enabled to confirm. The SPM package reference (`FlutterGeneratedPluginSwiftPackage`) is already in the pbxproj. +- **Rationale**: The example's only plugin dependency is this SDK, so no CocoaPods plugin is left to need a Podfile. `pod deintegrate` is the supported way to clean the pbxproj; editing it by hand is error-prone. +- **Out of scope**: `example/macos/Podfile` stays (macOS is out of scope). + +## R5. Test app (`optimizely-flutter-testapp`) + +- **Finding**: Both `.github/workflows/ios.yml:39` and `android.yml:57` pin `flutter-version: '3.16.0'`. With R1 the SDK requires Flutter ≥3.44, so **both** testapp workflows fail `flutter pub get` unless upgraded. The iOS project has no SPM integration (no `FlutterGeneratedPluginSwiftPackage` in the pbxproj) and still uses CocoaPods with `platform :ios, '13.0'`. +- **Decision**: + 1. In `ios.yml` and `android.yml`, change `flutter-version` from `3.16.0` to `3.44.0` (matching SDK CI). In `ios.yml`, add a `flutter config --enable-swift-package-manager` step before `flutter pub get`, mirroring SDK CI. + 2. Run `pod deintegrate` in `ios/`. Delete `ios/Podfile`. Remove the `#include? "Pods/..."` lines from `ios/Flutter/{Debug,Release}.xcconfig`. + 3. Change `IPHONEOS_DEPLOYMENT_TARGET` from 10.0 to 15.0 (3 occurrences). + 4. Run `flutter build ios --simulator` locally with SPM enabled. Flutter's auto-migration adds the `FlutterGeneratedPluginSwiftPackage` reference to the pbxproj; commit that. + 5. Verify the Android build locally on Flutter 3.44. The testapp is already on AGP 8.7.0, Gradle 8.10.2 and Kotlin 2.1.0, which are expected to be compatible. The CI run confirms it. +- **Rationale**: The testapp has to work against both SDK `master` (podspec still present, iOS 10) and the SDK feature branch. An iOS-15, SPM-only testapp works with both, because SPM resolves the SDK's existing `Package.swift`. So the testapp PR can merge first with no flag-day coordination. +- **Risk**: The jump from Flutter 3.16 to 3.44 may surface Dart analyzer or lint changes in the testapp's `integration_test/`. Mitigation: run `flutter analyze` and the integration suite locally before opening the PR. +- **Noted, out of scope**: `ios.yml` uses `inputs.tags` but the declared input is `release_version`. That's an existing bug in the release-install step and is left untouched. + +## R6. SDK CI + +- **Decision**: Delete `.github/scripts/check-version-drift.sh`. Remove the `version_drift_check` job (`flutter.yml:20-25`) and both `needs: version_drift_check` lines (`flutter.yml:29`, `:155`). +- **Rationale**: Only one manifest remains, so the parity check has nothing to compare (FR-007). +- **Alternatives considered**: Repurpose the script to check something else. Rejected as YAGNI. +- **Noted**: `build_test_ios` only runs `flutter pub get`/`flutter test`. It doesn't actually build iOS. The real iOS build is covered by `integration_ios_tests` (the testapp). Adding an iOS build step is out of scope. + +## R7. Documentation and constitution + +- **Decision**: + - `README.md:24`: say iOS 15.0 minimum, Flutter 3.44+, SPM only, and add a short "Migrating from CocoaPods" note (enable SPM, raise the deployment target, run `pod deintegrate` if no other pods remain). + - `CLAUDE.md`: in lines 39, 80-85 (iOS native version pair → single file), 193 (iOS 15.0), 195 and 214, remove the podspec references and the drift-check sentence. + - `.github/copilot-instructions.md:13`: change iOS 10.0+ to 15.0+. + - `.specify/memory/constitution.md`: in Principle VIII, keep only `Package.swift` as the iOS source of truth. In the Minimum Platform Versions table, set iOS to 15.0 and Flutter to >=3.44.0. Bump the constitution version **1.1.1 → 1.2.0** (MINOR: materially changed guidance, no principle removed), update the Sync Impact Report and Last Amended date, and use the commit message `docs: amend constitution to v1.2.0 (SPM-only iOS, iOS 15 minimum)`. +- **Rationale**: FR-010 and FR-011, plus the constitution's amendment procedure. +- **Not touched**: CHANGELOG (Ticket 3 writes the 4.0.0 entry), prior specs 001/002 (historical). diff --git a/specs/003-remove-cocoapods-ios15/spec.md b/specs/003-remove-cocoapods-ios15/spec.md index 69fd3dd..b2cf15f 100644 --- a/specs/003-remove-cocoapods-ios15/spec.md +++ b/specs/003-remove-cocoapods-ios15/spec.md @@ -32,11 +32,11 @@ A developer whose app targets iOS below 15.0, or whose Flutter version cannot co **Why this priority**: This is a breaking change. Constitution (Platform Compatibility Standards) requires raised minimums to ship as a major version, and consumers need a clear upgrade path. -**Independent Test**: Attempt to depend on the SDK from (a) an app with iOS target 14.0 and (b) a project on a Flutter version below the new minimum; verify each fails with a message that identifies the unmet requirement, and that the README migration note explains the fix. +**Independent Test**: Attempt to depend on the SDK from (a) an app with iOS target 14.0 and (b) a project on a Flutter version below the new minimum; verify (a) Flutter raises the app's deployment target to 15.0 and reports it, and (b) dependency resolution fails naming the Flutter constraint, and that the README migration note explains the fix. **Acceptance Scenarios**: -1. **Given** an app with iOS deployment target below 15.0, **When** it builds with the SDK, **Then** the build fails with an error identifying the iOS 15.0 minimum. +1. **Given** an app with iOS deployment target below 15.0, **When** it builds with the SDK, **Then** Flutter raises the app's iOS deployment target to 15.0, prints "Updating minimum iOS deployment target to 15.0", and the build succeeds (verified 2026-10-12). 2. **Given** a project on a Flutter version below the declared minimum, **When** the developer runs `flutter pub get`, **Then** dependency resolution fails, naming the Flutter version constraint. 3. **Given** the README and CHANGELOG, **When** a developer reads the upgrade section, **Then** it lists the new iOS and Flutter minimums and the steps to migrate an app from CocoaPods to SPM. diff --git a/specs/003-remove-cocoapods-ios15/tasks.md b/specs/003-remove-cocoapods-ios15/tasks.md new file mode 100644 index 0000000..90af296 --- /dev/null +++ b/specs/003-remove-cocoapods-ios15/tasks.md @@ -0,0 +1,150 @@ +--- + +description: "Task list for removing CocoaPods support and raising minimum iOS to 15.0" +--- + +# Tasks: Remove CocoaPods Support and Raise Minimum iOS to 15.0 + +**Input**: Design documents from `/specs/003-remove-cocoapods-ios15/` + +**Prerequisites**: plan.md, spec.md, research.md, data-model.md, contracts/platform-requirements.md, quickstart.md + +**Tests**: No new test code. The spec has no logic changes; verification is the existing `flutter test`/`flutter analyze`, the example app build, and the testapp integration suite (quickstart.md). + +**Organization**: Grouped by user story. **Phase order differs from priority order**: US3 (testapp, P2) runs first, because SDK CI builds testapp `master` (plan.md → Delivery Order). + +**Repos**: Paths prefixed `optimizely-flutter-testapp/` are in the sibling testapp repo (`/Users/muzahidul.islam/workspace/optimizely-flutter-testapp`). All other paths are relative to the SDK repo root (`/Users/muzahidul.islam/workspace/optimizely-flutter-sdk`). + +## Format: `[ID] [P?] [Story] Description` + +- **[P]**: Can run in parallel (different files, no dependencies) +- **[Story]**: Which user story this task belongs to (US1–US4) + +--- + +## Phase 1: Setup + +**Purpose**: Branches and local toolchain. + +- [X] T001 In the SDK repo, create branch `feature/remove-cocoapods-ios15` from an up-to-date `master` (`git checkout master && git pull && git checkout -b feature/remove-cocoapods-ios15`) +- [X] T002 [P] In the testapp repo, create branch `feature/spm-ios15` from an up-to-date `master`. The local checkout is currently on `ci/ios-single-entrypoint`, so switch first: `git checkout master && git pull && git checkout -b feature/spm-ios15` +- [X] T003 [P] Verify local Flutter is ≥3.44 (`flutter --version`) and SPM is enabled (`flutter config --enable-swift-package-manager`) + +--- + +## Phase 2: User Story 3 — Test App Validates the SPM-Only SDK (Priority: P2, runs first) → testapp PR + +**Goal**: The testapp builds iOS via SPM at iOS 15.0, with CI on Flutter 3.44, and passes against current SDK `master`. + +**Independent Test**: quickstart.md §1. iOS build with no `Pods/`; integration suite green on iOS and Android. + +- [X] T004 [US3] In `optimizely-flutter-testapp/.github/workflows/ios.yml`, change `flutter-version: '3.16.0'` to `'3.44.0'`. Add a step named `Enable Swift Package Manager` that runs `flutter config --enable-swift-package-manager`, immediately before the `Install Flutter dependencies` step. Do NOT touch the existing `inputs.tags` bug (research R5). +- [X] T005 [P] [US3] In `optimizely-flutter-testapp/.github/workflows/android.yml`, change `flutter-version: '3.16.0'` to `'3.44.0'` +- [X] T006 [US3] In `optimizely-flutter-testapp/ios/`, run `pod deintegrate`. This removes `Pods_Runner.framework`, the `Pods` group, `Pods-Runner.*.xcconfig` refs and the `[CP]` phases from `ios/Runner.xcodeproj/project.pbxproj`. Then delete `optimizely-flutter-testapp/ios/Podfile` (and local untracked `Podfile.lock`/`Pods/` if present). +- [X] T007 [P] [US3] Remove the line `#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"` from `optimizely-flutter-testapp/ios/Flutter/Debug.xcconfig`, and the matching `.release.xcconfig` line from `optimizely-flutter-testapp/ios/Flutter/Release.xcconfig`. Keep `#include "Generated.xcconfig"`. +- [X] T008 [US3] In `optimizely-flutter-testapp/ios/Runner.xcodeproj/project.pbxproj`, change all 3 occurrences of `IPHONEOS_DEPLOYMENT_TARGET = 10.0;` to `IPHONEOS_DEPLOYMENT_TARGET = 15.0;` (after T006, same file) +- [X] T009 [US3] Clone SDK `master` into `optimizely-flutter-testapp/home/runner/optimizely_flutter_sdk` (the path dependency in `pubspec.yaml`; **do not commit it**, since `home/` is not gitignored). Then run `flutter pub get` and `flutter build ios --simulator --target=integration_test/app_test.dart`. Flutter auto-migrates `ios/Runner.xcodeproj/project.pbxproj` to add the `FlutterGeneratedPluginSwiftPackage` reference; keep those changes. Confirm no `ios/Pods/` was created. +- [X] T010 [US3] Run `flutter analyze` in `optimizely-flutter-testapp/`. Fix only the errors and warnings introduced by the Flutter 3.16 → 3.44 jump, in `optimizely-flutter-testapp/lib/` and `optimizely-flutter-testapp/integration_test/`, with minimal edits and no refactors. +- [X] T011 [US3] Verify the Android build on Flutter 3.44: `flutter build apk --debug` in `optimizely-flutter-testapp/`. If AGP 8.7.0 / Gradle 8.10.2 / Kotlin 2.1.0 are rejected, bump only what the error names, in `optimizely-flutter-testapp/android/settings.gradle` / `android/gradle/wrapper/gradle-wrapper.properties`. +- [X] T012 [US3] Run `flutter test integration_test/app_test.dart -d ` on an iOS 15+ simulator and an Android emulator from `optimizely-flutter-testapp/`. All tests pass. +- [X] T013 [US3] Commit in the testapp (`ci: migrate iOS to SPM, require iOS 15 and Flutter 3.44`, ending with the `Co-Authored-By` line), push `feature/spm-ios15`, and open a PR to `master` referencing the Ticket 1 Jira key. Confirm the testapp `iOS` and `Android` workflows are green. **Merge before T030.** + +**Checkpoint**: the testapp on `master` is SPM-only and still green against SDK `master`. + +--- + +## Phase 3: User Story 1 — SDK Consumers Build iOS via SPM Only (Priority: P1) 🎯 MVP + +**Goal**: The plugin ships SPM-only for iOS at 15.0 minimum. All behaviour is unchanged. + +**Independent Test**: quickstart.md §2–3. `flutter analyze` is clean, `flutter test` is green, and `example` builds for the iOS simulator with no Podfile/Pods. + +- [X] T014 [P] [US1] Delete `ios/optimizely_flutter_sdk.podspec` (`git rm`) +- [X] T015 [P] [US1] Delete `ios/optimizely_flutter_sdk/Sources/optimizely_flutter_sdk/OptimizelyFlutterSdkPlugin.m` and the directory `ios/optimizely_flutter_sdk/Sources/optimizely_flutter_sdk/include/` (`git rm -r`). Do NOT touch `macos/Classes/` (research R2). +- [X] T016 [US1] Edit `ios/optimizely_flutter_sdk/Package.swift` (after T015): + - remove comment lines 2–4 (the ObjC/CocoaPods note), leaving `// swift-tools-version: 5.9` on line 1 + - change `platforms: [.iOS("10.0")]` to `platforms: [.iOS("15.0")]` + - remove the `exclude: ["OptimizelyFlutterSdkPlugin.m", "include"]` argument, together with the trailing comma after the target's `dependencies: [...]` array + - keep `exact: "5.4.2"` unchanged (FR-005) +- [X] T017 [US1] In `example/ios/`, run `pod deintegrate`, then `git rm example/ios/Podfile` (and delete untracked `Podfile.lock`/`Pods/`). Leave `example/macos/Podfile` alone. +- [X] T018 [P] [US1] Remove the `#include? "Pods/..."` first line from `example/ios/Flutter/Debug.xcconfig` and `example/ios/Flutter/Release.xcconfig`. Keep `#include "Generated.xcconfig"`. +- [X] T019 [US1] In `example/ios/Runner.xcodeproj/project.pbxproj`, change all 3 occurrences of `IPHONEOS_DEPLOYMENT_TARGET = 10.0;` to `15.0;` (after T017, same file) +- [X] T020 [US1] Validate: from the repo root run `flutter pub get && flutter analyze && flutter test` (zero issues, zero failures). Then `cd example && flutter build ios --simulator`: it succeeds, swift-sdk 5.4.2 resolves via SPM, and no `example/ios/Pods/` exists. + +**Checkpoint**: the SDK is SPM-only and the example builds. US1 is done. + +--- + +## Phase 4: User Story 2 — Developers Below the New Minimums Get a Clear Signal (Priority: P1) + +**Goal**: Old Flutter and low iOS targets fail early with clear messages, and the README explains the migration. + +**Independent Test**: quickstart.md §5, plus a README review against contracts/platform-requirements.md. + +- [X] T021 [US2] In `pubspec.yaml`, change `environment.flutter: ">=2.5.0"` to `">=3.44.0"`. Leave the Dart `sdk:` constraint unchanged (research R1). Run `flutter pub get`. +- [X] T022 [US2] In `README.md`, replace line 24 with: iOS minimum 15.0; Flutter 3.44+ required; iOS integration is via Swift Package Manager only (CocoaPods no longer supported). Add a short "Migrating from CocoaPods" subsection right after it, with the 4 steps from `specs/003-remove-cocoapods-ios15/contracts/platform-requirements.md` → "Migration steps for app developers". Match the README's existing heading level and tone. +- [X] T023 [US2] Negative check from quickstart.md §5: set `IPHONEOS_DEPLOYMENT_TARGET` to `14.0` in `example/ios/Runner.xcodeproj/project.pbxproj`, run `flutter build ios --simulator` in `example/`, and confirm the error names the iOS 15.0 minimum. Then revert the file (`git checkout -- example/ios/Runner.xcodeproj/project.pbxproj` only if T019 is already committed; otherwise set it back to 15.0). Run the Flutter-too-old check only if `fvm` is available; otherwise note it as skipped. + +**Checkpoint**: US1 and US2 are complete. This is consumer-facing done. + +--- + +## Phase 5: User Story 4 — Maintainers Have One iOS Dependency Path (Priority: P3) + +**Goal**: No parity CI job, and docs and the constitution describe SPM only and iOS 15. + +**Independent Test**: quickstart.md §6 (leftover grep returns nothing); the CI job list has no `version_drift_check`. + +- [X] T024 [P] [US4] Delete `.github/scripts/check-version-drift.sh` (`git rm`) +- [X] T025 [US4] In `.github/workflows/flutter.yml`, delete the `version_drift_check:` job (lines 20–25: job key, `runs-on`, `steps`, checkout, and the "Check OptimizelySwiftSDK version parity" step) and both `needs: version_drift_check` lines (currently lines 29 and 155). Keep YAML indentation valid. +- [X] T026 [P] [US4] Edit `CLAUDE.md`: + - line 39: change `check podspec/build.gradle` to `check Package.swift/build.gradle` + - lines 82–86: replace the "Two locations must stay synchronized (native iOS SDK version)" block and the following CI sentence with a single line saying the native iOS SDK version lives in `ios/optimizely_flutter_sdk/Package.swift` → `.package(url: "...swift-sdk.git", exact: "A.B.C")` + - Platform Requirements → Flutter: change `>=2.5.0` to `>=3.44.0` + - line 193: `iOS 10.0` → `iOS 15.0` + - line 195: drop `ios/optimizely_flutter_sdk.podspec and ` + - line 214: delete the `ios/optimizely_flutter_sdk.podspec - CocoaPods dependencies` bullet +- [X] T027 [P] [US4] In `.github/copilot-instructions.md` line 13, change `iOS 10.0+` to `iOS 15.0+` +- [X] T028 [US4] Amend `.specify/memory/constitution.md` per research R7: + - Principle VIII iOS bullet: only `ios/optimizely_flutter_sdk/Package.swift` (SPM); remove the podspec mention and "Both files MUST declare the same version." + - Minimum Platform Versions table: iOS `15.0`, Flutter `>=3.44.0` + - Replace the Sync Impact Report comment with Version change 1.1.1 → 1.2.0, modified Principle VIII and Platform Compatibility table + - footer: `**Version**: 1.2.0 | **Ratified**: 2022-06-07 | **Last Amended**: 2026-10-12` + - commit this file alone: `docs: amend constitution to v1.2.0 (SPM-only iOS, iOS 15 minimum)` + +**Checkpoint**: all user stories are complete. + +--- + +## Phase 6: Polish & Delivery + +- [X] T029 Run the leftover-reference grep from quickstart.md §6 in both repos. Fix any hit outside `CHANGELOG.md`, `specs/`, `macos/` and `example/macos/`. +- [ ] T030 Commit the remaining SDK changes as `chore!: remove CocoaPods support, require iOS 15 and Flutter 3.44`, with a `BREAKING CHANGE:` footer naming the new minimums and ending with the `Co-Authored-By` line. Push `feature/remove-cocoapods-ios15` and open a PR to `master` referencing the Ticket 1 Jira key (use the `pr` skill for the body). Confirm all SDK CI jobs, including `integration_ios_tests`, are green. Requires T013 to be merged. **Do not cut a release** (FR-012: 4.0.0 ships with Ticket 2, via Ticket 3). + +--- + +## Dependencies & Execution Order + +- **Setup (T001–T003)** → everything. +- **US3 (T004–T013)**: independent of SDK changes; must **merge** before T030. +- **US1 (T014–T020)**: after T001. T016 needs T015; T019 needs T017; T020 needs T014–T019. +- **US2 (T021–T023)**: T021/T022 are independent of US1; T023 needs T019. +- **US4 (T024–T028)**: independent of US1/US2; T025 should follow T024. +- **Polish (T029–T030)**: after all stories; T030 needs T013 merged. + +Phases 3–5 all live in one SDK PR, so splitting by story is for ordering only, not separate PRs. + +## Parallel Opportunities + +- T002 ∥ T003 +- US3: T005 ∥ T007 ∥ (T006 → T008) +- US1: T014 ∥ T015 ∥ T018, then T016, T017 → T019 +- US2/US4 file edits (T021, T022, T024, T026, T027) can all run alongside US1, since they touch different files +- SDK work (Phases 3–5) can proceed in parallel with the testapp PR review; only T030 waits on T013 + +## Implementation Strategy + +1. **Testapp first** (Phase 2). Merge it once green; it's safe against current SDK `master`. +2. **MVP = US1** (Phase 3): SPM-only plugin and example build. +3. Add **US2** (Flutter constraint + README) and **US4** (CI/docs/constitution) on the same branch. +4. Run Polish, then open the SDK PR. Hold the release for Ticket 2 + Ticket 3. From d86e3b5029a99fc1c497368c99a77c1767bb69f2 Mon Sep 17 00:00:00 2001 From: Muzahidul Islam Date: Mon, 12 Oct 2026 22:10:07 +0600 Subject: [PATCH 4/9] chore: keep only spec.md for 003-remove-cocoapods-ios15 in git Co-Authored-By: Claude Opus 5.5 --- .../checklists/requirements.md | 35 ---- .../contracts/platform-requirements.md | 18 --- .../003-remove-cocoapods-ios15/data-model.md | 18 --- specs/003-remove-cocoapods-ios15/plan.md | 119 -------------- .../003-remove-cocoapods-ios15/quickstart.md | 60 ------- specs/003-remove-cocoapods-ios15/research.md | 62 -------- specs/003-remove-cocoapods-ios15/tasks.md | 150 ------------------ 7 files changed, 462 deletions(-) delete mode 100644 specs/003-remove-cocoapods-ios15/checklists/requirements.md delete mode 100644 specs/003-remove-cocoapods-ios15/contracts/platform-requirements.md delete mode 100644 specs/003-remove-cocoapods-ios15/data-model.md delete mode 100644 specs/003-remove-cocoapods-ios15/plan.md delete mode 100644 specs/003-remove-cocoapods-ios15/quickstart.md delete mode 100644 specs/003-remove-cocoapods-ios15/research.md delete mode 100644 specs/003-remove-cocoapods-ios15/tasks.md diff --git a/specs/003-remove-cocoapods-ios15/checklists/requirements.md b/specs/003-remove-cocoapods-ios15/checklists/requirements.md deleted file mode 100644 index 97a2c97..0000000 --- a/specs/003-remove-cocoapods-ios15/checklists/requirements.md +++ /dev/null @@ -1,35 +0,0 @@ -# Specification Quality Checklist: Remove CocoaPods Support and Raise Minimum iOS to 15.0 - -**Purpose**: Validate specification completeness and quality before proceeding to planning -**Created**: 2026-10-12 -**Feature**: [spec.md](../spec.md) - -## Content Quality - -- [x] No implementation details (languages, frameworks, APIs) -- [x] Focused on user value and business needs -- [x] Written for non-technical stakeholders -- [x] All mandatory sections completed - -## Requirement Completeness - -- [x] No [NEEDS CLARIFICATION] markers remain -- [x] Requirements are testable and unambiguous -- [x] Success criteria are measurable -- [x] Success criteria are technology-agnostic (no implementation details) -- [x] All acceptance scenarios are defined -- [x] Edge cases are identified -- [x] Scope is clearly bounded -- [x] Dependencies and assumptions identified - -## Feature Readiness - -- [x] All functional requirements have clear acceptance criteria -- [x] User scenarios cover primary flows -- [x] Feature meets measurable outcomes defined in Success Criteria -- [x] No implementation details leak into specification - -## Notes - -- This feature is about the dependency mechanism itself, so naming SPM, CocoaPods, iOS, and Flutter versions is the subject of the spec, not leaked implementation. File-level details (which files to edit) are left to `/speckit-plan`. -- No clarification markers were needed: the Flutter minimum (3.44) follows the README and CI, macOS is out of scope per the constitution, and the release and Xcode 27 work are assigned to Tickets 3 and 2. diff --git a/specs/003-remove-cocoapods-ios15/contracts/platform-requirements.md b/specs/003-remove-cocoapods-ios15/contracts/platform-requirements.md deleted file mode 100644 index 7dec0e3..0000000 --- a/specs/003-remove-cocoapods-ios15/contracts/platform-requirements.md +++ /dev/null @@ -1,18 +0,0 @@ -# Contract: Consumer Platform Requirements - -The SDK's public Dart API is **unchanged**. The contract that changes is what an app must provide to integrate the plugin on iOS. - -| Requirement | Before (3.x) | After (4.0.0) | Enforced by | Failure signal | -|---|---|---|---|---| -| Flutter SDK | ≥2.5.0 | ≥3.44.0 | `pubspec.yaml` `environment.flutter` | `flutter pub get` fails, naming the Flutter constraint | -| iOS deployment target | ≥10.0 | ≥15.0 | `Package.swift` `platforms` | Flutter raises the app target to 15.0 automatically and prints "Updating minimum iOS deployment target to 15.0" | -| iOS dependency manager | CocoaPods or SPM | SPM only | No podspec shipped | Flutter reports that the plugin has no CocoaPods support when SPM is disabled | -| Android | API 21+ | API 21+ (unchanged) | — | — | -| Dart API surface | — | Unchanged | — | — | - -## Migration steps for app developers (source for README note) - -1. Use Flutter 3.44 or later (SPM is on by default; otherwise run `flutter config --enable-swift-package-manager`). -2. Set the iOS deployment target to 15.0 or higher (Xcode Runner target, plus `platform :ios` in the Podfile if one remains). -3. Run `flutter pub get` and `flutter build ios`. Flutter migrates the Xcode project to SPM automatically. -4. If no other CocoaPods plugins remain, you may run `pod deintegrate` and delete the Podfile. diff --git a/specs/003-remove-cocoapods-ios15/data-model.md b/specs/003-remove-cocoapods-ios15/data-model.md deleted file mode 100644 index 92e62ff..0000000 --- a/specs/003-remove-cocoapods-ios15/data-model.md +++ /dev/null @@ -1,18 +0,0 @@ -# Data Model: Remove CocoaPods Support and Raise Minimum iOS to 15.0 - -No runtime data, persistence or Dart types change. The "entities" are build-time declarations. - -| Entity | Location | Before | After | -|---|---|---|---| -| iOS SPM manifest | `ios/optimizely_flutter_sdk/Package.swift` | `.iOS("10.0")`; excludes ObjC files | `.iOS("15.0")`; no `exclude` | -| iOS CocoaPods spec | `ios/optimizely_flutter_sdk.podspec` | `OptimizelySwiftSDK 5.4.2`, `:ios, '10.0'` | Deleted | -| ObjC registration shim | `ios/.../OptimizelyFlutterSdkPlugin.m`, `include/` | Used by CocoaPods only | Deleted | -| Plugin registration | `pubspec.yaml` `flutter.plugin.platforms.ios.pluginClass` | `SwiftOptimizelyFlutterSdkPlugin` | Unchanged | -| Flutter constraint | `pubspec.yaml` `environment.flutter` | `>=2.5.0` | `>=3.44.0` | -| Native SDK pin | `Package.swift` `exact:` | `5.4.2` | `5.4.2` (Ticket 2 moves to 6.0.0) | -| Version-parity gate | `.github/scripts/check-version-drift.sh` + `flutter.yml` job | Compares podspec vs SPM | Deleted | -| Example app iOS project | `example/ios` | CocoaPods + SPM ref, target 10.0 | SPM only, target 15.0 | -| Testapp iOS project | testapp `ios/` | CocoaPods only, Podfile 13.0, target 10.0 | SPM only, target 15.0 | -| Testapp CI Flutter | testapp `ios.yml`, `android.yml` | `3.16.0` | `3.44.0` | - -**Invariant after change**: exactly one place declares the iOS native dependency (`Package.swift`). diff --git a/specs/003-remove-cocoapods-ios15/plan.md b/specs/003-remove-cocoapods-ios15/plan.md deleted file mode 100644 index 072e641..0000000 --- a/specs/003-remove-cocoapods-ios15/plan.md +++ /dev/null @@ -1,119 +0,0 @@ -# Implementation Plan: Remove CocoaPods Support and Raise Minimum iOS to 15.0 - -**Branch**: `003-remove-cocoapods-ios15` | **Date**: 2026-10-12 | **Spec**: [spec.md](spec.md) - -**Input**: Feature specification from `/specs/003-remove-cocoapods-ios15/spec.md` - -## Summary - -Make the plugin's iOS integration SPM-only, so a later change (Ticket 2) can adopt OptimizelySwiftSDK 6.0.0, which has no CocoaPods distribution. The work: -- delete the iOS podspec and the Objective-C shim used only by CocoaPods -- raise the iOS minimum to 15.0 and the Flutter minimum to 3.44.0 -- de-CocoaPod the in-repo example app -- drop the now-pointless podspec/SPM version-parity CI job -- update docs and the constitution - -The `optimizely-flutter-testapp` repo gets the same iOS migration plus a Flutter 3.16 → 3.44 CI upgrade, and merges first. No Dart, Swift or Java logic changes. - -## Technical Context - -**Language/Version**: Dart (Flutter ≥3.44.0), Swift 5.0 (swift-tools-version 5.9), build config (SPM manifest, Xcode project, GitHub Actions YAML) - -**Primary Dependencies**: OptimizelySwiftSDK 5.4.2 (unchanged, via SPM), Flutter SPM plugin integration - -**Storage**: N/A - -**Testing**: `flutter test`, `flutter analyze`, example app iOS build, testapp integration suite (`integration_test/app_test.dart`) on iOS and Android - -**Target Platform**: iOS 15.0+ (raised from 10.0); Android unchanged (API 21+) - -**Project Type**: Flutter plugin (library) + external test app repo - -**Performance Goals**: N/A (no runtime change) - -**Constraints**: -- No public Dart API changes -- Native SDK version unchanged (constitution VIII.1) -- Ships only in 4.0.0 together with Ticket 2 - -**Scale/Scope**: About 15 files in the SDK repo and about 8 in the testapp repo. Two PRs, testapp first. - -## Constitution Check - -*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.* - -| Principle / Standard | Status | Notes | -|---|---|---| -| I. Bridge Pattern Integrity | ✅ N/A | No feature added; the bridge is untouched. | -| II. Response Object Pattern | ✅ N/A | No method changes. | -| III. Platform Parity | ✅ Pass | Only the iOS build mechanism changes; runtime behaviour is identical on both platforms. Android is untouched. | -| IV. Type Safety | ✅ N/A | | -| V. Thread Safety | ✅ Pass | `mainThreadResult` lives in the Swift plugin, which stays. Only the ObjC shim is removed. | -| VI. Multi-Instance | ✅ N/A | | -| VII. Version Synchronisation | ✅ Pass | No SDK version bump in this change; Ticket 3 bumps all three locations to 4.0.0. | -| VIII. Native SDK Version Pinning | ⚠️ Amend | The swift-sdk version stays at 5.4.2 (no upgrade bundled). The principle text names the podspec as a source of truth, so it's amended in this change (research R7). | -| Platform Compatibility: raising iOS minimum = major bump | ✅ Pass | Spec FR-012: released only as 4.0.0. | -| Testing: tests accompany code changes | ✅ Pass (justified) | No Dart/native logic changes, so there are no new unit tests. Verification is the existing `flutter test`, the example app build, and the testapp integration suite on both platforms (quickstart.md). | -| CI: all jobs pass | ✅ Pass | One job (`version_drift_check`) is removed because it has no remaining purpose. The five constitution-listed jobs remain. | -| Licensing | ✅ N/A | No new source files. | -| Branching | ✅ Pass | Feature branch; PR targets `master`. Suggested git branch: `feature/remove-cocoapods-ios15`. | - -**Post-design re-check**: unchanged. The Principle VIII amendment is part of the deliverable (FR-011), not a violation. - -## Project Structure - -### Documentation (this feature) - -```text -specs/003-remove-cocoapods-ios15/ -├── plan.md -├── research.md -├── data-model.md -├── quickstart.md -├── contracts/ -│ └── platform-requirements.md -└── tasks.md # /speckit-tasks -``` - -### Source Code - -```text -# optimizely-flutter-sdk (PR 2 — after testapp) -pubspec.yaml # flutter: ">=3.44.0" -ios/ -├── optimizely_flutter_sdk.podspec # DELETE -└── optimizely_flutter_sdk/ - ├── Package.swift # .iOS("15.0"); drop exclude + ObjC comment - └── Sources/optimizely_flutter_sdk/ - ├── OptimizelyFlutterSdkPlugin.m # DELETE - └── include/ # DELETE -example/ios/ -├── Podfile # DELETE -├── Flutter/Debug.xcconfig, Release.xcconfig # drop Pods #include -└── Runner.xcodeproj/project.pbxproj # pod deintegrate; target 15.0 -.github/ -├── scripts/check-version-drift.sh # DELETE -├── workflows/flutter.yml # drop version_drift_check job + needs -└── copilot-instructions.md # iOS 15.0+ -README.md, CLAUDE.md # SPM-only, iOS 15, Flutter 3.44, migration note -.specify/memory/constitution.md # v1.2.0 amendment - -# optimizely-flutter-testapp (PR 1 — merges first) -.github/workflows/ios.yml # Flutter 3.44.0 + enable SPM -.github/workflows/android.yml # Flutter 3.44.0 -ios/Podfile # DELETE -ios/Flutter/Debug.xcconfig, Release.xcconfig # drop Pods #include -ios/Runner.xcodeproj/project.pbxproj # pod deintegrate; SPM ref; target 15.0 -``` - -**Structure Decision**: Existing layout from spec 001 is kept; this change only deletes files and edits config. Not touched: macOS (`macos/`, `example/macos/Podfile`), Android, and `lib/`. - -## Delivery Order - -1. **Testapp PR**: works against current SDK `master` (SPM resolves its existing `Package.swift`), so it merges independently. -2. **SDK PR**: after the testapp merges, `integration_ios_tests` exercises the SPM-only SDK. -3. No release until Ticket 2 merges; Ticket 3 cuts 4.0.0. - -## Complexity Tracking - -No violations to justify. diff --git a/specs/003-remove-cocoapods-ios15/quickstart.md b/specs/003-remove-cocoapods-ios15/quickstart.md deleted file mode 100644 index e8f6b7b..0000000 --- a/specs/003-remove-cocoapods-ios15/quickstart.md +++ /dev/null @@ -1,60 +0,0 @@ -# Quickstart Validation: Remove CocoaPods Support and Raise Minimum iOS to 15.0 - -**Prerequisites**: Flutter ≥3.44 with SPM enabled (`flutter config --enable-swift-package-manager`), Xcode with an iOS 15+ simulator, Android emulator for testapp Android checks. - -## 1. Testapp (PR 1): validate against current SDK master - -```bash -cd optimizely-flutter-testapp -# place SDK checkout at ./home/runner/optimizely_flutter_sdk (master) -flutter pub get -flutter analyze -flutter build ios --simulator --target=integration_test/app_test.dart -flutter test integration_test/app_test.dart -d -flutter test integration_test/app_test.dart -d -``` - -**Expected**: build succeeds with no `Pods/` generated; all integration tests pass on both platforms. `git grep -i "pod"` in `ios/` shows only Flutter-generated ignores. - -## 2. SDK (PR 2): unit and static checks - -```bash -cd optimizely-flutter-sdk -flutter pub get -flutter analyze # zero issues -flutter test # zero failures -``` - -## 3. SDK: example app builds SPM-only at iOS 15 - -```bash -cd example -flutter build ios --simulator -``` - -**Expected**: build succeeds; no `ios/Pods`, no `ios/Podfile`; the build log shows the swift-sdk package resolved. - -## 4. Testapp against the SDK feature branch - -Repeat step 1 with the SDK checkout on the feature branch. **Expected**: same results (spec US1-AS3, US3-AS2). - -## 5. Negative checks (spec US2) - -| Check | How | Expected | -|---|---|---| -| iOS target too low | In `example`, set the Runner target to 14.0 and run `flutter build ios --simulator` | Flutter bumps the target to 15.0 ("Updating minimum iOS deployment target to 15.0") and the build succeeds | -| Flutter too old | Use Flutter <3.44 (e.g., via fvm), run `flutter pub get` in `example` | Resolution fails, naming the Flutter constraint | - -Revert the target change afterwards. - -## 6. Leftover references (spec SC-002) - -```bash -git grep -n -i -E "cocoapod|podspec|Podfile|iOS 10|ios, '10" -- ':!CHANGELOG.md' ':!specs/' ':!macos/' ':!example/macos/' -``` - -**Expected**: no hits in either repo. - -## 7. CI - -Push both branches. **Expected**: testapp `iOS` and `Android` workflows are green. All SDK jobs are green, with no `version_drift_check` job listed. diff --git a/specs/003-remove-cocoapods-ios15/research.md b/specs/003-remove-cocoapods-ios15/research.md deleted file mode 100644 index e2f8bad..0000000 --- a/specs/003-remove-cocoapods-ios15/research.md +++ /dev/null @@ -1,62 +0,0 @@ -# Research: Remove CocoaPods Support and Raise Minimum iOS to 15.0 - -**Feature**: 003-remove-cocoapods-ios15 | **Date**: 2026-10-12 - -## R1. Minimum Flutter version - -- **Decision**: `pubspec.yaml` → `environment.flutter: ">=3.44.0"`. Leave the Dart constraint (`>=2.16.2 <4.0.0`) unchanged. -- **Rationale**: Flutter 3.44 resolves SPM plugins by default (README line 24 already says so), and SDK CI already pins `flutter-version: '3.44.0'` in all three jobs. Without a podspec, older Flutter versions (or SPM disabled) cannot integrate the plugin on iOS, so the constraint makes `flutter pub get` fail early with a clear message (spec US2-AS2). The Flutter constraint already implies a Dart 3 SDK, so changing the Dart constraint adds diff with no effect. -- **Alternatives considered**: Leave `>=2.5.0` and rely on the iOS build failing. Rejected because the failure is obscure (spec US2). Pinning a newer Flutter (e.g., the local 3.47.1) was also rejected: 3.44 is the lowest version that works and the one CI already proves. - -## R2. Objective-C registration shim - -- **Decision**: Delete `ios/optimizely_flutter_sdk/Sources/optimizely_flutter_sdk/OptimizelyFlutterSdkPlugin.m` and `.../include/` (the header). Remove the `exclude:` argument and the ObjC comment from `Package.swift`. -- **Rationale**: `Package.swift` already excludes these files, and its comment says they "remain on disk for CocoaPods builds". The iOS `pluginClass` in `pubspec.yaml` is already `SwiftOptimizelyFlutterSdkPlugin`, so registration doesn't change. macOS has its own `pluginClass` (`OptimizelyFlutterSdkPlugin`) backed by `macos/Classes/`, which is a separate directory, so macOS is unaffected. -- **Alternatives considered**: Keep the files but leave them unused. Rejected as dead code that FR-002 explicitly removes. - -## R3. Behaviour when the app targets iOS < 15.0 - -- **Decision**: No custom code. Flutter reads `Package.swift` `platforms: [.iOS("15.0")]` and raises the app's deployment target to 15.0 automatically ("Updating minimum iOS deployment target to 15.0"). Verified 2026-10-12: an example app at 14.0 was bumped and built successfully. -- **Rationale**: Flutter's SPM integration migrates the app's deployment target up to the highest plugin minimum, so developers get a working build plus a printed notice instead of an error. That satisfies spec US2-AS1 at no cost. -- **Alternatives considered**: A build-phase script that checks the deployment target. Rejected because it duplicates what SPM already does. - -## R4. In-repo example app (`example/ios`) - -- **Decision**: - 1. Run `pod deintegrate` in `example/ios`. This strips `Pods_Runner.framework`, the `Pods` group, the `Pods-Runner.*.xcconfig` refs and the `[CP]` build phases from `project.pbxproj`. - 2. Delete `example/ios/Podfile`. `Podfile.lock` and `Pods/` are untracked. - 3. Remove the `#include? "Pods/..."` line from `Flutter/Debug.xcconfig` and `Flutter/Release.xcconfig`. - 4. Change `IPHONEOS_DEPLOYMENT_TARGET` from 10.0 to 15.0 (3 occurrences in `project.pbxproj`). - 5. Run `flutter build ios --simulator` with SPM enabled to confirm. The SPM package reference (`FlutterGeneratedPluginSwiftPackage`) is already in the pbxproj. -- **Rationale**: The example's only plugin dependency is this SDK, so no CocoaPods plugin is left to need a Podfile. `pod deintegrate` is the supported way to clean the pbxproj; editing it by hand is error-prone. -- **Out of scope**: `example/macos/Podfile` stays (macOS is out of scope). - -## R5. Test app (`optimizely-flutter-testapp`) - -- **Finding**: Both `.github/workflows/ios.yml:39` and `android.yml:57` pin `flutter-version: '3.16.0'`. With R1 the SDK requires Flutter ≥3.44, so **both** testapp workflows fail `flutter pub get` unless upgraded. The iOS project has no SPM integration (no `FlutterGeneratedPluginSwiftPackage` in the pbxproj) and still uses CocoaPods with `platform :ios, '13.0'`. -- **Decision**: - 1. In `ios.yml` and `android.yml`, change `flutter-version` from `3.16.0` to `3.44.0` (matching SDK CI). In `ios.yml`, add a `flutter config --enable-swift-package-manager` step before `flutter pub get`, mirroring SDK CI. - 2. Run `pod deintegrate` in `ios/`. Delete `ios/Podfile`. Remove the `#include? "Pods/..."` lines from `ios/Flutter/{Debug,Release}.xcconfig`. - 3. Change `IPHONEOS_DEPLOYMENT_TARGET` from 10.0 to 15.0 (3 occurrences). - 4. Run `flutter build ios --simulator` locally with SPM enabled. Flutter's auto-migration adds the `FlutterGeneratedPluginSwiftPackage` reference to the pbxproj; commit that. - 5. Verify the Android build locally on Flutter 3.44. The testapp is already on AGP 8.7.0, Gradle 8.10.2 and Kotlin 2.1.0, which are expected to be compatible. The CI run confirms it. -- **Rationale**: The testapp has to work against both SDK `master` (podspec still present, iOS 10) and the SDK feature branch. An iOS-15, SPM-only testapp works with both, because SPM resolves the SDK's existing `Package.swift`. So the testapp PR can merge first with no flag-day coordination. -- **Risk**: The jump from Flutter 3.16 to 3.44 may surface Dart analyzer or lint changes in the testapp's `integration_test/`. Mitigation: run `flutter analyze` and the integration suite locally before opening the PR. -- **Noted, out of scope**: `ios.yml` uses `inputs.tags` but the declared input is `release_version`. That's an existing bug in the release-install step and is left untouched. - -## R6. SDK CI - -- **Decision**: Delete `.github/scripts/check-version-drift.sh`. Remove the `version_drift_check` job (`flutter.yml:20-25`) and both `needs: version_drift_check` lines (`flutter.yml:29`, `:155`). -- **Rationale**: Only one manifest remains, so the parity check has nothing to compare (FR-007). -- **Alternatives considered**: Repurpose the script to check something else. Rejected as YAGNI. -- **Noted**: `build_test_ios` only runs `flutter pub get`/`flutter test`. It doesn't actually build iOS. The real iOS build is covered by `integration_ios_tests` (the testapp). Adding an iOS build step is out of scope. - -## R7. Documentation and constitution - -- **Decision**: - - `README.md:24`: say iOS 15.0 minimum, Flutter 3.44+, SPM only, and add a short "Migrating from CocoaPods" note (enable SPM, raise the deployment target, run `pod deintegrate` if no other pods remain). - - `CLAUDE.md`: in lines 39, 80-85 (iOS native version pair → single file), 193 (iOS 15.0), 195 and 214, remove the podspec references and the drift-check sentence. - - `.github/copilot-instructions.md:13`: change iOS 10.0+ to 15.0+. - - `.specify/memory/constitution.md`: in Principle VIII, keep only `Package.swift` as the iOS source of truth. In the Minimum Platform Versions table, set iOS to 15.0 and Flutter to >=3.44.0. Bump the constitution version **1.1.1 → 1.2.0** (MINOR: materially changed guidance, no principle removed), update the Sync Impact Report and Last Amended date, and use the commit message `docs: amend constitution to v1.2.0 (SPM-only iOS, iOS 15 minimum)`. -- **Rationale**: FR-010 and FR-011, plus the constitution's amendment procedure. -- **Not touched**: CHANGELOG (Ticket 3 writes the 4.0.0 entry), prior specs 001/002 (historical). diff --git a/specs/003-remove-cocoapods-ios15/tasks.md b/specs/003-remove-cocoapods-ios15/tasks.md deleted file mode 100644 index 90af296..0000000 --- a/specs/003-remove-cocoapods-ios15/tasks.md +++ /dev/null @@ -1,150 +0,0 @@ ---- - -description: "Task list for removing CocoaPods support and raising minimum iOS to 15.0" ---- - -# Tasks: Remove CocoaPods Support and Raise Minimum iOS to 15.0 - -**Input**: Design documents from `/specs/003-remove-cocoapods-ios15/` - -**Prerequisites**: plan.md, spec.md, research.md, data-model.md, contracts/platform-requirements.md, quickstart.md - -**Tests**: No new test code. The spec has no logic changes; verification is the existing `flutter test`/`flutter analyze`, the example app build, and the testapp integration suite (quickstart.md). - -**Organization**: Grouped by user story. **Phase order differs from priority order**: US3 (testapp, P2) runs first, because SDK CI builds testapp `master` (plan.md → Delivery Order). - -**Repos**: Paths prefixed `optimizely-flutter-testapp/` are in the sibling testapp repo (`/Users/muzahidul.islam/workspace/optimizely-flutter-testapp`). All other paths are relative to the SDK repo root (`/Users/muzahidul.islam/workspace/optimizely-flutter-sdk`). - -## Format: `[ID] [P?] [Story] Description` - -- **[P]**: Can run in parallel (different files, no dependencies) -- **[Story]**: Which user story this task belongs to (US1–US4) - ---- - -## Phase 1: Setup - -**Purpose**: Branches and local toolchain. - -- [X] T001 In the SDK repo, create branch `feature/remove-cocoapods-ios15` from an up-to-date `master` (`git checkout master && git pull && git checkout -b feature/remove-cocoapods-ios15`) -- [X] T002 [P] In the testapp repo, create branch `feature/spm-ios15` from an up-to-date `master`. The local checkout is currently on `ci/ios-single-entrypoint`, so switch first: `git checkout master && git pull && git checkout -b feature/spm-ios15` -- [X] T003 [P] Verify local Flutter is ≥3.44 (`flutter --version`) and SPM is enabled (`flutter config --enable-swift-package-manager`) - ---- - -## Phase 2: User Story 3 — Test App Validates the SPM-Only SDK (Priority: P2, runs first) → testapp PR - -**Goal**: The testapp builds iOS via SPM at iOS 15.0, with CI on Flutter 3.44, and passes against current SDK `master`. - -**Independent Test**: quickstart.md §1. iOS build with no `Pods/`; integration suite green on iOS and Android. - -- [X] T004 [US3] In `optimizely-flutter-testapp/.github/workflows/ios.yml`, change `flutter-version: '3.16.0'` to `'3.44.0'`. Add a step named `Enable Swift Package Manager` that runs `flutter config --enable-swift-package-manager`, immediately before the `Install Flutter dependencies` step. Do NOT touch the existing `inputs.tags` bug (research R5). -- [X] T005 [P] [US3] In `optimizely-flutter-testapp/.github/workflows/android.yml`, change `flutter-version: '3.16.0'` to `'3.44.0'` -- [X] T006 [US3] In `optimizely-flutter-testapp/ios/`, run `pod deintegrate`. This removes `Pods_Runner.framework`, the `Pods` group, `Pods-Runner.*.xcconfig` refs and the `[CP]` phases from `ios/Runner.xcodeproj/project.pbxproj`. Then delete `optimizely-flutter-testapp/ios/Podfile` (and local untracked `Podfile.lock`/`Pods/` if present). -- [X] T007 [P] [US3] Remove the line `#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"` from `optimizely-flutter-testapp/ios/Flutter/Debug.xcconfig`, and the matching `.release.xcconfig` line from `optimizely-flutter-testapp/ios/Flutter/Release.xcconfig`. Keep `#include "Generated.xcconfig"`. -- [X] T008 [US3] In `optimizely-flutter-testapp/ios/Runner.xcodeproj/project.pbxproj`, change all 3 occurrences of `IPHONEOS_DEPLOYMENT_TARGET = 10.0;` to `IPHONEOS_DEPLOYMENT_TARGET = 15.0;` (after T006, same file) -- [X] T009 [US3] Clone SDK `master` into `optimizely-flutter-testapp/home/runner/optimizely_flutter_sdk` (the path dependency in `pubspec.yaml`; **do not commit it**, since `home/` is not gitignored). Then run `flutter pub get` and `flutter build ios --simulator --target=integration_test/app_test.dart`. Flutter auto-migrates `ios/Runner.xcodeproj/project.pbxproj` to add the `FlutterGeneratedPluginSwiftPackage` reference; keep those changes. Confirm no `ios/Pods/` was created. -- [X] T010 [US3] Run `flutter analyze` in `optimizely-flutter-testapp/`. Fix only the errors and warnings introduced by the Flutter 3.16 → 3.44 jump, in `optimizely-flutter-testapp/lib/` and `optimizely-flutter-testapp/integration_test/`, with minimal edits and no refactors. -- [X] T011 [US3] Verify the Android build on Flutter 3.44: `flutter build apk --debug` in `optimizely-flutter-testapp/`. If AGP 8.7.0 / Gradle 8.10.2 / Kotlin 2.1.0 are rejected, bump only what the error names, in `optimizely-flutter-testapp/android/settings.gradle` / `android/gradle/wrapper/gradle-wrapper.properties`. -- [X] T012 [US3] Run `flutter test integration_test/app_test.dart -d ` on an iOS 15+ simulator and an Android emulator from `optimizely-flutter-testapp/`. All tests pass. -- [X] T013 [US3] Commit in the testapp (`ci: migrate iOS to SPM, require iOS 15 and Flutter 3.44`, ending with the `Co-Authored-By` line), push `feature/spm-ios15`, and open a PR to `master` referencing the Ticket 1 Jira key. Confirm the testapp `iOS` and `Android` workflows are green. **Merge before T030.** - -**Checkpoint**: the testapp on `master` is SPM-only and still green against SDK `master`. - ---- - -## Phase 3: User Story 1 — SDK Consumers Build iOS via SPM Only (Priority: P1) 🎯 MVP - -**Goal**: The plugin ships SPM-only for iOS at 15.0 minimum. All behaviour is unchanged. - -**Independent Test**: quickstart.md §2–3. `flutter analyze` is clean, `flutter test` is green, and `example` builds for the iOS simulator with no Podfile/Pods. - -- [X] T014 [P] [US1] Delete `ios/optimizely_flutter_sdk.podspec` (`git rm`) -- [X] T015 [P] [US1] Delete `ios/optimizely_flutter_sdk/Sources/optimizely_flutter_sdk/OptimizelyFlutterSdkPlugin.m` and the directory `ios/optimizely_flutter_sdk/Sources/optimizely_flutter_sdk/include/` (`git rm -r`). Do NOT touch `macos/Classes/` (research R2). -- [X] T016 [US1] Edit `ios/optimizely_flutter_sdk/Package.swift` (after T015): - - remove comment lines 2–4 (the ObjC/CocoaPods note), leaving `// swift-tools-version: 5.9` on line 1 - - change `platforms: [.iOS("10.0")]` to `platforms: [.iOS("15.0")]` - - remove the `exclude: ["OptimizelyFlutterSdkPlugin.m", "include"]` argument, together with the trailing comma after the target's `dependencies: [...]` array - - keep `exact: "5.4.2"` unchanged (FR-005) -- [X] T017 [US1] In `example/ios/`, run `pod deintegrate`, then `git rm example/ios/Podfile` (and delete untracked `Podfile.lock`/`Pods/`). Leave `example/macos/Podfile` alone. -- [X] T018 [P] [US1] Remove the `#include? "Pods/..."` first line from `example/ios/Flutter/Debug.xcconfig` and `example/ios/Flutter/Release.xcconfig`. Keep `#include "Generated.xcconfig"`. -- [X] T019 [US1] In `example/ios/Runner.xcodeproj/project.pbxproj`, change all 3 occurrences of `IPHONEOS_DEPLOYMENT_TARGET = 10.0;` to `15.0;` (after T017, same file) -- [X] T020 [US1] Validate: from the repo root run `flutter pub get && flutter analyze && flutter test` (zero issues, zero failures). Then `cd example && flutter build ios --simulator`: it succeeds, swift-sdk 5.4.2 resolves via SPM, and no `example/ios/Pods/` exists. - -**Checkpoint**: the SDK is SPM-only and the example builds. US1 is done. - ---- - -## Phase 4: User Story 2 — Developers Below the New Minimums Get a Clear Signal (Priority: P1) - -**Goal**: Old Flutter and low iOS targets fail early with clear messages, and the README explains the migration. - -**Independent Test**: quickstart.md §5, plus a README review against contracts/platform-requirements.md. - -- [X] T021 [US2] In `pubspec.yaml`, change `environment.flutter: ">=2.5.0"` to `">=3.44.0"`. Leave the Dart `sdk:` constraint unchanged (research R1). Run `flutter pub get`. -- [X] T022 [US2] In `README.md`, replace line 24 with: iOS minimum 15.0; Flutter 3.44+ required; iOS integration is via Swift Package Manager only (CocoaPods no longer supported). Add a short "Migrating from CocoaPods" subsection right after it, with the 4 steps from `specs/003-remove-cocoapods-ios15/contracts/platform-requirements.md` → "Migration steps for app developers". Match the README's existing heading level and tone. -- [X] T023 [US2] Negative check from quickstart.md §5: set `IPHONEOS_DEPLOYMENT_TARGET` to `14.0` in `example/ios/Runner.xcodeproj/project.pbxproj`, run `flutter build ios --simulator` in `example/`, and confirm the error names the iOS 15.0 minimum. Then revert the file (`git checkout -- example/ios/Runner.xcodeproj/project.pbxproj` only if T019 is already committed; otherwise set it back to 15.0). Run the Flutter-too-old check only if `fvm` is available; otherwise note it as skipped. - -**Checkpoint**: US1 and US2 are complete. This is consumer-facing done. - ---- - -## Phase 5: User Story 4 — Maintainers Have One iOS Dependency Path (Priority: P3) - -**Goal**: No parity CI job, and docs and the constitution describe SPM only and iOS 15. - -**Independent Test**: quickstart.md §6 (leftover grep returns nothing); the CI job list has no `version_drift_check`. - -- [X] T024 [P] [US4] Delete `.github/scripts/check-version-drift.sh` (`git rm`) -- [X] T025 [US4] In `.github/workflows/flutter.yml`, delete the `version_drift_check:` job (lines 20–25: job key, `runs-on`, `steps`, checkout, and the "Check OptimizelySwiftSDK version parity" step) and both `needs: version_drift_check` lines (currently lines 29 and 155). Keep YAML indentation valid. -- [X] T026 [P] [US4] Edit `CLAUDE.md`: - - line 39: change `check podspec/build.gradle` to `check Package.swift/build.gradle` - - lines 82–86: replace the "Two locations must stay synchronized (native iOS SDK version)" block and the following CI sentence with a single line saying the native iOS SDK version lives in `ios/optimizely_flutter_sdk/Package.swift` → `.package(url: "...swift-sdk.git", exact: "A.B.C")` - - Platform Requirements → Flutter: change `>=2.5.0` to `>=3.44.0` - - line 193: `iOS 10.0` → `iOS 15.0` - - line 195: drop `ios/optimizely_flutter_sdk.podspec and ` - - line 214: delete the `ios/optimizely_flutter_sdk.podspec - CocoaPods dependencies` bullet -- [X] T027 [P] [US4] In `.github/copilot-instructions.md` line 13, change `iOS 10.0+` to `iOS 15.0+` -- [X] T028 [US4] Amend `.specify/memory/constitution.md` per research R7: - - Principle VIII iOS bullet: only `ios/optimizely_flutter_sdk/Package.swift` (SPM); remove the podspec mention and "Both files MUST declare the same version." - - Minimum Platform Versions table: iOS `15.0`, Flutter `>=3.44.0` - - Replace the Sync Impact Report comment with Version change 1.1.1 → 1.2.0, modified Principle VIII and Platform Compatibility table - - footer: `**Version**: 1.2.0 | **Ratified**: 2022-06-07 | **Last Amended**: 2026-10-12` - - commit this file alone: `docs: amend constitution to v1.2.0 (SPM-only iOS, iOS 15 minimum)` - -**Checkpoint**: all user stories are complete. - ---- - -## Phase 6: Polish & Delivery - -- [X] T029 Run the leftover-reference grep from quickstart.md §6 in both repos. Fix any hit outside `CHANGELOG.md`, `specs/`, `macos/` and `example/macos/`. -- [ ] T030 Commit the remaining SDK changes as `chore!: remove CocoaPods support, require iOS 15 and Flutter 3.44`, with a `BREAKING CHANGE:` footer naming the new minimums and ending with the `Co-Authored-By` line. Push `feature/remove-cocoapods-ios15` and open a PR to `master` referencing the Ticket 1 Jira key (use the `pr` skill for the body). Confirm all SDK CI jobs, including `integration_ios_tests`, are green. Requires T013 to be merged. **Do not cut a release** (FR-012: 4.0.0 ships with Ticket 2, via Ticket 3). - ---- - -## Dependencies & Execution Order - -- **Setup (T001–T003)** → everything. -- **US3 (T004–T013)**: independent of SDK changes; must **merge** before T030. -- **US1 (T014–T020)**: after T001. T016 needs T015; T019 needs T017; T020 needs T014–T019. -- **US2 (T021–T023)**: T021/T022 are independent of US1; T023 needs T019. -- **US4 (T024–T028)**: independent of US1/US2; T025 should follow T024. -- **Polish (T029–T030)**: after all stories; T030 needs T013 merged. - -Phases 3–5 all live in one SDK PR, so splitting by story is for ordering only, not separate PRs. - -## Parallel Opportunities - -- T002 ∥ T003 -- US3: T005 ∥ T007 ∥ (T006 → T008) -- US1: T014 ∥ T015 ∥ T018, then T016, T017 → T019 -- US2/US4 file edits (T021, T022, T024, T026, T027) can all run alongside US1, since they touch different files -- SDK work (Phases 3–5) can proceed in parallel with the testapp PR review; only T030 waits on T013 - -## Implementation Strategy - -1. **Testapp first** (Phase 2). Merge it once green; it's safe against current SDK `master`. -2. **MVP = US1** (Phase 3): SPM-only plugin and example build. -3. Add **US2** (Flutter constraint + README) and **US4** (CI/docs/constitution) on the same branch. -4. Run Polish, then open the SDK PR. Hold the release for Ticket 2 + Ticket 3. From c4fbcc6db2a80d1a8973eb2b5c759032bf643665 Mon Sep 17 00:00:00 2001 From: Muzahidul Islam Date: Tue, 13 Oct 2026 21:40:20 +0600 Subject: [PATCH 5/9] chore: move macOS plugin to SPM and remove its podspec Co-Authored-By: Claude Opus 5.5 --- example/macos/.gitignore | 1 + example/macos/Flutter/Flutter-Debug.xcconfig | 1 - .../macos/Flutter/Flutter-Release.xcconfig | 1 - example/macos/Podfile | 40 ------- .../macos/Runner.xcodeproj/project.pbxproj | 105 ++++++------------ .../xcshareddata/xcschemes/Runner.xcscheme | 21 +++- .../contents.xcworkspacedata | 3 - example/macos/Runner/AppDelegate.swift | 6 +- macos/.gitignore | 2 + macos/optimizely_flutter_sdk.podspec | 23 ---- macos/optimizely_flutter_sdk/Package.swift | 22 ++++ .../OptimizelyFlutterSdkPlugin.swift | 0 specs/003-remove-cocoapods-ios15/spec.md | 4 +- 13 files changed, 84 insertions(+), 145 deletions(-) delete mode 100644 example/macos/Podfile create mode 100644 macos/.gitignore delete mode 100644 macos/optimizely_flutter_sdk.podspec create mode 100644 macos/optimizely_flutter_sdk/Package.swift rename macos/{Classes => optimizely_flutter_sdk/Sources/optimizely_flutter_sdk}/OptimizelyFlutterSdkPlugin.swift (100%) diff --git a/example/macos/.gitignore b/example/macos/.gitignore index 746adbb..468c330 100644 --- a/example/macos/.gitignore +++ b/example/macos/.gitignore @@ -5,3 +5,4 @@ # Xcode-related **/dgph **/xcuserdata/ +**/swiftpm/ diff --git a/example/macos/Flutter/Flutter-Debug.xcconfig b/example/macos/Flutter/Flutter-Debug.xcconfig index 4b81f9b..c2efd0b 100644 --- a/example/macos/Flutter/Flutter-Debug.xcconfig +++ b/example/macos/Flutter/Flutter-Debug.xcconfig @@ -1,2 +1 @@ -#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" #include "ephemeral/Flutter-Generated.xcconfig" diff --git a/example/macos/Flutter/Flutter-Release.xcconfig b/example/macos/Flutter/Flutter-Release.xcconfig index 5caa9d1..c2efd0b 100644 --- a/example/macos/Flutter/Flutter-Release.xcconfig +++ b/example/macos/Flutter/Flutter-Release.xcconfig @@ -1,2 +1 @@ -#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" #include "ephemeral/Flutter-Generated.xcconfig" diff --git a/example/macos/Podfile b/example/macos/Podfile deleted file mode 100644 index dade8df..0000000 --- a/example/macos/Podfile +++ /dev/null @@ -1,40 +0,0 @@ -platform :osx, '10.11' - -# CocoaPods analytics sends network stats synchronously affecting flutter build latency. -ENV['COCOAPODS_DISABLE_STATS'] = 'true' - -project 'Runner', { - 'Debug' => :debug, - 'Profile' => :release, - 'Release' => :release, -} - -def flutter_root - generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) - unless File.exist?(generated_xcode_build_settings_path) - raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" - end - - File.foreach(generated_xcode_build_settings_path) do |line| - matches = line.match(/FLUTTER_ROOT\=(.*)/) - return matches[1].strip if matches - end - raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" -end - -require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) - -flutter_macos_podfile_setup - -target 'Runner' do - use_frameworks! - use_modular_headers! - - flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) -end - -post_install do |installer| - installer.pods_project.targets.each do |target| - flutter_additional_macos_build_settings(target) - end -end diff --git a/example/macos/Runner.xcodeproj/project.pbxproj b/example/macos/Runner.xcodeproj/project.pbxproj index 9f89128..4c2abd3 100644 --- a/example/macos/Runner.xcodeproj/project.pbxproj +++ b/example/macos/Runner.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 51; + objectVersion = 54; objects = { /* Begin PBXAggregateTarget section */ @@ -26,7 +26,7 @@ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; - 90B9B075FD9D5B075E83BE96 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5A8DB69DD1CEB9DE6D5A4070 /* Pods_Runner.framework */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -53,7 +53,6 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - 019D04A8C81D7599D2E638FA /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; 33CC10ED2044A3C60003C045 /* optimizely_flutter_sdk_example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = optimizely_flutter_sdk_example.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -68,11 +67,11 @@ 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; }; 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; }; 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; }; - 5A8DB69DD1CEB9DE6D5A4070 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; }; - 7EEA78DE428A25C4C9195EC6 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; - 8D05B77804EBF39FD6D6D079 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = ""; }; + 784666492D4C4C64000A1A5F /* FlutterFramework */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterFramework; path = ephemeral/Packages/.packages/FlutterFramework; sourceTree = ""; }; + 78DABEA22ED26510000E7860 /* optimizely_flutter_sdk */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = optimizely_flutter_sdk; path = ../../../macos/optimizely_flutter_sdk; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -80,7 +79,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 90B9B075FD9D5B075E83BE96 /* Pods_Runner.framework in Frameworks */, + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -104,8 +103,6 @@ 33FAB671232836740065AC1E /* Runner */, 33CEB47122A05771004F2AC0 /* Flutter */, 33CC10EE2044A3C60003C045 /* Products */, - D73912EC22F37F3D000D13A0 /* Frameworks */, - F74646C1D5F338EF32BE10E1 /* Pods */, ); sourceTree = ""; }; @@ -131,6 +128,9 @@ 33CEB47122A05771004F2AC0 /* Flutter */ = { isa = PBXGroup; children = ( + 78DABEA22ED26510000E7860 /* optimizely_flutter_sdk */, + 784666492D4C4C64000A1A5F /* FlutterFramework */, + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */, 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, @@ -152,39 +152,21 @@ path = Runner; sourceTree = ""; }; - D73912EC22F37F3D000D13A0 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 5A8DB69DD1CEB9DE6D5A4070 /* Pods_Runner.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; - F74646C1D5F338EF32BE10E1 /* Pods */ = { - isa = PBXGroup; - children = ( - 7EEA78DE428A25C4C9195EC6 /* Pods-Runner.debug.xcconfig */, - 8D05B77804EBF39FD6D6D079 /* Pods-Runner.release.xcconfig */, - 019D04A8C81D7599D2E638FA /* Pods-Runner.profile.xcconfig */, - ); - name = Pods; - path = Pods; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ 33CC10EC2044A3C60003C045 /* Runner */ = { + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); isa = PBXNativeTarget; buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( - 834CC0346DE4E8E368DFDFD4 /* [CP] Check Pods Manifest.lock */, 33CC10E92044A3C60003C045 /* Sources */, 33CC10EA2044A3C60003C045 /* Frameworks */, 33CC10EB2044A3C60003C045 /* Resources */, 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, - F96AA93D7120056FD027BD50 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -200,10 +182,13 @@ /* Begin PBXProject section */ 33CC10E52044A3C60003C045 /* Project object */ = { + packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */, + ); isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1300; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 33CC10EC2044A3C60003C045 = { @@ -256,6 +241,7 @@ /* Begin PBXShellScriptBuildPhase section */ 3399D490228B24CF009A79C7 /* ShellScript */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -291,45 +277,6 @@ shellPath = /bin/sh; shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; }; - 834CC0346DE4E8E368DFDFD4 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; - F96AA93D7120056FD027BD50 /* [CP] Embed Pods Frameworks */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", - ); - name = "[CP] Embed Pods Frameworks"; - outputFileListPaths = ( - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; - showEnvVarsInLog = 0; - }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -404,7 +351,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; @@ -483,7 +430,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -530,7 +477,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.11; + MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; @@ -627,6 +574,18 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ +/* Begin XCSwiftPackageProductDependency section */ + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 33CC10E52044A3C60003C045 /* Project object */; } diff --git a/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 0ded16c..2ab8be1 100644 --- a/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,10 +1,28 @@ + + + + + + + + + + diff --git a/example/macos/Runner.xcworkspace/contents.xcworkspacedata b/example/macos/Runner.xcworkspace/contents.xcworkspacedata index 21a3cc1..1d526a1 100644 --- a/example/macos/Runner.xcworkspace/contents.xcworkspacedata +++ b/example/macos/Runner.xcworkspace/contents.xcworkspacedata @@ -4,7 +4,4 @@ - - diff --git a/example/macos/Runner/AppDelegate.swift b/example/macos/Runner/AppDelegate.swift index d53ef64..b3c1761 100644 --- a/example/macos/Runner/AppDelegate.swift +++ b/example/macos/Runner/AppDelegate.swift @@ -1,9 +1,13 @@ import Cocoa import FlutterMacOS -@NSApplicationMain +@main class AppDelegate: FlutterAppDelegate { override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { return true } + + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool { + return true + } } diff --git a/macos/.gitignore b/macos/.gitignore new file mode 100644 index 0000000..2d9f16e --- /dev/null +++ b/macos/.gitignore @@ -0,0 +1,2 @@ +.build/ +.swiftpm/ diff --git a/macos/optimizely_flutter_sdk.podspec b/macos/optimizely_flutter_sdk.podspec deleted file mode 100644 index 9776129..0000000 --- a/macos/optimizely_flutter_sdk.podspec +++ /dev/null @@ -1,23 +0,0 @@ -# -# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html. -# Run `pod lib lint optimizely_flutter_sdk.podspec` to validate before publishing. -# -Pod::Spec.new do |s| - s.name = 'optimizely_flutter_sdk' - s.version = '0.0.1' - s.summary = 'A new Flutter plugin project.' - s.description = <<-DESC -A new Flutter plugin project. - DESC - s.homepage = 'http://example.com' - s.license = { :file => '../LICENSE' } - s.author = { 'Your Company' => 'email@example.com' } - - s.source = { :path => '.' } - s.source_files = 'Classes/**/*' - s.dependency 'FlutterMacOS' - - s.platform = :osx, '10.11' - s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' } - s.swift_version = '5.0' -end diff --git a/macos/optimizely_flutter_sdk/Package.swift b/macos/optimizely_flutter_sdk/Package.swift new file mode 100644 index 0000000..a02f57c --- /dev/null +++ b/macos/optimizely_flutter_sdk/Package.swift @@ -0,0 +1,22 @@ +// swift-tools-version: 5.9 + +import PackageDescription + +let package = Package( + name: "optimizely_flutter_sdk", + platforms: [.macOS("10.15")], + products: [ + .library(name: "optimizely-flutter-sdk", targets: ["optimizely_flutter_sdk"]) + ], + dependencies: [ + .package(name: "FlutterFramework", path: "../FlutterFramework") + ], + targets: [ + .target( + name: "optimizely_flutter_sdk", + dependencies: [ + .product(name: "FlutterFramework", package: "FlutterFramework") + ] + ) + ] +) diff --git a/macos/Classes/OptimizelyFlutterSdkPlugin.swift b/macos/optimizely_flutter_sdk/Sources/optimizely_flutter_sdk/OptimizelyFlutterSdkPlugin.swift similarity index 100% rename from macos/Classes/OptimizelyFlutterSdkPlugin.swift rename to macos/optimizely_flutter_sdk/Sources/optimizely_flutter_sdk/OptimizelyFlutterSdkPlugin.swift diff --git a/specs/003-remove-cocoapods-ios15/spec.md b/specs/003-remove-cocoapods-ios15/spec.md index b2cf15f..44023e9 100644 --- a/specs/003-remove-cocoapods-ios15/spec.md +++ b/specs/003-remove-cocoapods-ios15/spec.md @@ -78,7 +78,7 @@ SDK maintainers maintain a single iOS dependency manifest. CI no longer runs a p - **App has an existing Podfile with other pods**: Other plugins or pods may still use CocoaPods; Flutter's tooling supports mixed apps where SPM-capable plugins resolve via SPM. The SDK makes no assumptions about the app's other dependencies. - **SPM disabled in the developer's Flutter config**: With no CocoaPods spec, the plugin cannot be integrated. The migration note must tell developers to ensure SPM is enabled. -- **macOS plugin platform**: macOS has its own separate CocoaPods spec and is experimental (constitution, Platform Compatibility Standards). It is out of scope and remains unchanged. +- **macOS plugin platform**: macOS is experimental (constitution, Platform Compatibility Standards) and its plugin is a template stub. It moves to SPM too (Package.swift at macOS 10.15, Flutter 3.44's minimum), its podspec is removed, and the macOS example and test apps drop CocoaPods. Its behaviour is unchanged. - **Objective-C registration shim**: The iOS Objective-C plugin files exist only for CocoaPods builds (already excluded from the SPM target). They are removed with CocoaPods; registration continues through the Swift plugin class already declared in `pubspec.yaml`. - **Test app on `master` lags the SDK**: If the SDK change merges first, `integration_ios_tests` fails. Mitigated by merging the test-app change first or temporarily pointing SDK CI at the test-app feature branch, then reverting. - **Native SDK version**: This change keeps OptimizelySwiftSDK at its current version (5.4.2); the 6.0.0 bump is a separate ticket (Ticket 2) and must not be bundled (constitution VIII.1). @@ -122,7 +122,7 @@ SDK maintainers maintain a single iOS dependency manifest. CI no longer runs a p - Flutter 3.44+ resolves SPM-capable plugins by default (README already states this; CI already uses 3.44.0). - Existing SPM integration from spec 001 (Package.swift, directory layout, Swift plugin registration) is correct and needs no structural change beyond the platform minimum. -- The macOS and Windows plugin platforms are out of scope. +- The Windows plugin platform is out of scope (it has no CocoaPods files). - Android is unaffected except for test-app CI continuing to pass. - The release (version files, CHANGELOG, 4.0.0) is handled by Ticket 3, not this change; this change only adds README/migration content. - Xcode 27 runner migration belongs to Ticket 2 (required by OptimizelySwiftSDK 6.0.0), not this change. From cee73471aad020fbd4d6e4ae33571716120e90b1 Mon Sep 17 00:00:00 2001 From: Muzahidul Islam Date: Tue, 13 Oct 2026 22:12:35 +0600 Subject: [PATCH 6/9] chore: drop obsolete CocoaPods gitignore entries Co-Authored-By: Claude Opus 5.5 --- .gitignore | 3 --- example/ios/.gitignore | 2 -- example/macos/.gitignore | 1 - 3 files changed, 6 deletions(-) diff --git a/.gitignore b/.gitignore index 0a0224a..3d59528 100644 --- a/.gitignore +++ b/.gitignore @@ -82,7 +82,6 @@ unlinked_spec.ds **/ios/**/.vagrant/ **/ios/**/DerivedData/ **/ios/**/Icon? -**/ios/**/Pods/ **/ios/**/.symlinks/ **/ios/**/profile **/ios/**/xcuserdata @@ -90,7 +89,6 @@ unlinked_spec.ds **/ios/Flutter/.last_build_id **/ios/Flutter/App.framework **/ios/Flutter/Flutter.framework -**/ios/Flutter/Flutter.podspec **/ios/Flutter/Generated.xcconfig **/ios/Flutter/ephemeral **/ios/Flutter/app.flx @@ -102,7 +100,6 @@ unlinked_spec.ds # macOS **/Flutter/ephemeral/ -**/Pods/ **/macos/Flutter/GeneratedPluginRegistrant.swift **/macos/Flutter/ephemeral **/xcuserdata/ diff --git a/example/ios/.gitignore b/example/ios/.gitignore index ecbb615..8d79986 100644 --- a/example/ios/.gitignore +++ b/example/ios/.gitignore @@ -10,14 +10,12 @@ **/.vagrant/ **/DerivedData/ Icon? -**/Pods/ **/.symlinks/ profile xcuserdata **/.generated/ Flutter/App.framework Flutter/Flutter.framework -Flutter/Flutter.podspec Flutter/Generated.xcconfig Flutter/ephemeral/ Flutter/app.flx diff --git a/example/macos/.gitignore b/example/macos/.gitignore index 468c330..7b58324 100644 --- a/example/macos/.gitignore +++ b/example/macos/.gitignore @@ -1,6 +1,5 @@ # Flutter-related **/Flutter/ephemeral/ -**/Pods/ # Xcode-related **/dgph From 1d3124ada8540204efeff3b350ca1f5826891b75 Mon Sep 17 00:00:00 2001 From: Muzahidul Islam Date: Tue, 13 Oct 2026 23:00:04 +0600 Subject: [PATCH 7/9] chore: exclude specs and build output from published package Co-Authored-By: Claude Opus 5.5 --- .pubignore | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.pubignore b/.pubignore index 22ce619..012e365 100644 --- a/.pubignore +++ b/.pubignore @@ -6,3 +6,9 @@ CLAUDE.md # Spec Kit framework files .specify/ + +# Spec Kit feature specs +specs/ + +# Local build output (e.g. flutter test cache) +build/ From 157a34ea0a2ca0cc55a3e7ebb2d6202b60894dff Mon Sep 17 00:00:00 2001 From: Muzahidul Islam Date: Wed, 14 Oct 2026 20:23:38 +0600 Subject: [PATCH 8/9] docs: update spec to the Swift SDK version pinned since 3.8.0 Co-Authored-By: Claude Opus 5.5 --- specs/003-remove-cocoapods-ios15/spec.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/specs/003-remove-cocoapods-ios15/spec.md b/specs/003-remove-cocoapods-ios15/spec.md index 44023e9..412cc83 100644 --- a/specs/003-remove-cocoapods-ios15/spec.md +++ b/specs/003-remove-cocoapods-ios15/spec.md @@ -81,7 +81,7 @@ SDK maintainers maintain a single iOS dependency manifest. CI no longer runs a p - **macOS plugin platform**: macOS is experimental (constitution, Platform Compatibility Standards) and its plugin is a template stub. It moves to SPM too (Package.swift at macOS 10.15, Flutter 3.44's minimum), its podspec is removed, and the macOS example and test apps drop CocoaPods. Its behaviour is unchanged. - **Objective-C registration shim**: The iOS Objective-C plugin files exist only for CocoaPods builds (already excluded from the SPM target). They are removed with CocoaPods; registration continues through the Swift plugin class already declared in `pubspec.yaml`. - **Test app on `master` lags the SDK**: If the SDK change merges first, `integration_ios_tests` fails. Mitigated by merging the test-app change first or temporarily pointing SDK CI at the test-app feature branch, then reverting. -- **Native SDK version**: This change keeps OptimizelySwiftSDK at its current version (5.4.2); the 6.0.0 bump is a separate ticket (Ticket 2) and must not be bundled (constitution VIII.1). +- **Native SDK version**: This change keeps OptimizelySwiftSDK at its current version (5.5.0, from 3.8.0); the 6.0.0 bump is a separate ticket (Ticket 2) and must not be bundled (constitution VIII.1). ## Requirements *(mandatory)* @@ -91,7 +91,7 @@ SDK maintainers maintain a single iOS dependency manifest. CI no longer runs a p - **FR-002**: The SDK MUST remove iOS source files used only by CocoaPods builds (the Objective-C registration shim and its header). - **FR-003**: The SDK MUST declare iOS 15.0 as its minimum iOS platform. - **FR-004**: The SDK MUST declare a minimum Flutter version that resolves SPM-only plugins by default (Flutter 3.44, the version CI already uses with SPM). -- **FR-005**: The SDK MUST keep OptimizelySwiftSDK pinned at its current exact version (5.4.2) in this change. +- **FR-005**: The SDK MUST keep OptimizelySwiftSDK pinned at its current exact version (5.5.0, from 3.8.0) in this change. - **FR-006**: The SDK MUST preserve all existing iOS behaviour (MethodChannel handlers, logger bridge, type encoding, main-thread result dispatch) with no Dart public API changes. - **FR-007**: CI MUST remove the podspec/SPM version-parity check and any job dependencies on it; all remaining CI jobs MUST pass. - **FR-008**: The in-repo example app MUST build on iOS via SPM with deployment target 15.0 and no CocoaPods artifacts. From 026895f5d81d57995bcbe55759bf9cc8926849d6 Mon Sep 17 00:00:00 2001 From: Muzahidul Islam Date: Wed, 14 Oct 2026 21:18:17 +0600 Subject: [PATCH 9/9] docs: address review: native SDK versions, Android compile SDK, SPM opt-out migration step Co-Authored-By: Claude Opus 5.5 --- .github/copilot-instructions.md | 2 +- README.md | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 9e59e24..6799074 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -12,7 +12,7 @@ This is a **Flutter SDK** wrapping native Optimizely SDKs for iOS and Android. - **Dart/Flutter**: Cross-platform API layer (>=2.16.2, Flutter >=3.44.0) - **Swift**: iOS native bridge (Swift 5.0, iOS 15.0+) - **Kotlin/Java**: Android native bridge (Kotlin 2.1.0, Android 5.0+) -- **Native SDKs**: OptimizelySwiftSDK 5.2.1 (iOS), android-sdk 5.1.1 (Android) +- **Native SDKs**: OptimizelySwiftSDK 5.5.0 (iOS), android-sdk 5.4.0 (Android) ## Folder Structure diff --git a/README.md b/README.md index 7c3f812..c7708c8 100644 --- a/README.md +++ b/README.md @@ -19,13 +19,13 @@ Refer to the [Flutter SDK's developer documentation](https://docs.developers.opt See the [pubspec.yaml](https://github.com/optimizely/optimizely-flutter-sdk/blob/master/pubspec.yaml) file for Flutter version requirements. -On the Android platform, the SDK requires a minimum SDK version of 21 or higher and compile SDK version of 32. +On the Android platform, the SDK requires a minimum SDK version of 21 or higher and compile SDK version of 35. On the iOS platform, the SDK requires a minimum version of 15.0 and Flutter 3.44 or later. The SDK is integrated through Swift Package Manager (SPM) only; CocoaPods is no longer supported. #### Migrating from CocoaPods -1. Use Flutter 3.44 or later (SPM is enabled by default; otherwise run `flutter config --enable-swift-package-manager`). +1. Use Flutter 3.44 or later (SPM is enabled by default; otherwise run `flutter config --enable-swift-package-manager`). If your app's `pubspec.yaml` turns SPM off (`flutter: config: enable-swift-package-manager: false`), remove that setting. 2. Set your app's iOS deployment target to 15.0 or higher (Xcode Runner target, and `platform :ios` in your Podfile if you still have one). 3. Run `flutter pub get` and `flutter build ios`. Flutter migrates your Xcode project to SPM automatically. 4. If no other CocoaPods plugins remain, you may run `pod deintegrate` and delete your Podfile.