Skip to content

Instantly share code, notes, and snippets.

@MapaX
Created April 26, 2021 10:45
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save MapaX/5dc58ccb16ad1f772907154ae4991dca to your computer and use it in GitHub Desktop.
Save MapaX/5dc58ccb16ad1f772907154ae4991dca to your computer and use it in GitHub Desktop.
Shell script to create xcframeworks from MLKit frameworks
#!/bin/zsh
makeXCFramework () {
BASEDIR=$(pwd)
echo "Script location: ${BASEDIR}"
LIBNAME=$(basename $BASEDIR)
echo "lib is: $LIBNAME"
cd Frameworks
mkdir -p iphoneos
mkdir -p iphonesimulator
# Copy framework into the platform specific directories
cp -R $LIBNAME.framework/ iphoneos/$LIBNAME.framework
cp -R $LIBNAME.framework/ iphonesimulator/$LIBNAME.framework
xcrun lipo -remove x86_64 ./iphoneos/$LIBNAME.framework/$LIBNAME -o ./iphoneos/$LIBNAME.framework/$LIBNAME
xcrun lipo -remove arm64 ./iphonesimulator/$LIBNAME.framework/$LIBNAME -o ./iphonesimulator/$LIBNAME.framework/$LIBNAME
xcodebuild -create-xcframework -framework iphoneos/$LIBNAME.framework/ -framework iphonesimulator/$LIBNAME.framework/ -output ../../"$LIBNAME.xcframework"
cd ..
cd ..
}
cd MLKitCommon
makeXCFramework
cd MLKitVision
makeXCFramework
cd MLKitTextRecognition
makeXCFramework
@MapaX
Copy link
Author

MapaX commented Apr 26, 2021

This is a shell script which is used to circumvent the Google MLKit requirement to Use cocoa pods.
This is still work in progress, but it now creates 3 frameworks.

MLKitCommon.xcframework
MLKitVision.xcframework
MLKitTextRecognition.xcframework

https://issuetracker.google.com/issues/174418229

@AngryDuckFTW
Copy link

where do you run this in order for it to make frameworks?

@nilsnilsnils
Copy link

nilsnilsnils commented Apr 30, 2021

I think the script must be run from your local pod folder.

I use the MLKIt and my pod folder contains:

.....
MLKitVision
MLKitVisionKit
MLKitObjectDetectionCommon
MLKitImageLabelingCommon
MLKitImageLabeling
MLKitFaceDetection
MLKitCommon
....

In your terminal something like this:
$ cd PathToPodFolder e.g. ../AppProject/Pod/
$ makeXCFamework.sh

@MapaX
Copy link
Author

MapaX commented Apr 30, 2021

This is still work in progress. I initially thought that inside Pod folder, but first i need to figure out a way to get all frameworks there.

@nilsnilsnils
Copy link

nilsnilsnils commented May 3, 2021

Maybe this is a solution:

Pre-compiling dependencies

Test Podfile (nothing else is needed):

platform :ios, '10.0'
use_frameworks!

plugin 'cocoapods-binary'

pod 'GoogleMLKit/FaceDetection', '0.64.0', :binary => true
pod 'GoogleMLKit/ImageLabeling', '0.64.0', :binary => true

Run
$ pod install

After that there is a _Prebuild folder inside the Pods folder:
Bildschirmfoto 2021-05-03 um 16 37 03

Maybe I can combine all prebuilds into xcframework with a script based on this one here.
Will test that in the next days.

@AngryDuckFTW
Copy link

Maybe this is a solution:

Pre-compiling dependencies

Test Podfile (nothing else is needed):

platform :ios, '10.0'
use_frameworks!

plugin 'cocoapods-binary'

pod 'GoogleMLKit/FaceDetection', '0.64.0', :binary => true
pod 'GoogleMLKit/ImageLabeling', '0.64.0', :binary => true

Run
$ pod install

