Ludos Imago

moosader:

Intro to Source Control: A Must-Have Tool for Programmers (by Rachel Morris)

Have you been hearing about this “Source Control” or “Revision Control” thing, but don’t know what it is or where to begin with it?

That’s what this video is for!  This part is an introduction to what Source Control is and features it has!

Great Tips :-)

Independent Developer Exchange Group

Hi Indie Devs,

In relation to my last post about localization, I have started a forum for Indie Developers to exchange languages as well as cross promotion.

I was thinking about the language exchange and some cross promotion exchange, I came to the conclusion that the more we indie’s work together, the better are our chances of success. As most people use a lot of apps and are constantly looking for new ones, we can share exposure and all gain more downloads and sales. Marketing an app is very expensive as we all know, but by doing cross promotion we can reduce our costs and gain better exposure.

On the other hand, as Apple this year said on WWDC localization is a major topic. We can reach far more customers, if we localize our apps. This is a very expensive process if you, employ a professional agency, on the other hand google translate sounds horrible most of the times. On this lines we started the language exchange earlier this year (you can look up the doc in the group notes). I had very good experience with you guys there :-) and it is fantastic to connect that way…

Now in relation to translation exchange there are a lot of possibilities even if you only speak English, for example you could exchange house ads in your app for a translation…

What to do when you like to start an actual exchange? Who would be interested? How to work on translations together? And after some time you remember there was this post, but where is it now?

In this context I thought to start a private(closed) forum, where Indie developer can exchange Ads, Translations, and anything else which could make all of us stronger.

I created a subdomain for this forum: Independent Developer Exchange Group as said it is closed, so you need to apply for membership I will try to approve as fast as possible :-)

Indipendent Developer Exchange Group

Please tell me what you think and we can go from there :-)

If you are interested join the group.

Soon more :-)

Greetings

Anselm

App Localization for I-OS apps

Back again :-)

This time I would like to talk about localization of apps. As most app developers are aware of, to offer an app in different languages can make a big difference in downloads and sales.

In my own experience the US is still one of the biggest markets, but I get downloads from all over the world, especially in countries were I support the native language. As I only develop for I-Os at the moment I can only speak for this. Probably there are developers who had different experiences than I had.

I localized my first app BeeJuice in English,French,Spanish,German,Italian and Malay. I had Persian as well, but ran in some problems, so I hope I can put it properly in my update.

There is a group of indie devs who started a language exchange, which I used successfully for my new app Peckish Spider (soon to be submitted). For more information in regards to that just contact me… via DM :-)

Within XCode you can localize every file! but often this is not necessary.

Ok, but how do I localize my app?

Now I would like to start with strings. There is a very important and useful thing you should do with all your strings you want to localize:

Instead of the usual @”Your String” you put this

NSLocalizedString(@"Your String", @"Your description")

in.

Now when you finished your app and start to localize it, open your terminal.app, go into your app directory and type

genstrings *.m

You will get a file Localizable.strings.

A short digression, if your project has multiple directories you can use this command which worked very well for me:

find . -name \*.m | xargs genstrings

If you have any problems with that topic check this discussion on Stackoverflow.

Now that you have the Localizable.strings put it into XCode like this:

XCode import Screen

The content of the file will look like that:

/* Your Comments. */
"Your Strings…" = "Your Strings…";

on the left side is the original and on the right side there will be the translation of the target language.

Next step is to localize Localizable.strings:

Mark Localizable.strings in Xcode open the right Pane and click on + in the localize section, there you can choose the language you want to localize your app in.

When you have done that you should be able to see a drop down menu next to your Localizable.strings, it should look like that:

All these files will be the same as your original.

/* Your Comments. */
"Your Strings…" = "Your Strings…";

The left side is the reference in your app. On the right side fill in the translations.

In general you can localize really every file, even pictures, but most of the time you can localize everything via NSStrings.

Sometimes it makes sense to localize a xib file, but even that can be done using NSLocalizedString.

There is a project starting at the moment for a Language exchange, if you are interested contact me directly or check out @afterdarkdev’s Facebook indy dev group.

I hope this post will help you to localize your app and get a lot more downloads.

If you liked this post, please leave a comment. If you think I missed something or something is wrong, just tell me so I can correct it.

As a reference I would like to mention iPhone 3 Development, as I learned the basics in this book. You can get the newest version of the book here:

Beginning iPhone 5 Development: Exploring the iOS 5 SDK

How to create a Twitter Helper Class with I-OS 5 support and I-OS 4 Fallback for cocos2d!

In my last article I wrode about how to create a Facebook Helper Class. This time I would like to write about how to create a Twitter Helper Class which uses the I-Os 5 implementation of Twitter, but provides a fallback for devices with I-OS 4.

Before we start, you need to download Ben Gottlieb’s Twitterengine as I use it for the fallback… when you downloaded it drag it into your XCode Project.

We need to add some Frameworks for this to work:

  • Twitter.framework
  • libxml2.dylib

You need to add this line to header search paths: $(SDKROOT)/usr/include/libxml2 

Excursion: I added a root view controller instance in the app delegate:

@class RootViewController;

@interface AppDelegate : NSObject <UIApplicationDelegate> {
	UIWindow			*window;
	RootViewController	*viewController;
}

@property (nonatomic, retain) UIWindow *window;
@property (nonatomic,retain) RootViewController	*viewController;

@end

You need to synthesize it and change the

- (void) applicationDidFinishLaunching:(UIApplication*)application

viewController = [[RootViewController alloc] initWithNibName:nil bundle:nil];

First I would like to explain the TwitterHelper.h:

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import <Twitter/Twitter.h>
#import "AppDelegate.h"
#import "SA_OAuthTwitterController.h"

