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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 27 additions & 2 deletions .github/workflows/examples.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,40 @@ jobs:

example_ios:
runs-on: macos-latest
strategy:
fail-fast: false
matrix:
dependency_manager: [cocoapods, spm]
include:
- dependency_manager: cocoapods
build_name: "CocoaPods"
flutter_config: "flutter config --no-enable-swift-package-manager"
cleanup_step: "echo 'Using CocoaPods - no cleanup needed'"
- dependency_manager: spm
build_name: "Swift Package Manager"
flutter_config: "flutter config --enable-swift-package-manager"
cleanup_step: "cd example/ios && rm -f Podfile Podfile.lock && rm -rf Pods"
name: iOS Example (${{ matrix.build_name }})
steps:
- uses: actions/checkout@v4
- uses: subosito/flutter-action@v2
with:
channel: "stable"
cache: true

- name: build
- name: Configure Flutter for ${{ matrix.build_name }}
run: ${{ matrix.flutter_config }}

- name: Bootstrap project
run: |
dart pub global activate melos
melos bootstrap
cd example && flutter build ios --debug --no-codesign

- name: Cleanup for ${{ matrix.build_name }}
run: ${{ matrix.cleanup_step }}

- name: Build iOS example with ${{ matrix.build_name }}
run: |
cd example
flutter clean
flutter build ios --debug --no-codesign
182 changes: 182 additions & 0 deletions SPM_MIGRATION_PLAN.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
# Swift Package Manager Migration Plan

## Overview
Migrate `workmanager_apple` plugin to support Swift Package Manager (SPM) while maintaining full CocoaPods backward compatibility.

## Current Structure Analysis
```
workmanager_apple/ios/
├── Assets/
├── Classes/
│ ├── BackgroundTaskOperation.swift
│ ├── BackgroundWorker.swift
│ ├── Extensions.swift
│ ├── LoggingDebugHandler.swift
│ ├── NotificationDebugHandler.swift
│ ├── SimpleLogger.swift
│ ├── ThumbnailGenerator.swift
│ ├── UserDefaultsHelper.swift
│ ├── WMPError.swift
│ ├── WorkmanagerDebugHandler.swift
│ ├── WorkmanagerPlugin.swift
│ └── pigeon/
│ └── WorkmanagerApi.g.swift
├── Resources/
│ └── PrivacyInfo.xcprivacy
└── workmanager_apple.podspec
```

## Migration Strategy

### Phase 1: SPM Structure Setup
1. Create `workmanager_apple/ios/Package.swift`
2. Create new directory structure:
```
workmanager_apple/ios/
├── Sources/
│ └── workmanager_apple/
│ ├── include/
│ │ └── workmanager_apple-umbrella.h (if needed)
│ └── [all .swift files moved here]
└── Resources/
└── PrivacyInfo.xcprivacy
```

### Phase 2: File Migration
- **Move Swift files** from `Classes/` to `Sources/workmanager_apple/`
- **Preserve pigeon structure** as `Sources/workmanager_apple/pigeon/`
- **Update import statements** if needed
- **Handle resources** - PrivacyInfo.xcprivacy

### Phase 3: Configuration Files
- **Create Package.swift** with proper target definitions
- **Update podspec** to reference new file locations
- **Maintain backward compatibility** for CocoaPods users

### Phase 4: Testing Strategy
- **Dual build testing** in GitHub Actions
- **CocoaPods build**: Test existing workflow
- **SPM build**: New workflow for SPM validation
- **Example app testing**: Both dependency managers

## Implementation Details

### Package.swift Configuration
```swift
// swift-tools-version: 5.9
import PackageDescription

let package = Package(
name: "workmanager_apple",
platforms: [
.iOS(.v14)
],
products: [
.library(name: "workmanager_apple", targets: ["workmanager_apple"])
],
targets: [
.target(
name: "workmanager_apple",
resources: [.process("Resources")]
)
]
)
```

### GitHub Actions Strategy

**Matrix Strategy using Flutter SPM configuration:**
- **CocoaPods Build**: `flutter config --no-enable-swift-package-manager` + build
- **SPM Build**: `flutter config --enable-swift-package-manager` + build

**Key Features:**
1. **Flutter-native approach**: Use `flutter config` flags to switch dependency managers
2. **Simple validation**: Does example app build and run with both configurations?
3. **Matrix builds**: Test both `--enable-swift-package-manager` and `--no-enable-swift-package-manager`

**GitHub Actions Matrix:**
```yaml
strategy:
matrix:
spm_enabled: [true, false]
include:
- spm_enabled: true
config_cmd: "flutter config --enable-swift-package-manager"
name: "SPM"
- spm_enabled: false
config_cmd: "flutter config --no-enable-swift-package-manager"
name: "CocoaPods"
```

## Risk Mitigation

### Backward Compatibility
- **Keep CocoaPods support** indefinitely
- **Update podspec paths** to point to new locations
- **Test both build systems** in CI

### File Organization
- **Maintain logical grouping** of Swift files
- **Preserve pigeon integration** with generated files
- **Handle resources properly** in both systems

### Dependencies
- **No external Swift dependencies** currently - simplifies migration
- **Flutter framework dependency** handled by both systems

## Testing Requirements

### Pre-Migration Tests
- [ ] Current CocoaPods build works
- [ ] Example app builds and runs
- [ ] All functionality works on physical device

### Verification Strategy
**Simple test**: Does the example app build and run with both dependency managers?

**CocoaPods Build:**
```bash
flutter config --no-enable-swift-package-manager
cd example && flutter build ios --debug --no-codesign
```

