15

I'm trying to create a Flutter Plugin to use a native library. This library I'm trying to use is stored in a private repository and can be used with Swift Dependency Manager.

This is causing me a headache, cause I can't add a private repository dependency in my plugin (I couldn't find a way to do this in .podspec file), so what I've done:

  1. I've added the plugin to the Example project with Swift Package Manager
  2. Manually copied MyDependency.xcframework folder to MyPlugin/ios folder
  3. Referenced it in podspec file, like this:
s.preserve_paths = 'MyDependency.xcframework'
s.xcconfig = { 'OTHER_LDFLAGS' => '-framework MyDependency' }
s.vendored_frameworks = 'MyDependency.xcframework'

Doing this I'm able to use MyDependency inside plugin's sources.

My current problem is: This is only working in Simulator.

Before doing this, the project was running without any problem in real devices.

This is the error message I'm receiving every time I tried to run in a real device: enter image description here

Also, I've made a test using the dependency directly from Swift Dependency Manager and is working fine. I think the problem is the way I'm adding the framework to my plugin.

enter image description here

siega
  • 2,508
  • 1
  • 19
  • 22
  • Have you successfully built an app on an iOS physical device without `.xcframework`? Also, you don't need all 3 steps you added (cocoapods, SPM and xcframework). Just drag `.xcframework` into your project. – Ben Butterworth Sep 17 '21 at 18:01
  • Yeah, without the .xcframework it's working fine. I don't wan't to drag the .xcframework directly to my project. I need to create a plugin that can be used in other projects that we have here. Also, I (think) don't have directly access to the .xcframework. I do these 3 steps just to get the files downloaded by SPM. – siega Sep 17 '21 at 18:21
  • So what is your desired distribution strategy for that closed source code? I don't understand how you can use SPM if its closed source. – Ben Butterworth Sep 17 '21 at 18:44
  • That first screenshot happens when you haven't set up your project/ device correctly. Your device needs to be automatically added to your provisioning profile. I presume you have the Apple Developer Program membership and selected the correct team under signing and capabilities? – Ben Butterworth Sep 17 '21 at 19:04
  • The second screenshot (showing SPM in your example project Runner) is just your library, it may/ may not have the `.xcframework`. Also, when a user adds your plugin to their project, your plugin gets added via cocoapods, not SPM. When you block out names, you make things harder to see . SPM is not applicable for you, IMHO. – Ben Butterworth Sep 17 '21 at 19:06
  • I think you don't understand the point here... I'm creating a Flutter plugin, that will be used in our projects. It's just a wrapper that contains an Android/iOS SDK and provide some methods to interact with them. The iOS SDK is provided in a private repo. As I can't add a private repository to a podspec file, my alternative was to add this dependency in other project with SPM and manually copied the .xcframework to my plugin folder. So I've just used my Flutter Plugin in my main Flutter project. The problem is after that, I can't be able to run in a real iOS device anymore... – siega Sep 17 '21 at 19:15
  • `my alternative was to add this dependency in other project with SPM and manually copied the .xcframework to my plugin folder` It's really not clear why you're using SPM if you're going to drag the XCF into the plugin folder. Both are bad ideas though, for Flutter plugins. You should configure XCF in Cocoapods using `vendored_frameworks`. It's not a well documented feature though: [this](https://github.com/CocoaPods/CocoaPods/issues/10811) issue has an example project. I've never used it – Ben Butterworth Sep 17 '21 at 19:30
  • One reason why SPM is bad in Flutter is installing a package in Flutter is not going to add `Package.resolved` in the Xcodeproj. It only changes the dependencies on the Podfile/ output of `pod install`. – Ben Butterworth Sep 17 '21 at 19:32

3 Answers3

18

I got mine working by adding the following lines to the xxx.podspec:

s.preserve_paths = 'xxxxxx.xcframework/**/*'
s.xcconfig = { 'OTHER_LDFLAGS' => '-framework xxxxxx' }
s.vendored_frameworks = 'xxxxxx.xcframework'

Notice the /**/* at the end of s.preserve_paths

Here is the full xxx.podspec file:

#
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
# Run `pod lib lint xxx.podspec` to validate before publishing.
#
Pod::Spec.new do |s|
  s.name             = 'xxx'
  s.version          = '1.0.0'
  s.summary          = 'xxx'
  s.description      = <<-DESC
xxx is a flutter plugin
                       DESC
  s.homepage         = 'https://example.com'
  s.license          = { :file => '../LICENSE', :type => 'BSD' }
  s.author           = { 'xxx' => 'email@example.com' }
  s.source           = { :path => '.' }
  s.source_files = 'Classes/**/*'
  s.dependency 'Flutter'
  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'

  s.preserve_paths = 'xxxxxx.xcframework/**/*'
  s.xcconfig = { 'OTHER_LDFLAGS' => '-framework xxxxxx' }
  s.vendored_frameworks = 'xxxxxx.xcframework'
end

Make sure to copy the xxxxxx.xcframework directory to .../xxx/ios/xxxxxx.xcframework (where your plugin xxx.podspec is located)


Add xxxxxx.xcframework to Pods scheme to be able to import it

The Frameworks directory should be below the Pods scheme when running the flutter command to create a plugin ie.

flutter create --org com.xxx --template=plugin --platforms=android,ios -a java -i swift xxx

Source: Developing packages & plugins

(If not, add it)

Adding framework 1

  1. Navigate to Pods scheme and right-click on Frameworks
  2. Click Add Files to "Pods"...

enter image description here

  1. Navigate to the root of the ios directory where the xxxxxx.xcframework is located
  2. Uncheck Copy items if needed
  3. Select Create folder references
  4. Check Pods-Runner
  5. Check your plugin ie. xxx
  6. Click Add

Embed xxxxxx.xcframework into your project

(this should allow you to run/publish to real devices)

enter image description here

  1. Click on the Pods scheme
  2. In TARGETS select Pods-Runner
  3. Click on the General tab
  4. Ensure the xxxxx.xcframework is present underneath Frameworks and Libraries (if not, add it by clicking on the +)
  5. Change Embed to Embed & Sign

enter image description here

  1. Click on your plugin ie. xxx
  2. Ensure the xxxxx.xcframework is present underneath Frameworks and Libraries
  3. Change Embed to Embed & Sign

enter image description here

  1. Again, for both targets below Pods scheme, the next steps should already be fine, but just check it
  2. Click Build Phases
  3. Click Link Binary With Libraries
  4. Ensure xxxxx.xcframework is present, if not add it by clicking the + and that the Status is set to Required

Now you should be able to import the framework and reference it in your code and should also publish perfectly fine.

Hope this helps, and good luck. I hope this doesn't change again soon

fingers crossed

Pierre
  • 8,397
  • 4
  • 64
  • 80
  • Thanks for publishing your solution!! I was reading and had a doubt, maybe you can explain this... I've noticed you manually added your framework file to Pods... In the Flutter default .gitignore file, this folder is listed to not be versioned... This will really work for other developers as well or it's a local solution? – siega Dec 03 '21 at 11:40
  • 1
    @siega It will work, as long as you embed (step 8) the xcframework. – Pierre Dec 06 '21 at 05:11
  • @Pierre what role does the pubspec file play and do I have to run a cocoapods command? Also what if I have multiple xcframework's, how do I reference multiple frameworks in the pubspec file? – Neigaard Mar 22 '22 at 11:29
  • @Neigaard the `pubspec.yml` in the root of the Flutter plugin is basically just a description, version, etc. of the Flutter plugin, as well as a reference to the `pluginClass` in the different platforms (for iOS -> points to the `YourPlugin.h` header). This answer has everything to do with the iOS side of the plugin and nothing else. For multiple frameworks, you need to reference each in the `ios/xxx.podspec` file (first three lines of this answer duplicated below each other `s.preserve_paths, s.xcconfig, s.vendored_frameworks` then again `s.preserve_paths, s.xcconfig, s.vendored_frameworks`) – Pierre Mar 23 '22 at 02:47
  • @Pierre sorry I meant podspec not pubspec :) Should I duplicate the lines so that I have multiple s.preserve_paths lines, or something like this: s.preserve_paths = 'X1.xcframework/**/*, X2.xcframework/**/*' s.xcconfig = { 'OTHER_LDFLAGS' => '-framework WebRTC -framework X1 -framework X2' } s.vendored_frameworks = 'X1.xcframework, X2.xcframework' – Neigaard Mar 23 '22 at 09:10
  • @Neigaard To my mind separate entries will work, but if you can merge it it should be fine as well. I haven't tested it and can't at this point. Maybe if you know, update this answer with the solution for multiple xcframeworks – Pierre Mar 23 '22 at 11:37
  • @Pierre What if i have .a file? Its an Objective-C static library. Is the process the same? Thank you for any help! – pasty Oct 18 '22 at 20:17
  • @pasty I have never done that before, but it seems possible - have a look at this post https://stackoverflow.com/questions/50441763/embedding-pods-to-static-library-ios – Pierre Oct 19 '22 at 05:57
  • Is there a way to automate Add xcframework to Pods scheme? – Vignesh Jul 04 '23 at 07:37
  • @Vignesh What do you mean with "automate"? I'm not sure there is a script or something you can use. Because it is XCode - you have to use the UI in order for everything to work. – Pierre Jul 05 '23 at 08:16
  • @Pierre, Whenever I run the Flutter application from Android Studio, pod install gets executed and the changes we made in Pods project scheme gets lost and I have to do it again. – Vignesh Jul 06 '23 at 06:14
  • @Vignesh That doesn't sound right. Are you running Android Studio on a mac? The above describes how to include the framework in your `Flutter Plugin` you build. Then your plugin is referenced by your Flutter project. You shouldn't do this directly in your Flutter project. – Pierre Jul 06 '23 at 06:43
  • @Pierre, Yeah I am running Android Studio on a mac. I followed the exact steps from your answer to add XCFramework into my `Flutter Plugin`. Then if I run the example application directly from Xcode it is working as expected but if I run from the Android Studio internally it executes `pod install` and the configuration we made in Xcode is lost, so I have to again add the XCFramework under Framework and Libraries section then again it works from Xcode. – Vignesh Jul 06 '23 at 13:59
