How to properly embed 3rd party .dylib files in iOS app project for App Store release?
Asked Answered
M

6

6

I am building an iOS app using PJSIP library with H264 support. When building H264, I get 1 .a file and 2 .dylib files. I tried to use the .dylibs in my project by adding as "Embedded Libraries" and also by creating a separate framework and then adding it to "Embedded Libraries". But when uploading build to App Store, I'm getting errors "ERROR ITMS-90206:...", "ERROR ITMS-90171:..". All points to using external dynamic libraries in project. I followed https://developer.apple.com/library/content/technotes/tn2435/_index.html#//apple_ref/doc/uid/DTS40017543-CH1-TROUBLESHOOTING_BUNDLE_ERRORS-EMBEDDED__DYLIB_FILES

But they are asking to follow steps in "Adding A Framework Target". I cant figure out how to create a framework with only 2 .dylib files and no other source code or header files. Please show the steps for embedding .dylib files into iOS app for App Store submission.

Mcgrath answered 9/2, 2018 at 8:28 Comment(0)
S
2

You must add and embed all your shared libraries (.dylib files) directly to the toplevel application the same way as frameworks. You mustn't put dylibs inside a framework because Apple strongly recommends not to use such frameworks (known as Umbrella frameworks) and doesn't accept applications with such frameworks to AppStore (it is a reason for error ITMS-90171).

As Apple documentation says:

An umbrella framework is a framework bundle that contains other frameworks. While it is possible to create umbrella frameworks for macOS apps, doing so is unnecessary for most developers and is not recommended. Umbrella frameworks are not supported on iOS, watchOS, or tvOS.

Savannahsavant answered 6/5, 2018 at 11:7 Comment(5)
Hi, If I put the .dylibs into the My.App/Frameworks folder Apple rejects it because Invalid Swift Support - The SwiftSupport folder is missing. Rebuild your app using the current public (GM) version of Xcode and resubmit it. Could you please let me know if the iOS app can contain .dylib files or they need to be converted into frameworks?Jornada
@Jornada I have the same issue. See #53819272 Did you resolve it in the end?Amplification
Yes, but it got complicated. We made from all the .dylibs frameworks. Then the executable app is parsed and all the references to the dylibs are replaced with the path to the framework/library name.Jornada
2019, ended up having exactly that problem with Invalid Swift Support. Did you ever solve this issue @smaryus?Drover
Yes, we changed the dylibs to frameworks and use the frameworks at linking. You can try to recreate the project for libs and change the compilation output from dylibs to frameworks. Or the more complicate way manually create the minimalist framework structure and us it at linker with your application.Jornada
E
8