@interface TwitterHelper : NSObject<SA_OAuthTwitterControllerDelegate,UITextViewDelegate>{
    
    AppDelegate *appDelegate;
    BOOL iOS4vs5;
    
    
    SA_OAuthTwitterEngine    *_engine;
    UIViewController * viewController;
    UITextView *twitterTextView;
    
}

@property(assign)AppDelegate *appDelegate;

+(id)alloc;
+(TwitterHelper*)sharedTwitterHelper;
-(void)postTweed;

@end

Import this files into you .h file.

#import <UIKit/UIKit.h>
#import <Twitter/Twitter.h>
#import "AppDelegate.h"
#import "SA_OAuthTwitterController.h"

The Appdelegate.h is especially for cocos2d so we can access the root view controller.

Next we need to implement some delegates:

<SA_OAuthTwitterControllerDelegate,UITextViewDelegate>

The SA_OAuthTwitterControllerDelegate is needed for the fallback as well as the UITextViewDelegate.

Now we need to declare some variables:

    AppDelegate *appDelegate;
    BOOL iOS4vs5;    
    SA_OAuthTwitterEngine    *_engine;
    UIViewController * viewController;
    UITextView *twitterTextView;

The appDelegate is needed to access the RootViewController, iOS4vs5 is to decide which kind of Twittercontroller is to be called, the textview and the Viewcontroller are needed for the IOs4 fallback.

The rest is adding the properties and the functions we need.

@property(assign)AppDelegate *appDelegate;

+(id)alloc;
+(TwitterHelper*)sharedTwitterHelper;
-(void)postTweed;

As I am trying to create the TwitterHelper Class as Semi-Singleton we need sharedTwitterHelper and alloc for this purpose. And postTweed is obvious :-)

TwitterHelper.m:

#import "TwitterHelper.h"
#import "SA_OAuthTwitterEngine.h"
#import "Common.h"
@interface TwitterHelper(PrivateMethods)

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text;
-(void)sendTweetWithString;
-(void)cancelTweet;

@end


@implementation TwitterHelper
@synthesize appDelegate;

static TwitterHelper *instanceOfTwitterHelper;

#pragma mark Singleton
+(TwitterHelper*)sharedTwitterHelper
{
	@synchronized(self)
	{
		if (instanceOfTwitterHelper == nil)
		{
			[[TwitterHelper alloc] init];
		}
		
		return instanceOfTwitterHelper;
	}
	
	// to avoid compiler warning
	return nil;
}

+(id) alloc
{
	@synchronized(self)	
	{
		NSAssert(instanceOfTwitterHelper == nil, @"Attempted to allocate a second instance of the singleton: TwitterHelper");
		instanceOfTwitterHelper = [[super alloc] retain];
		return instanceOfTwitterHelper;
	}
	
	// to avoid compiler warning
	return nil;
}


