Codea 2.1 Runtime lib prerelease (Xcode project)

This post is for people who are currently building exported apps with Codea 2.0 — especially those who experience issues on iOS 8 (camera permission requests, for example).

It is also for people who are happy to mess around in Xcode to upgrade their projects, and for people who want to test and provide feedback.

You can download a zipped Xcode project here:

http://codea.io/prerelease/MyProject.zip

“MyProject” is a sample Xcode project that builds using the new Codea 2.1 runtime.

Some notable changes for Xcode projects:

  • CodeaViewController has been replaced by StandaloneCodeaViewController. This is a light wrapper that was required to simplify the startup sequence due to the new threaded nature of the runtime.
  • Your custom CodeaAddon classes now have some more stringent requirements relating to threading (detailed below)
  • It includes a sample GameCenterAddon that implements basic high score and leaderboard functionality. Not very well tested, but it should give you the basics of how addons work (see the Addons/Extra folder in Xcode).
  • If you have an existing exported Xcode project you’ll probably want to update how your AppDelegate is implemented (to follow this one), replace your CodeaViewController with StandaloneCodeaViewController and modify your addons accordingly. Make sure you branch before you attempt the upgrade in case things go wrong.

Because Lua now runs on a separate thread, any callbacks into your CodeaAddon occur on the Lua thread. If you want to touch UIKit you’ll have to dispatch_async to main. (See GameCenterAddon.m line 207 for an example function that does this.)

Of course, this version of the runtime includes all new APIs that will be included in Codea 2.1 (some APIs are deprecated but still functional, such as spriteList).

Edit: Codea 2.1’s will likely include basic addons when you export projects. If you want to contribute to addons (bug fixes, or whatever else) there is a github repo here: https://github.com/TwoLivesLeft/Codea-Addons

Thanks a lot !

Hi @Simeon,

If we use the Testflight 2.1 beta, do we get all this as standard on an export?

Thanks
brookesi

@brookesi no — that one still uses the 2.0 export. It will be in the next beta.

Whoops! Missed this discussion. If I were to fork the Codea Addons repository, would you consider adding some libraries I have written?
Thanks!

@Zoyt certainly, however please keep in mind that they require use of a slightly newer API and that all Lua functions will be called off the main thread.

@Simeon - Alright. If I have a bit of extra time, I’ll be sure to adapt them.
Thanks!

Boo! Hi guys. Long time no see. Sorry - been working 60 hour weeks, for well over a year now. Is crazy.

Just a note to say this is the first time a project just worked for me out of the box. Impressed as always, and thank you again for releasing a runtime. (I had an epiphany - if I have an oddball request, I can just modify the runtime. In this case - I need to use a client side SSL certificate, so I can talk to a docker server. Yeah, I finally sprung for a developer’s membership, yay.)

@Bortels - Great to see you back! Also, not sure you can really modify the runtime. You can make add ons, but you can’t actually modify it. It’s already compiled code.

Okay, feeling much like an idiot here.

I downloaded the sample project, renamed it to the name of my project, copied in my lua code and asset pack, did the provisioning profile shuffle, and when I compile it… it comes up with a screen like Codea running a blank main.

What am I missing? Is there something else I need to edit to get my lua code to execute?

@Mark Have you tried editing the Info.plist to specify what tabs there are, and their order?

@SkyTheCoder The info.plist within the program.cdea folder? Yes, it has all the classes detailed.

Found it. Needed to change the .codea name in the AppDelegate

However, I’m still having a trouble with location services. While it now works fine within Codea, when I compile the app doesn’t ask about location services and when I look in settings, it doesn’t appear in the list of apps that have location services available.

Should have mentioned this earlier…

Adding NSLocationWhenInUseUsageDescription, Privacy — Location Usage Descrption, and NSLocationAlwaysUsageDescription to the Custom iOS Target Properties build settings got GPS functional again. However, to get the apps running, the user still has to go into settings and turn on the location services for the app as no prompt for location services is appearing.

@Simeon My app got rejected because of the camera access bug in 2.0, so I’m trying to figure out how to get the iAds addon to work with 2.1. However, I’m having trouble figuring out how to use the iAds add on with the new runtime. It worked great with 2.0 version, but I can’t figure out how to use it with the way the new 2.1 runtime works.

I tried changing the appdelegate.h by adding


#import "IAdsAddOn.h"

@property (strong, nonatomic) IAdsAddOn *iAdsAddOn;

Then in appdelegate.mm adding


self.iAdsAddOn = [[IAdsAddOn alloc] init];
[self.viewController registerAddon: self.iAdsAddOn];

That is about all that had to be done in 2.0 to get the add-on to work, but i’m getting linking errors in Xcode now with the 2.1 runtime.

Below is the actual iAds add-on code:

IAdsAddOn.h


//
//  IAdsAddOn.h
//  30 Balls
//
//  Created by Théo on 6/19/14.
//  Copyright (c) 2014 MyCompany. All rights reserved.
//

