Over the past three months I’ve created three Air Native Extensions for iOS (available on Github), which were all used in our game Stick Cricket Super Sixes, and have discovered a lot along the way. I’ve forgotten some of it, but here’s a few of the tips I can remember for anyone else approaching this task.
1. Getting Started
Here’s a couple of useful getting started guides. If you don’t know what you’re doing, this is a good place to start. There are more on the web, just ask Google.
2. Read Adobe’s documentation.
Writing the Actionscript code
3. Pay attention to the Actionscript API
Make an Actionscript API that suits the functionality you’re implementing. Don’t get pulled into copying Apple’s iOS API in Actionscript if there’s a better, more Actionscript like, way to do it.
Remember, changing the implementation later only affects the extension, but changing this API breaks all projects that use the extension so make it as good as you can.
4. Maximise the error checking in your Actionscript code
You need to check the input to your extension methods to avoid errors. You could do this in the native code, but it’s a lot easier to do it in the Actionscript code. If you know you’ll always call the native code with the correct number of parameters, and that those parameters will always be valid values, you can avoid a lot of error checking in the native code.
Of course, if you enjoy writing error checking code in C, go right ahead. Personally, I prefer to do this in Actionscript.
5. Always create a default implementation
Even if you’re only planning to use your extension on iOS, create a default implementation anyway. You build this default implementation in pure Actionscript, so it works on all platforms. It implements the same API as the iOS version, but using only Actionscript. That means it may throw errors or returns null values, but it will be valid Actionscript code that you can compile against for platforms other than iOS.
Without a default implementation, you can only ever test your app on an iOS device. This makes testing your app during development very tiresome. Being able to compile your app for desktop Air to test a new feature that’s unrelated to your native extension is very useful.
Writing the native code
6. Use Objective-C to interface to Apple’s APIs.
You don’t have to use Objective-C, but if you’re interfacing with any of Apple’s APIs it’s easier if you do. Otherwise, you can use C or C++.
7. Place the C code for the Air-Native interface in a .m file.
The interface between the native code and Air is via C, so your native code will include one file of C code to provide this interface. If you place this code inside a .c file, you can’t use any Objective-C functionality in there (e.g. you can’t use objective-c style code to call objective-c APIs), but if you place it inside a .m file you can. So life is easier if you use a .m extension.
8. The .m file containing the Air-Native interface doesn’t need a .h header file.
But you’ll want to disable the error messages so turn off warnings for “missing function prototypes” in your XCode project settings.
9. Use unique names for your extension initializer and finalizer functions.
A number of example extensions use generic names like “extInitializer” and “extFinalizer” for the initializer and finalizer functions. No two extensions in your project may have the same names for these functions, so use names that are appropriate to your project.
10. Use macros in your source code
How many times can you type
FREObject someFunction(FREContext context, void* functionData, uint32_t argc, FREObject argv)
before you want to poke yourself in the eye with a blunt stick? Create a macro for this, and any other repetitive tasks for implementing the Air-Native interface. You can see a few of my macros in this source file. (Thank you to David Wagner for some of these.)
11. Set “Enable linking with shared libraries” to yes
There’s a lot of noise around the web about this XCode project setting, with some saying use “no” and some saying use “yes”. Basically, setting this to yes means your extension will be linked with the required libraries later when you compile your app. This is what you want. But it means when you compile your Air app that uses your native extension, you’ll need to direct the compiler to the iOS SDK so it can link to the libraries. See item 18 below.
N.B. If you get a build error
ld warning: unexpected srelocation type 9
then you probably either have this setting as no or you haven’t specified the location of the iOS SDK when building your app.
12. Be careful with threads
Your native extension can’t create Actionscript objects in any but the main thread from which it is called, and must do so during a call from Air. This makes using blocks in your objective-c code problematic. However, there is one thing you can do from another thread, and that is dispatch an event to the Actionscript ExtensionContext. This offers a solution
- In your block or other threaded code, store your data into a native object.
- Dispatch an event to your ExtensionContext with whatever details it needs for step 3.
- In response to the event, call a native method to fetch the object.
- In this native method, create the Actionscript object from the native object created in step 1 and return it.
13. Creating native views
Your Air application runs inside a standard native window. You can get a reference to this window as
Once you have this window reference you can add subviews to it to display native views.
Compiling the Extension
14. Automate the compiling and testing of your extension
When building my Game Center extension I created a simple air app for testing the extension.
I created an ant build script to compile the whole extension (build the native C/obj-C library, build the native AS3 library, build the default AS3 library, wrap them all as an Air native extension) and to build the test project.
The result, one click in Eclipse and I had the extension built and the test project built against it, ready to drop on my phone (and using the Organiser in XCode makes this last step easy too). The result – it took about 10 seconds to go from writing code to having the test project with the latest extension on my phone. That’s still 9 seconds too long but it’s a lot better than most workflows I’ve seen.
Maybe there’s a better way if you use Flash Builder (I use FDT), and you certainly don’t have to use Ant – any build tool will do. But streamline the build and test process as much as you can. My ant scripts are in my Game Center extension project if you want them.
15. Using a third party library
If you’re using a third party library in your extension (e.g. the Flurry analytics library in my Flurry extension), include that library along with your compiled native library in the “-platform iPhone-ARM” argument when compiling the extension.
16. You may need a platform.xml file.
If using any but the most common libraries in the iOS SDK, create a platform.xml file to specify the libraries you’re using and the minimum iOS version to build against.
I create a platform.xml file for all my extensions – it hides the warning about unknown minimum iOS version that Air 3.1 will display if you don’t.
Building your mobile application
17. If your IDE doesn’t know about native extensions, use the .swc from the default implementation.
This swc contains the full Actionscript API of the native extension in a pure Actionscript implementation, so you can code against it, and compile with it for testing on your dev computer.
Of course, an IDE that doesn’t know about native extensions won’t build your mobile app correctly, but you’re using a build script for that aren’t you.
18. Specify the location of the iOS SDK when compiling your Air mobile app.
By default, the Air compiler compiles against version 4.0 of the iOS SDK. If your native extension uses features that don’t exist in this version it will fail to compile because the required libraries can’t be found. Specify the location of a later version of the iOS SDK to enable this linking. See this blog post for more details.
In Flash Builder, there is a setting for this somewhere in the various screens for configuring the native extensions your project uses. If you’re using the command line compiler, use the -platformsdk compiler flag to specify the path to the iOS SDK.
Unfortunately, this is only possible if you’re compiling your app on Mac OS X. Adobe are, apparently, working to get this working on Windows too. If you try to use a native extension that requires you to link to the SDK and don’t link to it, you will get a compile error like this (the second line may or may not be present).
Compilation failed while executing : ld64 ld: unknown option: -ios_version_min
Unfortunately, this means that for the time being some native extensions can only be used if you compile on a Mac.
19. ld: warning: ARM function not 4-byte aligned
When compiling your app, you may get a number of warnings of the form
ld: warning: ARM function not 4-byte aligned
These are just warnings and nothing to worry about. 4-byte alignment is an optimisation rather than a requirement.
When it’s all done
20. Make your native extension available to others.
Having completed your extension, you have three choices
- Keep it to yourself
- Open-source it for other developers
- Sell it to other developers
Ask yourself what makes your app unique and special. If it’s not your native extension (and it very rarely is) then choosing option 2 or 3 may have many benefits for you like code contributions from others or additional income.
Further, by making your extension available to other developers you strengthen the ecosystem around Adobe Air as a tool for mobile development. This encourages other developers to use Air and encourages Adobe to continue to develop and improve it. This is in your interests.
At Stick Sports we favour option 2 because we’d rather spend time marketing and supporting our games than marketing and supporting our extensions as commercial products.