Showing posts with label iPad. Show all posts
Showing posts with label iPad. Show all posts

Saturday, November 17, 2012

Tutorial 23 - Implementing iAds with Codea


23.1 Introduction


A common business model in the App store is to give away your app for free and generate revenue by serving ads to your application. This model has benefits but can annoy your users if done in excess. Everyone likes stuff that is free, so setting a zero cost removes most of the barriers to downloading your app and should maximise download numbers.

Since iOS 4.0 the iAds framework has been available for developers. Using this you can add banner or full screen advertisements to your application. If you are planning to include ads on a screen then you need to ensure that you have left space within your interface to display them. Full screen ads (referred to as interstitial ads by Apple) are only available on the iPad and are only suitable in certain situations, so we wont cover those in this tutorial.

You receive revenue when users view (called impressions) or click advertisements on your app. Some apps use a bit of social engineering to maximise their click through rate. For example the Foxtel (cable TV) app in Australia has ads which appear at the top of the channel selection screen. As per the Apple guidelines these ads are only displayed once the banner has been loaded from the ad server. When this occurs, the ad slides in from the top and moves the content down by the height of the banner. However what happens 50% of the time (for us at least) is that you accidentally tap the ad when you are trying to select a channel.

It is difficult to estimate exactly what revenue you might receive from including iAds in your app. A graph of the impressions that we got for the last year is shown below. There was about 98k in total with 90% coming from one app (Personality). According to Apple the effective revenue per thousand ads (eCPM) served over this period was $0.82. In practice, we get on average $0.25 per day. So while this may not be a get rich quick scheme, it is not bad passive income, particularly given the app I'm deriving revenue on is not optimised for ads. 

We suspect you could do much better with a free game app with high reusability (people tend to use Personality only 2-3 times at best). Apple says that "hundreds of developers are making more than $15,000 per quarter". Given that there are 275,000 iOS developers in the US alone, this is perhaps not that impressive - albeit not all of those developers have iAd enabled apps.




23.2 Banner Views


From a Codea perspective, implementing iAds is easy. If you are happy to display ads on every screen then you just need to leave the appropriate amount of space on your screen to display the ad. In most cases there will be screens that you don't want to display ads on (e.g. the main game screen), so we have extended Codea Lua with a function, showBannerAd(true) to turn ads on and off. For our example, we will only display ads on the Menu and High Score screens.

For the iPad, banner sizes are set at:
  • Portrait: 66 (height) x 768 (width) points - see Interlude 13 if you dont understand the difference between pixels and points.
  • Landscape: 66 (height) x 1024 (width) points.
For completeness, the iPhone banner sizes are 320 x 50 points for a portrait advertisement and 480 x 32 points for a landscape advertisement.

The banner ads are intended to typically appear either at the top or bottom of the display and are provided in two formats so that ads may be displayed when the device is either in portrait or landscape orientation.


23.3 Setup iAds in iTunes Connect


As with Achievements and in-App purchasing, there are some things that you need to enable in iTunes Connect before your app will be able to receive ads from Apple's servers. Log in to your developer account, select iTunes Connect and then click on Manage your Apps. Click on the appropriate App icon and then click on the Setup iAds link (right hand side towards the top).

Answer the question regarding whether the primary target audience for your app is users under 17 years of age and then click Enable iAds and Save.

That's it! You can disable iAds just as simply but changes to your iAd settings will not take effect until your next app version is approved.


23.4 Creating Your Banner Views in the Codea runtime


You should only create a banner view when you intend to display it to the user. Otherwise, it may cycle through ads and deplete the list of available advertising for your application. As a corollary to this, avoid keeping a banner view around when it is invisible to the user. To demonstrate the implementation of iAds in the runtime we will use our old faithful MineSweeper application.

Based on our careful UI planning (or perhaps just through good luck) we happen to have enough space at the top of the MineSweeper Menu screen to fit in a banner ad, so that is where we will put it. As MineSweeper is now a universal application we will need to handle the iPhone sized banners as well. We have more room at the bottom of the iPhone Menu screen when it is in the portrait orientation, so when we detect that device and orientation we will place the banner at the bottom of the view. There is no room for a banner on an iPhone in landscape orientation, so we will disable ads when we detect this combination.

Banner views use a delegate to communicate with your application. Your application will thus need to implement a banner view delegate to handle common events. In particular we need to handle:
  1. when the user taps the banner.
  2. when the banner view loads an advertisement.
  3. when the banner view encounters an error.
Usually you would place this code in the relevant view controller but for simplicity we will handle this in the CodifyAppDelegate (the alternative is to extend the BasicRendererViewController class in the runtime).

If your application supports orientation changes, your view controller must also change the banner view’s size when the orientation of the device changes.

