Developing of Adobe AIR Native Extensions of iOS. Tutorial 1

Share Button

Screenshot+2014-01-02+22.10.03Hello to anyone! Just decided to write a small tutorial series about developing ANE for Adobe AIR for iOS. I’m not strong Objective-C developer so I can’t code by one hand, but my tries always awarded by working solutions.

Disclaimer: I’m not a native English speaker. Please sorry for my grammar. You can always write correction in comments.

osxFirst that we must to have it’s OSX and installed xcode. Maybe someone have Apple computer and someone don’t have – this is not so critical because you always can use hackintosh. In my case it’s Windows with installed VMWare with unlocker that allow to install OSX.

Before we go down inside of world of ugly and impossible (just on first look) Objective-C – let’s try to understand how Objective-C collaborate with Adobe AIR.

AIR it’s a usual native application for iOS. This mean that we can access to anything that you can reach from any other application that was developed in Objective-C.

Architecture of extension looks like this (also for the rest OS that can support AIR and not only iOS):

displaylistAll rendering done inside window that can be accessed from Objective-C. This mean we can place own UI over and under Adobe AIR. In some manner it’s like default Flash display List. Sure I simplified my description but this don’t change work flow. In this tutorial we will not to work with User Interface but now you can understand that is possible.

Let’s begin our practice!

Screenshot+2014-01-02+22.10.03I want to start from the simplest thing – number near application icon. It have default name in iOS – badge. Pretty useful thing if we want to inform user about something inside our app or just take it attention. In games this mostly used just for attention.

Step 1: Required files for developing ANE

Download latest Adobe AIR SDK and unpack it somewhere. Go inside \include folder and locate FlashRuntimeExtensions.h file. It’s contain methods header that we can call from our extension and it will provide data transport between native side and AIR.

Step 2: Project creation and configuration

Run xcode and create project using Cocoa Touch Static Library template. This something like DLL on Windows but just for OSX;

2014-12-09_012948 2014-12-09_013224

Press Next and fill all fields. This will not affect on anything:

2014-12-09_013250

Than you required to select (or create) a folder where your project will be created:

2014-12-09_013260

I’m created folder and select. Select our project BadgeANE and inside project settings we must setup Targeted Device Family: iPhone/iPad and select minimum iOS version. I selected 4.3.

2014-12-09_014708

Now we must add to our project FlashRuntimeExtensions.h file with headers from AIR. On the left side select context menu as on image:

2014-12-09_015025

At now we must select exact file. You don’t required to change this settings. Just go next.

2014-12-09_015230

On this step we are done.

Step 3: Writing extension header *.h file

Press once on BadgeANE.h at the left side and write this:

#import <Foundation/Foundation.h>
#import "FlashRuntimeExtensions.h"

@interface BadgeANE : NSObject

@end

void BadgeANEContextInitializer(void* extData, const uint8_t* ctxType, FREContext ctx, uint32_t* numFunctionsToTest, const FRENamedFunction** functionsToSet);
void BadgeANEContextFinalizer(FREContext ctx);

void BadgeANEExtInitializer(void** extDataToSet, FREContextInitializer* ctxInitializerToSet, FREContextFinalizer* ctxFinalizerToSet);
void BadgeANEExtFinalizer(void* extData);

I don’t see a reason for detailed description of this part. Just let you know that here we describe exported method that AIR require and make data transport available.

If you never developing on C-like languages – you may be was shocked. But it’s not so scary. On the left side we have object type and on the right side – variable or method name.

For example from code above:
void BadgeANEContextFinalizer(FREContext ctx);

In ActionScript 3 this is the same:
function BadgeANEContextFinalizer(ctx:FREContext):void

Before ContextFinalizer and etc. you can see BadgeANE. You can change BadgeANE to anything that you want because in the future this must be used in extension descriptor. But you must note that if someone develop extension with the same export methods – native extension will not work. I strongly recommends to use unique names for this methods that can’t be used in other extension.

Also I don’t suggest to use underscore in this methods names because this will fail on compiling or ANE became unusable.

Step 4: Developing body of extension in *.m file

We just finished with the headers. Now let’s begin to develop extension code.

Click on BadgeANE.m from the left and add this code after @end:

void BadgeANEContextInitializer(void* extData, const uint8_t* ctxType, FREContext ctx, uint32_t* numFunctionsToTest, const FRENamedFunction** functionsToSet)
{

    *numFunctionsToTest = 1;
    FRENamedFunction* func = (FRENamedFunction*) malloc(sizeof(FRENamedFunction) * (*numFunctionsToTest));

    func[0].name = (const uint8_t*) "setBadgeNumber";
    func[0].functionData = NULL;
    func[0].function = &setBadgeNumber;    

    *functionsToSet = func;

}