-(id)init{
    
    if ((self = [super init])) {
        
        NSString *deviceVersion = [UIDevice currentDevice].systemVersion;
        int deviceVersionInt = [deviceVersion intValue];
        
        NSLog(@"DeviceVersion: %i",deviceVersionInt);
        
        appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];

        if (deviceVersionInt < 5) {
            
            iOS4vs5 = NO;
            
            if(!_engine){  
                _engine = [[SA_OAuthTwitterEngine alloc] initOAuthWithDelegate:self];  
                _engine.consumerKey    = kOAuthConsumerKey;  
                _engine.consumerSecret = kOAuthConsumerSecret; 
                
            }  
            
            if(![_engine isAuthorized]){  
                UIViewController *controller = [SA_OAuthTwitterController controllerToEnterCredentialsWithTwitterEngine:_engine delegate:self];  
                
                if (controller){  
                    [appDelegate.viewController presentModalViewController: controller animated: YES];  
                }} 
            
        }else if ((deviceVersionInt == 5)||(deviceVersionInt>5)) {
            
            if ([TWTweetComposeViewController canSendTweet]) {
                iOS4vs5 = YES;
            }else {
                iOS4vs5 = NO;
if(!_engine){
_engine = [[SA_OAuthTwitterEngine alloc] initOAuthWithDelegate:self];
_engine.consumerKey = kOAuthConsumerKey;
_engine.consumerSecret = kOAuthConsumerSecret;

}

if(![_engine isAuthorized]){
UIViewController *controller = [SA_OAuthTwitterController controllerToEnterCredentialsWithTwitterEngine:_engine delegate:self];

if (controller){
[appDelegate.viewController presentModalViewController: controller animated: YES];
}}
 } } } return self; } -(void)postTweed{ if (!iOS4vs5) {

 viewController = [[[UIViewController alloc]init]autorelease]; UIView *view = [[[UIView alloc]init]autorelease]; view.backgroundColor = [UIColor blueColor]; CGFloat width = [UIScreen mainScreen].bounds.size.width; UIImageView *imageView = [[[UIImageView alloc]init]autorelease]; imageView.frame = CGRectMake(90, 10, 200, 150); imageView.image = [UIImage imageNamed:@"logotwitter.png"]; [imageView sizeToFit]; [view addSubview:imageView]; twitterTextView = [[UITextView alloc]initWithFrame:CGRectMake(width *0.015625, 50, width *0.96875, 80)]; twitterTextView.delegate = self; twitterTextView.text = @"Your Text"; [view addSubview:twitterTextView]; UIButton *tweetButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; [tweetButton setTitle:@"tweet!" forState:UIControlStateNormal]; [tweetButton addTarget:self action:@selector(sendTweetWithString) forControlEvents:UIControlEventTouchDown]; tweetButton.frame = CGRectMake(width *0.78125f,125.0f, 331.0f, 32.0f); [tweetButton sizeToFit]; [view addSubview:tweetButton]; UIButton *cancelButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; [cancelButton setTitle:NSLocalizedString(@"Back", @"TwitterHelper post to Wall:Back!") forState:UIControlStateNormal]; [cancelButton addTarget:self action:@selector(cancelTweet) forControlEvents:UIControlEventTouchDown]; cancelButton.frame = CGRectMake(width*0.03125f,125.0f, 331.0f, 32.0f); [cancelButton sizeToFit]; [view addSubview:cancelButton]; viewController.view = view; [appDelegate.viewController presentModalViewController:viewController animated:YES]; }else if (iOS4vs5) { if ([TWTweetComposeViewController canSendTweet]) { TWTweetComposeViewController *tweetSheet = [[TWTweetComposeViewController alloc] init]; [tweetSheet setInitialText:@"Your text"]; [tweetSheet addImage:[UIImage imageNamed:@"Your picture"]]; [tweetSheet addURL:[NSURL URLWithString:@"your Link"]]; [appDelegate.viewController presentModalViewController:tweetSheet animated:YES]; } else { UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Sorry" message:@"You can't send a tweet right now, make sure your device has an internet connection and you have at least one Twitter account setup" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alertView show]; } } } - (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{ NSUInteger newLength = [textView.text length] + [text length] - range.length; return (newLength > 140) ? NO : YES; } -(void)sendTweetWithString{ NSLog(@"%@",twitterTextView.text); [_engine sendUpdate:twitterTextView.text]; [appDelegate.viewController dismissModalViewControllerAnimated:YES]; } -(void)cancelTweet{ [appDelegate.viewController dismissModalViewControllerAnimated:YES]; } #pragma mark SA_OAuthTwitterEngineDelegate - (void) storeCachedTwitterOAuthData: (NSString *) data forUsername: (NSString *) username { NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; [defaults setObject: data forKey: @"authData"]; [defaults synchronize]; } - (NSString *) cachedTwitterOAuthDataForUsername: (NSString *) username { return [[NSUserDefaults standardUserDefaults] objectForKey: @"authData"]; } #pragma mark TwitterEngineDelegate - (void) requestSucceeded: (NSString *) requestIdentifier { NSLog(@"Request %@ succeeded", requestIdentifier); } - (void) requestFailed: (NSString *) requestIdentifier withError: (NSError *) error { NSLog(@"Request %@ failed with error: %@", requestIdentifier, error); } #pragma mark dealloc -(void) dealloc { [twitterTextView release]; [_engine release]; [instanceOfTwitterHelper release]; instanceOfTwitterHelper = nil; [super dealloc]; } @end

So lets start with our .m file:

#import "SA_OAuthTwitterEngine.h"
#import "Common.h"

First we need to import SA_OAuthTwitterEngine.h.

I use a Common.h to store my Consumer Key and Consumer Secret like this:

#define kOAuthConsumerKey                @”Your Twitter Consumer Key
#define kOAuthConsumerSecret            @”Your
Twitter Consumer Secret

Next up lets declare some private Methods:

@interface TwitterHelper(PrivateMethods)

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text;
-(void)sendTweetWithString;
-(void)cancelTweet;

@end

The first Method textView is to check the length of the string to make sure it is below 140 signs. The others are obvious… :-)


Lets have a look at the implementation:

@implementation TwitterHelper
@synthesize appDelegate;

static TwitterHelper *instanceOfTwitterHelper;

#pragma mark Singleton
+(TwitterHelper*)sharedTwitterHelper
{
	@synchronized(self)
	{
		if (instanceOfTwitterHelper == nil)
		{
			[[TwitterHelper alloc] init];
		}
		
		return instanceOfTwitterHelper;
	}
	
	// to avoid compiler warning
	return nil;
}

+(id) alloc
{
	@synchronized(self)	
	{
		NSAssert(instanceOfTwitterHelper == nil, @"Attempted to allocate a second instance of the singleton: TwitterHelper");
		instanceOfTwitterHelper = [[super alloc] retain];
		return instanceOfTwitterHelper;
	}
	
	// to avoid compiler warning
	return nil;
}

After synthesizing the appdelegate variable we create a singleton instance, to make sure that there can’t be more then one TwitterHelper instance at the time.

Let’s have a look at the init Method, where most of the magic happens: ;-)

-(id)init{
    
    if ((self = [super init])) {
        
        NSString *deviceVersion = [UIDevice currentDevice].systemVersion;
        int deviceVersionInt = [deviceVersion intValue];
        
        NSLog(@"DeviceVersion: %i",deviceVersionInt);
        
        appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];

        if (deviceVersionInt < 5) {
            
            iOS4vs5 = NO;
            
            if(!_engine){  
                _engine = [[SA_OAuthTwitterEngine alloc] initOAuthWithDelegate:self];  
                _engine.consumerKey    = kOAuthConsumerKey;  
                _engine.consumerSecret = kOAuthConsumerSecret; 
                
            }  
            
            if(![_engine isAuthorized]){  
                UIViewController *controller = [SA_OAuthTwitterController controllerToEnterCredentialsWithTwitterEngine:_engine delegate:self];  
                
                if (controller){  
                    [appDelegate.viewController presentModalViewController: controller animated: YES];  
                }} 
            
        }else if ((deviceVersionInt == 5)||(deviceVersionInt>5)) {
            
            if ([TWTweetComposeViewController canSendTweet]) {
                iOS4vs5 = YES;
            }else {
                iOS4vs5 = NO;
            }
            
        }
                
        
    }
    
    return self;
}

First we create a string of the device version that is used:

 NSString *deviceVersion = [UIDevice currentDevice].systemVersion;

Next we create an int from that:

 int deviceVersionInt = [deviceVersion intValue];