Step 1 - Add the iAd Framework to your App.

Fire up Xcode and load the runtime version of your Codea application. Click on the CodeaTemplate file at the top of the project navigator then in the summary tab, scroll down to the linked libraries area. Click on the "+" button below your existing frameworks to add a new framework. Find iAds and click on "Add".

Step 2 - Update the CodifyAppDelegate Files.

In CodifyAppDelegate.h you need to import the framework you just added using:

#import <iAd/iAd.h>

then modify the class interface definition so that it looks like this:

@interface CodifyAppDelegate : UIResponder<UIApplicationDelegate, ADBannerViewDelegate>

This indicates that the ADBannerViewDelegate protocol will be implemented. We will provide a link to download the updated CodifyAppDelegate files below. We have added two other things to the CodifyAppDelegate files:
  1. A boolean instance variable called displayingAds which is controlled from your Lua code via the modified aGameCenter_Codea class; and
  2. A call back method called iAdDisplayStatusChanged which is used to update the banner view if the MineSweeper game state or orientation changes. This also comes via the modified aGameCenter_Codea class.
Step 3 - Update the other Codea runtime Files.

As we saw in Tutorial 19 when integrating Game Center, there are changes required in LuaState.m, OSCommands.h and OSCommands.m, to allow Lua to communicate with our Objective C code. This mostly comprises registering our new showBannerAd(true or false) function.

In LuaState.m add the following to the  - (void) create method:

    //iAds Function
    LuaRegFunc(showBannerAd);

In OSCommands.h add the following below the similar Game Center definitions:

    //iAds
    int showBannerAd(struct lua_State *L);

Finally, in OSCommands.m add the following method below the Game Center definitions:

int showBannerAd(struct lua_State *L){
    [CodeaGameCenter showBannerAd:lua_toboolean(L,1)];
    return 0;
}

23.5 Codea Lua Code Changes


As mentioned in section 23.2, if you are happy to display adds on every screen then there is no changes required to your Lua code (assuming you left space for the ads). If like us you want to select when ads are displayed, then you need to use the new showBannerAd() function. If you pass true in this function then ads will be displayed and if you pass false, they wont.

As an example, the following shows our updated orientationChanged() function. 

function orientationChanged(newOrientation)

    print("Orientation Change Detected.")
    
    if setupHasRun and gameState == stateRun or gameState == stateWon or 
        gameState == stateLost then
            updateGridLocation()
    end 
    
    if setupHasRun and (gameState == stateMenu or gameState == stateScore) then
        showBannerAd(true)
    else
        showBannerAd(false)
    end
    
end

We also use showBannerAd()  when the gameState changes due to button taps.

The other class we need to modify is the SpashScreen. In the fadeAnimation call back, we turn on ads once the splash screen has faded away.

function fadeAnimationDone()
    
    -- Call back function for Fader complete
    
    gameState = stateMenu
    showBannerAd(true)
    
end

23.6 Download the Updated Runtime & Codea Files


The aGameCenter_Codea and CodifyAppDelegate files are available on GitHub. In addition, the following links allow downloading individual files from DropBox:

Saturday, November 10, 2012

Interlude 13 - Pixels vs Points in iOS

In the previous tutorial we had to deal with a number of different resolutions in order to be able to develop a universal app. The following is a quick refresher on how to do this.

From iOS 4, dimensions are measured in “points” instead of pixels. All coordinate values and distances are specified using points, which are floating-point values. The measurable size of a point varies from device to device and is largely irrelevant. The main thing to understand about points is that they provide a fixed frame of reference for drawing.

The iPhone screen is 320 x 480 points on both iPhone 4 and older models. The iPhone 4 has a screen resolution of 640 x 960 pixels (i.e. a scale factor of 2.0) while older models have a resolution of 320 x 480 pixels (i.e. a scale factor of 1.0).

The iPad screen is 768 x 1024 points. This scales to the following pixel resolutions:
  • iPad 1 - 768 x 1024 pixels (i.e. a scale factor of 1.0)
  • iPad 2 - 768 x 1024 pixels
  • iPad 3 - 1536 x 2048 pixels (i.e. a scale factor of 2.0)
  • iPad 4 - 1536 x 2048 pixels
  • iPad Mini - 768 x 1024 pixels
Retina is a marketing term developed by Apple. It refers to devices and monitors that have such a high pixel density (normally greater than 300 pixels per inch) that most people can't discern individual pixels at a normal viewing distance. We wont use this term in our explanation because it is inexact and differs by device (e.g. iPhone 4/5 is 326 ppi, iPad mini is 163 ppi, iPad 1 and 2 is 132 ppi, iPad 3 and 4 is 264 ppi - note that the iPad 3 and 4 don't strictly meet the Retina "standard"). 