void BadgeANEExtInitializer(void** extDataToSet, FREContextInitializer* ctxInitializerToSet, FREContextFinalizer* ctxFinalizerToSet)
{
    *extDataToSet = NULL;
    *ctxInitializerToSet = &BadgeANEContextInitializer;
    *ctxFinalizerToSet = &BadgeANEContextFinalizer;
}

void BadgeANEExtFinalizer(void* extData)
{
}
void BadgeANEContextFinalizer(FREContext ctx)
{
}

In this code I must tell only about most largest method – BadgeANEContextInitializer. This methods it’s a core of extension. When you initialize extension – it’s called.

Here we describe how many functions our ANE will service:

*numFunctionsToTest = 1;

Fill up array with our data:

func[0].name = (const uint8_t*) "setBadgeNumber";
func[0].functionData = NULL;
func[0].function = &setBadgeNumber;

Name – it’s string value that we calling from AS3 as method name, functionData it’s context data that we not require and at least we setup reference to C function that must be called.

Now we must define setBadgeNumber method. Because we must to work with a whole application – we must import some header because badge it’s part of User Interface.

Scroll up and after import of BadgeANE.h just add UIKit.h and FlashRuntimeExtensions.h:

#import "BadgeANE.h"
#import "FlashRuntimeExtensions.h"
#import <UIKit/UIKit.h>

Now go down to @end and add this code after:

FREObject setBadgeNumber (FREContext context, void* functionData, uint32_t argc, FREObject argv[]) {

    @autoreleasepool {

        int32_t int_badgeNumber;
        FREGetObjectAsInt32(argv[0], int_badgeNumber);
        [[UIApplication sharedApplication] setApplicationIconBadgeNumber: (NSInteger)int_badgeNumber ];

        return nil;
    }
}

Why you must to use @autoreleasepool – I described early one week ago.

After here is all understandable. We create int_badgeNumber variable with type of int32_t. After that we get first object in array of data that we sent from AS3 argv[0] and put it to int_badgeNumber.

Now I must describe detailed. Our AIR application – it’s native application with access to any features of native side. This access can be reached via sharedApplication, that exactly is whole app UIApplication. This is something like Stage from Flash Runtime that can be reached everywhere but can be only in a single instance.

After this we may update number on the badge.

Because we currently writing using C with Objective-C mix – we must convert int32_t type to other type that Objective-C know. In our case it’s NSInteger, because setApplicationIconBadgeNumber in sharedApplication required exactly NSInteger.

If we could do this in AS3 you can see it how:

function setBadgeNumber(ctx:FREContext, functionData:*, argc:int,argv:Array):FREObject{

var int_badgeNumber : int;
Utils.FREGetObjectAsInt32(argv[0],int_badgeNumber);
UIApplication.sharedApplication.setApplicationIconBadgeNumber(badgeNumber:int);

return null;
}

At the end of method we must return something because this required by FlashRuntimeExtensions.h. We don’t any data to return so just return nil.

Now we must to develop one more method that will return to AS3 side our badge number. Sure we can remember it inside AS3 but what if user closes app? Store it to the device drive? Definitely not! Let’s add new method after @end

FREObject getBadgeNumber (FREContext context, void* functionData, uint32_t argc, FREObject argv[]) {

 @autoreleasepool {

        int32_t int_badgeNumber = (int32_t) [UIApplication sharedApplication].applicationIconBadgeNumber;
        FREObject returnValue;
        FRENewObjectFromInt32(int_badgeNumber, &returnValue);
        return returnValue;
    }   
}

Also here is AS3 code-like so you better understand it:

function getBadgeNumber(ctx:FREContext, functionData:*, argc:int,argv:Array):FREObject{

var int_badgeNumber : int = int(UIApplication.sharedApplication.applicationIconBadgeNumber);
var returnValue: FREObject;
Utils.FRENewObjectFromInt32(int_badgeNumber,returnValue);

return returnValue;
}

At now we have 2 external methods inside our extension – setBadgeNumber and getBadgeNumber. Now we must update our code so AIR will know about new method. Go to BadgeANEContextInitializer and edit numFunctionsToTest value. Change 1 to 2. Now we must add into array new method. Go down and add this code:

func[1].name = (const uint8_t*) "getBadgeNumber";
func[1].functionData = NULL;
func[1].function = &getBadgeNumber;

On this step we finished with Objective-C programming.

Step 5: Compiling

Let’s compile our library. But before we required to configure two items- switch to iOS Device build target instead of Simulator target and turn on Release compiling and not Debug.

Switching to iOS Device is pretty simple:
2014-12-09_032306In this panel just select iOS Device. If you don’t do this – your ANE will not work on real device,

Second items it’s enabling Release build scheme.

2014-12-09_032508