#import "CodeaAddon.h"
#import <iAd/iAd.h>
#import <Foundation/Foundation.h>

// Device Detection Macros - can be used to position the banner advertisement based on device.
// Not used in this tutorial.

//#define IS_IPAD (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
//#define IS_IPHONE (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
//#define IS_IPHONE_5 (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 568.0f)
//#define IS_IPHONE_4 (IS_IPHONE && [[UIScreen mainScreen] bounds].size.height == 480.0f)

//  Create a variable which points to the instance of this class so that we can access self
//  from within the Lua c functions.
////
id iAdsAddOnInstance;

//  This class conforms to the CodeaAddon & ADBannerViewDelegate Protocols

@interface IAdsAddOn : NSObject<CodeaAddon, ADBannerViewDelegate>

@property BOOL isBannerVisible;
@property BOOL showBannerFromTop;
@property BOOL adsAllowed;
@property (strong, nonatomic) ADBannerView *bannerView;
@property (weak, nonatomic) CodeaViewController *currentController;

//  Forward declare our Lua iAd functions. These are static to confine their scope
//  to this file. By default c functions are global.

static int showAdFromTop(struct lua_State *state);
static int showAdFromBottom(struct lua_State *state);
static int hideAd(struct lua_State *state);

@end

IAdsAddOn.m


//
//  IAdsAddOn.m
//  30 Balls
//
//  Created by Théo on 6/19/14.
//  Copyright (c) 2014 MyCompany. All rights reserved.
//

#import "lua.h"
#import "IAdsAddOn.h"

@implementation IAdsAddOn

#pragma mark - Initialisation

- (id)init
{
    self = [super init];
    if (self)
    {
        
        
        //  audioAddOnInstance allows us to access self from within the c functions.
        
        iAdsAddOnInstance = self;
        
        // Initialise our Instance Variables
        
        _isBannerVisible = NO;
        _showBannerFromTop = YES;
        _adsAllowed = NO;
        
        //  Initialise our iAd Banner View
        
        CGRect frame = CGRectZero;
        //frame.size = [ADBannerView sizeFromBannerContentSizeIdentifier: ADBannerContentSizeIdentifierPortrait];
        
        _bannerView = [[ADBannerView alloc] initWithFrame: frame];
        //_bannerView.requiredContentSizeIdentifiers = [NSSet setWithObject: ADBannerContentSizeIdentifierPortrait];
        [_bannerView setAutoresizingMask: UIViewAutoresizingFlexibleWidth];
        _bannerView.delegate = self;
    }
    return self;
}

#pragma mark - CodeaAddon Delegate

//  Classes which comply with the <CodeaAddon> Protocol must implement this method

- (void) codea:(CodeaViewController*)controller didCreateLuaState:(struct lua_State*)L
{
    NSLog(@"iAdAddOn Registering Functions");
    
    //  Register the iAd functions, defined below
    
    lua_register(L, "showAdFromTop", showAdFromTop);
    lua_register(L, "showAdFromBottom", showAdFromBottom);
    lua_register(L, "hideAd", hideAd);
    
    //  Hook up with the CodeaViewController - don't try to add subviews in this method.
    
    self.currentController = controller;
}

#pragma mark - iAds Add On Functions and associated Methods

//  Objective C Methods

- (void)showBannerViewAnimated:(BOOL)animated
{
    if ([self.bannerView isBannerLoaded])
    {
        if (_adsAllowed) {
            //  We only display the banner View if it has ads loaded and isn't already visible.
            //  Set the banner view starting position as off screen.
            
            CGRect frame = _bannerView.frame;
            
            if (_showBannerFromTop)
                frame.origin.y = 0.0f - _bannerView.frame.size.height;
            else
                frame.origin.y = CGRectGetMaxY(self.currentController.view.bounds);
            
            _bannerView.frame = frame;
            
            // Set banner View final position to animate to.
            
            if (_showBannerFromTop)
                frame.origin.y = 0;
            else
                frame.origin.y -= frame.size.height;
            
            if (animated)
                [UIView animateWithDuration: 0.5 animations: ^{self.bannerView.frame = frame;}];
            else
                self.bannerView.frame = frame;
            
            _isBannerVisible = YES;
        }
        else
        {
           NSLog(@"Ads should not be shown right now");
            [self hideBannerViewAnimated: NO];
            
        
            
            
        }
    }
    else
        NSLog(@"showBannerViewAnimated: Unable to display banner, no Ads loaded.");
}

- (void)hideBannerViewAnimated:(BOOL)animated
{
    if (_isBannerVisible || !_adsAllowed)
    {
        CGRect frame = self.bannerView.frame;
        
        if (_showBannerFromTop)
            frame.origin.y -= frame.size.height;
        else
            frame.origin.y = CGRectGetMaxY(self.currentController.view.bounds);
        
        if (animated)
            [UIView animateWithDuration: 0.5 animations: ^{self.bannerView.frame = frame;}];
        else
            self.bannerView.frame = frame;
        
        _isBannerVisible = NO;
    }
}

