Common method in Swift


1. Check for any field empty in NSDictionary

class func checkforEmptyValueinDictioanty(dic:NSDictionary)-> Bool{

for (keyVal, dataVal) in dic {

  if (dataVal.length()==0){

                println(\(keyVal): \(dataVal.length()))

                return false

            }

            

        }

        return true

    }

2.Email validation

class func isValidEmail(testStr:String) -> Bool {

let fullNameArr = testStr.componentsSeparatedByString(“@”)

        var firstPart: String = fullNameArr[0]

        if let range = firstPart.rangeOfCharacterFromSet(NSCharacterSet.letterCharacterSet()){

 }else{

            return false

        }

      let emailRegEx = “[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}”

 var emailTest = NSPredicate(format:“SELF MATCHES %@”, emailRegEx)

        let result = emailTest.evaluateWithObject(testStr)

        return result

    }

3. Alert in Swift

class func commonAlert(title:String,msg:String,curView:UIViewController){

        var device : UIDevice = UIDevice.currentDevice();

        var systemVersion = device.systemVersion;

        var iosVerion : Float = (systemVersion as NSString).floatValue

        if(iosVerion >= 8.0) {

    

        var alert = UIAlertController(title: title, message: msg, preferredStyle: UIAlertControllerStyle.Alert)

            alert.addAction(UIAlertAction(title: “Ok”, style: UIAlertActionStyle.Default, handler: nil))

                  // return alert

            curView.presentViewController(alert, animated: true, completion: nil)

        }else{

            let alert=UIAlertView(title: title, message: msg, delegate: self, cancelButtonTitle: “ok”)

            alert.show()

           

        }

    }

4. NSUserDefaults in swift as common function

class func saveToUserDefault(value:AnyObject, key:String)

    {

        NSUserDefaults.standardUserDefaults().setObject(value, forKey:key)

        NSUserDefaults.standardUserDefaults().synchronize()

    }

    

    class func userDefaultForKey(key:String) -> String

    {

        return NSUserDefaults.standardUserDefaults().objectForKey(key) as NSString

        

    }

    class func userDefaultForAny(key:String) -> AnyObject

    {

        return NSUserDefaults.standardUserDefaults().objectForKey(key) as AnyObject!

    }

    

    class func userdefaultForArray(key:String) -> Array<AnyObject>

    {

        return NSUserDefaults.standardUserDefaults().objectForKey(key) as Array

    }

    

    class func removeFromUserDefaultForKey(key:String)

    {

        NSUserDefaults.standardUserDefaults().removeObjectForKey(key)

        NSUserDefaults.standardUserDefaults().synchronize()

        

    }

5.Get screen height and width

let _screenWidth=UIScreen.mainScreen().bounds.size.width

let _screenHeight=UIScreen.mainScreen().bounds.size.height

Advertisements

Use different font text in a Label in swift


let secondLabel=UILabel(frame: CGRectMake(0, 16, _screenWidth2, 20))

        secondLabel.textColor=UIColor.whiteColor()

        secondLabel.textAlignment=NSTextAlignment.Center

        secondLabel.font=UIFont(name: “Arial”, size: 12)

        bottomView.addSubview(secondLabel)

        

        let attrSting=NSMutableAttributedString(string: “I agree to the Terms of Service and Privacy Policy.”)

        

        NSLog(“text length is %d”, attrSting.length)

        

        attrSting.addAttribute(NSFontAttributeName, value: UIFont(name: “Helvetica-bold”, size: 14), range: NSMakeRange(15, 16))

        

        attrSting.addAttribute(NSFontAttributeName, value: UIFont(name: “Helvetica”, size: 12), range: NSMakeRange(31,4 ))

        

        attrSting.addAttribute(NSFontAttributeName, value: UIFont(name: “Helvetica-bold”, size: 14), range: NSMakeRange(35, 15))

        

        secondLabel.attributedText=attrSting

Use HexColor in Swift as a color


Add it in your class 

extension UIColor {

   convenience init(red: Int, green: Int, blue: Int) {

       assert(red >= 0 && red <= 255, “Invalid red component”)

       assert(green >= 0 && green <= 255, “Invalid green component”)

       assert(blue >= 0 && blue <= 255, “Invalid blue component”)

       self.init(red: CGFloat(red) / 255.0, green: CGFloat(green) / 255.0, blue: CGFloat(blue) / 255.0, alpha: 1.0)

   }

   convenience init(netHex:Int) {

       self.init(red:(netHex >> 16) & 0xff, green:(netHex >> 8) & 0xff, blue:netHex & 0xff)

   }

}

Use it where you need…….

var color = UIColor(red: 0xFF, blue: 0xFF, green: 0xFF)

var color2 = UIColor(netHex:0xFFFFFF)

Simple Sprite Kit game tutorial- Part1


Megha Gulati

In this tutorial we are gonna make  a basics 2D games with Sprite Kit. You can get the full source code of this tutorial on github.

iOS Simulator Screen shot Oct 28, 2013 10.51.25 PM

Getting Started

Start Xcode and select File\New\Project from the main menu. Select the iOS\Application\SpriteKit Game template and click Next.

spritekit_project copy

Enter your game name for Product Name, choose iPhone for devices and leave the Class Prefix blank and click Next

create-project copy

On Device Orientation deselect Portrait.
only-landscape copy
In XCode’s toolbar, select the iPhone (4-inch) Simulator and click Run
run
You will see a single label that says “Hello World!” and when you click anywhere on the screen, a rotating space ship will appear.
hello copyspaceship copy

SpriteKit Physics and Collision Detection

SpriteKit comes with Physics engine built-in. Which helps in simulating realistic movements and collision detection.

We are going to use SpriteKit physics engine to move our spaceship and detect the collision between spaceship and missile.

We are…

View original post 1,112 more words

In Apps Purchase tutorial for iOS


One of the great things about being an iOS devleoper is that there are a variety of models you can use to make money off of your apps, including paid, free with ads, and in-app purchases.

In-app purchases are a particularly compelling option, for several reasons:

  • You can earn more money than just the price of your app. Some users are willing to spend a lot more on extra content!
  • You can release your app for free (which is a no-brainer download for most people), and if they enjoy it they can purchase more.
  • Once you have it implemented, you can keep adding additional content in the future to the same app (rather than having to make a new app to earn more money!)

You can use In-App Purchases with varying business models. For example, in Wild Fables, I made the app free with three stories included, and more available as in-app purchases. And in my upcoming upgrade toBattle Map, it is a paid app with optional extra content as In-App Purchases.

In this tutorial, you’ll learn how to use in-app purchases to unlock local content embedded in your app, and I’ll show you how you can deal with the tricky asycnchronous nature of in-app purchases.

In App Rage

So what app are you going to make in this tutorial? Well, let me give you some background first…

Lately I’ve become addicted to these comics online called rage comics, or sometimes “F7U12″. If you haven’t heard of them before, they’re basically funny little comics where someone goes through a common and frustrating situation, and has a wild rage or other humorous expression at the end.

So for this tutorial, you’re going to make a little app called “In App Rage” where people can buy some of these comics. But before you can even start coding, you need to create a placeholder app entry using iOS Developer Center and iTunes Connect.

The first step is to create an App ID for the app. So log into the iOS Developer Center, select the “App IDs” tab, and click “New App ID”:

Creating a new App ID

Fill out the page by entering a description and bundle identifier, similar to the screenshot below:

Create an App ID in the iOS Provisioning Portal

Note you should change the bundle identifier to have your own unique prefix, by using your own domain name (if you have one), or if all fails a made-up one based on your name or something else fairly unique.

When you’re done, click Submit, and viola – you have a new App ID! Now you’ll use it to create a new app on iTunes Connect.

Log onto iTunes Connect, click “Manage Your Applications”, and then “Add New App”. If you’re prompted between iOS App or Mac OSX App, choose iOS App obviously. Then enter in an App Name, SKU number, and choose the Bundle ID you just made as shown below:

Create a new App in iTunes Connect

You’ll probably have to tweak the App Name, because app names need to be unique and I’ve added an entry for this one. Maybe replace the RW with your own initials.

The next two pages will ask you for your app’s information. Just put in placeholder information for now – you can change all of this later. But unfortunately – you have to fill out *each and every field* (including adding screenshots, which you don’t even have yet!)

Just so you know, here’s how I feel about this:

iPhone Rage with iTunes Connect!

If you get any errors like the above, just keep entering in dummy data. Also, here’s an icon and fake screenshot you can upload to make iTunes Connect happy.

After you get through all of the errors, you should have your placeholder app created – aww yeah!

Managing In App Purchases

The reason you just created a placeholder app is that before you can code in-app purchases, you have to set them up in iTunes Connect. So now that you have a placeholder app, just click on the “Manage In App Purchases” button, as shown below:

Create a new App in iTunes ConnectThe Manage In-App Purchases Button in iTunes Connect

Then click “Create New” in the upper left:

The Create New button for In-App Purchases

You will get to a screen that lets you select the type of In-App Purchase you want to add. Note that there are are two types of In-App Purchases most frequently used:

  • Consumables. This means things you can buy more than once and can be used up, like extra lives, in-game currency, temporary power-ups, and the like.
  • Non-Consumables. This means something that you buy once, and expect to have it permanently. This means things like extra levels, unlockable content, and the like.

For In App Rage, you are going to be selling comics. Once the user purchases them, they should always have them, so choose Non-Consumable.

Selecting a non-consumable in-app purchase

Note:Any Non-Consumable purchase should be available to a user on any device they have. I.e. you don’t get to charge a user twice if they have two devices!We’ll talk more about how to allow the user to get the non-consumable content they purchased on other devices later, when we discuss restoring transactions.

There is no such requirement for consumables though – consumables can be for just the device the user bought them on. If you want consumables to be cross-device, you’d have to implement that yourself with iCloud or some other technology.

Next, you will be taken to a page to enter some information about your In-App Purchase. Fill in the fields according to the screenshot below:

Info for the Nightly Rage In-App Purchase

Let’s cover what each of these fields means:

  • Reference Name: This is what shows up in iTunes Connect for this in-app purchase. It can be whatever you want since you won’t see it anywhere in the app.
  • Product ID: Also known as “product identifier” in the Apple docs, this is the unique string that identifies your in-app purchase. Usually it’s best to start out with your bundle id, and then append a unique name for the purchase at the end.
  • Cleared for Sale: If this in-app purchase is OK for users to purchase as soon as the app becomes available.
  • Price Tier: How much this in-app purchase should cost.

After you’ve set that up, scroll down to the Language section and click Add Language. Fill out the form that pops up with the following information:

Language info for the nightly rage in-app purchase

This information will be returned to you when you query the App Store later on for the in-app purchases that are available.

You might wonder why this step is necessary (after all, you could embed this information in your app!) Well, obviously Apple needs to know the price. Also in the App Store it displays some of this information (such as when it displays the top in-app purchases). Finally, it might make things easier for you too, since it avoids having this information hard-coded into your app and allows you to enable/disable purchases on the fly.