Inside each build type you must select Release configuration, and not Debug:
2014-12-09_032718Now go to Product >Build and wait for compiling is finished. During our compilation is everything fine but one error is occurred. It’s Test target build.
2014-12-09_032957

Just press on it and delete it. Than do again Product >Build. In this case we compiled extensions without any errors and we received our libBadgeANE.a library. Click on it and press Show in Finder.

2014-12-09_033151

Step 6: Developing AS3 side

Compile this source code. I’m doing this in Flash Builder 4.7. You must let know compiler that you require AIR classes, because if you compile SWC just for usual Flash – you will not reach ExtensionContext class:

package gamespoweredby.com.ios {

    import flash.external.ExtensionContext;
    import flash.system.Capabilities;     

    public class UIApplication {        

        private static var _context: ExtensionContext;
        private static const EXTENSION_ID: String = "gamespoweredby.com.ios.UIApplication.badge";         

        public static function set applicationIconBadgeNumber(value:int):void{        
            if ( isSupported() ) {

                _context.call("setBadgeNumber", value);
            }
        }

        public static function get applicationIconBadgeNumber():int{        
            if ( isSupported() ) {
                return _context.call("getBadgeNumber") as int;
            }
            return 0;
        }

        private static function createExtensionContext(){
            if ( _context == null) {
                _context = ExtensionContext.createExtensionContext(EXTENSION_ID, null);
            }
        }

        private static function isSupported():Boolean {
            if ( Capabilities.manufacturer.indexOf("iOS") > -1 ) {
                createExtensionContext();
                return true;
            }
            return false;
        }
    }
}

Now we have BadgeANE.swc and almost there!

Step 7: Preparing extension descriptor file

Our extension will have ID gamespoweredby.com.ios.UIApplication.badge. Always create unique ID, so this can protect you from other extension collision. In most cases this is third-party extensions. Better to edit your own because you have own sources.

In our working folder Badge ANE Tutorial you must create bin folder and put there descriptor.xml file with content:

<?xml version="1.0" encoding="UTF-8"?>
<extension xmlns="http://ns.adobe.com/air/extension/16.0">
	<id>gamespoweredby.com.ios.UIApplication.badge</id>
	<versionNumber>1.0.0</versionNumber>
	<platforms>

		<platform name="iPhone-ARM">
		<applicationDeployment>
			<nativeLibrary>libBadgeANE.a</nativeLibrary>
			<initializer>BadgeANEExtInitializer</initializer>
			<finalizer>BadgeANEExtFinalizer</finalizer>
		</applicationDeployment>
		</platform>

		<platform name="default">
            <applicationDeployment/>
        </platform>

	</platforms>
</extension>

As you can see – we want to utilize two platforms – iPhone-ARM and default. This mean that our real device will use it’s extension and for the rest we (default) use nothing and during testing on desktop we will not receive any errors like our platform do not have appropriate extension.

There is two way of developing for default platform – it’s developing separated SWC and using same SWC as device use but with calls avoiding. We just done this via isSupported() above where we check if on iOS we are.

Pay attention on two fields initializer and finalizer. Exactly this fields are used by Adobe AIR when it’s determinate which of fields will be used inside *.a file for initializing during context creating createExtensionContext and after finalizing after dispose() is called. The last one we don’t write in AS3 because we don’t use disposing.

Step 8: Compiling extension

Time for compiling our ANE and receiving some pleasure from it’s working state.
1) Inside bin folder place libBadgeANE.a and BadgeANE.swc that we done early
2) Using any text editor create Shell script. I will use Xcode for this
2014-12-09_131210After this xcode will offer to create the file. I named it Script.sh. Just press Ok and you will see it on the left side
3) Open it in Finder:2014-12-09_132811
4) Drag Script.sh inside Badge ANE Tutorial\ and delete if from the project.
5) Now we must edit it using Xcode:
2014-12-09_1330546) Put downloaded early AIR SDK inside Badge ANE Tutorial\ with folder name AIR_SDK, so we can fast add it path to the Shell script.
7) Go back to the Script.sh and write this code insde:

#!/bin/bash

ANE=$"BadgeExtension.ane"
SWC=$"BadgeANE.swc"
LIB=$"libBadgeANE.a"

IOS=$"library.swf libBadgeANE.a"
DEFAULT=$"library.swf"

AIRSDK=$"/AIR_SDK/bin/adt"

clear
echo ""

DIR=$"$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
ADT="$DIR$AIRSDK"
echo "Unpacking swc..."

cd "$DIR/bin/"
unzip -q -o "$SWC"
rm $"catalog.xml"

echo "Compiling..."
"$ADT" -package -target ane $ANE descriptor.xml -swc $SWC -platform iPhone-ARM $IOS -platform default $DEFAULT
echo "Finished!"

8) Run temrinal Applications > Utilities > Terminal
9) Inside terminal window write chmod 777 and drag and drop Script.sh. This action is required so we can grant access for Shell script execution under our user