//  C Functions
//
//  Note that the returned value from all exported Lua functions is how many values that function should return in Lua.
//  For example, if you return 0 from that function, you are telling Lua that function returns 0 values.
//  If you return 2, you are telling Lua to expect 2 values on the stack when the function returns.
//
//  To actually return values, you need to push them onto the Lua stack and then return the number of values you pushed on.

static int showAdFromTop(struct lua_State *state)
{
    
    NSLog(@"Show Ad From Top");
    
    [iAdsAddOnInstance setAdsAllowed: YES];
    [iAdsAddOnInstance setShowBannerFromTop: YES];
    [iAdsAddOnInstance showBannerViewAnimated: YES];
    
    return 0;
}

static int showAdFromBottom(struct lua_State *state)
{
    [iAdsAddOnInstance setAdsAllowed: YES];
    [iAdsAddOnInstance setShowBannerFromTop: NO];
    [iAdsAddOnInstance showBannerViewAnimated: YES];
    
    return 0;
}

static int hideAd(struct lua_State *state)
{
    [iAdsAddOnInstance setAdsAllowed: NO];
    [iAdsAddOnInstance hideBannerViewAnimated: YES];
    
    return 0;
}

#pragma mark - iAd Banner View Delegate

//  Your application implements this method to be notified when a new advertisement is ready for display.

- (void)bannerViewDidLoadAd:(ADBannerView *)banner
{
    NSLog(@"Banner View loaded Ads for display.");
    NSLog(@"Active View Controller: %@", self.currentController.class);
    
    //  Add our banner view to the CodeaViewController view, if we haven't already.
    
    if (![self.currentController.view.subviews containsObject: _bannerView])
        [self.currentController.view addSubview: _bannerView];
    
    [self showBannerViewAnimated: YES];
}

//  This method is triggered when an advertisement could not be loaded from the iAds system
//  (perhaps due to a network connectivity issue).

- (void)bannerView:(ADBannerView *)banner didFailToReceiveAdWithError:(NSError *)error
{
    NSLog(@"bannerview failed to receive iAd error: %@", [error localizedDescription]);
    
    [self hideBannerViewAnimated: YES];
}

//  This method is triggered when the banner confirms that an advertisement is available but before the ad is
//  downloaded to the device and is ready for presentation to the user.

- (void)bannerViewWillLoadAd:(ADBannerView *)banner
{
    
}

//  This method is triggered when the user touches the iAds banner in your application. If the willLeave argument
//  passed through to the method is YES then your application will be placed into the background while the user is
//  taken elsewhere to interact with or view the ad. If the argument is NO then the ad will be superimposed over your
//  running application.

- (BOOL)bannerViewActionShouldBegin:(ADBannerView *)banner willLeaveApplication:(BOOL)willLeave
{
    self.currentController.paused = YES;
    NSLog(@"Ad being displayed - Codea paused.");
    
    return YES;
}

//  This method is called when the ad view removes the ad content currently obscuring the application interface.
//  If the application was paused during the ad view session this method can be used to resume activity.

- (void)bannerViewActionDidFinish:(ADBannerView *)banner
{
    self.currentController.paused = NO;
    NSLog(@"Ad dismissed - Codea running.");
}

@end

@Mark I think you have to set the correct keys in the info plist file of your custom application. I will look them up and let you know what to set.

@Crumble your Lua functions are now called outside the main thread, so you’ll have to modify any code that touches UIKit to be dispatched to the main thread.

Here’s an example of changing one of your Lua functions:

static int showAdFromTop(struct lua_State *state)
{
    //We are currently in the Lua thread when this function is called
    // if you do any Lua stuff or computation, you can do it here

    NSLog(@"Show Ad From Top");

    //These properties don't touch UIKit, so it's okay to set them here
    [iAdsAddOnInstance setAdsAllowed: YES];
    [iAdsAddOnInstance setShowBannerFromTop: YES];

    dispatch_async( dispatch_get_main_queue(), ^{
        //We have to present any views on the main thread
        [iAdsAddOnInstance showBannerViewAnimated: YES];
    });

    return 0;
}

```

@Mark the key you will need to add to your info.plist file is:

NSLocationWhenInUseUsageDescription

Make it a string, this message will be used when the request alert to use location services is displayed.

@Simeon Thanks for the info! The iAds add-on is one that someone else made, I just plug it into my project in X code, objective C is totally over my head. Do I just need to add that dispatch_async line to any lines that do the BanneViewAnimated? Is there anything else that I need to change in the objective C code to get it to work?

Hopefully there is an iAds add-on in the final 2.1 version, iAds are one thing that pretty much every app uses.

Also, when using the 2.1 runtime, I notice that Gamecenter tries to authenticate the player automatically, even if I don’t have Gamecenter in my app. Is there a way to turn off the authentication?

Sorry for all of the questions.

I just want to say that I love Codea, thanks for making it!