**SPM Build:**
```bash
flutter config --enable-swift-package-manager
cd example && flutter build ios --debug --no-codesign
```

**Flutter Requirements:**
- Flutter 3.24+ required for SPM support
- SPM is off by default, must be explicitly enabled

### CI/CD Integration
- Use Flutter's built-in SPM configuration flags
- Test both dependency managers via matrix builds
- No separate long-lived branches needed

## Implementation Phases

### Phase 1: Directory Restructure (First Commit)
1. Create SPM-compliant directory structure
2. Move all Swift files to `Sources/workmanager_apple/`
3. Update podspec to reference new locations
4. Ensure CocoaPods + Pigeon still work
5. **Verification**: Example app builds and runs with CocoaPods

### Phase 2: SPM Configuration (Second Commit)
1. Add `Package.swift` with proper configuration
2. Handle resources (PrivacyInfo.xcprivacy)
3. **Verification**: Example app builds and runs with SPM

### Phase 3: CI Integration (Third Commit)
1. Update GitHub Actions to test both dependency managers
2. Use Flutter config flags for SPM/CocoaPods selection

## Success Criteria
- ✅ SPM support working in Flutter projects
- ✅ Full CocoaPods backward compatibility maintained
- ✅ All existing functionality preserved
- ✅ CI/CD tests both dependency managers
- ✅ No breaking changes for existing users
- ✅ Proper resource and privacy manifest handling
1 change: 0 additions & 1 deletion example/ios/Podfile
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ target 'Runner' do
use_modular_headers!

flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))

target 'RunnerTests' do
inherit! :search_paths
end
Expand Down
17 changes: 2 additions & 15 deletions example/ios/Podfile.lock
Original file line number Diff line number Diff line change
@@ -1,48 +1,35 @@
PODS:
- Flutter (1.0.0)
- integration_test (0.0.1):
- Flutter
- path_provider_foundation (0.0.1):
- Flutter
- FlutterMacOS
- permission_handler_apple (9.3.0):
- Flutter
- shared_preferences_foundation (0.0.1):
- Flutter
- FlutterMacOS
- workmanager_apple (0.0.1):
- Flutter

DEPENDENCIES:
- Flutter (from `Flutter`)
- integration_test (from `.symlinks/plugins/integration_test/ios`)
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- workmanager_apple (from `.symlinks/plugins/workmanager_apple/ios`)

EXTERNAL SOURCES:
Flutter:
:path: Flutter
integration_test:
:path: ".symlinks/plugins/integration_test/ios"
path_provider_foundation:
:path: ".symlinks/plugins/path_provider_foundation/darwin"
permission_handler_apple:
:path: ".symlinks/plugins/permission_handler_apple/ios"
shared_preferences_foundation:
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
workmanager_apple:
:path: ".symlinks/plugins/workmanager_apple/ios"

SPEC CHECKSUMS:
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
integration_test: 4a889634ef21a45d28d50d622cf412dc6d9f586e
path_provider_foundation: 608fcb11be570ce83519b076ab6a1fffe2474f05
permission_handler_apple: 4ed2196e43d0651e8ff7ca3483a069d469701f2d
shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7
workmanager_apple: 46692e3180809ea34232c2c29ad16d35ab793ded
workmanager_apple: 904529ae31e97fc5be632cf628507652294a0778

PODFILE CHECKSUM: bf5d48b0f58a968d755f5b593e79332a40015529
PODFILE CHECKSUM: 1959d098c91d8a792531a723c4a9d7e9f6a01e38

COCOAPODS: 1.16.2
26 changes: 22 additions & 4 deletions example/ios/Runner.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; };
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 */; };
Expand Down Expand Up @@ -73,6 +74,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */,
A4F342DE9D752B13EF553010 /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down Expand Up @@ -196,6 +198,9 @@
dependencies = (
);
name = Runner;
packageProductDependencies = (
78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */,
);
productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application";
Expand Down Expand Up @@ -251,6 +256,9 @@
Base,
);
mainGroup = 97C146E51CF9000F007C117D;
packageReferences = (
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */,
);
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = "";
projectRoot = "";
Expand Down Expand Up @@ -361,16 +369,12 @@
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/integration_test/integration_test.framework",
"${BUILT_PRODUCTS_DIR}/path_provider_foundation/path_provider_foundation.framework",
"${BUILT_PRODUCTS_DIR}/shared_preferences_foundation/shared_preferences_foundation.framework",
"${BUILT_PRODUCTS_DIR}/workmanager_apple/workmanager_apple.framework",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/integration_test.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider_foundation.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/shared_preferences_foundation.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/workmanager_apple.framework",
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down Expand Up @@ -840,6 +844,20 @@
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */

/* Begin XCLocalSwiftPackageReference section */
781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "FlutterGeneratedPluginSwiftPackage" */ = {
isa = XCLocalSwiftPackageReference;
relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage;
};
/* End XCLocalSwiftPackageReference section */

/* Begin XCSwiftPackageProductDependency section */
78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = {
isa = XCSwiftPackageProductDependency;
productName = FlutterGeneratedPluginSwiftPackage;
};
/* End XCSwiftPackageProductDependency section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,24 @@
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<PreActions>
<ExecutionAction
ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
<ActionContent
title = "Run Prepare Flutter Framework Script"
scriptText = "/bin/sh &quot;$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh&quot; prepare&#10;">
<EnvironmentBuildable>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</EnvironmentBuildable>
</ActionContent>
</ExecutionAction>
</PreActions>
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
Expand Down
Loading
Loading