So, it appears that Apple® has changed their App validation process; fat binaries are no longer passing validation, and, per our support inbox, this is a very real and present problem for a lot of people.
Well, fear not! After some nifty hacking and finagling we have come up with a process that is simple and straight-forward, continues to support universality in development, and archives artifacts that pass validation.
If you implement this method, please, let us know about it in the comments, and, if it is working for you, be sure to flood your social media with links back to this article. No, this is not a shameless attempt to get traffic, though traffic sure is sweet. This is evidently a very real problem for many developers, and it is certain that if they are currently sitting at their keyboard, slamming their face into it because they can’t resolve this issue, they will be thankful you gave them a heads up!
Alright, enough bloviating. Here is a “step by step” of the new process.
Creating a Framework
- Fire up Xcode and start a new Cocoa Touch Framework project (Fig. 1)
(Figure 1) - Go to the Build Settings of your project target and confirm or set the “Architectures” to “Standard Architectures.” These are arm64 and armv7, and are likely the default. (Fig. 2)
- Set “Build Active Architecture” to “NO” – Yes your build times may be a little longer, but this is what is going to allow your Frameworks to be “universal”. So, if you don’t do this, running on your sim will be a problem. Besides, if your builds are debilitatingly slow in a framework because you set “Build Active Architecture” to “NO,” you probably aren’t maximizing modularity. (Fig. 2)
- Set or confirm “Valid Architectures” to arm64, armv7 and armv7s. This is likely the default. (Fig. 2)
(Figure 2) -
Go to the Build Phases section of your project target and append a new Run Script phase. Paste this script in in it:UPDATE: Due to the latest version of Xcode changing its compilation process this script can no longer be placed in the Build Phases, as this causes the script to run too early, not giving the build process enough time to generate the module.modulemap files for your framework. If you previously used this process be sure to follow these new instructions for this step and remove the Build Phase Run Script that this step previously requested.
NEW INSTRUCTIONS: Add the script below to your project’s build Scheme as an Archive Post-action:
Archive Post-action script
set -e DEVICE_BIN="${OBJROOT}/UninstalledProducts/${TARGET_NAME}.framework" SIMULATOR_BIN="${SYMROOT}/../../../../Products/Debug-iphonesimulator/${TARGET_NAME}.framework" ARCHIVE_PATH="${SRCROOT}/_Archive" rm -rf "${ARCHIVE_PATH}" mkdir "${ARCHIVE_PATH}" if [ "${CONFIGURATION}" = "Release" ]; then if [ -d "${DEVICE_BIN}" ]; then DEVICE_PATH="${ARCHIVE_PATH}/Release" mkdir "${DEVICE_PATH}" cp -r "${DEVICE_BIN}" "${DEVICE_PATH}" fi if [ -d "${SIMULATOR_BIN}" ]; then SIMULATOR_PATH="${ARCHIVE_PATH}/Debug" mkdir "${SIMULATOR_PATH}" cp -r "${DEVICE_BIN}" "${SIMULATOR_PATH}" lipo -create "${DEVICE_BIN}/${TARGET_NAME}" "${SIMULATOR_BIN}/${TARGET_NAME}" -output "${SIMULATOR_PATH}/${TARGET_NAME}.framework/${TARGET_NAME}" fi fi exit 0;
There you go. Now you will simply develop.
Assuming that you are using your simulators and devices to test regularly during development before archiving, you will not need to take special note of this, BUT do know that to get your framework artifact you will have to Archive your project. Doing so will add an _Archive/ directory (Fig. 3) to the root of your project in Finder in which you will find your artifact. Know that you will have to build the project with a simulator and archive with a device to get a universal. If someone out there wants to contribute an xcodebuild or xctool script to build both of these on the fly, please, feel free. It would be a great contribution and a welcome addition for may, I’m sure!
(An example of a working framework template can be found on here on kodmunki™ Github.)
That’s it! Now, you have a Framework. But, if you are anything like me, you have likely built a kernel or some other low level Framework and will expect other higher level Frameworks to depend on this Framework. Like, say, persistence, or data. To do this you will do the following: