I am adding Swift Package Manager support to the camera_avfoundation
package. See: flutter/packages#6710
However, the Mac_x64 ios_build_all_packages master
check fails:
Warning: Building for device with codesigning disabled. You will have to manually codesign before deploying to device.
Building com.example.allPackages for device (ios)...
Running pod install... 3.6s
Running Xcode build...
Xcode build done. 16.7s
Failed to build iOS app
Parse Issue (Xcode): Build a shadowed submodule 'camera_avfoundation.messages_g'
Encountered error while building for device.
Building all_packages
using Cocoapods failed.
You can reproduce this locally using Xcode 15.2 or older; Xcode 15.3 and newer do not reproduce this error:
-
Checkout my branch: https://github.com/loic-sharma/flutter-packages/tree/spm_camera
git checkout -b loic-sharma-spm_camera main git pull [email protected]:loic-sharma/flutter-packages.git spm_camera
-
Add
use_frameworks!
topackages/camera/camera_avfoundation/example/ios/Podfile
:diff --git a/packages/camera/camera_avfoundation/example/ios/Podfile b/packages/camera/camera_avfoundation/example/ios/Podfile index bcdae34190..61fe86ef41 100644 --- a/packages/camera/camera_avfoundation/example/ios/Podfile +++ b/packages/camera/camera_avfoundation/example/ios/Podfile @@ -28,6 +28,8 @@ require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelpe flutter_ios_podfile_setup target 'Runner' do + use_frameworks! + flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) target 'RunnerTests' do
-
Now build the
camera_avfoundation
example app using Cocoapods:sudo xcode-select --switch /path/to/xcode/15.2/or/older flutter config --no-enable-swift-package-manager cd packages/camera/camera_avfoundation/example flutter clean flutter build ios
This will error:
Running Xcode build... Xcode build done. 16.1s Failed to build iOS app Parse Issue (Xcode): Build a shadowed submodule 'camera_avfoundation.messages_g' Encountered error while building for device.
-
Follow the simple reproduction steps above
-
Open the example app in Xcode 15.2 or older
-
In
Project navigator
, openRunner
>Flutter
>Debug
. AppendVERBOSE_SCRIPT_LOGGING=true
. -
Clean your build folder & do a build
-
Open the
Report navigator
and expand the failing step: -
Copy the
clang
command. -
In your terminal, and run the
clang
command. This should reproduce the build error:fatal error: build a shadowed submodule 'camera_avfoundation.FLTCamMediaSettingsAVWrapper' /Users/loicsharma/Library/Developer/Xcode/DerivedData/Runner-ajljfhewikpbjigrasflzcnbhont/Build/Intermediates.noindex/Pods.build/Debug-iphonesimulator/camera_avfoundation.build/module.modulemap:1:18: note: previous definition is here framework module camera_avfoundation { ^ 1 error generated.
The build error is caused a clang
invocation that compiles a .m
file. The intermediate build folder included in this build invocation contains only the correct Cocoapods module map (it does not contain the Swift Package Manager module map).
The app builds fine using Cocoapods if you use Xcode 15.3.
If you edit the clang
compile command from the minimal repro above to use Xcode 15.3's clang
executable, the file compiles successfully.
This build error only reproduces if you use Flutter's Cocoapods integration. This build error does not reproduce if you enable Swift Package Manager.
This build error only reproduces if your app's Podfile
has use_frameworks!
.
The camera_avfoundation
example app builds fine if this line is removed.
Flutter plugins that support Swift Package Manager and have custom module maps will have two .modulemap
files.
For camera_avfoundation
:
- Cocoapods module map:
packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/include/cocoapods_camera_avfoundation.modulemap
- Swift package manager module map:
packages/camera/camera_avfoundation/ios/camera_avfoundation/Sources/camera_avfoundation/include/module.modulemap
If you rename or delete the Swift package manager module map, the build error no longer reproduces.
This indicates that clang
is unexpectedly using the Swift Package Manager module map when Swift Package Manager is disabled.
It appears that the Swift package manager module map is used unexpectedly when Flutter's Swift package manager feature is disabled. This hypothesis would also explain bug flutter#148307.
I've created an experiment that separates the public header file directories for Swift package manager and Cocoapods: flutter/packages#6732
The camera_avfoundation
plugin now has two directories containing public header files:
packages/camera/camera_avfoundation/Sources/camera_avfoundation/include
- This is the Cocoapods public header directorypackages/camera/camera_avfoundation/Sources/camera_avfoundation/public
- This is the SPM public header directory
This experiment appears to solve this issue.
However, this experiment has a drawback: it requires significant code duplication. I plan to do some follow-up experiments:
- Reduce the number of public header files by converting them to private header files. This would be a breaking change but it does reduce the code duplication significantly
- Use symlinks instead of duplicating header files. This would make it easier to keep header files synced across SPM and Cocoapods.
So I played around with this too, and I'm also not sure why it sometimes seems to use the
module.modulemap
and sometimes doesn't. Perhaps becausemodule.modulemap
is the implicit modulemap file.I did discover that if you change the imports to be not-relative, except for
messages.g.h
, it works fine:I don't understand why, though...
Since have 2 modulemaps seems to be an issue, we could also consider solving with tooling. An idea could be to update documentation to name the SwiftPM modulemap something else (not
module.modulemap
), perhapsplugin_name_swift_package.modulemap
and then via Flutter tooling when using Swift Package Manager, create a symlink formodule.modulemap
toplugin_name_swift_package.modulemap
. We'd need to figure out how to do that without altering pub-cache, though, since currently SwiftPM is using the plugin directly from pub-cache.I think the solution of using separate directories for CocoaPods vs SwiftPM (flutter/packages#6732) could also work and may be better so it's independent of the Flutter CLI version, but I would probably do something like the structure below. Since SwiftPM is going to eventually become the new standard and we'll eventually deprecate CocoaPods, we want it to be the more simple and the CocoaPods can be more difficult.
Structure:
->
in snippet below represents a symlinkPackage.swift
camera_avfoundation.podspec