How to check a framework contains full bitcode? - ios

From Xcode7, any framework built with your app should have bitcode if you want to enable bitcode support for your app.
I'd like to know a way to check whether a framework project has bitcode or not.
There is a similar question here (How to check if a framework is BITCODE supported for Xcode7), but the answers there are about checking bitcode of static libary file of .a or .o extention.
I want to check the existence of bitcode inside .framework package, and have been wondering there should be a way to check it on .swiftmodule files inside a .framework package.
(One of the reasons I want to know is that I'm trying to support bitcode in my own framework project, but have never succeeded in doing so since I got missing-bitcode error when I exported my framework package to an app project and built the app. I thought it's nice to have a tool to check it directly rather than knowing it after embedding it to an app project and building the app.)

otool -l binary_name | grep __bitcode
you will see one or more segname __bitcode entries if it does have bitcode or empty output if does not.

Related

Does bitcode support weak linking third party frameworks?

Question stated simply in title. Here is my setup:
Building a dynamic framework that optionally links (weak link) to GoogleInteractiveMediaAds.framework. For apps that use my framework, GoogleInteractiveMediaAds is an optional dependency, and the framework will work fine without it. This is currently working.
However the problem arises when attempting to rebuild from bitcode, as typically happens when exporting an Ad Hoc build with "Rebuild from Bitcode" selected. The bitcode compile process fails with:
ipatool failed with an exception: #<CmdSpec::NonZeroExitException: $/Applications/Xcode.app/Contents/Developer/usr/bin/bitcode-build-tool ...
And looking deeper into the log file I find the error description:
Failed to resolve linkage dependency MyFramework arm64 -> #rpath/GoogleInteractiveMediaAds.framework/GoogleInteractiveMediaAds: Could not resolve #rpath in #rpath/GoogleInteractiveMediaAds.framework/GoogleInteractiveMediaAds from MyFramework
error: GoogleInteractiveMediaAds not found in dylib search path
Note: GoogleInteractiveMediaAds.framework does include bitcode.
Obviously this error is avoiding by not selecting "Rebuild from Bitcode". If I were to answer my own question, I'd say, no, it looks like when compiling from bitcode, you cannot use weakly linked frameworks. Simply from the fact that the bitcode compilation step is trying to link to a framework which isn't included in the app target. However I haven't been able to find any official documentation surround using weak linking with bitcode, or any relevant StackOverflow answers about it, so I'm not sure if I'm missing something or if there is some relevant compiler/linker settings that I am missing to get this to work.
In your case, you will need to wait for the fix in framework itself.
We've got similar issue while distributing our framework, which I described here and I just wan't to share results of our investigation, because seems that no-one has published their results.
No needs to distribute without bitcode. Long story short, there were LLVM instrumentation included, which prevents AppStore processing. I've written a whole blog about XCode 12 and BigSur issues with XCFramework.
To sum up, here is a few required steps to make sure while creating XCFramework for distribution:
Using archive builds is a MUST, release build isn't enough
BUILD_LIBRARY_FOR_DISTRIBUTION must be set to YES
SKIP_INSTALL must be set to NO
GCC_INSTRUMENT_PROGRAM_FLOW_ARCS = NO to turn off GCC instrumentation and remove them from the binary
CLANG_ENABLE_CODE_COVERAGE = NO to turn off code coverage tools from the binary
Having all of the above helped to solve our preparing and distribution problem and hopefully save you some time if you happened to face same issues as we did.

'MyClass' is unavailable: cannot find Swift declaration for this class - Simulator

I have my framework and I am supporting different architectures. For supporting simulator I am using x86_64 in valid architectures.
But when I use my framework in the app, I get the error while running the app on simulator :
'MyClass(In framework)' is unavailable: cannot find Swift declaration
for this class
Note : It works fine on device.
If memory serves me right, since 6-th version Xcode doesn't support so-called "universal" frameworks (frameworks that contains architectures for arm-family and x86_64/i386). So now when you build a framework Xcode will make two separate bundles for iphoneos and iphonesimulator (you can find them under corresponding folders in your derived data folder). The architectures these frameworks will contain depends on ARCHS variable. By default it's set to $ARCHS_STANDARD, that varies between platforms. You can play around with this setting, mixing architectures you really need, but Xcode will fail at build time if these architectures are incompatible (presumably x86_64 and arm-family architectures are considered incompatible).
To get round this limitation you actually have to do a little bit of "hard-work" yourself and "merge" framework bundles for simulators and devices manually.
1. Build your framework for iOS device and simulator
Here is nothing fancy, just go to your target build settings and ensure that "Build Active Architecture Only" (ONLY_ACTIVE_ARCH) is set to NO and add all required architectures for "Valid Architectures"(VALID_ARCHS, you have already done that):
Now build your framework, find the bundle file under the Product group in Xcode, and open it in Finder:
You should find two folders, one for each set of architectures:
2. Merge two frameworks into one
Now go into Debug-iphoneos folder, copy framework from there and paste it somewhere else, for example in the parent folder:
It will contain our universal framework in a short while. In order to do that we need first create universal dynamic library with the lipo tool. Open terminal, navigate to the folder where you are now (with the copied framework file) and type as follow:
$ lipo -create -output 'MyFramework' 'Debug-iphoneos/MyFramework.framework/MyFramework' 'Debug-iphonesimulators/MyFramework.framework/MyFramework'
If you are not in the derived data folder your paths for the framework libraries will differ of course. This command will produce new universal library containing both sets of architectures. Now just drag and drop it into your framework bundle and replace the existing one.
It's likely that your framework has architecture slices under a folder like MyFramework.framework/Modules/MyFramework.swiftmodule. Our universal framework should have slices for all supported architectures. You should already have arm slices inside, since we copied our framework from the Debug-iphoneos folder, so just find the same folder for the Debug-iphonesimulator and copy files from the folder to the destination framework's folder. You now should have this folder look something like that:
That's it! Now you have a universal framework that should work properly on both an iOS simulator and an iOS device.
3. Slice your framework when submitting to the Appstore
If you try to submit an application alongside a universal framework, it will be automatically rejected. So before submitting you will need to extract only devices' architectures one by one and then merge them into one framework using lipo:
$lipo -extract 'arm' 'MyFramework.framework/MyFramework' -output 'MyFramework-arm'
$lipo -extract 'arm64' 'MyFramework.framework/MyFramework' -output 'MyFramework-arm64'
$lipo -create -output 'MyFramework' 'MyFramework-arm' 'MyFramework-arm64'
Then copy it into your framework and remove redundant slices.
P.S.
Hopefully it helps, however this is kind of well known issue and there is already a lot of solutions in the internet. I suggest you googling terms like 'Create fat/universal framework iOS', and I believe you will find a lot of working scripts that will do all this stuff for you. Here is one I find the most helpful.
I see two possible options:
Your device and your simulator use different iOS versions, and your framework is using something from the latest iOS versions (also, I see that you have the option "Build Active Architecture only" switched to 'No'. Try to switch it back to 'Yes' and make a clean build. Probably, the compiler will find an issue, but it depends on how you link your framework to the app).;
Your framework built for a specific device. Try to switch to the 'framework' target, switch destination for build to 'Generic iOS device' and make clean builds for the 'framework' and the app.
Also, it will help if you update the question with the brief description of how you link your framework (via Workspace / via binary / via CocoaPods / etc.).
Usually, such issues caused by some stupid things, and you should check all theories, even dump or misleading at first sight :)

Xcode 8 : Disabling bitcode and Umbrella frameworks