1

After doing some research, I've found some links giving me an ideia about the real problem...

To solve this, I've added a simple script to my main project's build process.

This script adds the code signing to inner .framework files.

cd "${CODESIGNING_FOLDER_PATH}/Frameworks/"

# flatten nested frameworks by copying to APP.app/Frameworks
for framework in *; do
    if [ -d "$framework" ]; then
        if [ -d "${framework}/Frameworks" ]; then
            echo "Moving embedded frameworks from ${framework} to ${PRODUCT_NAME}.app/Frameworks"
            cp -R "${framework}/Frameworks/" .
            rm -rf "${framework}/Frameworks"
        fi
    fi
done

# remove any leftover nested frameworks (i.e. 'PackageName_359AFEED79E48935_PackageProduct.framework')
for framework in *; do
    if [ -d "$framework" ]; then
        if [ -d "${framework}/Frameworks" ]; then
            echo "Removing embedded frameworks from ${framework} to ${PRODUCT_NAME}.app/Frameworks"
            rm -rf "${framework}/Frameworks"
        fi
    fi
done

# codesign for Debugging on device
if [ "${CONFIGURATION}" == "Debug" ] & [ "${SDKROOT}" != *Simulator* ] ; then

    echo "Code signing frameworks..."
    find "${CODESIGNING_FOLDER_PATH}/Frameworks" -maxdepth 1 -name '*.framework' -print0 | while read -d $'\0' framework
    do
        # only sign frameworks without a signature
        if ! codesign -v "${framework}"; then
            codesign --force --sign "${EXPANDED_CODE_SIGN_IDENTITY}" --preserve-metadata=identifier,entitlements --timestamp=none "${framework}"
            echo "Added missing signature to '${framework}'"
        fi
    done
fi
siega
  • 2,508
  • 1
  • 19
  • 22
0

If non of the above fixes work and you still get errors like "framework xxx not found", verify if the xxx.xcframework and xxx.framework folders inside have the same name.

I spent a few days failing to add .xcframework to Flutter plugin, and it turned out there was a mismatch in naming.