Now we get our appdelegate instance:

appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];

Next we find out which IOs is on this device:

if (deviceVersionInt < 5) {
            
            iOS4vs5 = NO;
            
            if(!_engine){  
                _engine = [[SA_OAuthTwitterEngine alloc] initOAuthWithDelegate:self];  
                _engine.consumerKey    = kOAuthConsumerKey;  
                _engine.consumerSecret = kOAuthConsumerSecret; 
                
            }  
            
            if(![_engine isAuthorized]){  
                UIViewController *controller = [SA_OAuthTwitterController controllerToEnterCredentialsWithTwitterEngine:_engine delegate:self];  
                
                if (controller){  
                    [appDelegate.viewController presentModalViewController: controller animated: YES];  
                }} 
            
        }else if ((deviceVersionInt == 5)||(deviceVersionInt>5)) {
            
            if ([TWTweetComposeViewController canSendTweet]) {
                iOS4vs5 = YES;
            }else {
                iOS4vs5 = NO;
            }
            
        }

We set  iOS4vs5 = NO if the int is below 5 else we set  iOS4vs5 = YES.

In case the IOs is below 5 we initiate the Twitter Engine:

 if(!_engine){  
                _engine = [[SA_OAuthTwitterEngine alloc] initOAuthWithDelegate:self];  
                _engine.consumerKey    = kOAuthConsumerKey;  
                _engine.consumerSecret = kOAuthConsumerSecret; 
                
            }  
            
            if(![_engine isAuthorized]){  
                UIViewController *controller = [SA_OAuthTwitterController controllerToEnterCredentialsWithTwitterEngine:_engine delegate:self];  
                
                if (controller){  
                    [appDelegate.viewController presentModalViewController: controller animated: YES];  
                }} 

and autorize Twitter if necessary.

In case the device has iOs 5 or higher we check if we can use the Twitter.framework:

if ([TWTweetComposeViewController canSendTweet]) {
                iOS4vs5 = YES;
            }else {
                iOS4vs5 = NO;
if(!_engine){ _engine = [[SA_OAuthTwitterEngine alloc] initOAuthWithDelegate:self]; _engine.consumerKey = kOAuthConsumerKey; _engine.consumerSecret = kOAuthConsumerSecret; } if(![_engine isAuthorized]){ UIViewController *controller = [SA_OAuthTwitterController controllerToEnterCredentialsWithTwitterEngine:_engine delegate:self]; if (controller){ [appDelegate.viewController presentModalViewController: controller animated: YES]; }}

 }

If there is no account on the device we set up the iOs4 version…

So let’s have a look at the postTweet Method:

-(void)postTweed{
    
    if (!iOS4vs5) {

 viewController = [[[UIViewController alloc]init]autorelease]; UIView *view = [[[UIView alloc]init]autorelease]; view.backgroundColor = [UIColor blueColor]; CGFloat width = [UIScreen mainScreen].bounds.size.width; UIImageView *imageView = [[[UIImageView alloc]init]autorelease]; imageView.frame = CGRectMake(90, 10, 200, 150); imageView.image = [UIImage imageNamed:@"logotwitter.png"]; [imageView sizeToFit]; [view addSubview:imageView]; twitterTextView = [[UITextView alloc]initWithFrame:CGRectMake(width *0.015625, 50, width *0.96875, 80)]; twitterTextView.delegate = self; twitterTextView.text = @"Your Text"; [view addSubview:twitterTextView]; UIButton *tweetButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; [tweetButton setTitle:@"tweet!" forState:UIControlStateNormal]; [tweetButton addTarget:self action:@selector(sendTweetWithString) forControlEvents:UIControlEventTouchDown]; tweetButton.frame = CGRectMake(width *0.78125f,125.0f, 331.0f, 32.0f); [tweetButton sizeToFit]; [view addSubview:tweetButton]; UIButton *cancelButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; [cancelButton setTitle:NSLocalizedString(@"Back", @"TwitterHelper post to Wall:Back!") forState:UIControlStateNormal]; [cancelButton addTarget:self action:@selector(cancelTweet) forControlEvents:UIControlEventTouchDown]; cancelButton.frame = CGRectMake(width*0.03125f,125.0f, 331.0f, 32.0f); [cancelButton sizeToFit]; [view addSubview:cancelButton]; viewController.view = view; [appDelegate.viewController presentModalViewController:viewController animated:YES]; }else if (iOS4vs5) { if ([TWTweetComposeViewController canSendTweet]) { TWTweetComposeViewController *tweetSheet = [[TWTweetComposeViewController alloc] init]; [tweetSheet setInitialText:@"Your Text"]; [tweetSheet addImage:[UIImage imageNamed:@"Your Image"]]; [tweetSheet addURL:[NSURL URLWithString:@"Your link"]]; [appDelegate.viewController presentModalViewController:tweetSheet animated:YES]; } else { UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Sorry" message:@"You can't send a tweet right now, make sure your device has an internet connection and you have at least one Twitter account setup" delegate:self cancelButtonTitle:@"OK" otherButtonTitles:nil]; [alertView show]; } } }

This Method is separated in two different parts, first the IOs 4 part:

if (!iOS4vs5) {

 viewController = [[[UIViewController alloc]init]autorelease]; UIView *view = [[[UIView alloc]init]autorelease]; view.backgroundColor = [UIColor blueColor]; CGFloat width = [UIScreen mainScreen].bounds.size.width; UIImageView *imageView = [[[UIImageView alloc]init]autorelease]; imageView.frame = CGRectMake(90, 10, 200, 150); imageView.image = [UIImage imageNamed:@"logotwitter.png"]; [imageView sizeToFit]; [view addSubview:imageView]; twitterTextView = [[UITextView alloc]initWithFrame:CGRectMake(width *0.015625, 50, width *0.96875, 80)]; twitterTextView.delegate = self; twitterTextView.text = @"your text"; [view addSubview:twitterTextView]; UIButton *tweetButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; [tweetButton setTitle:@"tweet!" forState:UIControlStateNormal]; [tweetButton addTarget:self action:@selector(sendTweetWithString) forControlEvents:UIControlEventTouchDown]; tweetButton.frame = CGRectMake(width *0.78125f,125.0f, 331.0f, 32.0f); [tweetButton sizeToFit]; [view addSubview:tweetButton]; UIButton *cancelButton = [UIButton buttonWithType:UIButtonTypeRoundedRect]; [cancelButton setTitle:NSLocalizedString(@"Back", @"TwitterHelper post to Wall:Back!") forState:UIControlStateNormal]; [cancelButton addTarget:self action:@selector(cancelTweet) forControlEvents:UIControlEventTouchDown]; cancelButton.frame = CGRectMake(width*0.03125f,125.0f, 331.0f, 32.0f); [cancelButton sizeToFit]; [view addSubview:cancelButton]; viewController.view = view; [appDelegate.viewController presentModalViewController:viewController animated:YES]; }

As I did not want to make a Xib file for this I created a view controller within the Method:

        viewController = [[[UIViewController alloc]init]autorelease];
        UIView *view = [[[UIView alloc]init]autorelease];
        view.backgroundColor = [UIColor blueColor];

First we set up the viewController we declared in our .h file, next we declare a view. I gave this view the color blue, but you can change that to your liking.

Next up we get the screen dimesions of the actual device, that is important as the TwitterHelper Class is designed to work on Ipad as well as on Iphone:

CGFloat width = [UIScreen mainScreen].bounds.size.width;

Next we create an image view to display the twitter logo. You can get logos from the Twitter resources page.

        UIImageView *imageView = [[[UIImageView alloc]init]autorelease];
        imageView.frame = CGRectMake(90, 10, 200, 150);
        imageView.image = [UIImage imageNamed:@"logotwitter.png"];

        [imageView sizeToFit];
        [view addSubview:imageView];

And add this image view to our view we created before.

Next we create an UITextView:

        twitterTextView = [[UITextView alloc]initWithFrame:CGRectMake(width *0.015625, 50, width *0.96875, 80)];
        twitterTextView.delegate = self;
        twitterTextView.text = @"Your Text";

        [view addSubview:twitterTextView];

We use the width we got earlier to change the width according to the device the app is running on.

We set the delegate to self. In twitterTextView.text = @”Your Text” you can choose which text the user will be prompted to tweet… Then we add the text view to our view as well.

The next part is to create the necessary buttons Buttons:

UIButton *tweetButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
        [tweetButton setTitle:@"tweet!" forState:UIControlStateNormal];
        [tweetButton addTarget:self action:@selector(sendTweetWithString) forControlEvents:UIControlEventTouchDown];
        tweetButton.frame = CGRectMake(width *0.78125f,125.0f, 331.0f, 32.0f);
        [tweetButton sizeToFit];
        [view addSubview:tweetButton];
        
        UIButton *cancelButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
        [cancelButton setTitle:NSLocalizedString(@"Back", @"TwitterHelper post to Wall:Back!") forState:UIControlStateNormal];
        [cancelButton addTarget:self action:@selector(cancelTweet) forControlEvents:UIControlEventTouchDown];
        cancelButton.frame = CGRectMake(width*0.03125f,125.0f, 331.0f, 32.0f);
        [cancelButton sizeToFit];
        [view addSubview:cancelButton];
        

I use NSLocalizedString as it gives you the possibility to localize your app later…

And link the buttons to their Methods:

 [tweetButton addTarget:self action:@selector(sendTweetWithString) forControlEvents:UIControlEventTouchDown];
 [cancelButton addTarget:self action:@selector(cancelTweet) forControlEvents:UIControlEventTouchDown];

Next we set our view on the viewcontroller we declared earlier:

viewController.view = view;

At last we call the root controller an let him present our viewcontroller:

 [appDelegate.viewController presentModalViewController:viewController animated:YES];

This is specifically for cocos2d…

The second part of the postTweet Method is for I-Os 5 when there is a Twitter account on the device:

else if (iOS4vs5) {
        
        if ([TWTweetComposeViewController canSendTweet])
        {
            
            TWTweetComposeViewController *tweetSheet = 
            [[TWTweetComposeViewController alloc] init];
            [tweetSheet setInitialText:@"Your Text"];
            [tweetSheet addImage:[UIImage imageNamed:@"Your Image"]];
            [tweetSheet addURL:[NSURL URLWithString:@"Your link"]];
            [appDelegate.viewController presentModalViewController:tweetSheet animated:YES];
        }
        else
        {
            UIAlertView *alertView = [[UIAlertView alloc] 
                                      initWithTitle:@"Sorry"                                                             
                                      message:@"You can't send a tweet right now, make sure your device has an internet connection and you have at least one Twitter account setup"                                                          
                                      delegate:self                                              
                                      cancelButtonTitle:@"OK"                                                   
                                      otherButtonTitles:nil];
            [alertView show];
        }
    }

The first part is to tweet via the Twitter.framework:

 
        if ([TWTweetComposeViewController canSendTweet])
        {
            
            TWTweetComposeViewController *tweetSheet = 
            [[TWTweetComposeViewController alloc] init];
            [tweetSheet setInitialText:@"Your Text"];
            [tweetSheet addImage:[UIImage imageNamed:@"Your Image"]];
            [tweetSheet addURL:[NSURL URLWithString:@"Your link"]];
            [appDelegate.viewController presentModalViewController:tweetSheet animated:YES];
        }

First it is checked again if the app can send a tweet.

I just would like to mention the important parts:

            [tweetSheet setInitialText:@"Your Text"];
            [tweetSheet addImage:[UIImage imageNamed:@"Your Image"]];
            [tweetSheet addURL:[NSURL URLWithString:@"Your link"]];

You can select which text will be prompted, you can choose a picture to be shown and add a link…

After that we let the root view controller present the twitter view:

 [appDelegate.viewController presentModalViewController:tweetSheet animated:YES];

The second part:

else
        {
            UIAlertView *alertView = [[UIAlertView alloc] 
                                      initWithTitle:@"Sorry"                                                             
                                      message:@"You can't send a tweet right now, make sure your device has an internet connection and you have at least one Twitter account setup"                                                          
                                      delegate:self                                              
                                      cancelButtonTitle:@"OK"                                                   
                                      otherButtonTitles:nil];
            [alertView show];
        }

is just to make sure if anything gets wrong the user gets a feedback…

The next method is to make sure that in our IOs 4 version the user cannot type more than 140 signs:

- (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text{
    
    NSUInteger newLength = [textView.text length] + [text length] - range.length;
    return (newLength > 140) ? NO : YES;
    
}

The next two methods are for the IOs4 version as well:

-(void)sendTweetWithString{
    
    NSLog(@"%@",twitterTextView.text);
    [_engine sendUpdate:twitterTextView.text];
    [appDelegate.viewController dismissModalViewControllerAnimated:YES];
}

-(void)cancelTweet{
    
    
    [appDelegate.viewController dismissModalViewControllerAnimated:YES];
}

The sendTweetWithString obviously sends the text in the twitterTextView. The cancelTweet is the possibility for the user to cancel the operation.

The rest of the Methods is the integration of the SA_OAuthTwitterEngineDelegate:

#pragma mark SA_OAuthTwitterEngineDelegate 
- (void) storeCachedTwitterOAuthData: (NSString *) data forUsername: (NSString *) username {  
    NSUserDefaults          *defaults = [NSUserDefaults standardUserDefaults];  
    
    [defaults setObject: data forKey: @"authData"];  
    [defaults synchronize];  
}  

- (NSString *) cachedTwitterOAuthDataForUsername: (NSString *) username {  
    return [[NSUserDefaults standardUserDefaults] objectForKey: @"authData"];  
}  


#pragma mark TwitterEngineDelegate 
- (void) requestSucceeded: (NSString *) requestIdentifier {  
    NSLog(@"Request %@ succeeded", requestIdentifier);  
}  

- (void) requestFailed: (NSString *) requestIdentifier withError: (NSError *) error {  
    NSLog(@"Request %@ failed with error: %@", requestIdentifier, error);  
}  

And last but not least we clean the memory in our dealloc:

#pragma mark dealloc
-(void) dealloc
{

    [twitterTextView release];
    [_engine release];
	[instanceOfTwitterHelper release];
	instanceOfTwitterHelper = nil;
	
	[super dealloc];
}

For the fallback I used Ben Gottlieb’s Twitterengine (Thank you Ben), it includes Matt Gemmell's MGTwitterEngine (Thank you Matt), the OAuthConsumer Framework by Jon Crosby (Thanks Jon) and OAuth-MyTwitter by Chris Kimpton (Thanks Chris). And for the IOS 5 support I orientated myself on a tutorial by Mark Hammonds and a tutorial by Felipe Laso on Ray Wenderlich’ s site. I used some ideas from Steffen Itterheim’s Gamecenter Helper class.

Ok that was a really long post… again… I would appreciate your feedback and comments. If something is wrong or you have a question or an addition please tell me.

Next time I plan to go a bit into localization, if you have topics you would like to hear about, I’ll try my best…

I hope you enjoy this post and it helps you.

—————————-

Update: After the comment of Fenix I realized that I missed to mention that you need to add the Twitter-Framework as weak (optional). Thank you Fenix.

How to create a Facebook Wrapper class for cocos2d? (I-OS)

As I am working on my new App, I was trying to  implement Facebook, so the users can post on their wall, and as I had some trouble I thought I’ll make a post on how to do that.

My target was to get as much of the Facebook implementation in one Class as I could get… I called it FacebookHelper Class like the GamkitHelper Class Steffen Itterheim created in his book Learn cocos2d Game Development with iOS 5. I tried to design it as a semi singleton.

So first things first :-) I used a lot of the Facebook Tutorial especially the first few steps:

1. Set up your app on Facebook.

2. Download the Facebook SDK and put the necessary files into your Xcode project. as well as changing the info.plist!

The necessary steps are on the Facebook developer site!

I would like to focus on creating a Facebook Helper Class:

As first step I created the FacebookHelper.h and FacebookHelper.m as an NSObject.

Let’s have a look at the FacebookHelper.h:

#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import “FBConnect.h”
#import “Common.h”

@interface FacebookHelper : NSObject<UIApplicationDelegate,FBSessionDelegate,FBDialogDelegate>{

    Facebook *facebook;

}

@property (nonatomic, retain) Facebook *facebook;

+(id)alloc;
+(FacebookHelper*) sharedFacebookHelper;
-(void)postToWall;

@end

Import UIKit and FBConnect.h in my Common.h I store the Facebook Keys:

#define kFacebookAppId @”your App ID”
#define kFacebookAppSecret @”Your App Secret”

Add the following delegates <UIApplicationDelegate,FBSessionDelegate,FBDialogDelegate> which are needed later on.

The next step is to create an instance of Facebook including a property.

As I tried to design the class as a semi singleton you need to implement the following methods:

+(id)alloc;
+(FacebookHelper*) sharedFacebookHelper;

And to post on the Wall add the -(void)postToWall; as well.

The second part is the FacebookHelper.m:

#import "FacebookHelper.h"

@implementation FacebookHelper
@synthesize facebook;

static FacebookHelper *instanceOfFacebookHelper;

#pragma mark Singleton
+(FacebookHelper*) sharedFacebookHelper
{
    @synchronized(self)
    {
        if (instanceOfFacebookHelper == nil)
        {
            [[FacebookHelper alloc] init];
        }

        return instanceOfFacebookHelper;
    }

    // to avoid compiler warning
    return nil;
}

+(id) alloc
{
    @synchronized(self)   
    {
        NSAssert(instanceOfFacebookHelper == nil, @"Attempted to allocate a second instance of the singleton: FacebookHelper");
        instanceOfFacebookHelper = [[super alloc] retain];
        return instanceOfFacebookHelper;
    }

    // to avoid compiler warning
    return nil;
}

#pragma mark init

-(id)init {

    if ((self = [super init])) {

        facebook = [[Facebook alloc] initWithAppId:kFacebookAppId andDelegate:self];


        NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
        if ([defaults objectForKey:@"FBAccessTokenKey"]
            && [defaults objectForKey:@"FBExpirationDateKey"]) {
            facebook.accessToken = [defaults objectForKey:@"FBAccessTokenKey"];
            facebook.expirationDate = [defaults objectForKey:@"FBExpirationDateKey"];
        }

        NSLog(@"%@",facebook.isSessionValid? @"YES" : @"NO");

        if (![facebook isSessionValid]) {

  [facebook authorize:nil]; } } return self; } #pragma mark - FBSessionDelegate Methods - (void)fbDidLogin { NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; [defaults setObject:[facebook accessToken] forKey:@"FBAccessTokenKey"]; [defaults setObject:[facebook expirationDate] forKey:@"FBExpirationDateKey"]; [defaults synchronize]; NSLog(@"Did Log in!"); [self postToWall]; } - (void)fbDidNotLogin:(BOOL)cancelled{ NSLog(@"No Log in!"); } - (void)fbDidLogout{ NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; [defaults removeObjectForKey:@"FBAccessTokenKey"]; [defaults removeObjectForKey:@"FBExpirationDateKey"]; [defaults synchronize]; } - (void)fbDidExtendToken:(NSString*)accessToken expiresAt:(NSDate*)expiresAt{ NSLog(@"token extended"); [self storeAuthData:accessToken expiresAt:expiresAt]; } - (void)fbSessionInvalidated{ UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:@"Auth Exception" message:@"Your session has expired." delegate:nil cancelButtonTitle:@"OK" otherButtonTitles:nil, nil]; [alertView show]; [alertView release]; [self fbDidLogout]; } - (void)storeAuthData:(NSString *)accessToken expiresAt:(NSDate *)expiresAt { NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; [defaults setObject:accessToken forKey:@"FBAccessTokenKey"]; [defaults setObject:expiresAt forKey:@"FBExpirationDateKey"]; [defaults synchronize]; } -(void)postToWall{ NSMutableDictionary *params = [NSMutableDictionary dictionaryWithObjectsAndKeys: kFacebookAppId, @"app_id", @"http:// www. yourwebsite. com", @"link", @"http:// www. yourpicture. com/picture.png", @"picture", @"Your Title!", @"name", @"Your caption!", @"caption", @"Your description", @"description", nil]; [facebook dialog:@"feed" andParams:params andDelegate:self]; } #pragma mark dealloc -(void) dealloc { [facebook release]; [instanceOfFacebookHelper release]; instanceOfFacebookHelper = nil; [super dealloc]; } @end

Ok, I will go through the methods one by one and describe what each one does:

Don’t forget to @synthesize facebook…

First I create a static instance of FacebookHelper. This instance will be returned to the calling class.

The +(FacebookHelper*) sharedFacebookHelper  and +(id) alloc methods make sure only one Instance of FaceBookHelper is used at all times.

In the init method the facebook variable is initilized with the app id and self as delegate.

Furthermore the access token and the expiry date is accessed from the user defaults:

NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
        if ([defaults objectForKey:@”FBAccessTokenKey”]
            && [defaults objectForKey:@”FBExpirationDateKey”]) {
            facebook.accessToken = [defaults objectForKey:@”FBAccessTokenKey”];
            facebook.expirationDate = [defaults objectForKey:@”FBExpirationDateKey”];
        }

And it is checked if the session is valid. If not facebook is authorized:

if (![facebook isSessionValid]) {

            [facebook authorize:nil]; //here is the possibility to ask for more permissions!
        }

The next few methods are delegate methods for FBConnect:

The fbDidLogin method stores the access token and the expiry date, which can be used to access Facebook next time without extra authorization. In my case I added a call to post  here to make it easier for me.

The next one fbDidNotLogin gives you feedback if the user cancelled.

The fbDidLogout handles if the user logs out and removes the access token.

The fbDidExtendToken:(NSString*)accessToken expiresAt:(NSDate*)expiresAt method extends the access token.

The fbSessionInvalidated gives the user feedback if the session was invalidated for some reason.

The storeAuthData method is used to save the extension of the access token.

In the postToWall you can give the information you want to post with the users comment, you can localize the with NSLocalizedString.

The last step is to #import “FacebookHelper.h” in the class you want to call Facebook from.

You can call it like that:

FacebookHelper *facebookHelper = [FacebookHelper sharedFacebookHelper];
    NSLog(@”%@”,facebookHelper.description);
   
    if (facebookHelper.facebook.isSessionValid) {
       
        [facebookHelper saveScore:scoreVal];
       
        [facebookHelper postToWall];
       
    }

Ok, that was a long post, I hope it helps you to implement Facebook in your App. As you see you can expand the class to your liking… and add more features. As this is my first tutorial, I would appreciate some feedback and would be happy to add your suggestions…

Next time I plan to make a tutorial of my Twitter Helper class with I-OS 5 support and I-OS 4 fallback. So stay tuned…

How I created my Graphics!

When I started out to create BeeJuice, I planned to do some fancy 3d graphics.  I started to design my bee in blender, as it is a really good and free 3d software, but when I was ready, I found that I liked a 2d combination of photos and comic style graphics more appealing.

I used photos I made from grass and scaled them to 1024 × 764, using Gimp. Now I created a lot of my scene objects like the bee and my flowers etc.. To create them I used Inkscape, which is a fantastic open source vector based graphic software.

For texture atlases I used Zwoptex as it was the most known at the time.

For the starters, a texture atlas is a combination of a png file with all your sprites on it and a plist file which contains the name, the coordinates and additional infos of your sprite within the png.

Why to use a texture atlas? In short it helps you safe memory while your app is running.

For my particle design I used Particle Designer which is brilliant for creating particle emitter visually, as you have thousands of different possibilities to program them it makes life so much easier to see strait away what it looks like.

For my bitmap fonts I used Glyph Designer.

What do I need bitmap fonts for?

As the process of rendering TTF fonts is pretty high and can affect your frame rate, a bitmap font is pre rendered. So it is much easier for OpenGL ES to push the images to the screen.

I used a lot of open source software, as they are usually fulfilled my needs, and help to keep the budget small.

Along the way of creating my first app!

After finishing the books mentioned in my last article, I had found a book called Beginning IPhone Games Development. Now for me this book was helpful to understand some of the basic concepts of OpenGL-ES, but I would not fully recommend it! It is some sort of additional source, because, for one thing there are several chapters in it which are just useless, especially chapter 4… The other thing is, that it might give you the feeling that you can achieve a lot more very quick when designing your own framework. This was at least the case with me. It took me about two months to realise that you can achieve your goal a lot quicker, by using a framework like cocos2d etc.. Still I put a link to the book into this article just so you can have a look at it.

I really understood what I wanted to do, when I tried to move the window along with my bee… (Here the link to the free BeeJuice Lite in case you haven’t tried it yet!). I tried to get the “camera” to move along with my bee for at least two weeks, it just did not work as I wished it… In the end I thought, why should I, a programming newbie, reinvent the wheel? I checked out what frameworks there are. (Unity, corona, cocos2d etc.)

In the end I decided to go with cocos2d, mainly because it is open source. I found the book Learn iPhone and iPad cocos2d Game Development by Steffen Itterheim. This book is just absolutely brilliant! I achieved what I had done the four months before in one month, a huge part of that success came from the book itself, because Steffen explains the basics of cocos2d really brilliant, in my opinion…  The second part was the fantastic support through the cocos2d community! I would like to mention the two forums I used, one is Steffen’s cocos2d central the other one is the fantastic official cocos2d forum. Both did really help and have a very helpful and supporting community! I would like to post the links to the book I used and the all new Editions…

Next time I plan to go into the tools I used. I would love to get some feedback, so send a message or comment…

Which books came next?

Continuing my last post I would like to tell you about the next two books I read , to learn programming, especially IPhone programming. I would never say I am a crack… But slowly I seem to get somewhere…

So to get some basics I got “Learning Objective C on the Mac” by Mark Dalrymple and Scott Knaster:

It is a bit old by now, but it is simply perfect to show you the basics of Objective C. And every time I am stuck again I still use it. It helps to understand the basic concepts of Objective C and even if you plan using a framework like cocos2d, it still is really good to get the programming concepts.

The second book I would like to recommend today is “Beginning iPhone 5 Development: Exploring the iOS 5 SDK” from David Mark, Jack Nutting, Jeff LaMarche

I used an older version, but it was and is just brilliant. I really love it.

It helped me to understand some of the most important concepts of App development.

In my next post I plan to tell you how I came to develop with cocos2d and some of my experience along the way.

Which books did I use?

Before my first App I had never programmed anything. After my last job ended I decided to give it a try. As I had no programming experience, I had to learn everything from the start. As I had decided to start with IPhone programming I had to learn C first.

In the beginning I thought I would program my own framework, I soon decided against that, but I will come to that in a later post.

To learn C I bought a book called “Learn C on the Mac” by Dave Mark.

And today I would like to recommend it to any developer, who wants to start with C on a Mac. It is really brilliant, as the author is explaining the absolute basics of C programming in terms, that even a total newbie like me understands :-)

The book gives you the basic understanding of C programming language which you need to advance to Objective C.

Objective C is a superset of C , you need this language to program for the IPhone. In my next post I will talk about the books I used to learn Objective C and I-OS programming.

What I plan to post here…

I plan to tell you about my current projects like BeeJuice and my new projects coming up!

My first project was BeeJuice for I-OS I had never programmed before, so I had to learn first how to do it… Not that I am a master by now, but its getting better :-) In my next posts I will tell you about the sources I used, which books I bought and which of them helped me a lot.

But first things first, I’d like to tell you a bit of my first app:

The idea is that you fly with a bee through a maze of carnivorous plans, to design it I used cocos2d.

Here are some screen shots of it:

MainMenu

Level 1

Level 2

Poison

Wasp

here is the link to the game website: BeeJuice Game site

here is the link to BeeJuice on the Appstore: BeeJuice Appstore

here is the link to the Lite Version website: BeeJuice Lite

here is the link to BeeJuice Lite on the Appstore: BeeJuice Lite AppStore