Xcode 8 was out along with iOS 10, watchOS 3, tvOS 10 and macOS 10.12 Sierra last month. We spent a couple of days to upgrade our development environment and build machines of our continuous integration system to Xcode, just like many other developers do.
It was not as easy as just to click on the “Upgrade” button in Mac App Store. Things might be broken and need to be fixed. Upgrade is always somehow painful, but a yearly routine.
On the other hand, Apple added several additional validations that validates your new uploaded builds on iTunes Connect. If you cannot pass these validations, your builds will be invalid and you cannot complete app submission.
You may want to read our experience before upgrading to Xcode 8. We also suggest you to upload some new builds recently to see if you can pass these new validations.
16-bit or P3 Images
If your app bundle contains images assets with color profiles other than sRGB embedded, and you set deployment target to iOS 8 or earlier, you will get the following error message from Application Loader, the tool for uploading your builds.
“ERROR ITMS-90682: Invalid Bundle — The asset catalog at ‘Payload/XXXXX/Assets.car’ can’t contain 16-bit or P3 assets if the app supports iOS 8 or earlier.”
Assets.car is the compiled file of your image assets. What it exactly means is, you have one or more images using invalid color profiles.
You can fix the issue by following the thread Assets.car can’t contain 16-bit or P3 assets if the app supports iOS 8 or earlier? on Stackoverflow. It says, you can export metadata of your assets into a JSON file by the command: sudo xcrun -sdk iphoneos assetutil -info /path/to/a/Assets.car > /tmp/Assets.json, then you can find out which images are using 16 bit or P3 color profiles from the file. Finally, launch “Color Synch Utility” to open these image files, and assign sRGB color profile to them.
Info.plist Keys
Apple has some existing Info.plist keys that let you explain what you want to do when you want to grant permissions from users, such as to access their calendars, Bluetooth connection and so on.
They are optional in previous versions of iOS. However, since iOS 10 and Xcode 8, Apple forces you to add them into your Info.plist file if you use related APIs in your app. Otherwise, your app may crash and be rejected.
So, we added following keys into Info.plist file to our app, a client of our music streaming service:
- NSCameraUsageDescription and NSPhotoLibraryUsageDescription: Why you need to access users’ cameras and camera roll pictures. We use cameras to let users to take photos and upload them to our server as their avatars.
- NSMicrophoneUsageDescription: Why you need to use users’ microphones. We allow some advanced users to attach videos to their public playlists, in order to let them introduce the stories beyond their playlists. To make a video, accessing microphone is required.
- NSCalendarsUsageDescription: What you want to do with users’ calendars. We provide information about concerts and live events, and we can let users to save them to their calendars.
- NSBluetoothPeripheralUsageDescription: It is about the permission to access users’ Bluetooth connected devices. Actually it is used by a library from one of our partners in our app. The library would like to know which wireless headset that the user is currently using, and then apply the best matching profile to create the best surrounding sound effect.
Annoying “libswiftRemoteMirror.dylib”
If you use Swift programming language, and you use the xcodebuild command line tool to build your apps, xcodebuild may add a file whose name is “libswiftRemoteMirror.dylib” to your app bundle. If you just archive your built app bundle, rename it with an “ipa” file extension and upload it to iTunes Connect, we may get another error message:
ERROR ITMS-90171: “Invalid Bundle Structure — The binary file ‘MyApp.app/libswiftRemoteMirror.dylib’ is not permitted. Your app can’t contain standalone executables or libraries, other than the CFBundleExecutable of supported bundles. Refer to the Bundle Programming Guide at https://developer.apple.com/go/?id=bundle-structure for information on the iOS app bundle structure.”
There were some discussions, but we could not find any useful post can help us to prevent xcodebuild from adding the file and solve the issue from the Internet.
We tried all the possibilities in our build script, and we finally found the answer, you need to add the “-exportOptionsPlist” argument and specify a plist file in your xcodebuild command, to tell xcodebuild that the purpose of exporting the archive is for uploading it to App Store.
The plist file may look like:
<?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>method</key>
<string>app-store</string>
<key>teamID</key>
<string>$TeamID</string>
</dict>
</plist>
You may name the file as “export.list”, and your command may be like:
xcodebuild -exportArchive -archivePath "${AppTarget}.xcarchive" -exportPath ./ -exportOptionsPlist export.plist
Code Signing/Entitlements/App-Group
KKBOX has multiple Apple developer programs. We have a developer program for building apps to App Store, and we use another enterprose program for internal usages including building beta versions to our testers, since we have more than 100 iPhones in our campany.
Before Xcode 8, you can only assign one and only “developer team” setting in your Xcode project in the “General” tab. So, we need to pass code sign identity and provision profile argument to xcodebuild when we want to use our enterprise program.
Xcode 8 changes the way to do code signing. Now it supports manual code signing settings for each configuration in its GUI, and we changed our settings in Xcode and build script accordingly. Then, we encountered another pain: app-group setting.
We use app-group setting to let our iOS app and Apple Watch extension to share data. An app-gorup setting must be associated with an application ID in Apple’s developer web site, and app IDs can not be shared among different developer programs.
So, before Xcode 8, we ask xcodebuild to ignore app-group setting when we use provision profiles from our enterprise developer program with a wild-card app ID specified, even we ever did app-group setting in Xcode’s GUI.
There were several side effects. Since there is no shared data, our iOS app signed with our enterprise program could not communicate with Apple Watch. That was okay for us. We let only limited tester to use our Apple Watch features.
The second issue was, if you ever installed our app from App Store, you cannot upgrade it to our internal tester builds. iOS does not allow one app bundle with same bundle ID but signed with different developer program to replace another. But it was okay for us as well.
The app-group setting is contained in an entitlement file. What we did is to create an empty entitlement file and pass it to xcodebuild. After Xcode 8, we cannot do this anymore. Xcode 8 always asks app-group setting contained in your entitlement, if the setting in on in Xcode’s GUI.
It means, we have to setup a new app-group setting in Apple’s web page for the enterpise developer program. We need to set different bundle IDs for our iOS app, Apple Watch app and Apple Watch exntesion as well. We need to be able to apply these complex settings when we want to switch to another identity to sign our app as well, but it is a mass.
We use configurations to manage these settings. We created new configurations like “InHouseDebug”, “InHouseRelease” and so on, then, go to the “Build Setting” tab, and scroll down to “Product Bundle Identifier”. Give different settings by configurations here.
Create various entitlement files. Entitlement files are actually plist files, and you can edit them with any of your favotire text editors.
Change the value of “com.apple.security-groups” for your app-group. You may want leave the “aps-environment” section in your entitlement file for push notifications.
Scroll to “Code Signing Entitlements” in “Build Settings” to assign entitlement to each configuration.
Finally, we get shiny new builds using Xcode 8.