Category Archives: Developer Stuff

Awesome Cowbell 2 and in-app Purchase

My friend Carmine and I made an app a few years ago called Awesome Cowbell. We didn’t update anything for a few years and I decided to add in-app purchase to it instead of having a free and paid version on the App Store.

The app wasn’t updated for a while so there was a bunch of iOS 9 related things I had to fix. I created a new project (required iOS 9), fixed the bugs, added in-app purchase, and used storyboards this time instead of the xib I used years ago. It was ready to submit.

Only 4 days after I submitted the app was In Review. After a few hours in review it got rejected because I only implemented a way to use in-app purchase to Buy, not to Restore. I had the code for restoring done already but I didn’t want to add another button to the app just for this. The rejection came on a Thursday so I figured I would fix it on Saturday morning before the Islanders game at 3 (LETS GO ISLANDERS!!)

When I sat down to add the restore function I still didn’t want to add a button for restoring. I decided to use a UIAlertController for this. I just wanted three options (Buy, Restore, Cancel), that’s all I think I need to do to get the app approved. Here is the code I used to do this:

- (void)tapsBuy{
NSLog(@"User requests to Unlock");
UIAlertController * alert = [UIAlertController alertControllerWithTitle:@"Purchase" message:@"Do you want to Restore a previous purchase or Buy it for the first time?" preferredStyle:UIAlertControllerStyleAlert];

UIAlertAction *restoreAction = [UIAlertAction
actionWithTitle:NSLocalizedString(@"Restore", @"Restore Action")
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action)
{
[self restore];
}];

UIAlertAction *okAction = [UIAlertAction
actionWithTitle:NSLocalizedString(@"Buy", @"Buy action")
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action)
{
[self purchase:validProduct];
}];
UIAlertAction *cancelAction = [UIAlertAction
actionWithTitle:NSLocalizedString(@"Cancel", @"Cancel Action")
style:UIAlertActionStyleCancel
handler:^(UIAlertAction *action)
{
}];

[alert addAction:cancelAction];
[alert addAction:okAction];
[alert addAction:restoreAction];
[self presentViewController:alert animated:YES completion:nil];
}

That function is linked to a UIButton that starts the whole buying process. Here is the code I use for in-app purchase and restoring:

- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response{
validProduct = nil;
NSUInteger count = [response.products count];
if(count > 0){
validProduct = [response.products objectAtIndex:0];
NSLog(@"Products Available!");
}
else if(!validProduct){
NSLog(@"No products available");
//this is called if your product id is not valid, this shouldn't be called unless that happens.
}
}