Querying the scale factor is the best way to determine the relationship between pixels and points on the current device. In iOS 4 and later, UIScreen has a property called scale which is a floating point number that defines the pixel/point relationship. In Objective C you can get this number using:

[[UIScreen mainScreen] scale]

On an iPhone 4 and 5 a point is a four pixel square, so if you draw a one-point line, it shows up as two pixels wide. The theory is that you specify your measurements in points for all devices, and iOS automatically draws everything to the right proportion on the screen. The result is that if you draw the same content on two similar devices, with only one of them having a high-resolution screen, the content appears to be about the same size on both devices. This is why you go to the trouble of creating your @2x images.

The iPhone 5 has a physical resolution of 1136 x 640 pixels which equates to 568 x 320 points. Your WIDTH and HEIGHT constants return points not pixels. So the best test for an iPhone 5 within Codea is to look for the 568 dimension.

Within your Objective C code you can determine the device using something like the following:

if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
        NSLog(@"iPad device detected.");
else if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
{
        CGSize result = [[UIScreen mainScreen] bounds].size;
        if (result.height == 480)
        {
            NSLog(@"iPhone/iPod Touch detected (< v5).");
        }
        if (result.height == 568)
        {
            NSLog(@"iPhone/iPod Touch detected (> v5).");
        }
}

Sunday, October 21, 2012

Tutorial 22 - Building a Universal App in Codea

Figure 1.

22.1 Going Universal in Xcode


A universal application is defined as one targeted at both the iPhone/iPod touch and iPad. If you want to build a universal iOS application then you have to use the runtime and Xcode. There is currently no way to target iPhone development from within Codea.

This is handy, since we plan to use our old staple Mine Sweeper to demonstrate how to turn your Codea App into one which can run on an iPhone (or iPod Touch for that matter). As we have added Game Center code to MineSweeper, all future development of this game will have to occur in Xcode.

In principle this is a simple three step process:
  1. Target the application to iPhone/iPad in the Xcode deployment build settings;
  2. Add the relevant iPhone graphical assets (icons, default images etc.);
  3. Use a bunch of if (device is an iPhone) then do one thing else do another thing. Step 2 is simplified by Apple providing a new macro called UI_USER_INTERFACE_IDIOM() that will tell you what device your code is currently running on. There are two values defined, UIUserInterfaceIdiomPhone and UIUserInterfaceIdiomPad, and this macro will return the appropriate value based on the current device.
It would be a very short tutorial if that was all there was to it. If your App has any sort of complexity then there are a few things that you need to watch out for.

22.2 Step 1: Targeting iPhone and iPad in Xcode


As with the previous few tutorials, we are assuming that you have already got your Codea App up and humming in the Xcode iPad simulator using the runtime. If you haven't got to this state then go back and have a look at Tutorial 12.

Targeting your App for iPhone and iPad is easy one you have found the setting. Open up Xcode with your App. You need to have the Project Navigator open (top left hand tool bar item - it looks like a file folder). Click on the item at the very top of the Navigator called "Codea Template."

When you create a new project, it includes one or more targets, where each target specifies one build product and the instructions for how that product is to be built. You can use the project editor to specify every aspect of the build, from the version of the SDK to specific compiler options.

In the next column over you will see two items, Project (CodeaTemplate) and Targets (MineSweeper - in our case, this will be the name of your App). 

Selecting the project file opens the project settings editor. The project settings editor is Xcode 4′s replacement for Xcode 3′s project and target inspectors. Select the target from the project settings editor. 

Figure 2. Setting your App to Universal.

To specify the device families on which you want your app to be able to run:

  1. In the project navigator, select the project. 
  2. From the target list in the project editor, select the target that builds your app, and click Summary. 
  3. From the Devices pop-up menu, choose Universal (to target both families - iPhone and iPad are the other two options - see Figure 2.).
And that is it. You should now be able to select either iPhone or iPad from the simulator. It is unlikely that the iPhone version will work but you can give it a shot if you want. MineSweeper certainly crashed and burned. The app should still compile and run ok as an iPad app.

22.3 Step 2: Adding iPhone Graphical Assets


Once you have made your app universal, Xcode will allow you to set up the icon and default images for your iPhone. Before we do this, here is a quick refresher on the screen resolutions that we need to consider. 

iPad:
  • 1024 x 768 px; and
  • 2048 x 1536 px for Retina displays (@2x).
iPhone:
  • 320 x 480 px;
  • 640 x 960 px for Retina displays (@2x); and
  • 640 x 1136 px for the iPhone 5 and iPod touch (5th Generation).