Once you’re done, save the entry and create several more, similar to this screenshot below. Don’t worry about the descriptions – you won’t be using them in this tutorial, so you can just use the Display Name for those.

List of all in-app purchases to create

You might notice that this process takes a while, and I could imagine it gets annoying if you have a ton of in-app purchases in your app. Luckily you’re not in that situation, but if you are in your app, draw me a rage comic :]

Retrieving Product List

Before you can allow the user to purchase any products from your app, you must issue a query to iTunes Connect to retrieve the list of available products from the server.

We could just add the code to do this into the view controller that uses it, but that would be brittle since it wouldn’t be very easy to re-use. Instead, you will create a helper class to manage all aspects of in-app purchases for us, that you can easily re-use in your own projects!

Along with getting the list of products from the server, this helper class will also keep track of which products have been purchased or not. It will save the product identifier for each product that has been purchased in NSUserDefaults.

OK, so let’s try it out! In XCode, create a new project with the iOS\Application\Master-Detail Applicationtemplate. Enter InAppRage for the Product Name, make sure Devices is set to iPhone, and make sureUse Storyboards and Use Automatic Reference Counting is checked.

Project Settings for creating project

Next, you need to add library required for In-App Purchases to your project: StoreKit. To do this, select your project in the Project Navigator and select the In App Rage target. Select the Build Phases tab, expand the Link Binary with Libraries section, and click the + button. Find StoreKit.framework in the list, and click Add.

Adding StoreKit to the project

One final configuration step – open your Supporting Files\In App Rage-Info.plist and change the Bundle identifier to what you set up for your App ID:

Changing bundle identifier in info.plist

Finally time for code! Create a file with the iOS\Cocoa Touch\Objective-C class template, and click Next. Name the class IAPHelper, make it a subclass of NSObject, and click Next and finally Create.

Open IAPHelper.h and replace it with the following:

typedef void (^RequestProductsCompletionHandler)(BOOL success, NSArray * products);

@interface IAPHelper : NSObject

- (id)initWithProductIdentifiers:(NSSet *)productIdentifiers;
- (void)requestProductsWithCompletionHandler:(RequestProductsCompletionHandler)completionHandler;

@end

This class has two methods: an initializer that takes a list of product identifiers (such as com.razeware.inapprage.nightlyrage), and a method to retrieve information about the products from iTunes Connect. This second method is asynchronous, and it takes a block as a parameter so it can notify the caller when it is complete.

Note: Are blocks still confusing to you? Check out our How To Use Blocks in iOS 5 Tutorial Series.

Next switch to IAPHelper.m and add the first part of the implementation:

// 1
#import "IAPHelper.h"
#import <StoreKit/StoreKit.h>

// 2
@interface IAPHelper () <SKProductsRequestDelegate>
@end

@implementation IAPHelper {
    // 3
    SKProductsRequest * _productsRequest;
    // 4
    RequestProductsCompletionHandler _completionHandler;
    NSSet * _productIdentifiers;
    NSMutableSet * _purchasedProductIdentifiers;
}

@end

Let’s go over what you did here section by section.

  1. You need to use StoreKit to access the In-App Purchase APIs, so you import the StoreKit header here.
  2. To receive a list of products from StoreKit, you need to implement the SKProductsRequestDelegate protocol. Here you mark the class as implementing this protocol in the class extension.
  3. You create an instance variable to store the SKProductsRequest you will issue to retrieve a list of products, while it is active. You keep a reference to the request so a) you can know if you have one active already, and b) you’ll be guaranteed that it’s in memory while it’s active.
  4. You also keep track of the completion handler for the outstanding products request, the list of product identifiers passed in, and the list of product identifers that have been previously purchased.

Next, add the initializer:

- (id)initWithProductIdentifiers:(NSSet *)productIdentifiers {

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

        // Store product identifiers
        _productIdentifiers = productIdentifiers;

        // Check for previously purchased products
        _purchasedProductIdentifiers = [NSMutableSet set];
        for (NSString * productIdentifier in _productIdentifiers) {
            BOOL productPurchased = [[NSUserDefaults standardUserDefaults] boolForKey:productIdentifier];
            if (productPurchased) {
                [_purchasedProductIdentifiers addObject:productIdentifier];
                NSLog(@"Previously purchased: %@", productIdentifier);
            } else {
                NSLog(@"Not purchased: %@", productIdentifier);
            }
        }

    }
    return self;
}

This will check to see which products have been purchased or not (based on the values saved in NSUserDefaults) and keep track of the product identifiers that have been purchased in a list.

Next, add the method to retrieve the product information from iTunes Connect:

- (void)requestProductsWithCompletionHandler:(RequestProductsCompletionHandler)completionHandler {

    // 1
    _completionHandler = [completionHandler copy];

    // 2
    _productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:_productIdentifiers];
    _productsRequest.delegate = self;
    [_productsRequest start];

}

This first squirrels a copy of the completion handler block inside the instance variable so it can notify the caller when the product request asynchronously completes.

It then creates a new instance of SKProductsRequest, which is the Apple-written class that contains the code to pull the info from iTunes Connect. It’s very easy to use – you just give it a delegate (that conforms to the SKProductsRequestDelegate protocol) and then call start to get things running.

We set the IAPHelper class itself as the delegate, which means that it will receive a callback when the products list completes (productsRequest:didReceiveResponse) or fails (request:didFailWithErorr).

Speaking of delegate callbacks, add those next! Add the following code before the @end:

#pragma mark - SKProductsRequestDelegate

- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {

    NSLog(@"Loaded list of products...");
    _productsRequest = nil;

    NSArray * skProducts = response.products;
    for (SKProduct * skProduct in skProducts) {
        NSLog(@"Found product: %@ %@ %0.2f",
              skProduct.productIdentifier,
              skProduct.localizedTitle,
              skProduct.price.floatValue);
    }

    _completionHandler(YES, skProducts);
    _completionHandler = nil;

}

- (void)request:(SKRequest *)request didFailWithError:(NSError *)error {

    NSLog(@"Failed to load list of products.");
    _productsRequest = nil;

    _completionHandler(NO, nil);
    _completionHandler = nil;

}

Here you implement the two delegate callbacks – for success and failure. On success, you log out some information about the returned products, such as the product identifier, localized title, and price. Either way, you set the _productsRequest instance variable back to nil because you’re done with it and call the completion handler.

Go to Product\Build, and your project should compile with no errors.

Subclassing for your App

The IAPHelper class was written so that you can easily subclass it for your own app, specifying the product identifiers for your app. A lot of people recommend that you pull the list of product identifiers from a web server along with other information so you can add new in-app purchases dynamically rather than requiring an app update.

This is true and definitely recommended, but for the purposes of this tutorial you are going to keep things simple and just hard-code in the product identifiers for this app.

Create a new file with the iOS\Cocoa Touch\Objective-C class template, and click Next. Name the classRageIAPHelper, make it a subclass of IAPHelper, and click Next and finally Create.

Open RageIAPHelper.h and replace it with the following:

#import "IAPHelper.h"

@interface RageIAPHelper : IAPHelper

+ (RageIAPHelper *)sharedInstance;

@end

This just defines a static method to return the single, global instance of this class.

Next switch to RageIAPHelper.m and replace it with the following:

#import "RageIAPHelper.h"

@implementation RageIAPHelper

+ (RageIAPHelper *)sharedInstance {
    static dispatch_once_t once;
    static RageIAPHelper * sharedInstance;
    dispatch_once(&once, ^{
        NSSet * productIdentifiers = [NSSet setWithObjects:
                                      @"com.razeware.inapprage.drummerrage",
                                      @"com.razeware.inapprage.itunesconnectrage",
                                      @"com.razeware.inapprage.nightlyrage",
                                      @"com.razeware.inapprage.studylikeaboss",
                                      @"com.razeware.inapprage.updogsadness",
                                      nil];
        sharedInstance = [[self alloc] initWithProductIdentifiers:productIdentifiers];
    });
    return sharedInstance;
}

@end

The sharedInstance method implements the Singleton pattern in Objective-C to return a single, global instance of the RageIAPHelper class. It calls the superclasses initializer to pass in all the product identifiers that you created with iTunes Connect.

Don’t forget to replace these product identifiers with the actual identifiers you set up in iTunes Connect!

Go to Product\Build, and once again your project should compile with no errors.

Displaying the Products

You’ve got all of the pieces in place to return the product info, so it’s finally time to see something appear on the screen!

Open MasterViewController.m and replace the entire contents with the following:

#import "MasterViewController.h"
#import "DetailViewController.h"

// 1
#import "RageIAPHelper.h"
#import <StoreKit/StoreKit.h>

// 2
@interface MasterViewController () {
    NSArray *_products;
}
@end

@implementation MasterViewController

// 3
- (void)viewDidLoad
{
    [super viewDidLoad];

    self.title = @"In App Rage";

    self.refreshControl = [[UIRefreshControl alloc] init];
    [self.refreshControl addTarget:self action:@selector(reload) forControlEvents:UIControlEventValueChanged];
    [self reload];
    [self.refreshControl beginRefreshing];

}

// 4
- (void)reload {
    _products = nil;
    [self.tableView reloadData];
    [[RageIAPHelper sharedInstance] requestProductsWithCompletionHandler:^(BOOL success, NSArray *products) {
        if (success) {
            _products = products;
            [self.tableView reloadData];
        }
        [self.refreshControl endRefreshing];
    }];
}

#pragma mark - Table View

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}

// 5
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return _products.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath];

    SKProduct * product = (SKProduct *) _products[indexPath.row];
    cell.textLabel.text = product.localizedTitle;

    return cell;
}

@end

There’s a good bit of code here, so let’s go over it section by section.

  1. This imports the RageIAPHelper class you just wrote, along with the StoreKit headers since it will be using the SKProduct information returned from iTunes Connect.
  2. Adds an instance variable to store the SKProducts returned from iTunes Connect. Each row in the table view will display a product’s title.
  3. This is an example of the extremely handy new iOS 6 table view pull-to-refresh control! As you can see it’s incredibly simple to use – you just create a UIRefreshControl instance and store it in the built-in refreshControl variable (a member of UITableViewController). You then register a target to be called when the user pulls the table to refresh – so in this case the reload method will be called. By default this doesn’t occur when the table view first appears, so here you trigger it by manually calling reload and telling the refresh control to start refreshing.
  4. When the reload is called (whether it be the first manual call, or when the user pulls to refresh) it calls the RageIAPHelper’s requestProductsWithCompletionHandler method you wrote earlier to return the In-App Purchase product info from iTunes Connect. When this completes, the block will be called. All it does is store the list of products in the instance variable, reload the table view to display them, and tells the refresh control to stop animating.
  5. Standard table view stuff to display the localized title of a SKProduct in each row of the table.

Build and run, and you should now see the list of products appear in the table view!

In-App Rage products

