Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to archive an app that includes a custom framework?

Tags:

xcode

ios

I have an xcode framework project that I've created, which I can compile into a myframework.framework file. After compiling, I drag this framework into the Frameworks project folder of my application, and then to make use of classes from the framework I add the proper import statement to any class that needs it; this allows my app to successfully compile with references to classes defined in the framework. To get the app to deploy successfully to my device, I also add my custom framework to the "Embedded Binaries" section for my target. With all this in place, I can build my app from xcode and run it on my device.

My problem comes when I attempt to Archive my application for the app store. When I attempt to do this, I get a ton of compiler errors where xcode says it cannot find the declarations for any of the classes defined in my custom framework.

How can I set up Archive in xcode so that it properly references and embeds my custom framework?

like image 673
MusiGenesis Avatar asked Feb 26 '16 15:02

MusiGenesis


People also ask

How do I archive framework in Xcode?

Start your archiveNavigate to your project's settings. Under iOS (or the target you want to build your app for) > Identity, you'll want to increment the Build number. For example, if the Build number was 1, you'll want to set it to 2. Then, in the top menu, under Product, click on Archive.

How do I embed a framework?

Adding A Framework TargetCreate a new Xcode target and select the framework template appropriate for your app's target platform. Add all of the framework source files to the new target's Compile Sources build phase. Set the visibility of header files needed by clients of the framework to Public.

How do I archive my Xcode app?

Archive your App In Xcode with your project open, select the simulator (button near the top of the window next to your project name, typically named as a specific type of iPhone) – change it to Generic iOS Device. Open the Product menu and choose Archive. You will see the archive information. Click Validate App.


2 Answers

Not sure if the existing answers help you. I'll just give my solution. First, I want to explain several important Build Phases.

Target Dependencies

If you want to rebuild your own framework (which you linked to the same workspace) every time you build your host app. You'll need to added the framework target here.

Link Binary With Libraries

If you want to use your library in your code (say you want to do import MyFramework), then you need to linked it in this phase.

Embed Framework

This is the tricky part. Embed means to bundle the framework together with your app when distributing. For a system framework, such as AVFoundation, you don't need to embed it into your app since it already exists inside the iOS operation system. However, for your custom frameworks or third-party frameworks, you'll have to embed them into your app bundle so that when you deploy your app on the device, the app can actually find it. That's why if you use Cocoapods or Carthage, they all have an extra build phase to copy the frameworks to the app bundle. (For Cocoapods, it's called Embed Pods Framework and for Carthage, it's an action that runs copy-frameworks script) So for your custom framework, you'll either use the existing Embed Frameworks build phase or create a New Run Script Phase to copy the frameworks to your app bundle.

-- Update on 2017-05-17 --

For the embed framework stuff, there's another important fact I found recently:

You can use file PATH/TO/Framework to check whether a framework is static or dynamic. Check this stackoverflow question for details.

Dynamic Frameworks

Only dynamic frameworks need to be embedded into the app. These include the ones created by Carthage and Cocoapods. If you open the app bundle after you build your project, there's a Frameworks folder that contains all your embedded frameworks and you'll find the ones created by Carthage and Cocoapods as well as the ones you specified in the Embed Framework phase.

Static Frameworks

So now you might wonder where are those static frameworks? How can we still use it if it's missing from the app bundle? You're right. They are in the bundle but not in the Frameworks folder. They have been merged into the executable of your app. If you check the size of executable, you'll realise every time you add a static framework to your target, it will increase.

Static frameworks don't have to be embedded (just Link them), it's like a .swift or a .xib file that will be compiled into your executable.


And then, there's one more step to go before you can use any framework. The Framework Search Paths inside target Build Settings. Again, if you look at Carthage or Cocoapods, they all add extra paths into this setting. This tells Xcode (or the underlying compiler) where to find these linked or embedded frameworks.


So every time you'd want to use a framework, make sure you think about the above settings and you're all set. I've been using this method for a while and feel more confident when I encounter any linking issue.


Finally, there's a very important article from Apple you should read https://developer.apple.com/library/content/technotes/tn2435/_index.html

like image 143
J.Wang Avatar answered Nov 02 '22 23:11

J.Wang


You actually don't need to put it in the "embedded binaries" section. You only need it in the "linked frameworks and libraries section. Make sure that your framework is a Universal Framework (meaning it can compile for all architectures), and make sure you have the right compiler flags set (-ObjC if your framework has any categories etc) There may be some other things you need to set as well like "Other C Flags" if your framework includes any c code and you want to enable bitcode in your client app then you should put "-fembed-bitcode" in your framework Other C Flags. Those were the things I needed to do to get my framework app to the store. I think its a just a misconception that you need to put this in embedded binaries as well to get it to archive for the store.

This is the build script I use to generate the universal framework. It builds right to my desktop. You can uncomment section 8 if your framework is in Swift. You want to create an aggregate target and add this as a run script in build phases.

# Merge Script

# 1
# Set bash script to exit immediately if any commands fail.
set -e

# 2
# Setup some constants for use later on.
FRAMEWORK_NAME="MyFramework"

# 3
# If remnants from a previous build exist, delete them.
if [ -d "${SRCROOT}/build" ]; then
rm -rf "${SRCROOT}/build"
fi

# 4
# Build the framework for device and for simulator (using
# all needed architectures).
xcodebuild -target "${FRAMEWORK_NAME}" -configuration Release -arch arm64 -arch armv7 -arch armv7s only_active_arch=no defines_module=yes -sdk "iphoneos"
xcodebuild -target "${FRAMEWORK_NAME}" -configuration Release -arch x86_64 -arch i386 only_active_arch=no defines_module=yes -sdk "iphonesimulator"

# 5
# Remove .framework file if exists on Desktop from previous run.
if [ -d "${HOME}/Desktop/${FRAMEWORK_NAME}.framework" ]; then
rm -rf "${HOME}/Desktop/${FRAMEWORK_NAME}.framework"
fi

# 6
# Copy the device version of framework to Desktop.
cp -r "${SRCROOT}/build/Release-iphoneos/${FRAMEWORK_NAME}.framework" "${HOME}/Desktop/${FRAMEWORK_NAME}.framework"

# 7
# Replace the framework executable within the framework with
# a new version created by merging the device and simulator
# frameworks' executables with lipo.
lipo -create -output "${HOME}/Desktop/${FRAMEWORK_NAME}.framework/${FRAMEWORK_NAME}" "${SRCROOT}/build/Release-iphoneos/${FRAMEWORK_NAME}.framework/${FRAMEWORK_NAME}" "${SRCROOT}/build/Release-iphonesimulator/${FRAMEWORK_NAME}.framework/${FRAMEWORK_NAME}"

# 8
# Copy the Swift module mappings for the simulator into the
# framework.  The device mappings already exist from step 6.
#cp -r "${SRCROOT}/build/Release-iphonesimulator/${FRAMEWORK_NAME}.framework/Modules/${FRAMEWORK_NAME}.swiftmodule/" "${HOME}/Desktop/${FRAMEWORK_NAME}.framework/Modules/${FRAMEWORK_NAME}.swiftmodule"

# 9
# Delete the most recent build.
if [ -d "${SRCROOT}/build" ]; then
rm -rf "${SRCROOT}/build"
fi

Once your framework is on the desktop, if you go inside of it there will be a text document with the same name as your framework. If you navigate to that and run the command "lipo -info" on it in terminal you should get the following output:

Architectures in the fat file: MyFramework are: armv7 armv7s i386 x86_64 arm64 
like image 39
arc4randall Avatar answered Nov 02 '22 22:11

arc4randall