After that there is a _Prebuild folder inside the Pods folder:
Bildschirmfoto 2021-05-03 um 16 37 03

Maybe I can combine all prebuilds into xcframework with a script based on this one here.
Will test that in the next days.

This sounds quite promising, ill look forward to seeing if it works

@nilsnilsnils
Copy link

nilsnilsnils commented May 4, 2021

I have create a script which builds a XCFramwork for every framework inside the _Prebuild folder.
I hope that works for different Podfiles. I used the Podfile from above for all my tests.

You have to put the script inside the Pods folder (see screenshot) and run $ ./makeXCFrameworks.sh.
The script creates the folder _XCFramework under Pods/_Prebuild/GeneratedFrameworks which contains all created XCFrameworks after the script has finished.

GoogleMLKit is only the combine header and it will skipped.

Bildschirmfoto 2021-05-04 um 13 08 21

makeXCFrameworks.sh (Beta)

#!/bin/zsh

makeXCFramework () 
{
    if [ $# -eq 0 ]; then
        echo "Error: Missing arguments for makeXCFramework, (1) frameworkdir (2) libname (3) output folder"
        exit 1
    fi

    BASEDIR=$1
    LIBNAME=$2
    
    # Enable for debugging
    # echo "Script location: ${BASEDIR}\n"
    # echo "lib is: $LIBNAME"
    # echo "$(pwd)"
    
    # Copy framework into the platform specific directories
    
    # remove old
    rm -rf iphoneos
    rm -rf iphonesimulator

    # create dirs       
    mkdir -p iphoneos
    mkdir -p iphonesimulator

    cp -R $LIBNAME.framework/ iphoneos/$LIBNAME.framework
    cp -R $LIBNAME.framework/ iphonesimulator/$LIBNAME.framework

    xcrun lipo -remove x86_64 ./iphoneos/$LIBNAME.framework/$LIBNAME -o ./iphoneos/$LIBNAME.framework/$LIBNAME
    xcrun lipo -remove arm64 ./iphonesimulator/$LIBNAME.framework/$LIBNAME -o ./iphonesimulator/$LIBNAME.framework/$LIBNAME
    xcodebuild  -create-xcframework -framework iphoneos/$LIBNAME.framework/ -framework iphonesimulator/$LIBNAME.framework/ -output "$3/$LIBNAME.xcframework"
}

# Enter  _Prebuild/GeneratedFrameworks
PreBuildFrameworkdFolder="_Prebuild/GeneratedFrameworks" 

if [[ ! -d $PreBuildFrameworkdFolder ]]; then
    echo "Missing _Prebuild folder"
    exit 1
fi

cd $PreBuildFrameworkdFolder
echo "Enter $PreBuildFrameworkdFolder \n"

# Create output folder for XCFrameworks
XCFrameworksOutputFolder="$(pwd)/_XCFrameworks"

# echo "$XCFrameworksOutputFolder"
rm -rf "$XCFrameworksOutputFolder"
mkdir $XCFrameworksOutputFolder


WorkingDir=$(pwd)


for FrameworkBaseFolder in */
do
    # cut of "/"
    FrameworkBaseFolder=${FrameworkBaseFolder%*/}
    #echo ${FrameworkBaseFolder##*/}

    # Skip _XCFrameworks
    if [[ $FrameworkBaseFolder == "_XCFrameworks" ]] then
        continue
    fi

    # Only combined header - handled later
    if [[ $FrameworkBaseFolder == "GoogleMLKit" ]] then
        echo "\nSkip GoogleMLKit"
        continue
    fi

    FrameworkFolder=""
    FrameworkPath="$FrameworkBaseFolder"

    # All MLKit folder have a subfolder which contains the framework
    # E.g. MLKitCommon/Frameworks/MLKitCommon.framework
    #
    # All other framworks are in the root framwork folder
    # E.g. GoogleUtilities/GoogleUtilities.framework
    if [[ $FrameworkBaseFolder == MLKit* ]] then
        FrameworkPath="$FrameworkBaseFolder/Frameworks"
    fi

    cd "./$FrameworkPath"
    FrameworkName=$(find . -name "*.framework" -maxdepth 1)
    FrameworkName=$(basename $FrameworkName)
    FrameworkName=${$(basename $FrameworkName)%.*}

    FrameworkPath="$WorkingDir/$FrameworkPath/$FrameworkName"
    echo "\nMake XCFramework of $FrameworkPath"

    makeXCFramework $FrameworkPath $FrameworkName $XCFrameworksOutputFolder

    cd $WorkingDir
done

echo "\n ==== XCFrameworks can be found in: $XCFrameworksOutputFolder "

Next step is to create a complete swift package which includes all XCFrameworks and the GoogleMLKit.

@AngryDuckFTW
Copy link

when I run this I get errors on the if then statements starting from line 65. And once those were fixed I get an error on line 90: ${$(basename $FrameworkName)%.*}: bad substitution

do I need to be running this on a certain version of zsh or something? or is this an error in the file, sorry im not good with macOS terminal stuff.

@nilsnilsnils
Copy link

nilsnilsnils commented May 4, 2021

when I run this I get errors on the if then statements starting from line 65. And once those were fixed I get an error on line 90: ${$(basename $FrameworkName)%.*}: bad substitution

do I need to be running this on a certain version of zsh or something? or is this an error in the file, sorry im not good with macOS terminal stuff.

I used zsh yes and I am no bash expert ;) . Maybe I have installed some package for that.

