Apple Xcode plugin development FAQ – ShiVa Engine

Apple Xcode plugin development FAQ

Compiling plugins for the Apple eco system (iOS, tvOS and macOS) requires a Mac with Xcode installed. Xcode is a big and complex piece of software which can be very confusing at times, especially if you mostly work with Visual Studio on Windows. Some options have confusing names, some buttons are well hidden, and some errors cannot be debugged without checking your exports through a Terminal. This FAQ compiles the most common Xcode pitfalls for new developers.

Installing Xcode

The current public release can be installed through the App Store. Beta releases can be obtained through the Apple Developer Portal, but you should stick to the public releases if possible, since this is what we test against. After XCode.app has been installed, you need to run it at least once before you can start using it with ShiVa. On the first launch, Xcode will ask you to confirm its EULA and install additional software, which is required.
While we are at it, you should also install the Xcode Command Line Tools. Open Terminal.app and enter

xcode-select --install

Finally, you need to enter the Xcode path into ShiVa 2.0’s Settings window. If you got it from the App Store, it will have installed by default to /Applications.

Compiling from ShiVa

Technically, you never need to open Xcode again after the installation is complete. Plugins can be compiled directly from the ShiVa Plugin module.

This works well for simple plugins. However if you wish to add frameworks, manage the signing process, or take advantage of convenient features like Xcode syntax highlighting and code prediction, there is no way around working with XCode.

Using Xcode

The plugin project file for Xcode is stored in

yourGame/Plugins/com.yourcompany.pluginname/Make/_platform_/yourplugin.xcodeproj

where _platform_ is either iOS, tvOS or Mac. When you first open the project, Xcode will give you a yellow notification triangle with several suggestions to modernize the project file. ShiVa plugin projects come in an older project file specification since we have to be backwards compatible with versions of Xcode several years back.

You can blindly accept these suggested changes if you wish to stay inside Xcode, however this might cause problems if you want to go back and forth between ShiVa and Xcode, adding new functions and constats. If for some reason your Xcode file breaks, you can generate a new one through ShiVa’s Plugin Module via “Build > _platform_ > Generate project/makefile”. No code will be lost, but you will have to enter build/signing/dialect/… settings again.

C++ settings

Xcode is an IDE for several different languages. What we are interested in are C++ and Objective-C++. If you need to compile against a certain language dialect of C++, like C++11, 14 or 17, you can do so from the C++ Language Dialect section which is very far down the “Build Settings” page. Here you can also choose which STL to compile against (GNU or CLANG) as well as disable C++ Exceptions, which are of barely any use to game developers, but come with significant overhead.

Deployment target

The deployment target is the lowest version of the operating system you require to run. ShiVa’s defaults will always be as low and old as possible to ensure the widest possible compatibility, however many frameworks and 3rd party libraries require newer deployment targets. The setting can be changed in the Info tab.

Projects vs Targets

An Xcode project can contain multiple targets. For ShiVa plugins, this is not the case, since we offer individual Xcode projects for each individual platform. You still need to know about it, since both Project and Target come with their own settings. Code Signing for instance is only accessible in the Target settings, the deployment target only in the Project settings. On the other hand, most options in the Build Settings tab are both available for Project and Target, even where it makes little sense. If you get strange error messages, make sure to check out both Project and Target settings, so they do not contradict each other if you do not want them to.

Debug vs Release

By default, ShiVa plugins in Xcode build with the Debug profile. For optimal performance and smaller binaries, you should always switch to the Release profile before publishing. Apple has hidden the selector in its Schemes dialog. The quickest way to get there is by Alt-clicking on the Play/Build button.

Architectures

Plugin binaries on Apple systems can store code for multiple processor architectures (“universal binaries”). This can be both convenient and confusing at times. Especially when cross-compiling for ARM on a x86-base Mac, you can easily make mistakes which result in error messages like:

No native library file found for this platform in native plugin manifest file :
/var/mobile/Containers/Data/Application/[...]/Plugins/Tools/Manifest.xml

Target selector

In the top left of the main Xcode window, you have the target selector. Here you can switch between Simulator, Generic and Device targets. On iOS/tvOS by default, it is set to one of the installed simulators, which is useless for plugins. Even worse, plugins built under a simulator target are x86 binaries which will not run on actual iOS/tvOS devices.

If you have a test device attached and want to debug your plugin on it, you can switch to the device target instead. However in most cases, you should select the “Generic device” target.

ARCHS_STANDARD

Xcode has a variable called ARCHS_STANDARD which is set as the default architecture.

This means that Xcode will build your plugin for the common architectures associated with your target (selector for Simulator/Generic/Attached device) and scheme(debug/release). It does not mean that any of the architectures listed under “Valid architectures” will ever be built!

Terminal.app file command

You can check your compiled binary in the Terminal.app using the “file” command. This is useful to see which architectures are included. Here is what a properly compiled iOS plugin looks like:

RetiMac:~ broozar$ file /Users/devel/S20Projects/dummyplug/Plugins/com.tris.test/Contents/iOS/testplug.a
/Users/devel/S20Projects/dummyplug/Plugins/com.tris.test/Contents/iOS/testplug.a:
Mach-O universal binary with 2 architectures: [arm_v7:current ar archive] [arm64:current ar archive]
/Users/devel/S20Projects/dummyplug/Plugins/com.tris.test/Contents/iOS/testplug.a
(for architecture armv7):	current ar archive
/Users/devel/S20Projects/dummyplug/Plugins/com.tris.test/Contents/iOS/testplug.a
(for architecture arm64):	current ar archive

On the other hand, this is what the same plugin built with a Simulator target looks like:

RetiMac:~ broozar$ file /Users/devel/S20Projects/dummyplug/Plugins/com.tris.test/Contents/iOS/testplug.a
/Users/devel/S20Projects/dummyplug/Plugins/com.tris.test/Contents/iOS/testplug.a:
Mach-O universal binary with 2 architectures: [i386:current ar archive random library] [x86_64:current ar archive random library]
/Users/devel/S20Projects/dummyplug/Plugins/com.tris.test/Contents/iOS/testplug.a
(for architecture i386):	current ar archive random library
/Users/devel/S20Projects/dummyplug/Plugins/com.tris.test/Contents/iOS/testplug.a
(for architecture x86_64):	current ar archive random library

As you recall, none of these architectures were listed under “supported architectures”. ARCHS_STANDARD just changed the architectures for you based on the selected target and scheme. This code will not execute on ARM and only work in the simulator. The ShiVa iOS engine is configured to run on all common architectures, including the current ARM variants as well as x86/_64:

RetiMac:~ broozar$ file /Applications/ShiVa\ Editor.app/Contents/Resources/Data/Authoring/iOS/Build/S3DClient_iOS.a
/Applications/ShiVa Editor.app/Contents/Resources/Data/Authoring/iOS/Build/S3DClient_iOS.a:
Mach-O universal binary with 5 architectures: [arm_v7:current ar archive] [x86_64]
/Applications/ShiVa Editor.app/Contents/Resources/Data/Authoring/iOS/Build/S3DClient_iOS.a
(for architecture armv7):	current ar archive
/Applications/ShiVa Editor.app/Contents/Resources/Data/Authoring/iOS/Build/S3DClient_iOS.a
(for architecture armv7s):	current ar archive
/Applications/ShiVa Editor.app/Contents/Resources/Data/Authoring/iOS/Build/S3DClient_iOS.a
(for architecture arm64):	current ar archive
/Applications/ShiVa Editor.app/Contents/Resources/Data/Authoring/iOS/Build/S3DClient_iOS.a
(for architecture i386):		current ar archive
/Applications/ShiVa Editor.app/Contents/Resources/Data/Authoring/iOS/Build/S3DClient_iOS.a
(for architecture x86_64):	current ar archive

However, ShiVa assumes that you want to test and run your game on actual hardware, so we do not include an x86/_64 architecture in our manifest. If you really want to run your iOS game with plugins in the simulator, you will have to add the x86_64 architecture to the Mainfest.xml:

Build active architecture only

This selector is another common pitfall. If you are in the Debug scheme, this option is set to YES, but on release it is on NO.

Generally, it makes builds faster, since less architectures will be built, but it also makes your code less compatible and portable. A few examples:
– Simulator target, option YES: x86_64, option NO: both i386 and x86_64
– Device target, option YES: only the device architecture (probably arm64), option NO: arm64 and armv7
To avoid problems during debugging, you should consider setting it to NO for both schemes in case you want to test on multiple devices with potentially different architectures. In case you forget to switch to the Release scheme, this also makes sure all necessary architectures are included.

lipo

To create a truly universal plugin, meaning one that works on both Intel and ARM architectures, you can use the “lipo” command to sandwich two plugin binaries together.
First, create a “Generic device” build. Rename the compiled “Plugin.a” binary to “Plugin_device.a”. Next, create one of the simulator builds, which gives you an Intel version. Rename this one to “Plugin_simulator.a”. You can now glue the two plugins together using the following command in a Terminal:

lipo -create "Plugin_device.a" "Plugin_simulator.a" -output "Plugin.a"

Signing and Provisioning Profiles

There is often a bit of confusion regarding code signing and the Apple Provisioning Profile system.

Signing

Applications must be signed, Plugins should be signed too. For a while now, Xcode can manage signing for you. Go to “Target > Projectname > General” and check the “automatically manage signing” box. This will pull all required data from your Apple Developer Account.

Provisioning Profile

Provisioning Profiles allow your to publish your app to the App Store, earn money, and test your game on a local device. It is not required for building a plugin. If you want to learn more about the process of obtaining and installing an Apple Provisioning Profile, please check out our official documentation on the subject.

ShiVa plugin signing

All of the above is not to be confused with the signing/certificate field in ShiVa, which expects a VeriSign/Norton/… security certificate, which is not required for the Apple eco system.

Xcode build checklist

The TLDR version of this article is as follows:
– accept the suggested Xcode project changes (yellow warning)
– select the “Generic Device” target for proper iOS/tvOS testing and publishing
– select the correct scheme (debug/release)
– activate automatic code signing
– change C++ dialect settings if necessary
– change deployment target if necessary
– check your compiled result with the “file” command in the Terminal
– adjust Manifest.xml if necessary




Need more answers?

  • slackBanner