Blog

I write code, this blog is a dream.

2013.06.24OpenFL extension (2)

For the second part of this little personnal study let's create a Tweet feature for our haxe iOS application.

We should learn:

  • how to organize the code inside our extension for each platform
  • how to create the binding between haxe and our own native function
  • how to link an iOS framework (the Twitter framework)

Let's start with a look at another project's organization: NME github project.

The Build.xml is pretty complex but will be useful, looking at the folder's content we can see:

include/: defines headers which will be implemented by common/ and platforms

common/: platform independent c++ code (well more or less independant, we can use #if defined(HX_xxxx))

common/ExternalInterface.cpp: exports functions for our runtime (DEFINE_PRIM)

'platform' directories: implements things for custom platforms (iPhone, mac, windows, etc.)

Looks like a nice way to organize things, let's use it!

Starting with our previous extension, we are going to define a Tweet() function in the existing include/Utils.h file (we should use our own header but I am lazy today).

namespace testextension {

	int SampleMethod(int inputValue);

	bool Tweet(const char* msg);

}

Then we are going to implement it for the iphone platform:

cd project
mkdir iPhone

iPhone/Tweet.mm:

#import <Foundation/Foundation.h>
#import <Twitter/Twitter.h>

namespace testextension {
    bool Tweet(const char* message){
        // Sorry about this Objective-C block,
        // A quick and dirty dump from another project,
        // We should check that Twitter can be used but
        // that's another problem you will have to solve alone :)
        NSString* str = [[NSString alloc] initWithUTF8String:message];
        TWTweetComposeViewController* tweetView = [[TWTweetComposeViewController alloc] init];
        [tweetView setInitialText:str];
        TWTweetComposeViewControllerCompletionHandler completionHandler =
            ^(TWTweetComposeViewControllerResult result) {
                [[[[UIApplication sharedApplication] keyWindow] rootViewController] dismissModalViewControllerAnimated:YES];
            };
        [tweetView setCompletionHandler:completionHandler];
        [[[[UIApplication sharedApplication] keyWindow] rootViewController]  presentModalViewController:tweetView animated:YES];
        return true;
    }
}

We need to expose it to haxe, let's edit common/ExternalInterface.cpp:

static value testextension_tweet(value message){
    // message is a String, just get its value
    const char* cStr = val_get_string(message);
    // call our Tweet function with it
    // and return true or false to haxe
    if (testextension::Tweet(cStr))
        return val_true;
    return val_false;
}
// This will register our function with 1 argument
DEFINE_PRIM(testextension_tweet, 1)

DEFINE_PRIM, val get string, val true and many more comes from hxcpp: hx/CFFI.h

Again looking at ExternalInterface.cpp should help you understand how to register your stuff.

The haxe part in TestExtension.hx:

class TestExtension {

// load the defined primitive
private static var testextension_tweet = Lib.load("testextension", "testextension_tweet", 1);

// wrap its call inside a typed method
public static function tweet(message:String) : Bool {
    return testextension_tweet(message);
}

}

I had to fight a little to find out how to compile and link things but the result is pretty simple.

First the project/Build.xml:

<!-- we add the list of files for ios -->
<files id="iphone">
  <file name="iPhone/Tweet.mm"/>
</files>

<!--
  I had to compile the ndll for mac too to get an ndll file and make the system happy.
  "Library TestExtension version dev does not have a neko dll for your system"
  I just commented things in this file, see github.
-->
<files id="mac">
  <file name="Mac/Tweet.mm"/>
</files>

In the target id=NDLL we need to add the different files to compile:

<files id="mac" if="mac"/>
<files id="iphone" if="ios"/>

Finally we have to add the Twitter framework dependency in the include.xml. This way, hxcpp will link the framework to our applications and unicorns will be happy.

<dependency name="Twitter.framework" if="ios"/>

I compiled the extension from the project folder just like I did in part 1:

haxelib run hxcpp Build.xml -Dmac
haxelib run hxcpp Build.xml -Diphoneos -DHXCPP_ARMV7
haxelib run hxcpp Build.xml -Diphonesim

To test the feature I simply added to my TestApp:

TestExtension.tweet("This is my tweet message");

And run it in the simulator and on my iOS device:

cd TestApp
openfl test project.xml iphone -simulator
openfl test project.xml iphone

We can tweet using native api from our application! How cool it that?! :)

I hope these instructions will work as well on your system… sometimes a little bit of dust can destroy an empire. Please refer to the github repository to ensure that you are using the most up to date code I wrote.

What's important is the way I found the information: in hxcpp and nme sources, on github.

I experienced a few problems too while trying to make things work. Cleaning the ndll/ and the project/obj folders and recompiling helped a lot until I found the good Build.xml and include.xml format :)

So far the only real problem comes from the lack of real documentation of hxcpp Build.xml features. Thanks god we have a few examples to learn from.

My next experience should be with android to make sure things work there too. Learning and documenting how to write extensions in java may be useful too I eared :)

I updated the github repository of the TestExtension in the hope that my little contribution will be of some use to other haxers!