Can you test this for line 90

    TMP=$(basename $FrameworkName)
    FrameworkName="${TMP%.*}"

Can you send me your fix for line 65 ?

My specs:
macOS 11.3 BigSur 20E232
zsh 5.8 (x86_64-apple-darwin20.0)
GNU bash, version 3.2.57(1)-release (x86_64-apple-darwin20)

@AngryDuckFTW
Copy link

I have it running now, but it only generates frameworks for the stuff that starts with ML, but in your screenshot I see its made them for everything ie google utilities etc? only way I could get this working was to actually copy everything into the GeneratedFrameworks folder because I think the script runs in the wrong place... I might be wrong but im really not too sure what im doing. I just want MLKit as some frameworks so I can skip using cocoa pods!

@nilsnilsnils
Copy link

nilsnilsnils commented May 4, 2021

  1. Create new folder
  2. Create Pod file inside the new folder with the following content
platform :ios, '10.0'
use_frameworks!

plugin 'cocoapods-binary'

pod 'GoogleMLKit/FaceDetection', '0.64.0', :binary => true
pod 'GoogleMLKit/ImageLabeling', '0.64.0', :binary => true
  1. Open terminal and change to the created folder and run pod install
  2. Copy makeXCFrameworks.sh into Pods folder
  3. Change to Pods folder in terminal
  4. Call ./makeXCFrameworks.sh (current dir should be Pods)

After that I have all XCFrameworks in _XCFrameworks like in my screenshot above -> ML* , Google*, nanopb, etc.

If you have some problems you can enable the debug echo and maybe you found something that is not right on your side.

-> If all works change the pod file for your needs.

@MapaX
Copy link
Author

MapaX commented May 4, 2021

I got the swift package manager version of ml vision working. (Contains only the MLKitTextRecognition, not all libs)

Here is gist for the Package.swift: https://gist.github.com/MapaX/8c9b47b1683ef188eaf30d8b2d9d03f1

Here is image about the folder structure. It requires one dummy source file, so it bundles correctly.

Screenshot 2021-05-04 at 16 16 36

The MLKit .frameworks do not contain Info.plist files to those need to be generated manually under the .framework directories before installing to device happens correctly. (It will complain on startup that Info.plist is missing)

@nilsnilsnils
Copy link