You will also need an icon in the 57 x 57 pixel size and 114 x 114 pixels (@2x for the retina version). These are fairly easy to scale from your iPad icons.

You can also use your  default iPad images for your iPhone but they won't scale exactly (you will need to crop either the width or height) if you want to maintain the aspect ratio.

The easiest way to add the iPhone versions of your icon and default load images is to drag them from Finder onto the deployment information page in Xcode. To bring up this page, select the CodeaTemplate at the top of the left hand Project Navigator pane. Click on Targets (Minesweeper in our case) in the middle pane and Summary in the far right pane (see Figure 3.)

Figure 3. Adding the iPhone Graphical Assets

Once you have the screen shown in Figure 3, you can drag across the appropriate image files from Finder. If necessary, Xcode will rename the files and copy them into your project. You should now be able to run your application in the iPhone simulator and see the default image load and when you exit the app using the home button you will be able to see the appropriate icon. That may be all you need to do, but probably not.

22.4 Step 3: Code Modifications


To get our App running on the iPhone we need to make some changes. What you have to watch out for is any hard coded view sizes. Some of our graphical assets will have to be redone to suit the dimensions of the iPhone screen. Let's start by getting the splash screen to work.

In the CodifyAppDelegate of the runtime the base app window is created when the application finishes launching. The good news is that the following line of code will create a window which will fit either an iPhone or iPad.

self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];

So there is nothing we need to do from a runtime perspective (unless you are using Game Center - see below). If we did need to modify anything in the Objective C portion of our code we could use the following pattern:

// Check if device is an iPhone or iPad
// We now also need to check whether we have a iPhone 5 
// or 5th Generation iPod touch.
    
    if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad)
        NSLog(@"App Delegate - iPad device detected.");
    else if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone)
    {
        CGSize result = [[UIScreen mainScreen] bounds].size;
        if (result.height == 480)
        {
            NSLog(@"App Delegate - iPhone/iPod Touch detected (< v5).");
        }
        if (result.height == 568)
        {
            NSLog(@"App Delegate - iPhone/iPod Touch detected (> v5).");
        }
    }

Paste a copy of this in the - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions method of the CodifyAppDelegate.m file if you want to try it out.

Now we do have to modify our Lua splash screen class as it loads background images which are too large for the iPhone screen. To assist with the conditional coding we need to add, we wrote a new helper function for the Lua portion of the code to determine what device we are running on. This was added to the Main tab, so that we know it will be available throughout the rest of the code. Depending on what resolution the forthcoming iPad mini ends up with, we might need to update this in the next couple of weeks. 

-- This function will detect the Device which is in use, 
-- required as our app is now universal

function deviceIsIpad()

    if CurrentOrientation == LANDSCAPE_LEFT 

      or CurrentOrientation == LANDSCAPE_RIGHT then
        if WIDTH == 1024 then
            return true
        else
            return false
        end
    else
        if WIDTH == 768 then
            return true
        else
            return false
        end
    end
    
end

As the device isn't going to change mid program, you can call this function once and assign it to a global boolean if you are worried about performance. Performance isn't an issue for MineSweeper so we wont bother.

Using this function we can now conditionally load the background image depending on the device. In the function SplashScreen:init(splashText, fadeSpeed) we have modified the sprite loading as follows:

    if deviceIsIpad() then
        landscapeImage = readImage("Dropbox:MS_TitleScreen_1024x768")
        portraitImage = readImage("Dropbox:MS_TitleScreen_768x1024")
    else
        landscapeImage = readImage("Dropbox:Default-Landscape~iphone")
        portraitImage = readImage("Dropbox:Default-Portrait~iphone")
    end

Remember that if you need to add new images to your Dropbox folder on the Mac then you will have to do a clean build for Xcode to recognise them. If you don't do this you will get a sprite loading error. To find the folder on your Mac to add the images, right click on the Drop Box sprite pack folder in the Xcode Project Navigator (Frameworks -> Codea -> SpritePacks) and select "Show in Finder". You can then drag across the new images that you want to use.

The other change that we need to make is in the function SplashScreen:draw().

        if deviceIsIpad() then
            sprite(logoImage, WIDTH - logoImage.width - 20, 20)
            text(self.splashText, WIDTH/2, HEIGHT/2 + 250 + portraitTextOffset)
            fill(whiteColor)
            fontSize(20)
            text("Version: "..version, WIDTH/2, HEIGHT/2 + 200 + portraitTextOffset)
        else
            fontSize(42)
            text(self.splashText, WIDTH/2, HEIGHT - 80)
            fill(whiteColor)
            fontSize(14)
            text("Version: "..version, WIDTH/2, 50)
        end

