Thursday, November 10, 2011

OpenFeint - Upgrading to GameFeed

So OpenFeint is making a big deal of its GameFeed feature. The big claim is that GameFeed increases sessions per player as much as 65%!! If you have developed an OpenFeint game then you will need to upgrade to the latest version of OpenFeint 2.12.3. Here's the install instructions.

 In true style OpenFeint has messed-up its documentation as usual. There's a couple of gotchas to be aware of.

 (1) Framework Install Vs File Directory Install

If you are going with the 'framework' way of upgrading then you will need to change the #import statements such that

#import "OFNotificationData.h"

changes to:

#import "OpenFeint/OFNotificationData.h"

This issue will be picked up by the Xcode build results. So just work through it to resolve all instances of this error.

But there are other error types.....

(2) OFDelegate is gone, Say hello to OFInvocation

For some reason OpenFeint decided to completely get rid of OFDelegate as a class from its API. This means all uses of it in your code will return an error. This is quite problematic OFDelegate was used a hell of lot once you started getting deep into OpenFeint. Instead you must use OFInvocation. Here's a typical error case you are likely to encounter. 

The old way:



#import OpenFeint/OFDelegate.h
#import OpenFeint/OFChallengeService.h
. . .

[OFChallengeService getPendingChallengesForLocalUserAndLocalApplication:1
onSuccess:OFDelegate (self, @selector(_onGotNumPendingChallenges:))
onFailure:OFDelegate (self, @selector(_onGotNumPendingChallengesFailed)) ];


The new way:



#import OpenFeint/OFInvocation.h
#import OpenFeint/OFChallengeService.h
. . .

[OFChallengeService getPendingChallengesForLocalUserAndLocalApplication:1
onSuccessInvocation:[OFInvocation invocationForTarget:self selector:@selector(_onGotNumPendingChallenges:)]
onFailureInvocation:[OFInvocation invocationForTarget:self selector:@selector(_onGotNumPendingChallengesFailed)]
];


 Update - I didn't end up using GameFeed. It takes quite a bit of space and the constant feed of info will hit game performance I think. This is just a gut call. I didn't really look into it that deeply. My impression is that your game should be designed to integrate GameFeed. I don't think it's a feature you throw in at the end of the development cycle.

Best, Nige

Friday, October 14, 2011

Phonegap Plugins - Part 2 Developing plugin classes

Background


In my last post I talked about how to write ad-hoc plugins. To be honest I have largely used the ad-hoc approach for my plugins for a number of reasons:
  • My app is largely a javascript app. My snippets of obj-c do not warrant a full OO design. They warrant and ad-hoc approach IMO.
  • Some of the tasks I perform don't warrant their own obj-c class.
  • Some of the tasks I perform are complex, largely because I am talking to other 3rd party obj-c APIs like openFeint. Getting the the code in my app to talk to the phonegap and openFeint APIs was not obvious to me through delegation. I ended using NSNotificationCenter to setup event listeners in my code that would trigger when the event was invoked. I was more comfortable with NSNotificaitonCenter as a javascript developer. I will talk about the use of NSNotificationCenter in part 3 of this series.
However developing a plugin that is an obj-c class is very nice because it is very portable to other apps and because you can easily publish it to github to gain some credibility for yourself and maybe some extra app sales. To be honest, I will probably end up going through my ad-hoc plugins to extract some more "class" plugins that I can reuse easily and publish.

Why I developed this plugin


So the plugin I am going to talk about today is available on github here for you to download. It's also a very practical and useful plugin IMO. The plugin renders a native obj-c scrim loader/ajax spinner (never sure exactly what to call them!). In obj-c parlance, the plugin renders a UIActivityIndicator to the screen (yet another name for this thing!). 

Hold on! you might say. This doesn't sound too useful. This is pretty simple and low intensity stuff, especially to javascript developers. And I agree with you. It is. So let's examine the reason why we are going the plugin route in this case. It is always important to ask the "why" with plugins, otherwise just develop in obj-c. 

First of all, the inspiration for the ajax spinner came from MegaJump. They use them everywhere. I had a lot of fade in/outs between scenes and just felt that an ajax spinner would look good.

Secondly, when the user is left waiting for the game to start up or an image/data to download you need to let them know that every thing is good. Users already know that an ajax spinner tells them everything is fine.