Didn’t work? If this didn’t work for you, there are a number of things to check (this list courtesy of itsme.manish and abgtan from the forums:

  • Go to Settings\iTunes & App Stores, log out of any account, and try again so you’re sure you’re using a Sandbox account.
  • Check this link – if it doesn’t respond, the iTunes sandbox may be down.
  • Have you enabled In-App Purchases for your App ID?
  • Does your project’s .plist Bundle ID match your App ID?
  • Are you using the full product ID when when making an SKProductRequest?
  • Have you waited several hours since adding your product to iTunes Connect?
  • Are your bank details active on iTunes Connect?
  • Have you tried deleting the app from your device and reinstalling?

Tried all that and still stuck? Try the old forum thread or this thread’s comments for discussion with other readers.

Show Me The Money

This is an long tutorial already but the most important part is still left – making the purchase, and collecting the money!

The basic gist of making a purchase is the following:

  • You make a SKPayment object and specify what productIdentifier the user wants to purchase. You add it to a payment queue.
  • StoreKit will prompt the user “are you sure?”, ask them to enter their username/password (if appropriate), make the charge, and send you a success or failure. They’ll also handle the case where the user already paid for the app and is just re-downloading it, and give you a message for that as well.
  • You designate a particular object to receive purchase notifications. That object needs to start the process of downloading the content (not necessary in your case, since it’s hardcoded) and unlocking the content (which in your case is just setting that flag in NSUserDefaults and storing it in the purchasedProducts array).

Don’t worry – it’s pretty easy when you see the code. Once again, most of it’s going to be in the IAPHelper class for easy reuse. Start by making the following changes to IAPHelper.h:

// Add to the top of the file
#import <StoreKit/StoreKit.h>
UIKIT_EXTERN NSString *const IAPHelperProductPurchasedNotification;

// Add two new method declarations
- (void)buyProduct:(SKProduct *)product;
- (BOOL)productPurchased:(NSString *)productIdentifier;

Here you declare a notification you’ll use to notify listeners when a product has been purchased, a method to start buying a product, and a method to determine if a product has been purchased.

Next switch to IAPHelper.m and add the following:

- (BOOL)productPurchased:(NSString *)productIdentifier {
    return [_purchasedProductIdentifiers containsObject:productIdentifier];
}

- (void)buyProduct:(SKProduct *)product {

    NSLog(@"Buying %@...", product.productIdentifier);

    SKPayment * payment = [SKPayment paymentWithProduct:product];
    [[SKPaymentQueue defaultQueue] addPayment:payment];

}

First it checks to make sure the product is allowed to be purchased, and if so it marks the purchase as in-progress, and issues an SKPayment to the SKPaymentQueue.
That’s literally all it takes to let the user send you cold, hard cash. It’s almost as if you can hear your users saying “Shut up and take my money!”

However, if you’re letting your users give you money, you better give them something good in return! (After all, you aren’t the Greek government.)

So you need to add some code to identify when a payment “transaction” has finished, and process it accordingly.

Doing so is easy. First, modify the IAPHelper class extension to mark the class as implementing the SKPaymentTransactionObserver:

@interface IAPHelper () <SKProductsRequestDelegate, SKPaymentTransactionObserver>

Then add this to the bottom of your initWithProductIdentifiers: method (inside the if statement):

[[SKPaymentQueue defaultQueue] addTransactionObserver:self];

Now when the IAPHelper is initialized, it will make itself the transaction observer of the SKPaymentQueue. In other words, Apple will tell you when somebody purchased something!

There’s one really important thing about this. It could quite possibly happen that a user starts a purchase (and gets charged for it), but before Apple can respond with success or failure, the user suddenly loses network connection (or terminates your app). The user will still expect their purchase though, or lots of rage faces will ensue!

Luckily, Apple has a solution for this in mind. The idea is that Apple will keep track of any purchase transactions that haven’t yet been fully processed by your app, and will notify the transaction observer about them. But for this to work well, you should register your class as a transaction observer as early as possible in your app initialization.

To do this, switch to AppDelegate.m and add this import:

#import "RageIAPHelper.h"

And then add the following line at the beginning of application:didFinishLaunchingWithOptions:

[RageIAPHelper sharedInstance];

Now, as soon as your app launches it will create the singleton RageIAPHelper. This means the initWithProducts: method you just modified will be called, which registers itself as the transaction observer. So you will be notified about any transactions that were never quite finished.

You still have to implement the SKPaymentTransactionObserver protocol, so switch back to IAPHelper.mand add the following:

- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
    for (SKPaymentTransaction * transaction in transactions) {
        switch (transaction.transactionState)
        {
            case SKPaymentTransactionStatePurchased:
                [self completeTransaction:transaction];
                break;
            case SKPaymentTransactionStateFailed:
                [self failedTransaction:transaction];
                break;
            case SKPaymentTransactionStateRestored:
                [self restoreTransaction:transaction];
            default:
                break;
        }
    };
}

Actually, this is the only required method of the protocol. It gives you a list of transactions that have been updated, and all you have to do is loop through them and do different things based on their state. To keep the code clean, you call different methods if the transaction has completed, failed, or successfully restored.

Completed and failed makes sense, but what about restored? Remember back in the “In-App Purchase Types” section, I mentioned that there should be a way for users to restore their non-consumable purchases. This is important if the user has the same app on multiple devices (or deletes it and reinstalls it) and wants to get access to their prior purchases. Later you’ll add a way for users to do this in the app, but for now just know when the user restores their purchases, this is where it will come through.

Next for the important stuff – implementing the completeTransaction, restoreTransaction, and failedTransaction methods. Add these to the file:

- (void)completeTransaction:(SKPaymentTransaction *)transaction {
    NSLog(@"completeTransaction...");

    [self provideContentForProductIdentifier:transaction.payment.productIdentifier];
    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}

- (void)restoreTransaction:(SKPaymentTransaction *)transaction {
    NSLog(@"restoreTransaction...");

    [self provideContentForProductIdentifier:transaction.originalTransaction.payment.productIdentifier];
    [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}

- (void)failedTransaction:(SKPaymentTransaction *)transaction {

    NSLog(@"failedTransaction...");
    if (transaction.error.code != SKErrorPaymentCancelled)
    {
        NSLog(@"Transaction error: %@", transaction.error.localizedDescription);
    }

    [[SKPaymentQueue defaultQueue] finishTransaction: transaction];
}

completeTransaction and restoreTransaction do the same thing – they call a helper function to provide the given content (which you’ll write next).

failedTransaction is a bit different though. It calls a helper method to notify the user that the purchase failed (which you’ll also write next), marks the purchase as no longer in progress, and finishes the transaction.

Note: It is very important to call finishTransaction, or StoreKit will not know you’ve finished processing it, and will continue delivering the transaction to your app each time it launches!

Finally, add the final changes to the file:

// Add to top of file
NSString *const IAPHelperProductPurchasedNotification = @"IAPHelperProductPurchasedNotification";

// Add new method
- (void)provideContentForProductIdentifier:(NSString *)productIdentifier {

    [_purchasedProductIdentifiers addObject:productIdentifier];
    [[NSUserDefaults standardUserDefaults] setBool:YES forKey:productIdentifier];
    [[NSUserDefaults standardUserDefaults] synchronize];
    [[NSNotificationCenter defaultCenter] postNotificationName:IAPHelperProductPurchasedNotification object:productIdentifier userInfo:nil];

}

When a product is purchased, this method adds the product identifier to the list of purchaed product identifiers, marks it as purchased in NSUserDefaults, and sends a notification so others can be aware of the purchase.

You’re done with the buying code – all you have to do is hook it up to the user interface! First open MainStoryboard.storyboard, select the prototype table view cell, and set it to Subtitle:

Setting a table view cell to the subtitle style in the storyboard editor

Then open MasterViewController.m and make the following changes:

// Add new instance variable to class extension
NSNumberFormatter * _priceFormatter;

// Add to end of viewDidLoad
_priceFormatter = [[NSNumberFormatter alloc] init];
[_priceFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
[_priceFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];

// Add to bottom of tableView:cellForRowAtIndexPath (before return cell)
[_priceFormatter setLocale:product.priceLocale];
cell.detailTextLabel.text = [_priceFormatter stringFromNumber:product.price];

if ([[RageIAPHelper sharedInstance] productPurchased:product.productIdentifier]) {
    cell.accessoryType = UITableViewCellAccessoryCheckmark;
    cell.accessoryView = nil;
} else {
    UIButton *buyButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
    buyButton.frame = CGRectMake(0, 0, 72, 37);
    [buyButton setTitle:@"Buy" forState:UIControlStateNormal];
    buyButton.tag = indexPath.row;
    [buyButton addTarget:self action:@selector(buyButtonTapped:) forControlEvents:UIControlEventTouchUpInside];
    cell.accessoryType = UITableViewCellAccessoryNone;
    cell.accessoryView = buyButton;
}

Here you display the price in the subtitle along with the help of a price formatter. Also, if the user hasn’t bought a product you put a “buy” button as an accessory view. If the user has bought a product, you put a checkmark instead.

Add this method that will be called when the buy button is tapped:

- (void)buyButtonTapped:(id)sender {

    UIButton *buyButton = (UIButton *)sender;
    SKProduct *product = _products[buyButton.tag];

    NSLog(@"Buying %@...", product.productIdentifier);
    [[RageIAPHelper sharedInstance] buyProduct:product];

}

This simply determines the product that was tapped, and calls the method you just wrote to purchase the buy button.

Remember that when a purchase completes, it will send a notification. So register for that notification, and reload the appropriate cell when it occurs so you can display the checkmark if appropriate:

- (void)viewWillAppear:(BOOL)animated {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(productPurchased:) name:IAPHelperProductPurchasedNotification object:nil];
}

- (void)viewWillDisappear:(BOOL)animated {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}

- (void)productPurchased:(NSNotification *)notification {

    NSString * productIdentifier = notification.object;
    [_products enumerateObjectsUsingBlock:^(SKProduct * product, NSUInteger idx, BOOL *stop) {
        if ([product.productIdentifier isEqualToString:productIdentifier]) {
            [self.tableView reloadRowsAtIndexPaths:@[[NSIndexPath indexPathForRow:idx inSection:0]] withRowAnimation:UITableViewRowAnimationFade];
            *stop = YES;
        }
    }];

}

You’re so close you can almost taste it! But first, a brief word on accounts.

In App Purchases, Accounts, and the Sandbox

While you’re running your app in XCode, you’re not running against the real In-App Purchase servers – you’re running against the sandbox servers.

This means you can buy things without fear of getting charged, etc. But you need to set up a test account, and also make sure you’re logged out of the store on your device so you can see the whole process.

To make accounts, log onto iTunes Connect and click “Manage Users”. Click “Test User”, and follow the buttons to create a test user that you can make dummy in-app purchases with on the sandbox servers.

Then go to your iPhone and make sure you’re logged out of your current account. To do this, go to the Settings app and tap “Store”, and then click “Sign Out.”

Finally, go ahead and run your app and attempt to purchase a rage comic. Enter your test user account information and if all goes well, it should purchase with a happy check mark next to it!

A checkmark showing a successful in-app purchase

But wait a minute – where’s your comic?! You didn’t pay for no check mark! I can see the rage comic now…

Well, this tutorial is way long enough, and adding the display of the rage comics wouldn’t be much related to in-app purchases, so we’re going to leave that a practice exercise :]