You will probably need to make similar adjustments in font size and composition to suit the smaller iPhone screen We also don't load the Reefwing Software logo if the device is an iPhone due to the space constraints. Make sure that the resulting view looks okay in both landscape and portrait orientations (command right arrow to change the orientation of the simulator in Xcode). As an aside you should find that the Fader class works as expected regardless of the device.


Now that you have the splash screen sorted we can turn our attention to the Menu screen. For us the changes here are minimal for the portrait orientation. We load up a smaller MineSweeper logo to fit on the iPhone screen and once again there is no room for the Reefwing and Codea logos so we dispense with them. The RoundBorder class works on all devices without any changes.

In the Main setup function where we load images we added:

-- Load the MineSweeper Text Logo
    
    if deviceIsIpad() then
        msTextLogo = readImage("Dropbox:minesweeper_logo 400x76px")
    else
        msTextLogo = readImage("Dropbox:minesweeper_logo 200x38px")
    end

And then in drawMenu() we shuffle the button positions and eliminate the "Medium" and "Hard" game options since those grid sizes wont fit on an iPhone screen (see section 22.5 Lessons Learned). The upside of removing the "Medium" and "Hard" buttons is that the remaining three buttons fit easily on the iPhone screen in both portrait and landscape orientations. Since there is only one game difficulty available on the iPhone we also changed the button text from "Easy" to "Start".


The achievement and leader board Game Center functionality works fine on an iPad but due to the way these modal views are displayed, on an iPhone the Lua draw() loop gets stopped if you tap the "Achievements" or "High Score" buttons. This results in the app still running but the screen never gets refreshed. To prevent this from occurring we need to modify the aGameCenter_Codea.m file that we introduced in the previous Game Center tutorial. The fix is to call [[SharedRenderer renderer] startAnimation] when the Game Center view controller is dismissed. This method is in the BasicRendererViewController of the runtime. An example  for dismissing achievements is shown below. The same is required for dismissing leader boards.

- (void)achievementViewControllerDidFinish:(GKAchievementViewController *)viewController
{
    [[SharedRenderer renderer] dismissViewControllerAnimated:YES completion:nil];
    [[SharedRenderer renderer] startAnimation];
}

This isn't a problem on the iPad because the Game Center view doesn't cover the whole screen (which causes the viewWillDisappear method to be called in the rendering code and stops animating the draw loop).

We have two more screens that we need to tweak and then we are done. Firstly the High Score screen which shows the scores stored locally is not particularly useful on the iPhone version (and is somewhat redundant as we are now using Game Center). So in the function scoreButtonPressed() we wont show this screen if we are an iPhone using the following code.

if deviceIsIpad() then
        gameState = stateScore
end

Remember that the gameState determines what the draw() loop displays. By not setting this to stateScore if the device is an iPhone, this screen wont be displayed.


Our last job is to sort out the game playing screen. The grid display is platform agnostic so it is only the button and text placement that we need to worry about. In the function drawCellsLeftDisplay() we do some more conditional placement.

    local iPhoneHeightAdjustment = 0

    if CurrentOrientation == LANDSCAPE_LEFT
      or CurrentOrientation == LANDSCAPE_RIGHT then
        iPhoneHeightAdjustment = 50
    end



    if deviceIsIpad() then
        text(cellsLeft, w + 50, HEIGHT - 100)
        text(math.round(gameTime), WIDTH - 150, HEIGHT - 100)
    else 
        text(cellsLeft, w - 30, HEIGHT - 100 + iPhoneHeightAdjustment)
        text(math.round(gameTime), WIDTH - 70, HEIGHT - 100 + iPhoneHeightAdjustment)
    end

The buttons on the iPhone screen will need to be placed based on the current orientation due to the space constraints. We do this in the drawGameButtons() function.

function drawGameButtons()
    
    -- These are the buttons visible on the game run screen
    -- Adjust the button position based on the device and orientation.
    -- In particular, we need to move the button location
    -- if the current device is an iPhone/iPod touch.
    
    local mButtonSize = vec2(100, 50)
    local mLocX, mLocY = 0, 0

    if CurrentOrientation == LANDSCAPE_LEFT 
      or CurrentOrientation == LANDSCAPE_RIGHT then
        if deviceIsIpad() then
            mLocX = WIDTH - 200
            mLocY = HEIGHT - 195
        else
            mLocX = WIDTH - 200 + 90
            mLocY = HEIGHT - 195 - 100
        end
    else
        if deviceIsIpad() then
            mLocX = WIDTH - 125
            mLocY = HEIGHT - 320
        else
            mLocX = WIDTH - 120
            mLocY = HEIGHT - 320 - 140
        end
    end
    
    flagButton.x, flagButton.y = mLocX, mLocY
    
    if CurrentOrientation == LANDSCAPE_LEFT 
      or CurrentOrientation == LANDSCAPE_RIGHT then
        if deviceIsIpad() then
            mLocY = 110 + mButtonSize.y*2
        else
            mLocY = 110 + mButtonSize.y
        end
    else
        if deviceIsIpad() then
            mLocY = 240 + mButtonSize.y*2
        else
            mLocX = 20
        end
    end
    
    newGameButton.x, newGameButton.y = mLocX, mLocY
    
    if CurrentOrientation == LANDSCAPE_LEFT 
      or CurrentOrientation == LANDSCAPE_RIGHT then
        if deviceIsIpad() then
            mLocY = 110
        else
            mLocY = 90
        end
    else
        if deviceIsIpad() then
            mLocY = 240
        else
            mLocX = WIDTH / 2 - mButtonSize.x/2
            mLocY = HEIGHT - mButtonSize.y - 10
        end
    end    
        
    menuButton.x, menuButton.y = mLocX, mLocY
    
    flagButton:draw()
    newGameButton:draw()
    menuButton:draw()
    
end



22.5 Lessons Learned


Converting to a universal App was easier than we were expecting. The Codea runtime works out of the box without modification so it is only your Lua code and graphical assets which need to be modified.

For us the most important lesson is that if you want you App to eventually be universal then design it to work on an iPhone/iPod sized screen (320 x 480 pixels) first. It is much easier to convert this to work on a larger screen than vice versa.

The other lesson is to avoid using hard coded co-ordinates if possible and use positions relative to the WIDTH and HEIGHT constants. This will make conversion between different screen sizes much easier and in many cases wont require any changes to your code at all.

Finally if your app includes Game Center functionality then you need to modify the methods which dismiss the Game Center view controllers to restart the Lua draw() loop.



22.6 Download the Code


You can download the updated code below. For simplicity we have provided all the classes even if they haven't changed from a previous version. You should be able to tell from the version number whether you need to download the code or can use a previous version (v1.6 indicates code that was modified for this tutorial).

Lua Code:
  1. Main v1.6
  2. Cell v1.5
  3. Colors v1.1
  4. Physics v1.0
  5. PhysicsDebugDraw v1.0
  6. RoundBorder v1.2
  7. ScoreScreen v1.0
  8. SplashScreen v1.6
  9. Button v1.3
  10. Fader v1.1
  11. IconImages v1.1
  12. TextBox v1.0
  13. Twinkle v1.1
Runtime (Objective C) Code:

Saturday, August 18, 2012

Tutorial 13 - Submitting to the App Store Part 2

    

13.1 Some Runtime Things to Watch


Further to part 1 of this tutorial, there are a couple of things, which if you are not aware of them, can cause problems with running your Codea App in the runtime.

13.1.1 Using Version in the pList

The runtime expects your version variable to be a string. So if you set a version number in Codea using something like saveProjectInfo("Version", version), where version is a number then you will get an error when you try to compile your project.
  
The solution is to either delete this line in your code or to modify a line in the runtime objective C code. If you want to take the second option then in the CodifyAppDelegate class go to the - (NSString) versionForProjectAtPath:(NSString)path method and change the line:
   
NSString* version = [infoPlist objectForKey:@"Version"];
   
To:
   
NSString* version = [[infoPlist objectForKey:@"Version"] description];

   
If you are using a float to represent your version (e.g. 1.4) then it is likely that within the info.plist file (which is found in the Project.codea folder) that this wont be the number you  are expecting (e.g. you get 1.39999999999234 instead of 1.4). This is due to the size of the memory allocated to store numbers in Codea. Anyway, just edit this in your pList file in Xcode back to what it should be. Note that there are two info.plist files, one for your App (found in classes -> Supporting Files) and one for your Codea project.
    
13.1.2 Supporting Orientations

Another trick for young players is that the function orientationChanged(newOrientation) is called before the setup() function in Main. So if you use global variables which are defined in setup() in the orientationChanged() function you will get a runtime error because they haven't been defined yet.

If you are in this situation, the solution is a three step process:

1. In Main, add the following line before anything else:

setupHasRun = false

-- Other global stuff

2. Then in your setup() function, once your setup is done set the variable to true:
  
function setup()
    
    -- setup stuff
  
  setupHasRun = true
      
end
 
3. Finally in the orientationChanged() function:

function orientationChanged(newOrientation)

    if setupHasRun then
        -- Your existing orientation code
    end

end


And that should sort out the problem. Thanks very much to Simeon from Two Lives Left for tracking this solution down.

     