nilsnilsnils commented May 4, 2021

I got the swift package manager version of ml vision working. (Contains only the MLKitTextRecognition, not all libs)

Here is gist for the Package.swift: https://gist.github.com/MapaX/8c9b47b1683ef188eaf30d8b2d9d03f1

Here is image about the folder structure. It requires one dummy source file, so it bundles correctly.

Screenshot 2021-05-04 at 16 16 36

The MLKit .frameworks do not contain Info.plist files to those need to be generated manually under the .framework directories before installing to device happens correctly. (It will complain on startup that Info.plist is missing)

I saw that firebase use some of the same dependencies and the package file contains the links to the packages.
Maybe that can be used instead of precompiled frameworks

https://github.com/firebase/firebase-ios-sdk/blob/master/Package.swift

E.g.

 .package(
      name: "SwiftProtobuf",
      url: "https://github.com/apple/swift-protobuf.git",
      "1.15.0" ..< "2.0.0"
    ),

@AngryDuckFTW
Copy link

  1. Create new folder
  2. Create Pod file inside the new folder with the following content
platform :ios, '10.0'
use_frameworks!

plugin 'cocoapods-binary'

pod 'GoogleMLKit/FaceDetection', '0.64.0', :binary => true
pod 'GoogleMLKit/ImageLabeling', '0.64.0', :binary => true
  1. Open terminal and change to the created folder and run pod install
  2. Copy makeXCFrameworks.sh into Pods folder
  3. Change to Pods folder in terminal
  4. Call ./makeXCFrameworks.sh (current dir should be Pods)

After that I have all XCFrameworks in _XCFrameworks like in my screenshot above -> ML* , Google*, nanopb, etc.

If you have some problems you can enable the debug echo and maybe you found something that is not right on your side.

-> If all works change the pod file for your needs.

ive just tried that and its complaining a lot less now, but after its finished I still only have the ML frameworks, every other one says this on terminal when it gets to it "error: binaries with multiple platforms are not supported"

@nilsnilsnils
Copy link

nilsnilsnils commented May 4, 2021

  1. Create new folder
  2. Create Pod file inside the new folder with the following content
platform :ios, '10.0'
use_frameworks!

plugin 'cocoapods-binary'

pod 'GoogleMLKit/FaceDetection', '0.64.0', :binary => true
pod 'GoogleMLKit/ImageLabeling', '0.64.0', :binary => true
  1. Open terminal and change to the created folder and run pod install
  2. Copy makeXCFrameworks.sh into Pods folder
  3. Change to Pods folder in terminal
  4. Call ./makeXCFrameworks.sh (current dir should be Pods)

After that I have all XCFrameworks in _XCFrameworks like in my screenshot above -> ML* , Google*, nanopb, etc.
If you have some problems you can enable the debug echo and maybe you found something that is not right on your side.
-> If all works change the pod file for your needs.

ive just tried that and its complaining a lot less now, but after its finished I still only have the ML frameworks, every other one says this on terminal when it gets to it "error: binaries with multiple platforms are not supported"

Did you have the latest Xcode version 12.5 and cocoa pods 1.10.1 installed ?

I never saw this kind of problem on my side

@AngryDuckFTW
Copy link

  1. Create new folder
  2. Create Pod file inside the new folder with the following content
platform :ios, '10.0'
use_frameworks!

plugin 'cocoapods-binary'

pod 'GoogleMLKit/FaceDetection', '0.64.0', :binary => true
pod 'GoogleMLKit/ImageLabeling', '0.64.0', :binary => true
  1. Open terminal and change to the created folder and run pod install
  2. Copy makeXCFrameworks.sh into Pods folder
  3. Change to Pods folder in terminal
  4. Call ./makeXCFrameworks.sh (current dir should be Pods)