I'm currently building an Umbrella framework (before anyone say so, I know this is discouraged by Apple, but I am in the case were I'm the owner of all the sub-frameworks, they are not distributed if they are not part of this or another Umbrella framework I may create, and we are in a closed source configuration) within Xcode 8.
I've followed this post to create the framework: https://stackoverflow.com/a/41815368/2572568
Everything is working fine except that I got the following error:
dyld: Library not loaded: #rpath/B.framework/B
Referenced from: /private/var/containers/Bundle/Application/E0113060-CA91-47F8-BEE3-BDF1F847DB3A/app.app/Frameworks/A.framework/A
Reason: no suitable image found. Did find:
/private/var/containers/Bundle/Application/E0113060-CA91-47F8-BEE3-BDF1F847DB3A/app.app/Frameworks/A.framework/Frameworks/B.framework/B: required code signature missing for '/private/var/containers/Bundle/Application/E0113060-CA91-47F8-BEE3-BDF1F847DB3A/app.app/Frameworks/A.framework/Frameworks/B.framework/B'
app is the Application using the Umbrella framework A which has a sub-framework B
I found that disabling bitcode from all the projects is solving this issue (and that's what I am doing now) from this thread : https://github.com/CocoaPods/CocoaPods/issues/3661
So here are my questions:
What does disabling bitcode is exactly doing ? I found that Apple can run optimization after you submitted your code. Are these speed optimization or disk space optimization, or any other optimization ?
Am I doing something wrong building my Umbrella framework ? Is it possible not to disable bitcode ?
First of all:
I came into the exact same problem today and I couldn't fix it. But removing Bitcode fixt it for me. Thanks for that
To your questions:
Bitcode is something kind of similar to Java's Bytecode. Your app gets compiled completely, but not in machine code (like assembler). Your app is compiled to Bitcode. This helps Apple to build different versions of your app on their server. One for 64bit and one for 32bit devices. Then they separate both apps, which saves disk space on the actual device.
And they probably have some further optimisations, which could speed up the app. Generally it's not needed today.
I tried almost everything I could imagine and at the moment I would say: No it's not possible to disable bitcode, if you have a big umbrella framework. Can you check if you have sub-frameworks in your umbrella framework which do not support Bitcode ? Like AWS SDK, Facebook SDK...
It may be possible to support Bitcode if all sub-frameworks support Bitcode. This answer seems promising me, but it's a bit old: https://stackoverflow.com/a/27638841/1203713
Regards,
Alex
Ok so after a few more researches on completely unrelated questions, I found this thread : https://github.com/Carthage/Carthage/issues/535
In substance, this solve question 2 and another one : Yes, you can enable bitcode for your Umbrella framework.
To do so, you must manually set a User-defined setting (inside Build settings, click on the plus in the top bar) named BITCODE_GENERATION_MODE to bitcode. This will force Xcode to build you project with real bitcode and not only a bitcode subset.
You have to set this flag for all the frameworks under your Umbrella framework.

Conditionally specify an external static library in an XCode4 iOS project?

I have some externally pre-compiled libraries for iOS some for debug and some for release
I can just "add new files" to the project with the .a files but this is for all configurations. I want some when Debug is built, and the others when Release is built.
I did it by putting the libraries in the OTHER_LDFGLAGS setting.
They have two fields one for Debug and one for Release I just put the paths to the files in there. Is there a better way?
I had the same issue earlier today - the suggested solution was to add the libraries to the Other Linker Flags setting on a per-configuration basis. The details are here.

iOS: Anybody gotten libtar or libarchive to build? (Problem with configure script.)

I'm having a problem building libraries that have a "configure" script, namely such scripts are not meant for compiling for iOS.
Is there a set of environment variables that I can set to induce "configure" to work for iOS?
I tried setting CC but that was not nearly enough.
Thanks.
Github project with static libraries and headers that build properly for iOS.
From the readme (Nov 2011):
LibArchive Static Library for iOS Unfortunately, while
libarchive.dylib and libbz2.dylib are included in the iOS SDK, the
header files are not. This means that they are private APIs and cannot
be used if you intend to submit to the App Store.
Never fear! This repository contains everything you need to build a
static version of libarchive for iOS. libbz2 is also included for
extra goodness.
To keep naming of things sane, we build the library as libarc.a.
For iOS 4.3+ copy the header files and library from the
build-for-iOS-4.3 directory into your project.
For iOS 4.2 copy the header files and library from the
build-for-iOS-4.2 directory into your project.
If you need to build this for an earlier version of iOS, you can
easily modify the build.sh script to point to whatever SDKs you like.
It should build fine on 3.x.
TO GET IT FULLY LINKING you must also include libz.dylib in your list
of linked libraries. To do this in XCode 4, click on your project,
choose the Build Phases tab, go to Link Binary With Libraries, press
+, and choose libz.dylib from the (long) list of possible libraries to link against. This is because libarc.a links dynamically to libz.dylib
-- this is okay since, for whatever reason, AAPL saw fit to include the libz headers in the iOS SDK.
The current libarchive version is 2.8.4. The bzlib2 version is 1.0.6.

Resources