13.2 Customising the Runtime


     
Before submitting your App to iTunes, there are a few things that you will probably want to change in the runtime. We will look at the simple things in this tutorial, a subsequent tutorial will explore how to add things like iAds and Game Centre support.
   
  Figure 1. Changing your Icon & Launch Images in Xcode.
    
13.2.1 App Icons and Launch Images
      
The runtime comes with some default icons and launch images. The easiest way to replace these with your own is to use Xcode.
     
  1. Select the Project Navigator (top left button in the tool bar) and click on the top most item in the left pane, called Codea Template.
  2. If you then select the summary tab in the right hand (or middle pane depending on how you have Xcode configured) you should see something like Figure 1.
  3. Right click (control click) on any of these images and chose "Select File" to change them to your custom versions.
  4. There is a plethora of app icon and launch image sizes required, but you can get away with just having these six plus the App store image (which we will get to shortly). The iOS Custom Icon and Image Creation Guidelines lists exactly what dimensions are required and the naming conventions for these images.
           
13.2.2 Hiding the Status Bar
        
Note that you should take into account whether you will be showing the status bar when creating your launch images. Xcode will warn you if the dimensions of your selected images are incorrect. You can hide the status bar by setting the "Status bar is initially hidden" to YES in the info.plist file for your App (found in classes -> Supporting Files).
      
Figure 2. iOS Dev Centre Web Site.
          

13.3 The iOS Dev Centre

            
13.3.1 Provisioning Portal
      
For this next step we are assuming that you already have set up a paid developer account with Apple. The good news is that the App provisioning and certification process has gotten much easier than it was, but it is still a bit tricky so we will step you through the correct order in which things need to happen.
       
Figure 3. iOS Provisioning Portal.
                
  1. Open up you favourite browser and go the iOS Dev Centre site (Figure 2). Log in using the Apple ID associated with your Developer Account.
  2. Once you are logged in, at the top right of the iOS Dev Centre page is a set of links grouped as "iOS Developer Program". Click on the link which says iOS Provisioning Profile.
  3. You should already have a development and distribution certificate. If you haven't, the easiest way to do this is to use the Development Provisioning Assistant. You can start this off by clicking on the button which says "Launch Assistant", it is located in the middle bottom of the page (Figure 3).
  4. After you are certified we need to create a new App ID for our App. In the left hand pane of the iOS Provisioning Profile page, click on the link called "App IDs". On the App ID page you will be able to read more than you ever wanted to know about the care and feeding of App ID's. You will also be able to see the status of any App ID's that you currently have in place. Before your eyes glaze over there is one important paragraph buried in there. It starts with "The Bundle Identifier portion of an App ID can be substituted with a wild-card character (asterisk '*') so that a single App ID may be used to build and install multiple applications." We suggest that you don't do this, as wild-card App IDs cannot be used with Push Notifications or for In-App Purchase. 
  5. Click on the "New App ID" button in the top right of the screen, and this will take you to the Create a New App ID page shown in Figure 4. Here you have to answer just three questions but the first time you see this, it can be a bit confusing as to what to do. 
  6. For description we used "MinesweeperAppID". The description is not important, it is just to remind you which App this ID is for. The next question is important. It is to set the "Bundle Seed ID (App ID Prefix)". We suggest using the default "Use Team ID" as the prefix. As mentioned previously don't use a wildcard "*" for the prefix. The last question is used to set the "Bundle Identifier (App ID Suffix)". This needs to be unique for every App and the usual practice is to use a reverse domain name string. In our case we used "au.com.reefwing.Minesweeper". So what if you don't have a domain? It doesn't matter you just need to use something unique - you could perhaps base it on your name (e.g. "davidsuch.YourAppName"). Click on submit and you will be taken back to the previous App ID status screen where you should now be able to see your shiny new App ID.
  7. The last step in this stage is creating our Provisioning Distribution Profile. In the left hand navigation pane click on the "Provisioning" link and then on the Distribution tab. This will show a list of existing Distribution Profiles. Click on the "New Profile" button in the top right. Fill in the details. Distribution method is "App Store", choose an appropriate profile name (we used Minesweeper Distribution Profile), and select the App ID that you just created. Click on "Submit" and you are done.
  8. In the top right you will see a link which says "Go to iOS Dev Centre". Click on that and we will be ready for the next stage.
                
Figure 4. Creating a New App ID.
               
13.3.2 iTunes Connect
             
Figure 5. ITunes Connect.
             
