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.

App Santa (2015)

Get them while you can! These deals are only good from today till Christmas.

The apps are:

My New App – Barcode Saver

Scan. Save. Repeat.

Do you want to store your Serial Numbers but hate copying down the numbers? Or maybe you want to just save the URL from a QR Code. Work in a warehouse and need to share bar code information with vendors from your stock? Scan the barcode, Save the text and you are all done! When you are done, open the saved text and Share it via Email, Text, or using the iOS 8 Extensions you have. In the free version of the app, you can display your last 5 scans. You can unlock all of the features of the app via in app purchase including:

Multiple barcode scans
Retake barcode
Unlimited rows
Edit existing text
And More!

I am going to add sync to dropbox/google drive/one drive in the future too. Download it now, it’s FREE to use.

Follow me on Twitter @barcodesaver

App Santa (2014)

Here are the 2014 App Santa deals, up to 60% off!

  • Calendars 5 for iOS $6.99 > $2.99
  • Castro for iOS $3.99 > $1.99
  • Clear for iOS $4.99 > $2.99 and Mac $9.99 > $5.99
  • ColorStrokes $1.99 > $0.99
  • Day One for iOS $4.99 > $0.99 and Mac $9.99 > $7.99
  • Deliveries for iOS $4.99 > $2.99 and Mac $4.99 > $3.99
  • Drafts 4 $9.99 > $4.99
  • Ember for iOS $4.99 > $2.99
  • FX Photo Studio $2.99 > $0.99
  • Gneo $9.99 > $5.99
  • Go Couch to 5K $4.99 > $1.99
  • Group Text+ $2.99 > $1.99
  • Launch Center Pro $4.99 > $0.99
  • Manual $1.99 > $0.99
  • Mileage Log+ $9.99 > $4.99
  • MindNode for iOS $9.99 > $4.99 and Mac $19.99 > $9.99
  • PCalc for iOS and Mac $9.99 > $6.99
  • PDF Expert 5 $9.99 > $6.99
  • Printer Pro $6.99 > $2.99
  • Scanner Pro $6.99 > $2.99
  • Screens for iOS $19.99 > $9.99 and Mac $29.99 > $14.99
  • Solar Walk $2.99 > $0.99
  • Star Walk HD $2.99 > $0.99
  • Terminology $2.99 > $1.99
  • TextExpander touch $4.99 > $2.99
  • Tweetbot 3 for iPhone $4.99 > $2.99
  • Windy $2.99 > $1.99
  • Workflow

    I love this app. I post a lot of links on my @dailyappios twitter account (please follow) using my affiliate token (from phg). So I made a Workflow that you can launch from the share sheet that comes up when you copy a link in tweetbot for example. The workflow strips out all the extra stuff from the link, adds your affil token and then asks you for a campaign name (&ct=) to add. You are left with a link with your affiliate token and a campaign name to help you track it. This link gets copied to your clipboard but feel free to change it to open twitter or whatever 🙂

    You will have to go in to the workflow app and change “AFFILTOKEN” in the text field to your affil token. Let me know how it works for you!

    Affil Token Workflow

    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.

    App Santa! (2013)

    There are some great deals out there on some very popular apps!

    • Tweetbot 3: $4.99 > $1.99
    • 1Password 4: $17.99 > $9.99
    • Day One: $4.99 > $2.99
    • Vesper: $4.99 > $2.99
    • PCalc: $9.99 > $6.99
    • Delivery Status: $4.99 > $2.99
    • Printer Pro: $6.99 > $2.99
    • Mileage Log+: $9.99 > $4.99
    • Perfect Weather: $2.99 > $1.99
    • Launch Center Pro: $4.99 > $2.99
    • Clear+: $4.99 > $1.99
    • Screens VNC: $19.99 > $14.99
    • PDF Converter: $6.99 > $2.99
    • Calendars 5: $6.99 > $2.99
    • Scanner Pro: $6.99 > $2.99

    Thanks App Santa! Check out my App Recommendations for even more great apps.

    App Recommendations

    I just wanted to write a quick post with a few of my favorite apps and apps I use on a regular basis. Here they are in no particular order:

    Fantastical 2 – The best calendar app, hands down
    Tweetbot 3 – Great twitter client
    BBM (really?!) – Friend lives in Canada, other has an android. We can text for free.
    Facebook – I use this when I’m bored
    Simpsons Tapped Out – I’ve been a fan of the Simpsons since it started in the 80s.
    Words with Friends – Don’t play a ton, I’m losing interest I think
    Letterpress – Still fun
    Quintet – My friends Carmine’s game.
    Polymer – Fun addicting game
    Podcasts – I have a 1+ hr commute each way, I listen to a lot of podcasts.
    The Weather Channel – I like weather 😛
    NHL Game Center – Hockey is my favorite sport, LETS GO ISLANDERS!!
    MLB At Bat – Baseball is my second favorite sport, LETS GO METS!!
    ESPN Score Center – I check scores for the NFL and College Football

    Of course I have to include the Apps I have done:

    iLearn Math – Great math game for kids ages 4-9
    iLearn Math Lite – Free version, try it out!
    Multipli – The first app I made. I made this for my son who, at the time, was learning his multiplication tables.
    Awesome Cowbell – The best Cowbell app for iOS!
    Awesome Cowbell Free – Free version of the best Cowbell app

    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.