- (IBAction)purchase:(SKProduct *)product{
SKPayment *payment = [SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}

- (IBAction) restore{
//this is called when the user restores purchases, you should hook this up to a button
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
[[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
isUnlocked = YES;
[[NSUserDefaults standardUserDefaults] setBool:isUnlocked forKey:@"isUnlocked"];
[[NSUserDefaults standardUserDefaults] synchronize];
}

- (void) paymentQueueRestoreCompletedTransactionsFinished:(SKPaymentQueue *)queue
{
NSLog(@"received restored transactions: %lu", (unsigned long)queue.transactions.count);
for (SKPaymentTransaction *transaction in queue.transactions)
{
if(SKPaymentTransactionStateRestored){
NSLog(@"Transaction state -> Restored");
//called when the user successfully restores a purchase
[self doUnlockAll];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
}

}

}

- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions{
for(SKPaymentTransaction *transaction in transactions){
switch (transaction.transactionState){
case SKPaymentTransactionStatePurchasing: NSLog(@"Transaction state -> Purchasing");
//called when the user is in the process of purchasing, do not add any of your own code here.
break;
case SKPaymentTransactionStatePurchased:
//this is called when the user has successfully purchased the package (Cha-Ching!)
[self doUnlockAll]; //you can add your code for what you want to happen when the user buys the purchase here, for this tutorial we use removing ads
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
NSLog(@"Transaction state -> Purchased");
break;
case SKPaymentTransactionStateRestored:
NSLog(@"Transaction state -> Restored");
//add the same code as you did from SKPaymentTransactionStatePurchased here
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
[_buyButton setTitle:@"Unlocked" forState:UIControlStateNormal];
_buyButton.enabled = NO;
break;
case SKPaymentTransactionStateDeferred:
break;
case SKPaymentTransactionStateFailed:
//called when the transaction does not finnish
if(transaction.error.code != SKErrorPaymentCancelled){
NSLog(@"Transaction state -> Cancelled");
//the user cancelled the payment ;(
}
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
break;
}
}
}

If you are still reading this at this point you might have noticed that I call a function doUnlockAll after the user buys or restores. Here is that code:

- (void)doUnlockAll{
isUnlocked = YES;
[[NSUserDefaults standardUserDefaults] setBool:isUnlocked forKey:@"isUnlocked"];
[[NSUserDefaults standardUserDefaults] synchronize];
_buyButton.enabled = NO;
_buyButton.hidden = YES;
volLabel.hidden = NO;
panLabel.hidden = NO;
pitchLabel.hidden = NO;
volSlider.hidden = NO;
panSlider.hidden = NO;
pitchSlider.hidden = NO;
self.admobBannerView.hidden = YES;
}

This is where everything is done when the purchase was made. First I set a BOOL to YES and store it in NSUserDefault (which I call in the viewDidLoad). Then I disabled the buy button and hid it and made the other buttons and sliders visible which are included when you buy the app. I also use Google Ad Mob (I use to use iAds but thats going away in June 2016) so I disabled that after the in-app purchase is made as well.

I have no idea if this is the best way to implement the buy/restore in-app purchases but it’s the way I chose to do it. Get Awesome Cowbell, it’s FREE to download! Follow me on Twitter @jon01 and let me know what you think.

Accelerometer, Sprite Kit, and Swift

So when iOS 7 was released I re-did my tutorial on using the Accelerometer with Sprite Kit instead of cocos2d. Now, in the fall, when iOS 8 is released there a whole new programming language for us to learn called “Swift“. There are plenty of articles out there about Swift, so I won’t go into it, i’ll stick to the programming and assume you know the basics. Apple released two iBooks on using Swift that are definitely recommended, “The Swift Programming Language” and “Using Swift with Cocoa and Objective-C

First, you have to make sure you have the latest Xcode 6 beta and running iOS 8 on one of your devices. I do not recommend using your everyday device for the betas since they are in fact betas. My iPad Air has beta 2 on it and the battery lasts 24 hrs and storage is always at 0, it made testing this code fun 🙂 Anyway, here is what I did. This may not be the best way to do this but it works.

Start Xcode 6 beta goto File > New > Project, make sure iOS > Application > Game is selected as your template. In GameScene.swift add this line import CoreMotion. You don’t need to #define things anymore, in Swift, you just use the let command so add these lines after your import:
let kPlayerSpeed = 250
let screenSize: CGRect = UIScreen.mainScreen().bounds

Delete everything after override func didMoveToView(view: SKView) { (make sure you keep the appropriate } though) and add the following to set up your sprite:
let monkey = SKSpriteNode(imageNamed:"monkey_arms_up")
monkey.position = CGPointMake(screenSize.width/2, screenSize.height/2)
self.addChild(monkey)

Pretty easy so far right? This last chunk of code was converted from my previous tutorial:
let motionManager: CMMotionManager = CMMotionManager()
if (motionManager.accelerometerAvailable) {
motionManager.startAccelerometerUpdatesToQueue(NSOperationQueue()) {
(data, error) in
let currentX = monkey.position.x
let currentY = monkey.position.y
if(data.acceleration.y < -0.25) { // tilting the device to the right var destX = (CGFloat(data.acceleration.y) * CGFloat(kPlayerSpeed) + CGFloat(currentX)) var destY = CGFloat(currentY) motionManager.accelerometerActive == true; let action = SKAction.moveTo(CGPointMake(destX, destY), duration: 1) monkey.runAction(action) } else if (data.acceleration.y > 0.25) { // tilting the device to the left
var destX = (CGFloat(data.acceleration.y) * CGFloat(kPlayerSpeed) + CGFloat(currentX))
var destY = CGFloat(currentY)
motionManager.accelerometerActive == true;
let action = SKAction.moveTo(CGPointMake(destX, destY), duration: 1)
monkey.runAction(action)
}
}

Build and run on your device and you should be set. Tilt your device to move the monkey left and right. Let me know via Twitter if this works for you! You can get all of the code on github.

Accelerometer and Sprite Kit

Hi again everyone. Since Apple released iOS 7 I figured it was time to do another quick tutorial on the Accelerometer and Sprite Kit. Sprite Kit is VERY similar to Cocos2d for iPhone. I was able to take my last tutorial, Accelerometer and Cocos2d, and port it over pretty quickly.

First start with a new project and select “SpriteKit Game” as the template. You now have a working project that will show display “Hello World” on a black background. Open the ViewController.m file and change the skView.showsFPS and skView.showsNodeCount to NO.

Then, Go to the MyScene.m file. Delete everything inside of the brackets after the first if statement in the initWithSize method so it looks like this:

-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {

}
return self;
}

Next, delete the entire touchesBegan method since we won’t need it at all in this tutorial. Now we have a blank project to work with.

Open MyScene.h and add a SKSpriteNode: SKSpriteNode *monkey;. Then open MyScene.m and this to the initWithSize method from above to add the monkey (or any sprite) to the scene:
monkey = [SKSpriteNode spriteNodeWithImageNamed:@"monkey_arms_up.png"];
monkey.position = CGPointMake(self.frame.size.width/2, self.frame.size.height/2);
[self addChild:monkey];

Add this line to make the background white so it’s easier to see:
self.backgroundColor = [SKColor colorWithRed:1.0 green:1.0 blue:1.0 alpha:1.0];

If you build and run now, you should have the monkey in the middle of the screen with a white background. To move the monkey left and right its pretty simple, in fact, it’s almost exactly how it was in cocos2d! First add the CoreMotion framework to your app and #import in your MyScene.h file. Also add CMMotionManager *motionManager; to your .h file. Back in the .m file, add this #define kPlayerSpeed 250 at the top under the implementation line. Then add this in the initWithSize method under where we set up the monkey before.


motionManager = [[CMMotionManager alloc] init];
if ([motionManager isAccelerometerAvailable] == YES) {
[motionManager startAccelerometerUpdatesToQueue:[[NSOperationQueue alloc] init]
withHandler:^(CMAccelerometerData *data, NSError *error)
{
float destX, destY;
float currentX = monkey.position.x;
float currentY = monkey.position.y;
BOOL shouldMove = NO;

if(data.acceleration.y < -0.25) { // tilting the device to the right destX = currentX + (data.acceleration.y * kPlayerSpeed); destY = currentY; shouldMove = YES; } else if (data.acceleration.y > 0.25) { // tilting the device to the left
destX = currentX + (data.acceleration.y * kPlayerSpeed);
destY = currentY;
shouldMove = YES;
}
if(shouldMove) {
SKAction *action = [SKAction moveTo:CGPointMake(destX, destY) duration:1];
[monkey runAction:action];
}
}];
}

Hope this was easy to follow, let me know if there are any errors in the code on twitter @jon01. All of the code can be downloaded from my github.

Accelerometer and cocos2d v2.x

I decided to mess around with the accelerometer a little using cocos2d. I started a new project in Xcode using the basic cocos2d template. I dragged over a few images from Vicki Wenderlich’s web site into my Resources folder and I’m ready to go.

In the AppDelegate.m file change [director_ setDisplayStats:YES]; to [director_ setDisplayStats:NO]; to get rid of the frame rate and all of those stats in the simulator. I also changed the top line from #import "IntroLayer.h" to #import HelloWorldLayer.h then I changed the line [director runWithScene: [IntroLayer scene]]; to [director runWithScene: [HelloWorldLayer scene]];. This makes the app go right to the HelloWorldLayer instead of going to the IntroLayer first.

Ok, in HelloWorldLayer.h change the @interface line to say @interface HelloWorldLayer : CCLayer {. Then add these two lines:
CCSprite *background;
CCSprite *monkey;

Then in the HelloWorldLayer.m delete everything from the init after the line if( (self=[super init]) ) { and add this to setup a background:
background = [CCSprite spriteWithFile:@"bg-jungle.png"];
background.anchorPoint = ccp(0,0);
[self addChild:background];

Build and Run and you should have a background image with no stats and no default stuff (hopefully) that the cocos2d template does for you.

Ok now what? This was all pretty simple so far. First we need to add a line to the init to enable the accelerometer:
self.AccelerometerEnabled = YES;

Ok that was easy. Add the monkey to your project now using this code:
-(void) monkey {
CGSize winSize = [[CCDirector sharedDirector] winSize];
monkey = [CCSprite spriteWithFile:@"monkey_arms_up.png"];
monkey.position = ccp(winSize.width/2, monkey.contentSize.height/2);
monkey.tag = 2;
[self addChild: monkey];
}

Make sure you add [self monkey]; to your init. Build and Run and you should have a monkey at the bottom of the screen in the middle.

Do you see the line above that says monkey.tag = 2;? In the +(CCScene *) scene method above, add this before the return scene;:
layer.tag = 1;

These tags are used later in the accelerometer method. In fact, let’s add that code now!

#define kPlayerSpeed 20
#define kHeroMovementAction 1
-(void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration
{
HelloWorldLayer *layer = (HelloWorldLayer *)[[[CCDirector sharedDirector] runningScene] getChildByTag:1];
CCSprite *player = (CCSprite *)[layer getChildByTag:2];

float destX, destY;
float currentX = player.position.x;
float currentY = player.position.y;
BOOL shouldMove = NO;

if(acceleration.y < -0.25) { destX = currentX + (acceleration.y * kPlayerSpeed); destY = currentY; shouldMove = YES; } else if (acceleration.y > 0.25) {
destX = currentX + (acceleration.y * kPlayerSpeed);
destY = currentY;
shouldMove = YES;
}

if(shouldMove) {
CGSize wins = [[CCDirector sharedDirector] winSize];
if(destX < 30) { id move = [CCMoveBy actionWithDuration:3 position:ccp(10,0)]; id action = [CCEaseBackOut actionWithAction:move]; [monkey runAction: action]; } else if (destX > wins.width - 30) {
id move = [CCMoveBy actionWithDuration:3 position:ccp(-10,0)];
id action = [CCEaseBackOut actionWithAction:move];
[monkey runAction: action];
} else {
CCAction *action = [CCMoveTo actionWithDuration:1 position: CGPointMake(destX, destY)];
[action setTag:kHeroMovementAction];
[player runAction:action];
}
} else {
[player stopActionByTag:kHeroMovementAction];
}
}

This code will make the monkey move right or left. Connect your iOS device then build and run to use it. The accelerometer does not work in the simulator. Most of this code was taken from the official cocos2d site where they showed how to use the accelerometer. I added a way to make the monkey bounce off the sides when he reaches the end so he doesn’t go off the screen.

Contact me via twitter at @jon01 if you have any questions! You can download the whole project from Github.

Indie Devs

So I just released a FREE version of my app iLearn Math. Now I need downloads and ratings. I was thinking, there are so many indie devs out there and they are all on twitter, hmmmm.

I recently joined the IDRTG in hopes of improving sales, but that hasnt done a lot for me yet. What if we posted our apps on twitter with a hashtag, then every dev then does the download, rate (5 stars with or without a written review), delete (or DRD). Tag the post as #DRD and check that hashtag whenever you can. Obviously don’t delete the app if its something you would use or even share with others 🙂

What do you think? Contact me on twitter @jon01. I also made a Facebook group called Indie Devs, UNITE feel free to join and post your app!

I’ll do it if you do it!

UPDATE: It seems the facebook group is doing really well. Please join it and share your app! Indie Devs, UNITE