Back at the iOS Dev Centre we now want to prepare our App for upload to Apple for review. We do this by using the link below the Provisioning Portal called iTunes Connect. Click on this now. You will probably be asked to sign in again at this stage. 
            
  1. On the iTunes Connect page (Figure 5) you will see a link to "Manage Your Applications" at the top of the right hand column. Click on this and then click on the "Add new App" button (top left).
  2. You will be asked to select your App type (iOS or Mac OS X). Click on iOS.
  3. Enter your App information. This is pretty self explanatory. For SKU we usually use the date (SKU = stock keeping unit, businesses use them to identify a unique product) and make sure that for Bundle ID you select the App ID created in section 13.3.1 above. The App name that you select will also need to be unique - in our case Minesweeper was taken so we went with iMinesweeper instead. When done click "Continue".
  4. Next select the availability date and price tier. Use the current (default) date if you want your App released as soon as it is approved by Apple. Select some future date if you want to control the release date. This can be handy if you are co-ordinating a massive marketing effort to drive up demand for your App and its first day sales. Selecting anything else but FREE for the price will bring up a matrix showing the price for that tier in different countries. Click "Continue".
  5. Fill out the version information and the age rating survey to determine its rating in the App store (e.g. Minesweeper comes out at a 4+ rating, the lowest possible). Clicking Frequent/Intense for some of these questions will mean that your App can't be sold in selected countries or in some instances at all. Next fill out your App's Metadata, this information will appear in the iTunes App store. The keywords are important because this is how customers will discover your App using the search functionality in iTunes. You are probably okay to go with the standard End User Licence Agreement (EULA), so the last step is to upload the images displayed in the App Store. The large App Icon needs to be 1024x1024 pixels in size. For the iPad screenshots, the easiest way to get these is using Organiser in Xcode (select your iPad from the left hand navigation panel and then screenshots. Run the App on your iPad using Xcode and then click the new screenshot button to take the images you want. You can then export these to a handy folder for upload to iTunes Connect). You can have up to 5 screenshots, drag and drop to change the order. Click "Save" when you are done.
  6. After saving your App data you will be brought to the App summary screen. Check that all the details are correct. The status at this stage should be a yellow "Prepare for Upload". Click the "View Details" button below your App icon, then in the top right hand corner, click on "Ready to upload binary".
  7. Answer the question about cryptography then click "Save" and then "Continue". Your App status will now be a yellow "Waiting for Upload". Click on App Summary and Done and you should see something like Figure 6, and we need to head back to Xcode for the next step.
            
Figure 6. iTunes Connect - Manage Your Apps.
       
13.3.3 Uploading Your Binary
         
In Xcode, we need to associate our App with the bundle ID that we set up in iTunes Connect. 

  1. Before proceeding, we need to download the distribution provisioning profile. In the Organiser window, click on Devices and then Provisioning Profiles in the left hand navigation pane. Click on the Refresh button (bottom left corner) and your new profile will be downloaded.
  2. Select the Project Navigator (top left button in the tool bar) and click on the top most item in the left pane, called Codea Template.
  3. If you then select the summary tab in the right hand (or middle pane depending on how you have Xcode configured) you should see something like Figure 1. Scroll up to the top of the Summary tab. The Bundle Identifier will currently have: com.twolivesleft.Minesweeper. This needs to be changed to the bundle ID that we established in the steps above. In our case we changed it to au.com.reefwing.Minesweeper. Check that the correct version number is listed while you are here.
  4. While we are here we will sign our code with the appropriate distribution profile. Select the "Build Settings" tab and scroll down to the Code Signing section. Under Code Signing Identity -> Release -> Any iOS SDK, select the distribution profile that you just downloaded.
  5. Before uploading we need to recompile the binary with all this good stuff included. Select Product -> Archive from the menu bar. After compiling you should see Archive Succeeded in Xcode and the Organiser window will automatically be presented showing your App Archive (Figure 7).
  6. In Archives (Figure 7), click on the Validate button (top right) and your archive will be validated prior to upload to Apple. At this stage, you may get an error - "No identities are available for signing." If you do, choose the Connect to iOS Dev Centre, "Download Identities" button. You should now be validated, click on "Finish"
  7. Click on Distribute, select submit to the iOS App Store, click "Next" three times and you will see a message "Your application is being uploaded".
  8. If all goes well you will get a "Submission Succeeded" message (Figure 8).
          

  Figure 7. Your App Archive in the Organiser Window.
    
Well done if you made it to this point. Believe it or not, Apple have actually streamlined and automated a number of steps in this process which used to be much more cryptic (especially for your first attempt at it).
  
If you want, you can go back into iTunes connect and validate that your App status has changed to orange and "Waiting for Review". In fact, we would recommend that you do this. There have been instances where people have uploaded their binary but for some reason it never moves on to "Waiting for Review." If this happens you can try rejecting the binary and re-uploading it. It will take about a week for the App to work its way up the review queue. Watch this space for updates...
     


Figure 8. Success! You App has been submitted for Review.