2014-12-09_160838

After you dropped the file – terminal will add full file path. Now just press Enter. It’s one time action for this file.

10) Let’s check Script.sh. In the first lines we have some variables, that have path to AIR_SDK/bin/adt application, name of BadgeANE.swc file and name of compiled extension – BadgeExtension.ane. Strings IOS and DEFAULT it’s a path to files that we must to include for our extensions. In this tutorial we will not change anything. Using default terminal everything must work as expected.

Inside Badge ANE Tutorial\bin\ you must have:
- BadgeANE.swc
- descriptor.xml
- libBadgeANE.a

11) Drag and drop Script.sh into terminal and press Enter.
12) After Shell script is finished you will see new files inside your bin folder:
- library.swf
- BadgeExtension.ane

Now we finished with Native Extension development.

Step 9: Testing of Native Extension

Create any project on and test this on real device:

UIApplication.applicationIconBadgeNumber = 7;

With a first access request to badge API – iOS will request access from user:
IMG_0720After user grant access – you will see badge on your icon:

2014-12-09_171805

If you want to remove badge – just pass zero value:

UIApplication.applicationIconBadgeNumber = 0;

According to our code we can request current badge value using getter:

c.text = UIApplication.applicationIconBadgeNumber.toString();

You must note that if user not granted access – our API still works but you will not see any changes on your icon.

This tutorial is finished. Thanks to all who meet this useful.

The project including ready to use binary in my dropbox.

Share Button

This Post Has Been Viewed 7,503 Times

17 thoughts on “Developing of Adobe AIR Native Extensions of iOS. Tutorial 1

    1. TheRabbit Post author

      Really I don’t know because I never interesting in this. But you can create threads inside your ANE. For example you can create Physics in ANE that will not hog AIR drawing events.

      Also here is some copy-paste from http://help.adobe.com/en_US/air/extensions/air_extensions.pdf, page 29
      When coding your native implementation, consider the following:
      - The runtime can concurrently call an extension context’s FREFunction functions on different threads.
      - The runtime can concurrently call different extension contexts’ FREFunction functions on different threads. Therefore, code your native implementations appropriately. For example, if you use global data, protect it with some form of locks.

      Reply
  1. Pingback: Developing of Adobe AIR Native Extensions of iO...

  2. Pingback: My most important Twitter Messages #18 | der hess

  3. PANKAJ

    I am facing some issue with given example.

    ArgumentError: Error #3500: The extension context does not have a method with the name setBadgeNumber.
    at flash.external::ExtensionContext/_call()
    at flash.external::ExtensionContext/call()

    I tried so many things but still same. I have downloaded your given sample from dropbox and tried using given ANE but still same issue with given ANE. Recompiled ANE but still same issue.

    I am just using below statement from example: UIApplication.applicationIconBadgeNumber = 5;

    Please suggest me what can be wrong. Need help.

    Thanks.
    Pankaj

    Reply
    1. PANKAJ

      Hi TheRabbit,
      Did you get time to check this. I am still struggling with this issue. Need your suggestion to fix this.
      Thanks,
      Pankaj

      Reply
  4. PANKAJ

    Hi,
    I would like to give you some more info. I am using latest AIR SDK and compiler 17.0 and Latest XCode v6.2.

    Thanks,
    Pankaj

    Reply
  5. Neha

    Sorry.. my question might be foolish. But I am not able to understand where to write badge number. I am totally new to FLASH.

    Reply
  6. Carlos Bernal

    Thank you for this post, it has help me a lot. However, i’m having troubles trying to display native controls and views over the AIR stage.

    You say its possible, but i cant seem to find any documentation about it. Can you help me?.

    How can i do this?, and how can i define if i want the view over o under the AIR Stage.

    Greeting from Colombia,
    Carlos.

    Reply
    1. TheRabbit Post author

      Hi Carlos ;)
      Adding native elements over the AIR can be easy. You just need to use root view. I can show simple example but this can take up to 1 week because I don’t have a free time at now..

      Main Idea is get [[UIApplication sharedApplication] and add subviews to it. Each native element is UIView like DisplayObject in Flash.

      And place native view under the AIR can’t be possible on most cases. But I don’t know any case that requiring UNDER.

      Greetings from Ukraine :)

      Reply
  7. Carlos Bernal

    Thank you Rabbit!

    As a test, I was able to put a native button on top of the Air Stage.

    My final goal is an iOS GoogleMaps ANE, but i’m having problems packaging the extension with the GoogleMaps SDK dependency.

    The GoogleMaps SDK has to be downloaded as a CocoaPod. Do you know what is the right way to package a CocoaPod in an ANE?

    Reply
  8. Pingback: URL

Leave a Reply

Your email address will not be published.

Blue Captcha Image Refresh

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>