Finally, in my game I have alot of graphical rendering going on using webkit transition. Webkit transitions are good if there is only one of them rendering to screen at a time, but if there's a few of them going on then  they tend to stutter and look cheap. So if I use a webkit transition to render an ajax spinner then I am going to eat up resource that could be better used elsewhere. My solution was to use the UIActivityIndicator in obj-c which is low-resource.

The strategy for designing the plugin was to create an instance of the class, render, hide it and then destroy it. The plugin is not designed to allow for multiple instances to be created in the background and rendered as required. 

To the meat of the matter


So let's look at the .h file the scrimLoader class:
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "PhoneGapCommand.h"

@interface ScrimLoader: PhoneGapCommand {
  UIView *scrimView;
  UIActivityIndicatorView *myIndicator;
}

@property(nonatomic, retain) UIView* scrimView;
@property(nonatomic, retain) UIActivityIndicatorView* myIndicator;

- (void) showScrimLoader:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;
- (void) dismissScrimLoader:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;

@end

Any phonegap plugin must contain @interface MyClass: PhoneGapCommand. The class should obviously contain a UIActivityIndicatorView, but it also requires a UIView to render on to which we name scrimView in our class. Getting the setup right in your .h file is probably the hardest part of writing plugin classes and there are different approaches.

In the EmailComposer plugin class (available here) the .h file class is written as follows:

@interface EmailComposer : PhoneGapCommand <MFMailComposeViewControllerDelegate> {

}

A special type of view controller delegate (MFMailComposeViewControllerDelegate) is made available in obj-c. The developer can leverage this delegate to create instances of MFMailComposeView and does not need to declare properties directly in the interface file.

But let's get back to our scrimLoader class. Here it is in full:

#import "ScrimLoader.h"

@implementation ScrimLoader
@synthesize scrimView;
@synthesize myIndicator;

- (void) showScrimLoader:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options 
{
  UIApplication* app = [UIApplication sharedApplication]; 
  UIWindow* appWindow = [app keyWindow];
  self.scrimView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)] aut  orelease];
  self.scrimView.backgroundColor = [UIColor clearColor];
  [appWindow addSubview:self.scrimView];
  [appWindow makeKeyAndVisible];
  self.myIndicator = [[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge] autorelease];
  self.myIndicator.center = CGPointMake(160, 440);
  self.myIndicator.hidesWhenStopped = NO;
  [self.scrimView addSubview:self.myIndicator];
  [self.myIndicator startAnimating];
}

- (void) dismissScrimLoader:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options 
{
  [self.myIndicator stopAnimating];
  [self.myIndicator removeFromSuperview];
  [self.scrimView removeFromSuperview];
}
It's interesting to go through the two methods. Let's look at the showScrimLoader method. The purpose of this method is to show the scrimLoader. Well it's more than than. It's actually to create a scrimView instance and a myIndicator instance and then to show them both. Let's go through the code line by line.

UIApplication* app = [UIApplication sharedApplication]; 
UIWindow* appWindow = [app keyWindow];
app is a handle to the singleton object [UIApplication sharedApplication]. A singleton object is an object which can only have one instance. The handle to [UIApplication sharedApplication] is also globally available to all classes. This is a very handy thing to know since we can leverage this to get to other parts of the app from anywhere in the code. In this case I wanted to get to the appWindow. Why? So I can create my scrimView. In obj-c a UIView needs to be created in a UIWindow.

The next 4 lines of code now make alot of sense:

self.scrimView = [[[UIView alloc] initWithFrame:CGRectMake(0, 0, 320, 480)] autorelease];
self.scrimView.backgroundColor = [UIColor clearColor];
[appWindow addSubview:self.scrimView];
[appWindow makeKeyAndVisible];
We create a scrimView that has an origon of (0,0) and takes up the full iPhone screen. I like to use autorelease so that obj-c uses its own garbage collector to dispose of the object at its leisure (like javascript). You could also manage the gc yourself. The scrimView has clear color ie it will be transparent and by default does not listen for user gestures (something that I required for my app). Now that we have created the scrimView and styled it we add it as a subview of the appWindow and then make it visible to the user.

Now we largely apply the same process to the myIndicator instance:

self.myIndicator = [[[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge] autorelease];
self.myIndicator.center = CGPointMake(160, 440);
self.myIndicator.hidesWhenStopped = NO;
[self.scrimView addSubview:self.myIndicator];
[self.myIndicator startAnimating];
We create the instance of myIndicator, style it (ie position it mid-lower screen), add it to the scrimView (remember it needs a UI view to render to) and activate it (ie get it to start spinning once rendered to screen).


From your javascript code you can call the ajax spinner into action as follows:

PhoneGap.exec('ScrimLoader.showScrimLoader');
The dismissScrimLoader method is relatively easy to understand:

[self.myIndicator stopAnimating];
[self.myIndicator removeFromSuperview];
[self.scrimView removeFromSuperview];
The order in which we remove the object instances is important. The superView of myIndicator is scrimView so can't obviously remove it before removing myIndicator.

Finally to sue this propery simply make to following call from javascript:

PhoneGap.exec('ScrimLoader.showScrimLoader');

If you want to explore further then a collection of good phonegap plugins is available here.

Hope you enjoyed. And a twitter follow, stumble, digg and/or Bubble Electric download (don't worry it's free) is always appreciated.

Best, Nige

Friday, September 16, 2011

Phonegap plugins - Part 1 Developing simple plugins

What is a phonegap plugin?


A phonegap plugin is a javascript handle to native device code. So on the iPhone, a plugin is a javascript handle to a function or even class that is written in objective-c. From javascript you simply make call the native code with a command that looks like:
Phonegap.exec("myObjectiveCFunction", myArgument1, myArgument2, etc);

Background


I have been writing a HTML5 game for iPhone for a good while now (too long a while). The game is at last near completion. It's called Bubble Electric and here's a landing page.

The game has been out for a while now on webOS, as Bubble Puzzle, and is approaching a respectable 100k downloads. Unfortunately alot of these downloads are for the free version. Whilst I have not made my fortune I can at least point to 4/5 user rating I get for the game as a soft consolation.

I have written the majority of the game in javascript and worked hard to get webkit transitions working well. It's been difficult but I think I've done a good job (of course I must say this!).

It has to be said that the iPhone is well ahead of the competition when it comes to webkit transitions. webOS is not bad but seems to favour canvas and Android looks to be good on higher end phones like the Samsung Galaxy but sucks big time on my Android: an old HTC Hero.

But I digress. Let's get back to phonegap plugins. You may ask why use plugins when the iphone does such a good job of rendering webkit. Well everything is not about rendering in a game. Sound is important, connecting your game to social marketing tools is important (like Openfeint, Flurry), etc.

In the case of sound, Phonegap by default calls to objective-c to play audio. It does not go near HTML5 audio tags. Why? Because Apple has done a great job with audio in obj-c. Playing sounds with native obj-c does not disrupt rendering performance but HTML5 audio tags will. webOS has failed to tackle the the audio issue in a meaningful way and is one reason why webOS did not hit the ground running. I tested HTML5 audio tags in the webOS version of the game and playability of the game was ruined.

In the case of services like Openfeint, the detailed API has been written in objective-c so there's simply no other way to use it. Yes they have released a RESTful api but it's very much a beta and lacks the power and functionality of the obj-c version.

Let's get started


So let's get to the point and hack together a simple plugin for phonegap. We're going to create an alert dialog box. This is a little bit redundant as you can call alert(message) in js anyway but it will show the basics of a plugin. This is how I started writing plugins.

1. Open up your phonegap project in Xcode and add a new file to the Plugins folder.
2. Select Objective-C class as the file type.
3. Name the file and save it.
4. You should have 2 files relating to the class you created: MyPlugIns.h and MyPlugIns.m.
5. In MyPlugIns.h type the following:

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


@interface MyPlugIns : PhoneGapCommand {
}

- (void)logAlert:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options;

6. In MyPlugins.m type the following:

@implementation MyPlugIns


- (void)logAlert:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options {

[webView stringByEvaluatingJavaScriptFromString:@"alert('This is your log alert.');"];

}

@end

7. So what are we going here? We are telling the webView to interpret the js and render it to the webView. What's the webView? The webView is the chromeless browser view that your phonegap runs in. Once you import PhonegapCommand.h the properties and methods of the webView become available for manipulation to the class you imported it to. This in itself is a handy thing to know.

8. stringByEvaluatingJavaScriptFromString is a method of the webview that simply interprets javascript in the string that follows it. In this case: @"alert('This is your log alert.')". In obj-c a string object is represented as @"string". The @ requirement is v annoying for me.

9. To call this plugin from javascript you simply write the following code in your javascript file(s) like so:

Phonegap.exec("MyPlugIns.logAlert");

10. Well that's nice you may say but what if I want to pop up a different alert message. No problem here's how to do it. Go back to the MyPlugIns.m file and edit the logAlert function as follows:

- (void)logAlert:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options {

NSString *startJS = [NSString stringWithFormat:@"%@", @"alert('"];
NSString *message = [NSString stringWithFormat:@"%@",[arguments objectAtIndex:0] ];
NSString *endJS = [NSString stringWithFormat:@"%@", @"');"];

NSString *jsString = [NSString stringWithFormat:@"%@%@%@", startJS, message, endJS];
[webView stringByEvaluatingJavaScriptFromString: jsString];

}

11. What we're doing here is basically assembling the js string we want to interpret. It's messy but it works. Note that the source data for the message string is coming via the arguments parameter. arguments is an array and so we reference it as an array to get at what it's holding using [arguments objectAtIndex:0]

Please be warned that it is easy to use NSString *myString = @"a string" but this causes problems when interpreting non string parameters you pass into the function. For example, if your message was an integer number you may want to use the following to convert it into a string:

NSString *message = [NSString stringWithFormat:@"%d",[arguments objectAtIndex:0]];

12. To call this plugin from javascript you simply write the following code in your javascript file(s) like so:

Phonegap.exec("MyPlugIns.logAlert", "This is your log alert");

To pass more than one parameter simply do the following:

Phonegap.exec("MyPlugIns.logAlert", "This is your log alert", "Hello function");

Of course you need to pick up this parameter in your obj-c function as follows:

NSString *logDetail = [NSString stringWithFormat:@"%@",[arguments objectAtIndex:1]];

13. Finally you may have noticed that the resulting alert screen always has a index.html in its head. Everyone will know this is a js app. How do I change this. Change your logAlert function as follows:

- (void)logAlert:(NSMutableArray*)arguments withDict:(NSMutableDictionary*)options {

[webView stringByEvaluatingJavaScriptFromString:@"navigator.notification.alert('This is your log alert.', 'Log alert', 'OK');"];

}

14. Hey! That function is not parameterizable. Well I will let you work that out!

Well that's it for now. In my next post I'll show you how to write more sophisticated plugins that are classes in their own right and that bring native views into play.

Hope you enjoyed. And a twitter follow, stumble, digg and/or Bubble Electric download (don't worry it's free) is always appreciated.

Best, Nige

Monday, March 7, 2011

Gripe - objC: Comparing Strings in IF statements

I'm definitely not a fan of objC and here's one reason:

NSString *test;

if ([test isEqualToString:@"passed"]) {


}

Why oh why can't it just be

if (test=="passed") {


}

I just lost 1-2 hrs eliminating other non-issues in my code because of this.

Aaargh!!!

Saturday, March 5, 2011

Telling Openfeint that the challenge has been won/lost/tied

Are you having trouble telling the OpenFeint service when player B has won/lost/tied a challenge created by Player A?

Probably because the documentation is awful and there's little out there on OpenFeint challenges. It's relatively easy to get Player A to create challenges, but it's difficult to get Player B to get the challenge, its data, play it and then send back the result to the OpenFeint servers. One tip I have for you is to do with storing and using challenge data.

In your app's implementation of the challenge delegate sure that you have included #import "OFChallengeToUser.h" in the .h or .m of the delegate. Check out the main method of the delegate:

-(void) userLaunchedChallenge:(OFChallengeToUser*)challengeToLaunch withChallengeData:(NSData*)challengeData

Its first parameter is a OFChallengeToUser type. With OFChallengeUser.h included you can now call methods on challengeToLaunch:

NSString *challengeOriginatorUserID = [challengeToLaunch resourceId]


I could not call this method without the include. This data can now be stored and used where it is required by Player B to inform Openfeint that he has completed the challenge sent by Player A using the call:

[OFChallengeService submiteChallengeResult: challengeOriginatorUserID result:kChallengeResultRecipientWon resultDescription:@"I won" onSuccess:success onFailure:failure]


Friday, March 4, 2011

Openfeint server connectivity issues

Are you developing an OpenFeint app?

Are you experiencing unstable connections to the OpenFeint service?

Are you developing on an iPhone/Pod 4 device?

If yes then this could be the solution to your connectivity problem.

1. Open <yourApp>-Info.plist

2. Add the "Application does not run in background" key

3. Click it on

Like so:


This should fix your issues with OpenFeint service connectivity issues.

The issues occur because of the multi tasking features of iPhone 4. These features seem to mess up the OpenFeint service.

Share