After that I have all XCFrameworks in _XCFrameworks like in my screenshot above -> ML* , Google*, nanopb, etc.
If you have some problems you can enable the debug echo and maybe you found something that is not right on your side.
-> If all works change the pod file for your needs.

ive just tried that and its complaining a lot less now, but after its finished I still only have the ML frameworks, every other one says this on terminal when it gets to it "error: binaries with multiple platforms are not supported"

Did you have the latest Xcode version 12.5 and cocoa pods 1.10.1 installed ?

I never saw this kind of problem on my side

Im using Catalina 10.15.7 and Xcode 12.4. Im updating to Big Sur now and then installing 12.5 so hopefully that will make it work, ill check back in when its all updated

@MapaX
Copy link
Author

MapaX commented May 5, 2021

I got the swift package manager version of ml vision working. (Contains only the MLKitTextRecognition, not all libs)
Here is gist for the Package.swift: https://gist.github.com/MapaX/8c9b47b1683ef188eaf30d8b2d9d03f1
Here is image about the folder structure. It requires one dummy source file, so it bundles correctly.
Screenshot 2021-05-04 at 16 16 36
The MLKit .frameworks do not contain Info.plist files to those need to be generated manually under the .framework directories before installing to device happens correctly. (It will complain on startup that Info.plist is missing)

I saw that firebase use some of the same dependencies and the package file contains the links to the packages.
Maybe that can be used instead of precompiled frameworks

https://github.com/firebase/firebase-ios-sdk/blob/master/Package.swift

E.g.

 .package(
      name: "SwiftProtobuf",
      url: "https://github.com/apple/swift-protobuf.git",
      "1.15.0" ..< "2.0.0"
    ),

I tried to use the protobuff from swiftpm, but there is something hardcoded to libs, those require .framework versions of libs and if you use the package, it will be in different form.

That is also the reason why the FBLPromises is bundled in with different name. So if you have firebase in use, there will be two versions of FBLPromises in the release build. One as FBLPromises.o and one as FBLPromises.framework.

I think this is something what needs to be fixed to the GoogleUtilitities.framework.

@nilsnilsnils
Copy link

I created my own MLKit Package.

  1. Read Readme.md and create Package
  2. Compile for Sim
  3. Compile for device over Xcode
  4. Upload archive to Apple for Testflight. Some errors only occur during the upload. See Readme.md
  5. Test your App using the Testflight version

Readme & Tools

@Alesete
Copy link

Alesete commented Dec 16, 2021

Hi! Thanks for this workaround, you save my day!

I had to make a fix in the script related the issue @AngryDuckFTW commented here. The only frameworks generated was the MLKit ones.

I found that the error "binaries with multiple platforms are not supported" was produced in the Google's dependencies because they have the armv7 architecture inside the frameworks (not only x86_64 and arm64), so the simulator fails because it has 2 architectures (x86_64 & armv7) of 2 different platform (device & simulator).

The fix in the script is quite easy. add the following line:

xcrun lipo -remove armv7 ./iphonesimulator/$LIBNAME.framework/$LIBNAME -o ./iphonesimulator/$LIBNAME.framework/$LIBNAME

below line 49.

I had also to change line 100 from this

if [[ $FrameworkBaseFolder == MLKit* ]] then

to this

if [[ $FrameworkBaseFolder == ML* ]] then

because I have MLImage as a dependency also

Thanks again, and best regards

@Mcrich23
Copy link

Mcrich23 commented Apr 5, 2022

How would I apply this to GoogleMLKit/Translate?

@MapaX
Copy link
Author

MapaX commented Apr 5, 2022

I think the easy way is to clone the https://github.com/nilsnilsnils/MLKitFrameworkTools repo, tune the Pod file and just follow the instructions in there.

I have not checked what the translate contains, but same approach should work just fine.

@huuchi207
Copy link

can anyone give me xcframework file of GoogleMLKit/FaceDetection. I tried the following script but in output folder, there is no framework at all :(

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment