iOS Build Environment for Windows

Thank you for using this build environment which enables you to create and deploy iOS apps from Windows. I hope you enjoy using it as much as I enjoyed creating it.

I know it's boring, but still, please take five minutes of your time to read this document!


Summary of this document

  1. What is included
  2. First steps
  3. How to activate the product (and unlock code signing)
  4. How to transfer your app to your device
  5. (OPTIONAL) How to jailbreak your device
  6. How to catch application logs, system logs and stack traces
  7. How to debug your app
  8. How to use a dynamic library from the SDK in your project
  9. How to use a dynamic library that is NOT present in the SDK, or a third-party framework
  10. How to submit an app to the Cydia Store
  11. How to upload an app to Apple's App Store Connect (formerly iTunes Connect) or TestFlight (requires product activation)
  12. About digital signing identities (requires product activation)
  13. About provisioning profiles (requires product activation)
  14. How do I add extra entitlements (a.k.a Application Services) to my app?
  15. What's a "pre-packaging script" and why would I need one?
  16. Can I build or upload my iOS projects from the command line?
  17. Can I add embedded binaries (dylibs, frameworks) into my app?
  18. Limitations (and workarounds)
  19. Troubleshooting
  20. Change log

Legal foreword

In the license that comes with the iOS SDK, Apple Inc. states that the iOS SDK shall only be deployed on Apple-branded computers. So, for the rest of this document, I will assume that your Windows computer is an Apple-branded computer running Windows through Boot Camp. I do not condone the use of this program outside this scope, and cannot be held responsible for any misuse you make of it.


1. What is included

The build environnement comprises:

As well as a handful of examples, including source code and build scripts, compilable out of the box.

[ Back to top ]


2. First steps

First-time setup

In order to use this builder, you need to copy some files from the Mac side to the Windows side of your computer. Just follow these directions step by step.

Installing the SDK

click to enlarge

Login onto Windows and plug a FAT-formatted USB key (not NTFS! macOS can't write to NTFS keys) with at least 50 Mbytes free into your computer.

Open the SDK migration assistant shortcut from your Start menu (it's in All Programs > iOS Build Environment)

Drag both these files to your USB key and reboot into macOS.

click to enlarge

Login onto macOS. NOTE: if you can't use your Mac for this, you may ask a friend with a working Mac to help.

Make sure that Xcode is installed and up to date (you can install or update it for free from the Mac App Store).

If your Mac is too old for the latest Xcode, here's a hint: download it anyway from https://xcodereleases.com, extract it and put the resulting Xcode.app in your Applications directory (backup your previous Xcode first). The new Xcode.app will have a forbidden icon on it (as it's incompatible with your Mac), but it won't prevent the builder's migration tools to find it and make use of it. Once the SDK migration is done, you can remove the new Xcode and put your previous, working one back in place.

Open your USB key and double-click the item called Migration assistant (step 1, Mac).command.

click to enlarge

Let it do its job, and when it tells you so, reboot into Windows.

(You will notice that a SDK.zip, and possibly a Keychain.zip file too, will have been created on your USB key. They contain the files we need.)

click to enlarge

Login onto Windows.

Open your USB key and double-click the item called Migration assistant (step 2, PC).

click to enlarge

The SDK unpacks. Wait for it to finish.

Now you can build iOS projects. But in order to deploy them to your (non-jailbroken) device, you'll also need to sign them with your digital signing identity; so read on.

Setting up your signing identity

click to enlarge

On Windows, open the Keychain Tool shortcut from your Start menu (it's in All Programs > iOS Build Environment).

If your Mac was previously used to build iOS apps, the migration assistant will have found your existing signing identity and you should see that appear in the Keychain Tool. That is to say, you should see things called certificates named iPhone Developer: <Your Name> and/or iPhone Distribution: <Your Name>, along with at least one private key and probably some provisioning profiles.

click to enlarge

On the other hand, if you see nothing in the listbox like on this image, then it means no existing signing identity was imported from your Mac, and you need to create one from scratch.

In which case please see 12: About digital signing identities case c) "You don't have any signing identity yet".

click to enlarge

A signing identity, in a nutshell, is the combination of 3 things: a certificate that attests of your identity, the private key (and its protecting passphrase) out of which this certificate was created, and a provisioning profile that tells which iOS device(s) are allowed to install the app that carries it.

Select the provisioning profile to use by default. This will auto-select the certificate it contains (if your profile contains multiple certificates, pick one of them), and the private key that was used to create it. Enter that private key's passphrase. When everything matches, the lock turns green. This will be your default signing identity.

Note that for a generic identity, you should select a generic, development provisioning profile, i.e. one that's not specific to a particular app. Later, when you build your apps for release, you will select for each of them a distribution certificate and a specific provisioning profile.

Voilą. Your build environment is ready!

Now here are the instructions to build your first project.

a) If you want to create an iOS project from scratch with Visual Studio

Start the "Project Creation Wizard for Visual Studio" from your Start menu (it's located in the "iOS Build Environment" programs group, under "All programs"). Enter the name for the project you wish to create, select the directory where it should be created, specify the project type and click Create. The necessary files for a new iOS project are created in a new directory with a .vcproj file to regroup them, and Visual Studio opens. Depending on your Visual Studio version, you may need to follow a project conversion wizard - this process should run without error.

You may now browse your project files within the IDE and review them. When you're ready, hit F7 or click the Build toolbar button to compile your project. This action calls your project's build script, which is the make.cmd file that comes with it. The iOS builder output should scroll in the usual Visual Studio compiler output window.

Depending on the project type you selected, the build will produce an installable app, or something else (for example a library). Refer to the builder output and the contents of the make.cmd file to know what to do next (for example, to install an app after each build you just have to switch a setting in that file).

b) If you want to build a project from the command-line (or from another IDE)

In this case, just call make.cmd from the directory where your project's files resides. If you don't use Visual Studio, you should also delete the Visual Studio project file (.vcproj / .vcxproj), so that the build script doesn't rely on it to figure out which files to compile. The rule is that if no Visual Studio file is present, all the files with a compilable extension in the project directory and its subdirectories (.c, .cc, .cpp, .cxx, .m, .mm, .mx, .mxx and .a) are compiled as part of the project. So, if you intend to use the build environment from the command-line and don't want a particular file to be compiled, just change its extension.

The build system relies on the build.cmd master script that is located at the root of the build environment. If you intend to create an automated build chain, you should read and study this file (and especially its command-line arguments: invoke it with build /? to display them). This build script has been carefully crafted to make toolchain integration as easy as possible.

c) If you want to build an Xcode project created by the Unity game engine

click to enlarge

Create your iOS project within Unity on Windows (select File - Build Settings... or Ctrl+Shift+B, then select iOS, and hit Build.)

Unity will prepare a directory containing a project to be compiled with Xcode.

click to enlarge

Start the "Project Builder for Unity" from your Start menu (it's located in the "iOS Project Builder for Unity" programs group in the Start menu, under "All programs").

Pickup the location of the Xcode project created by Unity using the Browse button.

Check that your signing identity is correct (see 12.) and that the provisioning profile is suitable for your app (see 13.), review the build options, then hit Build.

click to enlarge

Grab a cookie while your project is being built.

In all cases

click to enlarge

After a successful compilation, your apps can be found in the 'Packages' subdirectory of your project.

These packages are ready to be deployed on your iOS device (iPhone, iPod or iPad).

click to enlarge

If you opted so, your app should also be available for OTA (over the air) installation to your device. Simply follow the on-screen instructions.

OTA deployment users, please note that:


IMPORTANT: iOS code signature requires product activation. To import and use your digital signing identity to sign the app, use the Keychain tool (see 12.) As long as you haven't set up a signing identity, only a pseudo-signature can be performed. Pseudo-signed apps can only be deployed on jailbroken devices, while fully signed apps can be deployed on any device, jailbroken or not. See 12 - About digital signing identities for a brief howto on how to use a digital signing identity from the iOS Developer Program.

Where are my project's build settings?

Except for projects created by Unity whose settings belong inside the Unity editor, all your other projects' build settings (preprocessor definitions, compiler and linker options, targeted iOS version, signing identity, etc) are in the project's make.cmd file, which is a text-editable file that you can open in your IDE. If you use Visual Studio, your project's make.cmd is already included in the Solution Explorer; simply click on it to open and edit its contents.

Following the UNIX C rule, all the compiler and linker options are set in the IOS_CPPFLAGS, IOS_CFLAGS, IOS_CXXFLAGS and IOS_LDFLAGS variables, respectively. The contents of these variables are treated as command-line options (a.k.a. "flags") when invoking the compiler and the linker to build your project. If you need to find out the name or syntax of a particular compiler or linker option, please refer to the following online documentation:

Tip: use your browser's "search in page" and specify a keyword to quickly locate the option you need.

[ Back to top ]


3. How to activate the product (and unlock code signing)

In order to produce signed iOS apps, that can be deployed to stock, non-jailbroken devices, an activation is required to unlock the code signer.

To activate the product, open either the Keychain tool, the App Store Connect upload tool or the Project Builder for Unity and click the button labeled Unlock!.

  • Click on the PayPal button
  • Commit your contribution (if you can't use PayPal, please contact me).
  • Once this is done, check your email. You should have received your activation code. If you haven't (automated emails can be late, or mistaken as spam), please click here to retrieve your activation code manually.
  • Fill the dialog box with your PayPal email and your activation code. Optionally fill in your name or your company's name, then click Unlock.

The software is now unlocked. You can now produce signed iOS apps and deploy them to non-jailbroken devices :-)

[ Back to top ]


4. How to transfer your app to your device

4.a) You have a digital signing identity AND your app is a regular iOS app

Your device doesn't need to be jailbroken. To install regular iOS apps on your device, you must ensure:

If the above conditions are met, you can install your app in the following two ways. These concern regular iOS apps only.

Over The Air installation (recommended way)

If you would like your app to be automatically deployed after each build, simply set the value of the DEPLOY variable to yes in your app's make.cmd. Doing so will ensure your app will be immediately served on a local webserver, enabling your iDevice to install it immediately over Wifi.

You can also deploy your apps at any time by using the command-line uploader called ideployota. Type "%IOSBUILDENV_PATH%\ideployota.exe" on a Windows command prompt to learn about the possible options. An example to upload an app file called MyCoolApp.ipa using a QR code would be: "%IOSBUILDENV_PATH%\ideployota.exe" MyCoolApp.ipa qrcode

Please note that:

Side-loading with iTunes (old way)

Open iTunes. Locate your device in the left-hand panel (connect it via USB if necessary). Click the little arrow next to it so as to develop its contents. Now drag-and-drop the created app package (.ipa file) onto any of these categories in iTunes. Sync, and your app icon appears.

These instructions are known to work for iTunes 12.7 (see this thread on StackOverflow). It has been reported to me that in some cases you need to select and copy (CTRL+C) the .ipa file then paste it (CTRL+V) on the device's Music tab in iTunes. You might need to experiment a bit if you use another version (please don't ask me: truly, I don't know how other iTunes versions are meant to work.)

4.b) You don't have any digital signing identity OR your app isn't a regular iOS app

You can do so in 3 different ways, but first your device must be jailbroken (see 5.)

[ Back to top ]


5. (OPTIONAL) How to jailbreak an iPhone/iPod/iPad

Jailbreaking your device is the process of lifting some security barriers that Apple put on it, that prevent it to run unsigned code (which is literally code for which we can't know the author. You understand why it's an important security restriction). Although Apple would prefer you to abstain, lifting this restriction by "jailbreaking" is authorized, safe, and reversible.

To jailbreak your device, first check its model and iOS version in Preferences > General > Informations. Once you know these, head up to http://jailbreak-me.info and let the website select the right jailbreak program for you. Make sure you have a good ad blocker enabled so as not to download anything else than the jailbreak program (I am not affiliated with this website nor with the ad campaigns it runs - you have been warned). Get the jailbreak program that's right for your device (they're free), run it and follow the instructions.

You can read more on iOS jailbreaking here: http://en.wikipedia.org/wiki/IOS_jailbreaking

[ Back to top ]


6. How to catch application logs, system logs and stack traces

Open the remote debug console by clicking on the link in the builder window, or from your Windows start menu: All Programs > iOS Build Environment > Remote debug console.

click to enlarge

This cute piece of witchcraft allows any iOS app to send all that it would normally write to the standard output and the standard error streams stdout and stderr, to a window on your PC. That is to say, everything that is logged in your app using NSLog() macros, with printf(), fprintf(stderr, ...) or with cout << ... and cerr << ... will be caught, even stack dumps when it crashes, sent over the network to display before your eyes, on your computer, while your app is running on your device. And this works even for release builds.

It catches the iOS system logs. If your device is plugged to your computer with a USB cable and if you have iTunes installed, you can catch the syslogs in glorious ANSI colors. This is invaluable to find out what happens when your app refuses to launch (for example, because of an invalid entitlement). To opt for it, tick the "System logs" checkbox in the debug console's bottom status bar. Beware: iOS is a very chatty system.

Data filtering. Say you've enabled all checkboxes and are now receiving encyclopediae of data flooding the console window so fast that you can't read anything. Halp. You may filter all this garbage to either exclude, or allow, or highlight a character string. No less valuable.

When you need a break, Stop and resume logging by clicking the "Stop / Record" button (top right of the console window).

To enable debug cuteness, make sure that the REMOTELOG_IP variable in your project's make.cmd file is defined. This variable can have two values: either a dotted IP address that should be the address of your computer (such as 192.168.1.10), or just "myself" (in which case, the builder will substitute your own IP address) [Unity users: simply tick the "Enable remote debug console" checkbox in the builder window and specify your address there]. The app will send its data over TCP ports 5001 and 5002, so you should also make sure that the remote debug console window is open on your PC and that no firewall or other "Internet Security" stupidware gets in the way (at least for these protocol/port pairs).

NOTE: each time you toggle this checkbox, the project must be rebuilt from source in order to embed or disembed some additional code. If you get linker errors such as: duplicate symbol _main or ld: entry point (_main) undefined, then it means you toggled the remote debug console on/off without rebuilding. Simply tick the "Rebuild all files (once)" checkbox to fix it.

[ Back to top ]


7. How to debug your app

Running an app under debugger control enables you to stop your app's execution at certain points, display the contents of variables, display the call stack, and step line after line into your program at critical places. If you want to run your app under debugger control, you may do so with the GNU Debugger (GDB) over SSH.

Direct debugging on the device is only possible if your device is jailbroken (see 5.)

First, install the debugger package provided with iOS Build Environment on your device. The package file is located under the Debugger directory. Then, edit your project's make.cmd so as to make sure it builds a debug database: the IOS_CFLAGS line should have the -O0 (no assembler optimization - PAY ATTENTION: this is "dash, capital letter O, number zero") and -g (generate debug information) flags. If these flags aren't there, add them, like in this example:

rem # Define here the compile options and the linker options for your project.
rem # CPPFLAGS are compiler flags that will be applied to all source file types.
rem # CFLAGS are compiler flags that will affect C and Objective-C source files,
rem # whereas CXXFLAGS will affect C++ and Objective-C++ source files only.
rem # If you want debug symbols to use with gdb, replace "-O2" with "-O0 -g".
set IOS_CPPFLAGS=-W -Wall -Wno-unused-parameter -O0 -g
set IOS_CFLAGS=
set IOS_CXXFLAGS=
set IOS_LDFLAGS=

Rebuild your project and you should see that a new YourAppName.DEBUG directory has been created in the project's output directory, next to your project's main executable. This is the debug database that GDB will use. Note that each time you change something in your program and recompile it, a new debug database will be created. Don't forget to transfer both of them to your device, if you copy your app manually.

Now, install your app onto your device, using either of the 4 methods described in section 4. Log in as root over SSH, and type debugapp YourAppName. The GNU debugger will load, verify that your app and its debug database match together, and wait for your app to start. As soon as you start it (by touching its icon from the SpringBoard screen), the debugger will break in, load its debug information, and wait for your orders.

iPhone-a-PM:~ root# debugapp Wolf3D
==============================================================================
Please start Wolf3D manually on your device once GDB has loaded
==============================================================================
Loading GDB...
GNU gdb 6.3.50-20050815 (Apple version gdb-1708) (Fri Sep 16 07:00:41 UTC 2011)
Copyright 2004 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "arm-apple-darwin"...Reading symbols for shared libraries . done

Waiting for process 'Wolf3D' to launch.
Attaching to program: `/private/var/mobile/Applications/6E86BB25-4866-402D-9C3E-8CF7D85753F3/Wolf3D.app/Wolf3D', process 30696.
Reading symbols for shared libraries + done
Reading symbols for shared libraries .............................................................................. done
0x2fe6b796 in __dyld__ZN4dyldL10loadPhase5EPKcS1_RKNS_11LoadContextEPSt6vectorIS1_SaIS1_EE ()

(gdb)

From then on, you can place breakpoints, and start examining your program execution.

(gdb) break Level_PrecacheTextures
Breakpoint 1 at 0x1ed68: file wolf\wolf_level.c, line 436.
(gdb) continue
Continuing.
Reading symbols for shared libraries ...................... done
Reading symbols for shared libraries . done
Reading symbols for shared libraries .. done
Reading symbols for shared libraries ... done

Breakpoint 1, Level_PrecacheTextures () at wolf\wolf_level.c:436
warning: Source file is more recent than executable.
436        for (x = 0; x < 64; x++)
(gdb) step
Current language:  auto; currently minimal
437           for (y = 0; y < 64; y++)
(gdb) step
440              if (leveldata.tilemap[x][y] & WALL_TILE)
(gdb) display leveldata.tilemap[x][y]
1: leveldata.tilemap[x][y] = 1
(gdb)

The GNU debugger understands many commands. If you need to learn them, there are a lot of very good tutorials on the Internet, such as this one. Look up Google for "GDB tutorial". In a nutshell, the most interesting commands are:

More information on the GNU Debugger's official project page.

[ Back to top ]


8. How to use a dynamic library from the SDK in your project

As with XCode, it is possible to use any dynamic library (.dylib) from the iOS SDK with your project. Let's say you want to use the popular libXML2 dynamic library, which corresponds to the libxml2.dylib file in the usr/lib subdirectory of the SDK. To link your project against this library, open your project's make.cmd file and add the search path and the name of this library to the linker flags line (IOS_LDFLAGS) in the following way:

rem # Define here the compile options and the linker options for your project.
rem # CPPFLAGS are compiler flags that will be applied to all source file types.
rem # CFLAGS are compiler flags that will affect C and Objective-C source files,
rem # whereas CXXFLAGS will affect C++ and Objective-C++ source files only.
rem # If you want debug symbols to use with gdb, replace "-O2" with "-O0 -g".
set IOS_CPPFLAGS=-W -Wall -Wno-unused-parameter -O2
set IOS_CFLAGS=
set IOS_CXXFLAGS=
set IOS_LDFLAGS=-L/usr/lib -lxml2

The -L***** (capital L) tells the linker to look for dylibs in this directory (from the root of the SDK), and the -l***** flag (lowercase L) tells the linker to link against a library called lib*****.dylib. As such, -L/usr/lib -lxml2 will link against the /usr/lib/libxml2.dylib file, just like -L/usr/lib -lsqlite3 will link against /usr/lib/>libsqlite3.dylib, -L/some/funny/path -lSomeFunnyLibrary will link against /some/funny/path/libSomeFunnyLibrary.dylib file, and so on.

IMPORTANT: don't miss out that you MUSTN'T specify the "lib" prefix nor the ".dylib" suffix in the flags!

You can add as many flags as there are libraries you want to link against your project. For example:

IOS_LDFLAGS=-L/usr/lib -lxml2 -lcharset -lbz2 -lIOKit

Here your project will be linked against the libxml2.dylib, libcharset.dylib, libbz2.dylib and libIOKit.dylib, all of them from the /usr/lib subdirectory of the iOS SDK.

If you get #include or #import errors at build time while trying to include the headers of these libraries, you might need to specify the location of these header files in the make.cmd file too. For example, the libXML2 headers are not located at the root of the traditional usr/include directory of the SDK, but in a subdirectory of it. So, you need to tell the compiler to go and look for header files in that location too. This is done in the C[PP,XX]FLAGS, using the -I flag:

IOS_CPPFLAGS=-W -Wall -O2 -I"C:/Users/Pierre-Marie Baty/iOS Build Environment/SDK/usr/include/libxml2"

[ Back to top ]


9. How to use a dynamic library that is NOT present in the SDK, or a third-party framework

Apple puts some restrictions on this. The only allowed way is to embed all of the library or framework code directly within your app.

The canonical way to achieve this is to use the library or framework you want as a static library.

The other option, much simpler, is to simply include all of the framework's source files within your project, for example by putting them in a subdirectory. This way, there is no need to link against any external library. Just compile your project with the framework's source files in it, as if they were part of your projet.

Please read the "How to use an external framework.txt" help file in the ExternalFrameworkDemo project directory for further instructions.

[ Back to top ]


10. How to submit an app to the Cydia Store

To send your app to Cydia, it depends on which source (repository) you want to host it. For example, submissions on the BigBoss repository happen here: http://thebigboss.org/hosting-repository/submit-your-app. For the other repositories, please refer to their owner's website. I don't provide a list, as every now and then the URLs change.

[ Back to top ]


11. How to upload an app to Apple's App Store Connect (formerly iTunes Connect) or TestFlight

In order to submit an app to the App Store, you need to send it using your App Store Connect account to the App Store Connect website. This can be done using the App Store Connect upload tool.

You need a few things to upload your app to App Store Connect. Checklist:

First, you need a build of your app signed (or re-signed) with a signing identity that's fit for App Store submissions. App Store Connect will only accept apps that are signed using a distribution certificate, so make sure your app is signed with such a certificate. In case it's not, see the signing identities section of this document to learn how to get one. Luckily for you, the App Store Connect upload tool that comes with this toolchain allows you to re-sign already packaged apps before submission.

You also need your app to embed a provisioning profile that has an explicit app ID in it. See the provisioning profiles section of this document to learn about the difference between explicit and wildcard app IDs: basically, a provisioning profile with an explicit app ID is made for one and only one app, whereas a provisioning profile with a wildcard app ID can be reused across multiple apps. Apple doesn't allow the latter for App Store submissions. So, if your current provisioning profile is a wildcard one, you will need to create an explicit app ID for your app first, then an explicit provisioning profile out of it.

You also need your provisioning profile to be fit for App Store submissions. Choose App Store (under the Distribution category) when creating such a provisioning profile.

Now, if you haven't already done so, you need to prepare a record for your app on the App Store connect website. Log in to the App Store Connect website, go to My Apps and click the plus sign to create a new record. Fill the form that describes your app: the platform on which it'll run (obviously you want iOS), the app's name, its main language, its bundle identifier (which must match the one you specified in your app's make.cmd file or in the Unity editor if your app is a Unity app), and a unique string for you to identify it easily.

If your account uses two-factor authentication, Apple will require you to set up an app-specific password to use in the upload tool in place of your App Store Connect account's main password. Click here to find out how to create an app-specific password.

click to enlarge

When you're set, fire up the App Store Connect upload tool. Browse to the .ipa file you want to upload, specify a signing identity suitable for the App Store, fill in the fields as appropriate (your login/password or app-specific password), and hit the Upload button. A window will pop up with a progress bar during which your app will be delivered to App Store Connect.

After the upload is done, Apple will send you an e-mail at your App Store Connect account's address to tell you whether it accepted your app for delivery (w00t!) or whether there are still some problems to fix in it. Pay attention to these messages if you want to correct them quickly and efficiently. A Google search on a particular phrase usually helps in understanding what's wrong.

Once your build has been accepted by Apple, it's up to you to choose what to do with it: for example, distribute it to your beta testers through the TestFlight program, or send it to the App Store. All the rest of this process now takes place on the App Store Connect website, which you will soon be accustomed to use. :-)

[ Back to top ]


12. About digital signing identities (requires product activation)

iOS apps require to be signed with a signing identity in order to be deployed on non-jailbroken (stock) devices, and/or submitted to App Store Connect and the App Store.

A "signing identity" is the association of a certificate approven by Apple and a private key, that is used to seal that certificate into the app, AND a provisioning profile that tell which devices (typically yours and your testers') are allowed to install your apps.

3 cases:

case a) You already have these files on Windows.

In order to sign your app with a signing identity, put these certificate(s) (.cer files), private key (.key file) and provisioning profile(s) (.mobileprovision files) in the Keychain directory where the iOS Project Builder for Unity is installed. The build tools will then be able to pick them up and let you select which identity to use.

case b) You don't have them on Windows but you have a signing identity on Mac.

All iOS signing identities found on your Mac are automatically migrated along with the iOS SDK by the Migration Assistant when you run it. If you want to grab a signing identity from your Mac that was created after the iOS SDK was migrated, simply run the Migration Assistant again.

case c) You don't have any signing identity yet.

First, you need to enroll in the iOS Developer Program: https://developer.apple.com/programs/ios/ (if you wonder whether you can use a free Xcode certificate, read about it a few paragraphs below).

Now, you need to generate a new signing identity. Use the iOS Project Builder for Unity's Keychain tool to generate a new private key, and a certificate signing request (CSR). Your private key will be automatically installed in the Keychain directory, and you get a .certSigningRequest file that you can save somewhere on your disk.

Now visit the iOS Developer Portal at https://developer.apple.com/account/, login with your Apple iOS developer ID, click on Certificates, Identifiers & Profiles in the menu and create the certificates you need using your .certSigningRequest file ('+' button). Once the Apple website created your certificate, download it and save it in the Keychain directory, alongside with your private key.

Finally, you need a provisioning profile, to tell iOS which devices are allowed to install your apps. Read on the next section to find out how to create that.

all cases

Once you have your private key and certificate installed in the Keychain directory, they become usable by the builder. Please refer to your iOS app project's make.cmd file and follow the instructions in it so that your digital identity is embedded in your app at build time, or use the Keychain Tool to specify a default signing identity. Please note that only valid, non-expired certificates will appear in the certificates list.

You may then deploy your signed apps to all the devices that are listed in the app's provisioning profile (see 13) with the generated .ipa, automatically after each build (OTA), or manually (see 4.)

Frequently asked: Is enrolling into the iOS Developer Program (and paying $99 to Apple) really mandatory? Can I use that "free" signing identity that Apple offer to Xcode users?

It is not strictly necessary to enroll in the iOS Developer Program, but in my opinion the benefits exceed the expense. Simply put, if you don't, you will need to use those "free" code signing certificates generated by Xcode, and since these certificates have a very limited lifetime (10 days) by Apple's design, be prepared to migrate them each and every week from your Mac and rebuild the apps that use them to make them installable again. Not only that, but by Apple's design, over-the-air deployment can't be used with those free certificates, which means you'll need to deploy your apps by hand on your devices (using iTunes sync for example), and when you do this 20 times a day or more, that's a consequent waste of time. A lot of advanced features related to signing identities, application entitlements, device provisioning, are not available to free users (or seriously hindered), which mean you won't be able to test many things that you code. That's why my advice is to bite the bullet and go the official way, unless you're really budget-constrained - and be prepared for a lot of hassle if you choose to go the narrow way.

[ Back to top ]


13. About provisioning profiles (requires product activation)

Provisioning profiles are authorization files that, when installed on an iOS device, tells iOS which apps it is allowed to let you install. When you install a custom app on your iOS device, iOS complies to the provisioning profiles it has to decide whether it lets the app in or throws it away. App Store apps are not concerned, because they are all re-signed by Apple as part of the app submission process (this is why App Store apps are always accepted by iOS.)

If you want to deploy your own signed apps on an iOS device, you need a provisioning profile for it.

I already have a provisioning profile from Xcode. Can I reuse it?

Sure. Just make sure that the App ID you specify in your project matches the App ID in the provisioning profile.

If it's a "wildcard" provisioning profile, you're good. Else, if it's an "explicit" one, the IDs must match exactly. To find out the difference between a "wildcard" app ID and an "explicit" app ID, see this page.

In all cases, if you have the slightest doubt, the safest way is certainly to obtain a new provisioning profile from the iOS Provisioning Portal.

How to obtain a provisioning profile?

Provisioning profiles instruct iOS to let you install apps signed by yourself. Here is how to create one.

First, create an app ID:

Now, let's instruct which device(s) this provisioning profile will concern:

Finally, create a provisioning profile for this app ID and the device(s) you just registered:

Your provisioning profile is ready. You may download and save it in the Keychain directory, alongside with your private key.

And now that you have a private key, a signing certificate and a provisioning profile with your app and device IDs in it, your signing identity is usable and you're good to go.

[ Back to top ]


14. How do I add extra entitlements (a.k.a Application Services) to my app?

If you want to enable your app to access special iOS services (such as Apple Pay, iCloud, etc) it must fullfill two conditions:

Enabling these services is done when you register your app ID on the iOS Developer portal here: https://developer.apple.com/account/ios/identifier/bundle/

When you create an app ID, either generic (wildcard ID) or specific (explicit ID), you are allowed to choose which extra Application Services will be accessible to an app wearing this ID. Technically speaking, these services are called an app's entitlements.

Configuring these services now is done through an external .plist file, a text-editable ASCII property list that will contain the app-specific entitlements data, that will be hard-stamped in the app binary at signing time. In a nutshell, this is a text file you create with the .plist extension, and in which you put a dictionary of key/values like this:

<?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>com.apple.developer.associated-domains</key>
	<array>
		<string>applinks:some-machine.somedomain.com</string>
		<string>applinks:other-machine.somedomain.com</string>
	</array>
</dict>
</plist>

In this example, the Associated Domains app service is set to trust the some-machine.somedomain.com and other-machine.somedomain.com hosts. The key to use for this service is com.apple.developer.associated-domains.

Then, in your project's make.cmd file, simply assign the variable called ENTITLEMENTS the pathname to your entitlements .plist file, so as to tell the builder to stamp these entitlements in your app at code signing time (if the variable is :commented_out, remove the leading colon to enable it). Like this:

rem # If you need to stamp special entitlements into your app (or in Apple words,
rem # configure some special "Application Services"), you can do so here by using
rem # a .plist file that will contain the entitlements key/values pairs to seal
rem # into your app. Note that the corresponding services must have been turned
rem # on when you created your App ID online in the iOS Developer Portal.
set ENTITLEMENTS=C:\Users\Pierre-Marie Baty\Documents\iOS Projects\MyCoolApp\MyCoolApp-entitlements.plist

When you need to configure a particular service and you don't know which entitlement key(s) it uses, a good starting point is to read this page in the Apple online documentation to learn which key names are associated with a particular service. Then use Google to find out about its possible values.

[ Back to top ]


15. What's a "pre-packaging script" and why would I need one?

A "pre-packaging script" is an executable file that you specify (either .bat, .cmd, or even .exe, or any extension for which you have an interpreter: .js for Javascript, .py for Python, etc. as long as the corresponding interpreter is installed), that the builder will run just before your app is signed and packaged. You can use this extra build step to arrange custom resource files in the app's directory, move some from here to there, include some others, exclude other ones, and even generate new ones on the fly.

Your app's file layout, i.e. the directory in which you're interested to make changes, is typically the build\Release-iphoneos\iPhone-target\YourAppName.app directory that is created in your project directory by the builder (Release-iphoneos being the Xcode project's configuration name and the SDK's target system, and iPhone-target being the Xcode project's target name which in Apple's terminology is equivalent to a Visual Studio's project name). Anyway, this is the directory whose contents will be signed and packaged in the final .ipa file, that will be deployed to your iDevice. To find out which change(s) are needed, you must know which file(s) your app needs and where it expects to find them.

If you want to use a pre-packaging script for your app, create a script that will make the necessary changes to this directory, and specify it in the PREPKGSCRIPT variable in your make.cmd file, like this:

rem # If you need to use a pre-packaging script (that is to say, a custom script
rem # that the build system should execute after the app is built and before it
rem # is sealed with a code signature), you can define it here. Such a script can
rem # be used to perform better resource placement within the app directory.
rem # Enter here the path, file name and optional arguments of your script, as it
rem # should be invoked by the command processor.
set PREPKGSCRIPT=C:\Users\Pierre-Marie Baty\Documents\iOS Projects\MyCoolApp\MyCoolApp-prepkg.bat

Note: you can also use a relative path to the script to call here, in which case the path is relative to your project's root directory.

Unity apps that don't use make.cmd files may instruct the builder to call a pre-packaging script by checking the corresponding checkbox in the builder's interface.

Here's an example of a fictious pre-packaging script, called MyCoolApp-prepkg.bat:

@echo off

rem // hint: there are a few environment variables that you can use in your script. For example:
rem // %NAME% - will expand to your app's main binary name, e.g. "MyCoolApp"
rem // %CONFIGURATION% - will expand to the Xcode project configuration, e.g. "Release"
rem // %TARGET_BUILD_DIR% - path where the app was constructed, e.g. "build\Release-iphoneos\iPhone-target"

rem // see if we're calling this for the right app
if not exist "%TARGET_BUILD_DIR%\MyCoolApp.app\." (
	echo Looks like you're not calling this script for the right project... ^(exiting with an error code^)
	exit /b 1
)

rem // copy the missing .png files that my app expects to find in 'ExtraResources'
mkdir "%TARGET_BUILD_DIR%\MyCoolApp.app\ExtraResources"
copy /b /y C:\Users\Me\DevelopmentPictures\*.png "%TARGET_BUILD_DIR%\MyCoolApp.app\ExtraResources"

rem // remove a big duplicate file that was incorrectly bundled in two places
del /y "%TARGET_BUILD_DIR%\MyCoolApp.app\BigFatDataBase.sqlite"

[ Back to top ]


16. Can I build or upload my iOS projects from the command line?

Yes you can!

The witchcraft to use lies in the build.cmd file that's in the main directory where the iOS Project Builder was installed. Basically, you call it with the path to an Xcode project generated by Unity, your signer identity string, and a couple of options to tell whether you want it to produce an .ipa file, a .deb file, or both, and whether you want to deploy the app to your device directly.

Invoke it with build.cmd /? to see the full list of command-line options, and to learn more about what is a signer identity string in the context of this builder. An example could look like this:

"%IOSBUILDENV_PATH%\build.cmd" "C:\Users\Pierre-Marie Baty\Desktop\AngryBots" -identity "my_certificate.cer:private_key.key:MySecretPassPhrase" -provision "C:\path\to\provisioning-profile.mobileprovision" -ipa

This command line builds the AngryBots Unity project that's sitting on my desktop and outputs a signed .ipa file. Ain't it cute?

Extra magic: in the Xcode project builder UI, if you hit the Build button while holding the Shift key pressed, the build log will output a lot of debug info. The first line will be the full invokation of build.cmd, that you can copy/paste and use in your own continuous integration tools!

Building from the command line is specially useful if you want to integrate this builder into your own custom build chain. If you are a professional developer, it will make your testers save an insane amount of time!

Uploading (with or without resigning) an .ipa file to App Store Connect is also possible from the command line. See upload.cmd /? for the gory details. Here's an example:

"%IOSBUILDENV_PATH%\upload.cmd" "C:\Users\Pierre-Marie Baty\Desktop\AngryBots\Packages\AngryBots.ipa" -identity "my_certificate.cer:private_key.key:MySecretPassPhrase" -provision "C:\path\to\provisioning-profile.mobileprovision" -asclogin "your@email" -ascpassword "AppStoreSecretPassword"

This command line re-signs then uploads the AngryBots.ipa file to App Store Connect.

[ Back to top ]


17. Can I add embedded binaries (dylibs or frameworks) into my app?

Yes.

This is normally automatic if those embedded binaries or frameworks are defined as such in your Unity-generated Xcode project file. The builder should simply detect them, and do the job appropriately.

In the case where it wouldn't be the case (you can tell so if you examine the contents of your MyAppName.app directory after a successful build, and check whether your embedded binaries are there or not in the Libraries or Frameworks subdirectories), you may try to force-embed them by specifying them explicitly on the command line in a command-line build.

"%IOSBUILDENV_PATH%\build.cmd" "C:\Users\Pierre-Marie Baty\Desktop\AngryBots" [...build options...] -embed "relative/path/to/SomeLibrary.dylib relative/path/to/SomeOtherFramework.framework" [...other build options...]

Please note: specifying the embedded binaries on the command-line will add them to the list of embedded binaries that the builder found in the Xcode project. It will not supercede them! So, if there's an error in your Xcode project file, it's wiser to fix it so that your extra binaries are embedded automatically instead of relying on a command-line build to do the job.

[ Back to top ]


18. My project uses CocoaPods. Will it work?

Yes. At last.

It took me ages, but I finally managed to make this horrific bloat that's impersonating itself as a dependency manager to work on Windows. Since the NTFS filesystem used by Windows had limitations that were definitely incompatible with the way CocoaPods works, a quasi Darwin-compatible lightweight BSD UNIX subsystem had to be created to host the CocoaPods client. This subsystem comes pre-installed with the toolchain, but not activated. If you want to use it, you'll need to acquire an activation key: simply follow the instructions when you start it the first time.

To use it, cd to the directory where your Podfile is, and prefix your pod command by the pathname of the CocoaPods daemon control script, like this:

"%IOSUNITYBUILDER_PATH%\Toolchain\cocoapods\cocoapods.cmd" pod install

All the pod commands are supported (see the full list), and also Ruby gem commands (such as gem update cocoapods) to update or fix CocoaPods itself. Note that when the build script finds a Podfile in your project and the CocoaPods daemon is usable, it automatically calls it to set up your project dependencies, which means you should rarely ever need to issue a pod command yourself.

Since this subsystem is implemented as a daemon, it must be started first, which can take some time at the first call (around one minute, the time it takes for booting an operating system), after what it stays resident in memory until you decide to stop it. Be prepared that CocoaPods will initialize itself by downloading a mind-boggling 1.5 gigabyte of data off GitHub to your computer to construct its database of all the available Pods. Of which only a ridiculous kilobyte is actually pertaining to your application — I leave the computation of the bloat factor of this ubiquitous software as an exercice to the astute reader.

Note that during that initial setup phase, you need a good and steady Internet downlink, else CocoaPods will likely issue this error: [!] Unable to add a source with url `https://github.com/CocoaPods/Specs.git` named `cocoapods`. You can try adding it manually in `/Users/cocoapods/.cocoapods/repos` or via `pod repo add`. It simply means your connection to GitHub was interrupted, and CocoaPods isn't smart enough to recover from where it stopped.

Why make this an optional add-on? Because even if I spent an insane amount of time (i.e. months) figuring out a way to make it work, not all of those who build iOS or macOS apps on Windows also need CocoaPods to work on Windows, and trying to recover the development cost by raising the whole toolchain's would have turned out unfair for many. The legacy workarounds still work (e.g. downloading and laying out your project's dependencies by hand, although more fastidious), so this add-on is strictly optional.

[ Back to top ]


19. Limitations (and workarounds)

[ Back to top ]


20. Troubleshooting

If you still get the nag popup in your app even after registration:

If your app fails to build:

Make sure you're using the latest version of the builder, and update if necessary. While doing this, it is advisable to keep a backup copy of your current installer for reference.

The next thing to do is to have a readable builder log. As most build errors are consequences of earlier errors, you need to ensure the build will process only one file at a time, and immediately stop at the first error : it is that one you need to fix first.

The second thing to do is to make sure you're not trying to compile a project that requires a version of the iOS SDK that's higher than the one you have migrated. If in doubt, update your iOS SDK to the latest one by first updating Xcode on Mac, then using the Migration Assistant again to transfer the iOS SDK from Mac to Windows. Also, avoid using "beta" versions of the iOS SDK unless you really have to: they might be less well digested by this toolchain.

  • if it's a Unity app:
  • if it's NOT a Unity app:
  • in all cases:
  • If your app fails to install (you can tell it's so if the app icon isn't rendered correctly):

    If your app installs successfully, but fails to start:

    If your app fails to send anything to the remote debug console:

    If your app starts, but the ARKit features don't work:

    If your app crashes:

    If your app works, but fails to upload to App Store Connect (formerly iTunes Connect):

    [ Back to top ]


    21. Change log

    [ Back to top ]