The resources zip for this tutorial contain images for all of the the comics, so if you’re so inclined you can wrap it up by showing the comic in a new view controller when a purchased row is tapped! If you want to do this, you can just check if the productIdentifier is in the purchasedProducts array of the InAppRageIAPHelper before allowing the user access to the content.

Restoring Transactions

But wait! There’s one more thing before I can let you go.

These days it is a requirement for any app that implements In-App Purchases to have a button to allow the user to restore their transactions. As I mentioned earlier, this is useful if users want to get access to their purchases on a different device.

The good news is this is incredibly simple since you have already laid a solid framework.

First open IAPHelper.h and declare the following method:

- (void)restoreCompletedTransactions;

And implement it in IAPHelper.m as follows:

- (void)restoreCompletedTransactions {
    [[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
}

LOL – can’t get any easier than that! What that will do is contact iTunes Connect and find out which non-consumable products the user has already purchased. It will then call paymentQueue:updatedTransactions for each of these, with the SKPaymentTransactionStateRestored case. And you’ve already implemented this to unlock your content!

So all that’s left is to call this method. Make the following changes to MasterViewController.m:

// Add to end of viewDidLoad
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:@"Restore" style:UIBarButtonItemStyleBordered target:self action:@selector(restoreTapped:)];

// Add new method
- (void)restoreTapped:(id)sender {
    [[RageIAPHelper sharedInstance] restoreCompletedTransactions];
}

Now delete the app off your device, and re-install. Note that the checkmark is no longer there because by deleting the app you removed any NSUserDefaults.

Now tap the Restore button, and it will think for a few minutes, then restore your purchase so the checkmark appears again – not bad!

iOS Connect to Foursquare using OAuth


Recently, I needed to connect to the Foursquare API for an app that I was working on.  Foursquare uses OAuth 2.0, which is the standard that most API providers use these days.   I ran into a couple of issues and thought I would write up a short tutorial to help someone else out.  In this tutorial, I will show you how to handle OAuth in an iOS application.  We will be referencing foursquare’s document locatedhere in this tutorial.

Foursquare Api setup

The first thing you need to do is register your app in order to get foursquare API credentials.  You can go to this link to register your app.

Registering your app is pretty easy and the documentation is good.  The goal for today is obtain access tokens via our app that we can use to make requests with the foursquare api on behalf of our user(s).  Foursquare provides two authentication routes to connect to its api: code or token (refer to the documentation for more details).  For iOS apps, the preferred method is to go the token route.

The token access flow works by redirecting your user to an authentication page using the following format:

https://foursquare.com/oauth2/authenticate
?client_id=CLIENT_ID
&response_type=token
&redirect_uri=YOUR_REGISTERED_REDIRECT_URI

If the user accepts, the user is redirected to a callback uri with an access token.  In our app, we will need to intercept and store the access token for future requests against the api.  The redirected response will have this format:

http://YOUR_REGISTERED_REDIRECT_URI/#access_token=ACCESS_TOKEN

Notice that we will have to replace CLIENT_ID with the client id that our app is assigned and the YOUR_REGISTERED_REDIRECT_URI with the callback we registered when creating our app.  Creating the app on foursquare is pretty straightforward and so we will just talk through the tricky parts that you have to deal with in your code.

iOS App

Now for the fun stuff.  I believe in having a clear separation of responsibilities when developing apps.  Basically, it would be great if we could create a generic class to handle the foursquare authentication for us and then give control back to our app.  We still need an app to test our application. Let’s fire up Xcode and create a single view application using the template.  I chose to use storyboards, but it doesn’t really matter for this example.  We next need to create a new class to handle our authentication work.  Create a new class and name it FoursquareAuthentication and have it inherit from UIViewController.  Our class is going to make a call to foursquare, display a webpage for users to put in their credentials, and ultimately receive an access token when foursquare redirects after authentication.  In order to make that work, we will need to add a webview and handle the interactions using a delegate.  This means we need to add the UIWebviewDelegate protocol to our class.  Your header should look like this:

@interface FoursquareAuthentication : UIViewController<UIWebViewDelegate>
@end

Now switch over to the implementation file.  The first thing I did was add an UIWebView property to the anonymous category.  Next, we initialize our webview and add it to our view in our -viewDidLoad method.  We also have to set the webview’s delegate to self so that we can handle and respond to the network requests we intend to make. Finally, we create and make a request to foursquare’s authenticate endpoint.  See below for the code I used to make all this happen.

#import “FoursquareAuthentication.h”

  // 5. setup some helpers so we don’t have to hard-code everything

#define FOURSQUARE_AUTHENTICATE_URL @”https://foursquare.com/oauth2/authorize&#8221;

#define FOURSQUARE_CLIENT_ID @”YOUR CLIENT ID”

#define FOURSQUARE_CLIENT_SECRET @”YOUR CLIENT SECRET”

#define FOURSQUARE_REDIRECT_URI @”ios-app://redirect”

@interface FoursquareAuthentication ()

    // 1. create webview property

@property (nonatomic, strong) UIWebView *webView;

@end

@implementation FoursquareAuthentication

– (void)viewDidLoad

{

    [super viewDidLoad];

    // Do any additional setup after loading the view.

        // initialize the webview and add it to the view

        //2. init with the available window dimensions

    self.webView = [[UIWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 460)];

        //3. set the delegate to self so that we can respond to web activity

    self.webView.delegate = self;

        //4. add the webview to the view

    [self.view addSubview:self.webView];

        //6. Create the authenticate string that we will use in our request to foursquare

        // we have to provide our client id and the same redirect uri that we used in setting up our app

        // The redirect uri can be any scheme we want it to be… it’s not actually going anywhere as we plan to

        // intercept it and get the access token off of it

    NSString *authenticateURLString = [NSString stringWithFormat:@”%@?client_id=%@&response_type=token&redirect_uri=%@”, FOURSQUARE_AUTHENTICATE_URL, FOURSQUARE_CLIENT_ID, FOURSQUARE_REDIRECT_URI];

        //7. Make the request and load it into the webview

  NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:authenticateURLString]];

    [self.webView loadRequest:request];

}

Next we need to handle one of the webview’s delegate methods:

-(BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType

We will use the webview:shouldStartLoadWithRequest:navigationType method to check for our callback url and intercept.  We will allow all other requests to pass through.  I’m using the custom scheme ios-app:// as the base of my callback url.  This is the most confusing part of the process.  We want the authentication request to foursquare to load the webpage in our webview.  However, we want the redirect callback to be intercepted so that we can read off the access token and store it.  Hence, why we use the custom url.  We will store the access token in user defaults for future requests.

#pragma mark – Web view delegate

– (BOOL)webView:(UIWebView *)webView shouldStartLoadWithRequest:(NSURLRequest *)request navigationType:(UIWebViewNavigationType)navigationType {

 if([request.URL.scheme isEqualToString:@”ios-app”]){

     // 8. get the url and check for the access token in the callback url

        NSString *URLString = [[request URL] absoluteString];

        if ([URLString rangeOfString:@”access_token=”].location != NSNotFound) {

                // 9. Store the access token in the user defaults

            NSString *accessToken = [[URLString componentsSeparatedByString:@”=”] lastObject];

            NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];

            [defaults setObject:accessToken forKey:@”access_token”];

            [defaults synchronize];

                // 10. dismiss the view controller 

            [self dismissViewControllerAnimated:YES completion:nil];

        }

    }

    return YES;

}

Testing our Work

To test the app, we will add a button to our viewcontroller to initiate our authentication to foursquare.  In our view did load method we check to see if we have an access token.  If not, we display our foursquare button so that our user can connect to foursquare and authorize our app.  We also add an action button to launch our authentication controller facilitate getting us an access token from our user.

– (void)viewDidLoad

{

    [super viewDidLoad];

    // Do any additional setup after loading the view, typically from a nib.

        //check for foursquare and authorize

    NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];

    if(![defaults objectForKey:@”access_token”]){

        [self.connectToFoursquare setHidden:NO];

    }

    else{

        [self.connectToFoursquare setHidden:YES];

    }

}

– (IBAction)connect:(id)sender {

    FoursquareAuthentication *controller = [[FoursquareAuthentication alloc] init];

    [self presentViewController:controller animated:NO completion:nil];

    [self.connectToFoursquare setHidden:YES];

}

That’s it.  I have included a sample project on github that has the entire project.  Enjoy!

Creating Sliding Menu with iOS SlidingMenu Component


Introduction

This a component which is a menu has several buttons coming from the right and left side of the screen with an animation. Here is a small video that will give you the idea:

Click here for non HTML5 Video supported browsers

Integrating SlidingMenu

First you have to import SlidingMenu.h in your view controller “h” file.
You have to create an array which will hold buttons in your sliding menu. Like these:

#import “SlidingMenu.h”

@interface ViewController : UIViewController
{
    SlidingMenu *menu;
}
– (IBAction)openOrCloseMenu:(UIButton *)sender;
@property (strong, nonatomic) NSMutableArray *buttons;

@end

In your viewdidLoad method make these:

– (void)viewDidLoad
{
    [super viewDidLoad];
    
    self.title = @”Sliding Menu”;
    
    _buttons = [NSMutableArray array];
    [self createButtonsWithQuantity:7];
    menu = [[SlidingMenu alloc]init];
    [menu setButtonsArray:_buttons];

    
    // Do any additional setup after loading the view, typically from a nib.
}

This will create buttons that will  be in sliding menu. You can modify:

-(void)createButtonsWithQuantity:(NSInteger)quantity{
    for (int i = 0; i&lt;quantity; i++) {
        UIButton *button = [UIButton buttonWithType:UIButtonTypeCustom];
        button.tag = i;
        [button addTarget:self action:@selector(menuButonAction:) forControlEvents:UIControlEventTouchUpInside];
        [button setBackgroundImage:[UIImage imageNamed:@&quot;button-blue&quot;] forState:UIControlStateNormal];
        [button setBackgroundImage:[UIImage imageNamed:@&quot;button-blue-pressed&quot;] forState:UIControlStateSelected];
        [button setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
        [button setTitle:[NSString stringWithFormat:@&quot;Button %i&quot;,button.tag] forState:UIControlStateNormal];
        button.layer.cornerRadius = 5.0;
        [self.view addSubview:button];
        [_buttons addObject:button];
    }
}

And the action method of the buttons of course:

– (void)menuButonAction:(UIButton *)sender {
    NSLog(@”%i. button clicked”,sender.tag);
    [self performSegueWithIdentifier:@”detail” sender:sender];
}

And decide when your sliding menu comes to the screen. I will do it in viewDidAppear method:

-(void)viewDidAppear:(BOOL)animated{
    [super viewDidAppear:animated];
    for (UIButton *button in _buttons) {
        if (button != nil) {
            [self.view addSubview:button];
        }
    }
    [menu openOrCloseButtonsMenu];
}

If you use this in viewDidAppear method. Make sure you use this in viewDidDisappear method:

-(void)viewDidDisappear:(BOOL)animated{
    [super viewDidDisappear:animated];
    [menu closeButtonsMenu];
    for (UIButton *button in _buttons) {
        [button removeFromSuperview];
    }
}

Customizing SlidingMenu

You can customize SlidingMenu with these properties:

Setting SlidingMenu Origins:

menu.buttonMenuOriginYOffset = 120;
menu.buttonMenuOriginXOffset = 10;

Setting Space between buttons:

menu.spaceBetweenButtons = 20;

Setting buttons size:

menu.buttonSize = CGSizeMake(73, 36);

Download Source Files from Git

 

 

 

How to make a magazine app in iOS – Part II


The iOS5 revolution

A lot of water has flowed under the bridge since our first part of this tutorial. We’re sorry for the delay, but at the time of writing we were aware of the new featured introduced with iOS5 but we were still under NDA and not authorized to disclose anything about the SDK. Finally we’re now able to provide our example magazine app with the double iOS4/iOS5 compatibility.

I will not spend my time explaining all the Newsstand features, all in all we’re creating a magazine app here and the Newsstand implementation details go beyond our original purposes. I have written a two parts tutorial in my personal blog that you can read here and here, and that covers all Newsstand aspects and the required (by Apple reviewers) subscriptions. In short, Newsstand is both a new way to present magazines in the iPad and iPhones, where the original icon is replaced by a cover of the magazine or newspaper and is placed under a special group dedicated to collect all Newsstand icons installed by the user. For the developer it is also a framework, called Newsstand Kit, that introduces a new methodology to organize, download and install the app content.

The example app

The screenshot shows the final appearance of the app. A set of nine magazines with their nine fruity cover. You can download a magazine, see the progress bar change while the operation is in progress and finally read it. The other screenshot shows the appearance in the Newsstand. The typical icon has been replaced by a magazine-like representation inside the Newsstand group. The same, if run on an iPad with iOS4 installed will show the classical icon instead.


The full app code is available on GitHub. Don’t consider it as a production code, so please don’t try to reuse as it is with your clients unless you have tested it in all possible corner cases. Anyway it can be considered as a valid starting point for real apps. Essentially the principles behind the code architecture have been depicted inPart I of this tutorial; if you haven’t read it yet I recommend to jump to that article before entering in this one, at least to understand the key components of such an app. The driving aim is to keep the issues management (controller, don’t confuse it with “view controller”) separated as much as possible from the UI concerns. This means that in theory the whole code behind the Store Manager and Issue Modelspart can be reused even in a Mac OSX app, as they relationship with UI stuff is minimum.

I have setup this app using the basic single window Xcode template and then added the two main components in the application delegate startup method:

– (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{    
    // here we create the “Store” instance
    _store = [[Store alloc] init];
    [_store startup];

    self.shelf = [[[ShelfViewController alloc] initWithNibName:nil bundle:nil] autorelease];
    _shelf.store=_store;

    self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
    self.window.rootViewController = _shelf;
    [self.window makeKeyAndVisible];
    return YES;
}

The two components are:

  • the Store class, which represents the Store Manager block in the app architecture; this Class is a subclass of NSObject and is not linked to the user interface.
  • the ShelfViewController represents the UI of the application. It is linked to the store using a specific property. Note that the controller will not access the store exposed properties, but will get the needed information via a simple API. We could have used a delegate pattern instead, but as both blocks are essentially custom blocks in a custom app, there is not much need to define a protocol for their interaction. We can say that the controller part has been split in two parts, one for the UI, the shelf, and one for the back-end, the Store. Their link is such that the Shelf depends on the Store (strict link), not viceversa. Any Store to Shelf communication is done using lazy methods, based on notifications.

The app has then been integrated with the new Newsstand requirements in the info plist. Again have a look at Apple docs or my tutorial for a detailed guide.

Models and Controllers

We have one model in this app, it is the Issue model which represents a single magazine issue in the store or in the user library. There is also one controller, which is the Store. As I said this controller is not a UI component but the app back-end is self sufficient with these two components. In theory we can retrieve the store status and download magazines even without any user interface. This is a basic concept in magazine apps, where many events happen in the background are not related to direct user interaction: this means that the app should be capable to perform several tasks even if the user interface has not been loaded at all.

The role of the Issue class is to represent all relevant magazine properties, that is a unique identifier, a title, a release date, a link to the cover image, obviously the link (URL) to the content (which can be a pdf file, an epub file or a zip file for complex packages). In particular the unique identifier is a piece of data that must be maintained along the full life of the issue: it is the only way to uniquely identify a particular issue, independently of localization issues (e.g. the title can change in different countries, but not the unique ID). Besides it is the link to the Newsstand way to identify issues (the NKIssue‘s name field) and it can be also used to link the product with the App Store if we’re going to implement In App Purchases.

Other than this role, the Issue class will have a central role in the downloading of a magazine. Essentially the Store class will schedule a download but it will be monitored (progress) and then finished (effective magazine installation) by the Issue class.

Finally this class has limited capabilities to represent a magazine already downloaded and available in the user library. For this we provide the simpleisIssueAvailableForRead that will be used by the user interface to know which action is possible with an issue (read or download) and eventually display the contents.

The Store class is the app controller. It is initialized at the very beginning of the app and its instance is never released. Besides immediately after initialization this class will fetch the store contents from the publisher server. In the example we decided to implement the store contents as a simple property list, and we instruct the store to retrieve this property list, decode it and create the Issue objects, and finally download their cover images. All this is done asynchronously using the Grand Central Dispatch and at the end a notification will be sent to inform all interested objects (in particular the view controller) that the store contents are ready and the UI can be safely displayed. Note that the store status is represented by a propertystatus. We have overridden its setter method to post a notification that informs any interested observer of the new state. For simplicity we decided to limit the possible statuses to “not initialized”, “downloading”, “ready” and “error”. A more complex app can introduce extra states if needed. Finally as a fallback in case of missing connection (the user must be able to access his/her content even if not connected to the Internet) we reload a locally saved copy of the store.

The central piece of the code is in the downloadStoreIssues method of the class, that we report below:

-(void)downloadStoreIssues {
    self.status=StoreStatusDownloading;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH,0), ^{
        NSArray *_list = [[NSArray alloc] initWithContentsOfURL:[NSURL URLWithString:@”http://www.viggiosoft.com/media/data/iosblog/magazine/store.plist”%5D%5D;
        if(!_list) {
            // let’s try to retrieve it locally
            _list = [[NSArray alloc] initWithContentsOfURL:[self fileURLOfCachedStoreFile]];
        }
        if(_list) {
            // now creating all issues and storing in the storeIssues array
            [_list enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
                NSDictionary *issueDictionary = (NSDictionary *)obj;
                Issue *anIssue = [[Issue alloc] init];
                anIssue.issueID=[issueDictionary objectForKey:@”ID”];
                anIssue.title=[issueDictionary objectForKey:@”Title”];
                anIssue.releaseDate=[issueDictionary objectForKey:@”Release date”];
                anIssue.coverURL=[issueDictionary objectForKey:@”Cover URL”];
                anIssue.downloadURL=[issueDictionary objectForKey:@”Download URL”];
                anIssue.free=[(NSNumber *)[issueDictionary objectForKey:@”Free”] boolValue];
                [anIssue addInNewsstand];
                [storeIssues addObject:anIssue];
                [anIssue release];
                // dispatch cover loading
                if(![anIssue coverImage]) {
                    dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
                        NSData *imgData = [NSData dataWithContentsOfURL:[NSURL URLWithString:anIssue.coverURL]];
                        if(imgData) {
                            [imgData writeToURL:[anIssue.contentURL URLByAppendingPathComponent:@”cover.png”] atomically:YES];
                        }
                    });
                }
            }];
            // let’s save the file locally
            [_list writeToURL:[self fileURLOfCachedStoreFile] atomically:YES];
            [_list release];
            self.status=StoreStatusReady;
        } else {
            ELog(@”Store download failed.”);
            storeIssues = nil;
            self.status=StoreStatusError;
        }
    });
}

Note that in the code above for simplicity I have queued the cover downloading immediately after the property list download. Probably this is not the best way to do this, as we are adding extra networking steps before presenting the app status, with possible delays if network conditions are not good. We can do this in an example where we know that the number of issues is limited, probably in a real app it makes more sense to do this in a background job and then notify the UI to update its status every time a new cover is ready to be displayed.

The view controller

The user interface will wake up immediately, and will change its appearance based on the notifications coming from the store. When the notification center will deliver the store “I’m ready” message to the UI, this one will load all UI issue representations (the CoverView class in the code, a basic view with really minimum application logic) and show the shelf to the user.

At this point the app terminated its back-end processing and is waiting for user commands. Here we have two possibilities:

  • A magazine has been downloaded, the user will see a “READ” button, clicking on it he will be able to read it. In our example all our content is made of pdf files, we decided to use the Quick Look framework in iOS, it is enough for our purposes.
  • A magazine has not been downloaded yet, the user will see a “DOWNLOAD” button, clicking on it the download will start and we’ll show a progress bar. At the end we need to replace the button label and hide the progress. We’ll see this part in more detail.

The view controller is dependent on the store. In order to get the issues information (number of issues, detail of each issue) it will not access directly to the store properties but will use a very simple API, made of three methods:

/* “numberOfIssues” is used to retrieve the number of issues in the store */
-(NSInteger)numberOfStoreIssues;

/* “issueAtIndex:” retrieves the issue at the given index */
-(Issue *)issueAtIndex:(NSInteger)index;

/* “issueWithID:” retrieves the issue with the given ID */
-(Issue *)issueWithID:(NSString *)issueID;

Based on this information, and on the Issue properties, it will create the Issue view representation (CoverView) and place it on the screen.

Downloading a magazine

In a magazine app there are three critical sections: retrieve and display of the store contents, download the contents and finally read them (a good PDF or epub reader is mandatory in an app like that; it’s not the purpose of this tutorial, aimed to explain the architecture and the techniques needed to make such an app, but it is a relevant part in the user experience).

My approach in the magazine download is that the user must be completely free to do any action and such actions shouldn’t interfere with the result of the download. Now many apps take a shortcut: they put a spinner in the center of the screen, then block any user interaction with the UI objects behind the spinner and ask the user to wait. It’s easy to do this, but it is not the better user experience. While waiting the user can read another issue, can navigate inside the store or decide to switch temporarily to another application or finally can temporarily lose the network.Newsstand Kit provides a system level methodology that simplifies (in some cases) the developer life but at the same time removes any excuse on not doing a good job in term of user experience.