I was able to submit an app with a .dylib by creating a framework first. I tried the method described in the apple documentation, but got errors when submitting to the store ( "invalid bundle. the bundle at xyz.framework contains disallowed file 'Frameworks' )

I used the lipo tool to manually package my .dylib into a framework.

$ lipo -create mylib.dylib -output mylib

Now you get a binary file called 'mylib' Create a folder called mylib.framework and put the binary file in it. Then add an Info.plist ( you can just copy and modify one from an existing framework. ) In the Info.plist, fill out the fields for your framework. The main one to update or add is "Executable" which should be the name of that binary file.

I had another issue where another framework I was using was referencing my original .dylib, which is gone and now inside the framework. To fix this I used the "install_name_tool" to modify the other framework to look for my new framework.

$ cd OtherLib.framework 
$ install_name_tool -change @rpath/mylib.dylib @rpath/mylib.framework/mylib OtherLib

You can read more about that tool and modifying various paths in your libs in this good blog post: https://medium.com/@donblas/fun-with-rpath-otool-and-install-name-tool-e3e41ae86172

Elexa answered 17/7, 2020 at 22:39 Comment(0)
S
2

You must add and embed all your shared libraries (.dylib files) directly to the toplevel application the same way as frameworks. You mustn't put dylibs inside a framework because Apple strongly recommends not to use such frameworks (known as Umbrella frameworks) and doesn't accept applications with such frameworks to AppStore (it is a reason for error ITMS-90171).

As Apple documentation says:

An umbrella framework is a framework bundle that contains other frameworks. While it is possible to create umbrella frameworks for macOS apps, doing so is unnecessary for most developers and is not recommended. Umbrella frameworks are not supported on iOS, watchOS, or tvOS.

Savannahsavant answered 6/5, 2018 at 11:7 Comment(5)
Hi, If I put the .dylibs into the My.App/Frameworks folder Apple rejects it because Invalid Swift Support - The SwiftSupport folder is missing. Rebuild your app using the current public (GM) version of Xcode and resubmit it. Could you please let me know if the iOS app can contain .dylib files or they need to be converted into frameworks?Jornada
@Jornada I have the same issue. See #53819272 Did you resolve it in the end?Amplification
Yes, but it got complicated. We made from all the .dylibs frameworks. Then the executable app is parsed and all the references to the dylibs are replaced with the path to the framework/library name.Jornada
2019, ended up having exactly that problem with Invalid Swift Support. Did you ever solve this issue @smaryus?Drover
Yes, we changed the dylibs to frameworks and use the frameworks at linking. You can try to recreate the project for libs and change the compilation output from dylibs to frameworks. Or the more complicate way manually create the minimalist framework structure and us it at linker with your application.Jornada
B
1

While the answer of @Morty points you in the right direction, it leaves a lot open to interpretation and didn't work for me, so here's what I had to do:

Open a terminal at your Mylib.dylib file and run

$ lipo -create Mylib.dylib -output Mylib 

Create a folder with the same name as the created binary.framework and mirror this folder structure:

.
└── Mylib.framework
    ├── Mylib <- Binary created by lipo
    ├── Headers
    │   └── Mylib.h <- Symbols you want to access from the binary
    ├── Info.plist
    └── Modules
        └── module.modulemap

Minimal Info.plist file for iOS Device:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>BuildMachineOSBuild</key>
    <string>XXXXX</string> <- See Mac System Report/Software
    <key>CFBundleDevelopmentRegion</key>
    <string>en</string>
    <key>CFBundleExecutable</key>
    <string>Mylib</string>
    <key>CFBundleIdentifier</key>
    <string>some.bundle.identifier</string>
    <key>CFBundleInfoDictionaryVersion</key>
    <string>6.0</string>
    <key>CFBundleName</key>
    <string>Mylib</string>
    <key>CFBundlePackageType</key>
    <string>FMWK</string>
    <key>CFBundleShortVersionString</key>
    <string>1.0.0</string>
    <key>CFBundleSupportedPlatforms</key>
    <array>
        <string>iPhoneOS</string> 
    </array>
    <key>CFBundleVersion</key>
    <string>1</string>
    <key>MinimumOSVersion</key>
    <string>16.0</string>
    <key>UIDeviceFamily</key>
    <array>
        <integer>1</integer>
        <integer>2</integer>
    </array>
</dict>
</plist>

Make sure CFBundleExecutable is set to the name of your binary, otherwise you'll get the signing issue: "The code signature version is no longer supported.".

Example module.modulemap:

framework module Mylib {
    umbrella header "Mylib.h"
    export *
}

Now you should be able to add the framework with Embed & Sign to your target, import it in swift via import Mylib and use the symbols defined by Mylib.h. When you run the app, you might encounter the crash Library not loaded: @rpath/Mylib.dylib, which can be fixed with this command in the directory of the binary:

$ install_name_tool -id @rpath/Mylib.framework/Mylib Mylib
Baribaric answered 21/2, 2023 at 17:57 Comment(0)
B
0

Not enough reputation to comment, but Morty's answer worked for me.

I ended up writing a shell script for a post-build phase to do this automatically.

Helpful Tip for the rpath:

If you were building the .dylib file from source, you can change the rpath value in the project settings to be the MyFramework.framework/MyFramework executable that it will be packaged into by the lipo tool.

This will save you an extra step post-build

Bergson answered 21/1, 2022 at 2:39 Comment(1)
This does not provide an answer to the question. Once you have sufficient reputation you will be able to comment on any post; instead, provide answers that don't require clarification from the asker. - From ReviewChoroid
P
0

Not enough reputation to comment, but I came across this problem and Morty's answer helped me very much. So as not to make mistakes, you can create a framework project, compile it once and then you can follow Morty's answer. Issues the lipo command, copy the resulted binary into the root of the framework directory and then open terminal where the framework directory is and issue the install_name_tool command like this: install_name_tool -id @rpath/framework_directory/binaryname framework_directory/binaryname

Using the above we could submit an application with the framework included to the App Store and got accepted.

Protero answered 22/11, 2022 at 7:19 Comment(0)
P
0

In my case I have the GDAL dynamic libraries with it's C# bindings so, I don't need the headers but do need the info.plist file for framework to upload. So I created a script for this:

export CUR_DIR=$(pwd)
export LIB_NAME="gdal"
export SOURCE_INFO_PLIST="${CUR_DIR}/info.plist"
export DYLIB="libgdal.dylib"

# set CUR_DIR and LIB_NAME
FW_PATH="${CUR_DIR}/$LIB_NAME.framework"
INFO_PLIST="$FW_PATH/Info.plist"
OUT_DYLIB="$FW_PATH/$LIB_NAME"

# set the DYLIB, SOURCE_INFO_PLIST and HEADER_PATH for the library
mkdir -p "$FW_PATH"
cp "${SOURCE_INFO_PLIST}" "$INFO_PLIST"
lipo "${CUR_DIR}/lib/${DYLIB}" -output "$OUT_DYLIB" -create

install_name_tool -id @rpath/$LIB_NAME.framework/$LIB_NAME "$OUT_DYLIB"
chmod +x ${OUT_DYLIB}

Notice chmod command at the end, without this my framework wasn't working, so I have to give permission first.

I have some dependent libraries of GDAL also, I followed same approach for those too but one more thing I had to do is changing the dependent path:

install_name_tool -change "old/path/of/libgdal.dylib" "@rpath/gdal.framework/gdal" ${OUT_DYLIB}

Then for XCFrameworks:

export CUR_DIR=$(pwd)
export FRAMENAME=gdal

xcodebuild -create-xcframework \
-framework "${CUR_DIR}/arm64/${FRAMENAME}.framework" \
-framework "${CUR_DIR}/x86_64/${FRAMENAME}.framework" \
-output "${CUR_DIR}/${FRAMENAME}.xcframework"

My app uploads fine, Thanks to all the above answers. Cheers!

Professorship answered 5/3 at 15:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.