As soon as the user triggers a new download, the view controller will send its download request to the store class. In the code below you can see thescheduleDownloadOfIssue: code. This method will prepare the network request and will send it in the background. Note how we split the app behavior based on the operating system version we’re on. If it is iOS5 then we must use the Newsstand approach – where the downloading will be managed by the system in a Newsstand queue – , if we are in iOS4 we’ll follow a classic methodology based on NSOperation: in this case I missed for simplicity to get the content length of the content to be downloaded, so in the iOS4 case the progress bar will not be displayed. This information instead if provided “for free” by Newsstand.

-(void)scheduleDownloadOfIssue:(Issue *)issueToDownload {
    NSString *downloadURL = [issueToDownload downloadURL];
    NSURLRequest *downloadRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:downloadURL]];
    if(isOS5()) {
        // iOS5 : use Newsstand
        NKIssue *nkIssue = [issueToDownload newsstandIssue];
        NKAssetDownload *assetDownload = [nkIssue addAssetWithRequest:downloadRequest];
        [assetDownload downloadWithDelegate:issueToDownload];
    } else {
        // iOS4 : use NSOperation
        NSURLConnection *conn = [NSURLConnection connectionWithRequest:downloadRequest delegate:issueToDownload];
        NSInvocationOperation *op = [[NSInvocationOperation alloc] initWithTarget:self selector:@selector(startDownload:) object:conn];
        if(!downloadQueue) {
            downloadQueue = [[NSOperationQueue alloc] init];
            downloadQueue.maxConcurrentOperationCount=1;
        }
        [downloadQueue addOperation:op];
        [downloadQueue setSuspended:NO];
    }
}

// iOS4 only
-(void)startDownload:(id)obj {
    NSURLConnection *conn = (NSURLConnection *)obj;
    [conn start];
}

In the two cases I’d like to emphasize the fact that the Store initiates the download operation but then it will delegate any further processing to the interested part, the is the Issue to be downloaded. So it will be the Issue class that will monitor the download and then will finalize it.

The Issue class will behave as delegate of the download operation triggered by the Store class. The delegate protocol changes if you’re using Newsstand or not. In the former case you will need to be compliant with theNSURLConnectionDownloadDelegate protocol while in the latter the protocol to be respected will be the un-documented (or: documented in the header files only!)NSURLConnectionDataDelegate which is a “spin-off” of the classicNSURLConnectionDelegate. The difference between the two protocols in that one will write in the file system, the other in memory. Again don’t use the “data” approach for production purposes, here we’re loading the whole content in memory and then saving on disk when the content has been completely downloaded: really not the best approach if your content is hundred of megabytes, your app will certainly crash.

In order to visually track the progress and then update the user interface, we decided, being coherent of our principles, to keep the application store controller independent on any UI choice. To do this we make use of KVO and Notifications. Essentially when the view controller starts a download, it will set itself and the magazine view as notification observers of the download result, in order to update its status as soon the download terminates (with success or with error):

-(void)downloadIssue:(Issue *)issue updateCover:(CoverView *)cover {
    cover.progress.alpha=1.0;
    cover.button.alpha=0.0;
    [issue addObserver:cover forKeyPath:@”downloadProgress” options:NSKeyValueObservingOptionNew context:NULL];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(issueDidEndDownload:) name:ISSUE_END_OF_DOWNLOAD_NOTIFICATION object:issue];
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(issueDidFailDownload:) name:ISSUE_FAILED_DOWNLOAD_NOTIFICATION object:issue];
    [[NSNotificationCenter defaultCenter] addObserver:cover selector:@selector(issueDidEndDownload:) name:ISSUE_END_OF_DOWNLOAD_NOTIFICATION object:issue];
    [[NSNotificationCenter defaultCenter] addObserver:cover selector:@selector(issueDidFailDownload:) name:ISSUE_FAILED_DOWNLOAD_NOTIFICATION object:issue];
    [_store scheduleDownloadOfIssue:issue];
}

Both the cover view and the view controller will need to unregister from the notification center as soon as the download operation terminates. Besides to allow the cover view to keep track of the progress status, as the download starts it will be registered as a KVO observer of the downloadProgress property of the Issue. This means that for each change of this property, that will occur during the downloading phase, the cover view will be informed of the change to update the progress bar (so we have here a separation again of the back-end property, the download progress, from the UI, the UIProgressBar). The cover view will unregister itself as soon as download terminates.

When the download terminates, the Issue instance will copy the downloaded content to the final destination. In the Newsstand framework this destination is specified by the system, in iOS4 will copy it in the Caches directory to be compliant with iCloud requirements (yes, iOS4 doesn’t work with iCloud but our app is the same for both generations of the OS, so we must be good citizens in any case).

In the Newsstand case we’ll also need to update the cover image for the Newsstand icon. We’ll do it with a simple command inside the code that manages the download termination (at the end we also send the end of download notification we mentioned previously):

-(void)connectionDidFinishDownloading:(NSURLConnection *)connection destinationURL:(NSURL *)destinationURL {
    // copy the file to the destination directory
    NSURL *finalURL = [[self contentURL] URLByAppendingPathComponent:@”magazine.pdf”];
    ELog(@”Copying item from %@ to %@”,destinationURL,finalURL);
    [[NSFileManager defaultManager] copyItemAtURL:destinationURL toURL:finalURL error:NULL];
    [[NSFileManager defaultManager] removeItemAtURL:destinationURL error:NULL];
    // update Newsstand icon
    [[UIApplication sharedApplication] setNewsstandIconImage:[self coverImage]];
    // post notification
    [self sendEndOfDownloadNotification];
}

Conclusions

This is the conclusion of this tutorial. We decided after a long interval to make an effort to propose a complete app valid for both iOS4 and iOS5 environments. In the code you will find more interesting stuff, e.g. a “hook” to the Store Kit: this is a typical issue not considered by developers when they make applications that sell magazines. In such case storing the issue price in the publisher server is not of help as we must retrieve the pricing information from the only relevant source, that is the App Store. To do this our app must – asynchronously – query the iTunes store for the prices of all the publications and then display them once retrieved. I’m not adding this extra step in the code, just the hook as placeholder for future extensions, but of course if required by our readers I can extend this tutorial further. I would appreciate any suggestion and any contribution in the GitHub hosted code, I would like to see the example code provided with this tutorial as a good skeleton for new apps. If you didn’t enjoy this article, at least you can enjoy the literature classics (and a Django manual…) that I used to create my PDF files.

Storyboards Tutorial in iOS 7: Part 1


Tutorial Team member Matthijs Hollemans (the iOS Apprentice Series author) has ported this popular tutorial from iOS 5 by Tutorials to iOS 7. This is a sneak peek of the third edition of the book, which will be updated to iOS 7. We hope you enjoy!

Storyboarding is an exciting feature first introduced with iOS 5 that saves you a lot of time building user interfaces for your apps.

To show you what a storyboard is, I’ll let a picture do the talking. This is the storyboard that you will be building in this tutorial:

The full storyboard for the app

You may not know exactly yet what the app does but you can clearly see which screens it has and how they are related. That is the power of using storyboards.

If you have an app with many different screens then storyboards can help reduce the amount of glue code you have to write to go from one screen to the next. Instead of using a separate nib file for each view controller, your app uses a single storyboard that contains the designs of all of these view controllers and the relationships between them.

Storyboards have a number of advantages over regular nibs:

  • With a storyboard you have a better conceptual overview of all the screens in your app and the connections between them. It’s easier to keep track of everything because the entire design is in a single file, rather than spread out over many separate nibs.
  • The storyboard describes the transitions between the various screens. These transitions are called “segues” and you create them by simply ctrl-dragging from one view controller to the next. Thanks to segues you need less code to take care of your UI.
  • Storyboards make working with table views a lot easier with the new prototype cells and static cells features. You can design your table views almost completely in the storyboard editor, something else that cuts down on the amount of code you have to write.

Not everything is perfect, of course, and storyboards do have some limitations. The storyboard version of Interface Builder isn’t as powerful as the old nib editor and there are a few handy things nibs can do that storyboards unfortunately can’t. You also need a big monitor, especially when you write iPad apps!

If you’re the type who hates Interface Builder and who really wants to create his entire UI programmatically, then storyboards are probably not for you. Personally, I prefer to write as little code as possible — especially UI code! — so this tool is a welcome addition to my arsenal.

And if you want to keep using nibs then go right ahead, but know that you can combine storyboards with nibs. It’s not an either-or situation.

In this tutorial you’ll take a look at what you can do with storyboards. You’re going to build a simple app that lets you create a list of players and games, and rate their skill levels. In the process, you’ll learn the most common tasks that you’ll be using storyboards for.

Storyboards tutorial: iOS 7 style

Fire up Xcode and create a new project. You’ll use the Single View Application template as the starting point and then build up the app from there.

01_sb_newproject

Fill in the template options as follows:

  • Product Name: Ratings
  • Organization Name: fill this in however you like
  • Company Identifier: the identifier that you use for your apps, in reverse domain notation
  • Class Prefix: leave this empty
  • Devices: iPhone

With previous versions of Xcode you had to specifically choose to use storyboards in the new project, but as of Xcode 5 this no longer an option; storyboards are enabled by default.

After Xcode has created the project, the main Xcode window looks like this:

New project in Xcode

The new project consists of two classes, AppDelegate and ViewController, and the star of this tutorial: theMain.storyboard file. Notice that there are no .xib files in the project.

This is a portrait-only app, so before you continue, uncheck the Landscape Left and Landscape Rightoptions under Deployment Info, Device Orientation.

Let’s take a look at that storyboard. Click Main.storyboard in the list of files to open it in Interface Builder:

The initial storyboard

Editing storyboards in Interface Builder works pretty much the same way as editing nibs. You can drag new controls from the Object Library (see bottom-right corner) into your view controller to design its layout. The difference is that the storyboard doesn’t contain just one view controller from your app, but all of them.

The official storyboard terminology for a view controller is “scene”, but you can use the terms interchangeably. The scene is what represents the view controller in the storyboard. Previously you would use a separate nib for each scene / view controller, but now they are all combined into a single storyboard.

On the iPhone only one of these scenes is visible at a time, but on the iPad you can show several at once, for example the master and detail panes in a split-view, or the content of a popover.

Note: Xcode 5 enables Auto Layout by default for storyboard and nib files. Auto Layout is a cool new technology for making flexible user interfaces that can easily resize, which is useful on the iPad and for supporting the larger iPhone 5, but it only works on iOS 6 and up. It also has a bit of a learning curve, which is why you’re not using it in this tutorial. To learn more about Auto Layout, see our booksiOS 6 by Tutorials and iOS 7 by Tutorials.

Disable Auto Layout from the File inspector for the storyboard:

Disabling auto layout

To get some feel for how the storyboard editor works, drag some controls into the blank view controller:

Dragging controls into storyboard

Find this button at the bottom of the storyboard canvas:

The document outline button

Click it to open the Document Outline in the sidebar on the left:

Document outline

When editing a nib this area lists just the components from that one nib, but for a storyboard it shows the contents of all your view controllers. Currently there is only one view controller (or scene) in your storyboard but in the course of this tutorial you’ll be adding several others.

There is a miniature version of this Document Outline below the scene, named the Dock:

Dock

The Dock shows the top-level objects in the scene. Each scene has at least a View Controller object, aFirst Responder object, and an Exit item, but it can potentially have other top-level objects as well. The Dock is convenient for making connections to outlets and actions. If you need to connect something to the view controller, you can simply drag to its icon in the Dock.

Note: You probably won’t be using the First Responder very much. This is a proxy object that refers to whatever object has first responder status at any given time. It was also present in your nibs and you probably never had a need to use it then either. As an example, you can hook up the Touch Up Inside event from a button to First Responder’s cut: selector. If at some point a text field has input focus then you can press that button to make the text field, which is now the first responder, cut its text to the pasteboard.

Run the app and it should look exactly like what you designed in the editor (shown here on the 4-inch Retina iPhone simulator running iOS 7.0):

First app in the simulator

If you’ve ever made a nib-based app before then you always had a MainWindow.xib file. This nib contained the top-level UIWindow object, a reference to the App Delegate, and one or more view controllers. When you put your app’s UI in a storyboard, however, MainWindow.xib is no longer used. So how does the storyboard get loaded by the app?

Let’s take a peek at the application delegate. Open up AppDelegate.h and you’ll see it looks like this:

#import <UIKit/UIKit.h>
 
@interface AppDelegate : UIResponder <UIApplicationDelegate>
 
@property (strong, nonatomic) UIWindow *window;
 
@end

It is a requirement for using storyboards that your application delegate inherits from UIResponder and that it has a UIWindow property. If you look into AppDelegate.m, you’ll see that it does absolutely nothing. All the methods are practically empty. Even application:didFinishLaunchingWithOptions: simply returns YES.

The secret is in the Ratings-Info.plist file. Click on Ratings-Info.plist (you can find it in the Supporting Files group) and you’ll see this:

09_sb_infoplist

Storyboard apps use the key UIMainStoryboardFile, or “Main storyboard file base name”, to specify the name of the storyboard that must be loaded when the app starts. When this setting is present,UIApplication will load the named storyboard file and automatically instantiates the first view controller from that storyboard, then puts its view into a new UIWindow object. No programming necessary.

You can also see this in the Project Settings screen in the Deployment Info section:

10_sb_targetsummary

For the sake of completeness, also open main.m to see what’s in there:

#import <UIKit/UIKit.h>
 
#import "AppDelegate.h"
 
int main(int argc, char *argv[])
{
    @autoreleasepool {
        return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
    }
}

The app delegate is not part of the storyboard. You have to pass the name of your app delegate class toUIApplicationMain(), otherwise it won’t be able to find it.

Just Add It To My Tab

The Ratings app has a tabbed interface with two screens. With a storyboard it is really easy to create tabs.

Switch back to Main.storyboard, and drag a Tab Bar Controller from the Object Library into the canvas. You may want to maximize your Xcode window first, because the Tab Bar Controller comes with two view controllers attached and you’ll need some room to maneuver. You can zoom out using the little floating panel in the bottom-right corner of the canvas.

Tab bar controller

The new Tab Bar Controller comes pre-configured with two other view controllers, one for each tab. UITabBarController is a so-called container view controller because it contains one or more other view controllers. Two other common containers are the Navigation Controller and the Split View Controller (you’ll see both of them later).

The container relationship is represented by the arrows between the Tab Bar Controller and the view controllers that it contains.

12_sb_containment

Note: If you want to move the Tab Bar Controller and its attached view controllers as a group, you can ⌘-click to select multiple scenes and then move them around together. (Selected scenes have a thick blue outline.)

Drag a label into the first view controller and give it the text “First Tab”. Also drag a label into the second view controller and name it “Second Tab”. This allows you to actually see something happen when you switch between the tabs.

Note: You can’t drag stuff into the scenes when the editor is zoomed out. You’ll need to return to the normal zoom level first. You can do that quickly by double-clicking in the canvas.

Select the Tab Bar Controller and go to the Attributes inspector. Check the box that says Is Initial View Controller.

13_sb_initialcontroller

In the canvas the arrow that at first pointed to the regular view controller now points at the Tab Bar Controller:

13_sb_initialcontroller2

This means that when you run the app, UIApplication will make the Tab Bar Controller the main screen. The storyboard always has a single view controller that is designated the initial view controller, that serves as the entry point into the storyboard.

Tip: To change the initial view controller, you can also drag the arrow between view controllers.

Run the app and try it out. The app now has a tab bar and you can switch between the two view controllers with the tabs:

App with tabs

Xcode actually comes with a template for building a tabbed app (unsurprisingly called the Tabbed Application template) that you could have used, but it’s good to know how this works so you can also create a Tab Bar Controller by hand if you have to.

Note: If you connect more than five scenes to the Tab Bar Controller, it automatically gets a More… tab when you run the app. Pretty neat!

Remove the view controller that was originally added by the template, as you’ll no longer be using it. The storyboard now contains just the tab bar and the two scenes for its tabs.

Adding a Table View Controller

The two scenes that are currently attached to the Tab Bar Controller are both regularUIViewControllers. You are going to replace the scene from the first tab with a UITableViewControllerinstead.

Click on that first view controller to select it, and then delete it. From the Object Library drag a new Table View Controller into the canvas in the place where that previous scene used to be:

15_sb_tablecontroller

With the Table View Controller selected, choose Editor\Embed In\Navigation Controller from Xcode’s menubar. This adds yet another view controller to the canvas:

15_sb_navcontroller

You could also have dragged in a Navigation Controller from the Object Library, but this Embed In command is just as easy.

Because the Navigation Controller is also a container view controller (just like the Tab Bar Controller), it has a relationship arrow pointing at the Table View Controller. You can also see these relationships in the Document Outline:

16_sb_do_relations

Notice that embedding the Table View Controller gave it a navigation bar. Interface Builder automatically put it there because this scene will now be displayed inside the Navigation Controller’s frame. It’s not a real UINavigationBar object but a simulated one.

If you look at the Attributes inspector for the Table View Controller, you’ll see the Simulated Metricssection at the top:

17_sb_simmetrics

“Inferred” is the default setting for storyboards and it means the scene will show a navigation bar when it’s inside of a Navigation Controller, a tab bar when it’s inside of a Tab Bar Controller, and so on. You could override these settings if you wanted to, but keep in mind they are here only to help you design your screens. The Simulated Metrics aren’t used during runtime; they’re just a visual design aid that shows what your screen will end up looking like.

Let’s connect these two new scenes to the Tab Bar Controller. Ctrl-drag from the Tab Bar Controller to the Navigation Controller:

18_sb_dragtonavcontroller

When you let go, a small popup menu appears:

19_sb_createrel

Choose the Relationship Segue – view controllers option. This creates a new relationship arrow between the two scenes:

19_sb_createrel_2

The Tab Bar Controller has two such relationships, one for each tab. The Navigation Controller itself has a relationship connection to the Table View Controller. There is also another type of arrow, the segue, that we’ll talk about later.

When you made this new connection, a new tab was added to the Tab Bar Controller, simply named “Item”. For this app, you want this new scene to be the first tab, so drag the tabs around to change their order:

Drag tab items

Run the app and try it out. The first tab now contains a table view inside a navigation controller.

First tab with table view

Before you put some actual functionality into this app, let’s clean up the storyboard a little. You will name the first tab “Players” and the second “Gestures”. Unlike what you may expect, you do not change this on the Tab Bar Controller itself, but in the view controllers that are connected to these tabs.

As soon as you connect a view controller to the Tab Bar Controller, it is given a Tab Bar Item object. You use the Tab Bar Item to configure the tab’s title and image.

Select the Tab Bar Item inside the Navigation Controller, and in the Attributes inspector set its Title toPlayers:

22_sb_renametab

Rename the Tab Bar Item for the view controller from the second tab to Gestures.

A well-designed app should also put some pictures on these tabs. The resources for this tutorial contains a subfolder named Images. Add that folder to the project. In the Attributes inspector for the Players Tab Bar Item, choose the Players.png image. You probably guessed it, but give the Gestures item the imageGestures.png.

A view controller that is embedded inside a Navigation Controller has a Navigation Item that is used to configure the navigation bar. Select the Navigation Item for the Table View Controller (you can find it in the Document Outline) and change its title in the Attributes inspector to Players.

Alternatively, you can double-click the navigation bar and change the title there. (Note: you should double-click the simulated navigation bar in the Table View Controller, not the actual Navigation Bar object in the Navigation Controller.)

23_sb_edittitle

Run the app and marvel at your pretty tab bar, all without writing a single line of code!

App with tab bar images

Prototype cells

Prototype cells allow you to easily design a custom layout for your table view cells directly from within the storyboard editor.

The Table View Controller comes with a blank prototype cell. Click on that cell to select it and in theAttributes inspector set the Style option to Subtitle. This immediately changes the appearance of the cell to include two labels.

If you’ve used table views before and created your own cells by hand, you may recognize this as theUITableViewCellStyleSubtitle style. With prototype cells you can either pick one of the built-in cell styles as you just did, or create your own custom design (which you’ll do shortly).

Set the Accessory attribute to Disclosure Indicator and in the Identifier field type PlayerCell. All prototype cells are still regular UITableViewCell objects and therefore should have a reuse identifier.

25_sb_cellsetup

Run the app, and… nothing has changed. That’s not so strange: you still have to make a data source for the table so it will know what rows to display.

Add a new file to the project. Choose the Objective-C class template. Name the classPlayersViewController and make it a subclass of UITableViewController. The With XIB for user interface option should be unchecked because you already have the design of this view controller in the storyboard. No nibs today!

26_sb_createtblcontrollerclass

Go back to the storyboard and select the Table View Controller (make sure you select the actual view controller and not one of the views inside it). In the Identity inspector, set its Class toPlayersViewController. That is the essential step for hooking up a scene from the storyboard with your own view controller subclass. Don’t forget this or your class won’t be used!

Custom class

From now on when you run the app that table view controller from the storyboard is an instance of thePlayersViewController class.

Add a mutable array property to PlayersViewController.h:

#import <UIKit/UIKit.h>
 
@interface PlayersViewController : UITableViewController
 
@property (nonatomic, strong) NSMutableArray *players;
 
@end

This array will contain the main data model for the app, an array that contains Player objects. Add a new file to the project using the Objective-C class template. Name it Player, subclass of NSObject.

Change Player.h to the following:

@interface Player : NSObject
 
@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *game;
@property (nonatomic, assign) int rating;
 
@end

There’s nothing special going on here. Player is simply a container object for these three properties: the name of the player, the game he’s playing, and a rating of 1 to 5 stars.

You’ll make the array and some test Player objects in the App Delegate and then assign it to thePlayersViewController’s players property.

In AppDelegate.m, add an #import for the Player and PlayersViewController classes at the top of the file, and add a new instance variable named _players:

#import "AppDelegate.h"
#import "Player.h"
#import "PlayersViewController.h"
 
@implementation AppDelegate 
{
    NSMutableArray *_players;
}
 
// Rest of file...

Then change the application:didFinishLaunchingWithOptions: method to:

 
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    _players = [NSMutableArray arrayWithCapacity:20];
 
    Player *player = [[Player alloc] init];
    player.name = @"Bill Evans";
    player.game = @"Tic-Tac-Toe";
    player.rating = 4;
    [_players addObject:player];
 
    player = [[Player alloc] init];
    player.name = @"Oscar Peterson";
    player.game = @"Spin the Bottle";
    player.rating = 5;
    [_players addObject:player];
 
    player = [[Player alloc] init];
    player.name = @"Dave Brubeck";
    player.game = @"Texas Hold’em Poker";
    player.rating = 2;
    [_players addObject:player];
 
    UITabBarController *tabBarController = (UITabBarController *)self.window.rootViewController;
    UINavigationController *navigationController = [tabBarController viewControllers][0];
    PlayersViewController *playersViewController = [navigationController viewControllers][0];
    playersViewController.players = _players;
 
    return YES;
}

This simply creates some Player objects and adds them to the _players array. But then it does the following:

UITabBarController *tabBarController = (UITabBarController *)self.window.rootViewController;
UINavigationController *navigationController = [tabBarController viewControllers][0];
PlayersViewController *playersViewController = [navigationController viewControllers][0];
playersViewController.players = _players;

Yikes, what is that?! You want to assign the _players array to the players property ofPlayersViewController so it can use this array for its data source. But the app delegate doesn’t know anything about PlayersViewController yet, so it will have to dig through the storyboard to find it.

Note: This is one of the limitations of storyboards. With nibs you always had a reference to the App Delegate in your MainWindow.xib and you could make connections from your top-level view controllers to outlets on the App Delegate. That is currently not possible with storyboards. You cannot make references to the app delegate from your top-level view controllers. That’s unfortunate, but you can always get those references programmatically, which is what you do here.

Let’s take it step-by-step:

UITabBarController *tabBarController = (UITabBarController *)self.window.rootViewController;

You know that the storyboard’s initial view controller is a Tab Bar Controller, so you can look up the window’s rootViewController and cast it to UITabBarController.

The PlayersViewController sits inside a navigation controller in the first tab, so you first look up thatUINavigationController object,

UINavigationController *navigationController = [tabBarController viewControllers][0];

and then ask it for its root view controller, which is the PlayersViewController that you are looking for:

PlayersViewController *playersViewController = [navigationController viewControllers][0];

It takes a bit of effort to dig through the storyboard to get the view controller you want, but that’s the way to do it. Of course, if you change the order of the tabs, or change the app so that it no longer has a Tab Bar Controller at the root, then you will have to revise this logic as well.

Now that you have an array full of Player objects, you can continue building the data source forPlayersViewController. Open up PlayersViewController.m and add an import at the top:

#import "Player.h"

Change the table view data source methods to the following:

- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
{
    return 1;
}
 
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
    return [self.players count];
}

The real work happens in cellForRowAtIndexPath. Previously, this method typically looked something like this:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
 
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                                      reuseIdentifier:CellIdentifier];
    }
 
    // Configure the cell...
    return cell;
}

You would ask the table view to dequeue a cell and if that returned nil because there were no free cells to reuse, you would create a new instance of the cell class. That is no doubt how you’ve been writing your own table view code all this time. Well, no longer!

Replace that method with:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"PlayerCell"];
 
    Player *player = (self.players)[indexPath.row];
    cell.textLabel.text = player.name;
    cell.detailTextLabel.text = player.game;
 
    return cell;
}

That looks a lot simpler! The only thing you need to do to get a new cell is:

UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"PlayerCell"];

If there is no existing cell that can be recycled, this will automatically make a new copy of the prototype cell and return it to you. All you need to do is supply the re-use identifier that you set on the prototype cell in the storyboard editor, in this case PlayerCell. Don’t forget to set that identifier, or this little scheme won’t work!

Run the app, and lo and behold, the table view has players in it:

App with players

It just takes one line of code to use these newfangled prototype cells. I think that’s just great!

Note: In this app you’re using only one prototype cell but if your table needs to display different kinds of cells then you can simply add additional prototype cells to the storyboard. You can either duplicate the existing cell to make a new one, or increment the value of the Table View’s Prototype Cells attribute. Be sure to give each cell its own re-use identifier, though.

Designing Your Own Prototype Cells

Using a standard cell style is OK for many apps, but for this app you want to add an image on the right-hand side of the cell that shows the player’s rating (one to five stars). Having an image view in that spot is not supported by the standard cell styles, so you’ll have to make a custom design.

Switch back to Main.storyboard, select the prototype cell in the table view, and set its Style attribute toCustom. The default labels now disappear.

First make the cell a little taller. Either drag its handle at the bottom or change the Row Height value in the Size inspector. Make the cell 55 points high.

Drag two Label objects from the Objects Library into the cell and place them roughly where the standard labels were previously. Just play with the font and colors and pick something you like.

Drag an Image View into the cell and place it on the right, next to the disclosure indicator. Make it 81 points wide; the height isn’t very important. Set its Mode to Center (under View in the Attributes inspector) so that whatever image you put into this view is not stretched.

Make the labels 190 points wide so they don’t overlap with the image view. The final design for the prototype cell looks something like this:

Custom cell design

Because this is a custom designed cell, you can no longer use UITableViewCell’s textLabel anddetailTextLabel properties to put text into the labels. These properties refer to labels that aren’t on this cell anymore; they are only valid for the standard cell types. Instead, you will use tags to find the labels.

Give the Name label tag 100, the Game label tag 101, and the Image View tag 102. You can do this in the Attributes inspector.

Then open PlayersViewController.m and add a new method, imageForRating:.

- (UIImage *)imageForRating:(int)rating
{
    switch (rating) {
        case 1: return [UIImage imageNamed:@"1StarSmall"];
        case 2: return [UIImage imageNamed:@"2StarsSmall"];
        case 3: return [UIImage imageNamed:@"3StarsSmall"];
        case 4: return [UIImage imageNamed:@"4StarsSmall"];
        case 5: return [UIImage imageNamed:@"5StarsSmall"];
    }
    return nil;
}

Change tableView:cellForRowAtIndexPath: to the following:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"PlayerCell"];
 
    Player *player = (self.players)[indexPath.row];
 
    UILabel *nameLabel = (UILabel *)[cell viewWithTag:100];
    nameLabel.text = player.name;
 
    UILabel *gameLabel = (UILabel *)[cell viewWithTag:101];
    gameLabel.text = player.game;
 
    UIImageView *ratingImageView = (UIImageView *)[cell viewWithTag:102];
    ratingImageView.image = [self imageForRating:player.rating];
 
    return cell;
}

That should do it. Now run the app again. It is possible that the app shows up like this:

Wrong table cell height

Hmm, that doesn’t look quite right, the cells appear to overlap one another. You did change the height of the prototype cell but the table view doesn’t necessarily take that into consideration. There are two ways to fix it: you can change the table view’s Row Height attribute or implement thetableView:heightForRowAtIndexPath: method. The former is much easier, so let’s do that.

Note: You would use heightForRowAtIndexPath if you did not know the height of your cells in advance, or if different rows can have different heights.

Back in Main.storyboard, in the Size inspector of the Table View, set Row Height to 55:

31_sb_cells_rightheight

If you run the app now, it looks a lot better!

App with proper row height

By the way, if you changed the height of the cell by dragging its handle rather than typing in the value, then the table view’s Row Height property was automatically changed too. So it may have worked correctly for you the first time around.

Using a Subclass for the Cell

The table view already works pretty well but I’m not a big fan of using tags to access the labels and other subviews of the prototype cell. It would be much more handy if you could connect these labels to outlets and then use the corresponding properties. As it turns out, you can.

Add a new file to the project, with the Objective-C class template. Name it PlayerCell and make it a subclass of UITableViewCell.

Change PlayerCell.h to:

@interface PlayerCell : UITableViewCell
 
@property (nonatomic, weak) IBOutlet UILabel *nameLabel;
@property (nonatomic, weak) IBOutlet UILabel *gameLabel;
@property (nonatomic, weak) IBOutlet UIImageView *ratingImageView;
 
@end

The class itself doesn’t do much; it just adds properties for nameLabelgameLabel and ratingImageView, all of which are IBOutlets.

Back in Main.storyboard, select the prototype cell and change its Class to PlayerCell on the Identity inspector. Now whenever you ask the table view for a new cell withdequeueReusableCellWithIdentifier:, it returns a PlayerCell instance instead of a regularUITableViewCell.

Note that you gave this class the same name as the reuse identifier — they’re both called PlayerCell — but that’s only because I like to keep things consistent. The class name and reuse identifier have nothing to do with each other, so you could name them differently if you wanted to.

Now connect the labels and the image view to these outlets. Select the label and drag from New Referencing Outlet in its Connections inspector to the table view cell and select nameLabel andgameLabel, respectively:

32_sb_connectname

Important: You should hook up the controls to the table view cell, not to the view controller! You see, whenever your data source asks the table view for a new cell withdequeueReusableCellWithIdentifier, the table view doesn’t give you the actual prototype cell but acopy (or one of the previous cells is recycled if possible).

This means there will be more than one instance of PlayerCell at any given time. If you were to connect a label from the cell to an outlet on the view controller, then several copies of the label will try to use the same outlet. That’s just asking for trouble. (On the other hand, connecting the prototype cell to actions on the view controller is perfectly fine. You would do that if you had custom buttons or other UIControls on your cell.)

Now that you’ve hooked up the properties, you can simplify the data source code one more time. First import the PlayerCell class in PlayersViewController.m:

#import "PlayerCell.h"

And then change cellForRowAtIndexPath to:

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    PlayerCell *cell = (PlayerCell *)[tableView dequeueReusableCellWithIdentifier:@"PlayerCell"];
 
    Player *player = (self.players)[indexPath.row];
    cell.nameLabel.text = player.name;
    cell.gameLabel.text = player.game;
    cell.ratingImageView.image = [self imageForRating:player.rating];
 
    return cell;
}

That’s more like it. You now cast the object that you receive from dequeueReusableCellWithIdentifierto a PlayerCell, and then you can simply use the properties that are wired up to the labels and the image view. Isn’t it great how using prototype cells makes table views a whole lot less messy?

Run the app and try it out. It should still look the same as before, but behind the scenes it’s now using your own table view cell subclass!