Objectively Speaking 2: A Crash Course in Objective-C for iOS 6

Linda Burke

This post is also available in: Japanese

This is the 2nd post by iOS Tutorial Team Member Linda Burke, an indie iOS developer and the founder of canApps.

Driving test

If you are a software developer skilled in another platform, but want to start learning iPhone development (and hence Objective-C) – this is the tutorial series for you!

This tutorial picks up where the first Objective-C tutorial left off. Now that you’re feeling the need for speed, it’s time to head out on a road trip and test your new-found skills!

This tutorial will take you on an Objective-C excursion to see some new sights, travel down some winding roads, and put the pedal to the metal while learning some great new concepts.

In this Objective-C tutorial, you will create a simple movie quotes quiz app. Along the way, you’ll become acquainted with a number of aspects of Objective-C, including:

  • mutable arrays
  • property lists
  • classes
  • methods
  • @property
  • protocols and delegates
  • additional string functions
  • and much more!

Warning: iPhone development with Objective-C can be highly addictive! Don’t be surprised to find yourself burning the midnight oil while a nice stack of dishes piles up in the kitchen! :]

Getting Started

Before you begin, make sure you have an Apple developer account with a provisioning profile for your device and the latest version of Xcode installed on your computer. This tutorial is based on iOS6 using Xcode 4.6. If you don’t have Xcode installed, you can download it from the App Store.

Note: To learn more about Apple developer accounts and how to provision your device, check out this tutorial: How to Submit Your App to Apple: From No Account to App Store

Start up Xcode , go to File\New\New Project, choose the iOS\Application\Single View Application template, and click Next.

Name your project QuoteQuiz, enter your organization name and company identifier, enter QuoteQuiz for the Class Prefix, set the device family to iPhone, and make sure to check Use Storyboards and Use Automatic Reference Counting, as shown below:

Leave Include Unit Tests unchecked. Click Next, select the location to save your project, and click Create.

Take a look at your newly created project structure in Xcode, shown in the screenshot below:

You will notice that your project contains the following key files:

  • QuoteQuizAppDelegate.h
  • QuoteQuizAppDelegate.m
  • QuoteQuizViewController.h
  • QuoteQuizViewController.m and
  • MainStoryboard.storyboard

QuoteQuizAppDelegate contains some code gets called when the app first starts up, or other “events of interest” occur like the app entering the background. For this tutorial, that’s all you need to know. Here are some brief explanations of the other files, with which you will be working directly:

  • MainStoryboard.storyboard contains the visual layout of your app. This is where you use a visual designer to create the screens for your app.
  • QuoteQuizViewController.m is the view controller class for your scene. This file contains your app’s Objective-C code. The storyboard above links the scene to this class in the storyboard; Xcode did this automatically when you created your project. Any scenes that you add to your app will need to be linked manually; you’ll cover this later in the tutorial.
  • QuoteQuizViewController.h is the header file for your view controller. This is where you declare public properties and methods that need be accessed from outside the class.

Okay, enough theory — the best way to learn Objective-C is to roll up your sleeves and get coding!

Building Your Main View Controller

In the Project Navigator on the left, select MainStoryboard.storyboard.

To start, you’ll just work with the single view that is already present in your app. Later on in the tutorial, you’ll create other views as they are required.

Look for the right sidebar in your Xcode window. If you don’t see one, click the rightmost button in the Views section to make the right hand sidebar visible, as shown in the screenshot below:

Open Xcode side panel

For simplicity’s sake, you will be working with a fixed layout instead of the default Autolayout.

Note: What’s Autolayout? Autolayout defines the way your interface arranges itself for different screen sizes and device orientations. To learn more about Autolayout, you can read more about it in our tutorial here.

To disable Autolayout, open the File Inspector tab, then uncheck the Autolayout checkbox, as shown below:

Disable Autolayout in Xcode

The lower part of the righthand sidebar has four tabs to access the File Template, Code Snippet, Object, and Media Libraries, respectively. You can click the little icons on the top of each section to switch between the four tabs.

Select Object Library — the third tab in – like so:

Select the Object Library in Xcode

From the Object Library, drag a Label to the top center section of the view, then select the Attributes Inspector (the fourth tab in at the top of the sidebar), as seen here:

Adding a Label

The Attributes Inspector allows you to change most object properties. Set the label’s alignment to be centered by clicking the center button in the Alignment section, like so:

Setting the alignment of a label

Double-click the label and change the text to Name the Movie. It should look like the following screenshot:

Name the movie

Drag another label onto the view underneath the previous one. As before, center align the label, then modify the label’s text to be Question.

You’ll likely need more space than that little bitty text label to display the movie quotes on-screen.

Select the Question label and click on the Size Inspector (the fifth icon in). Set width to 280 points and the height to 90 points, as shown below:

Setting the width and height of a label

You’ll also want your movie quote to stand out from the rest of the text on the screen; you can accomplish this by modifying the background color of your label. As well, having a color other than white as the background color of your labels makes it much easier to arrange them on-screen!

Select the Question label and return to the Attributes Inspector. Click the background color dropdown in the Attributes inspector, and select Other…. From the color palette, select Cyan, as seen here.

Setting the background color of the label

Although cyan is nice, you’re really just using the color here as a placeholder. You’ll set the actual background color of this label later on in code.

Label objects default to one line of text. If the label’s text is larger than the label’s bounds, then the text will be cut-off, followed by “…”.

For example, the first quote in the quiz is “Frankly my dear, I don’t give a damn.” Since the quote exceeds the label’s bounds, the text will actually read, “Frankly my dear, I don’t give a da..”. You want to make the quiz challenging — but not that challenging! :]

This is easily solved by changing your label’s to show an unlimited number of lines.

Select the Question label, and use the Attributes Inspector to set the Lines field to 0, as below:

Setting the lines

“0″ sounds like you don’t want any lines, but to Xcode, it means “unlimited” — and that’s all that matters!

Now that you have the question on-screen, you should probably give the player a few answers to choose from! :]

Adding Labels and Buttons To Your View

Drag a new Label from the Object Library and place it underneath the Question label. Change the width and height of the new label to 235 points and 50 points respectively. As before, set the background color of your new label to be Cyan.

Now, move the new label over so that the two right-hand sides of the labels are aligned.

Modify the text of the new label to read Answer 1, and then center align the text. Finally, set the Lines property of the label to 0.

Okay, that does it for displaying the text of the answer. But you still need to provide a button for the player to indicate their answer!

From the Object Library, drag a round rect button to the left hand side of the label. Use the Size Inspector to change the width of the button to be 37 points, and the height to be 50 points. Finally, modify the text of the label to read 1.

Your storyboard should look like this:

Add answer label

Now, you could go and drag in more labels and buttons for the other answers, but since you already have some on the screen, why not just copy and paste the existing labels and buttons for the other two answers?

To do this, hold down the Command button, and using the mouse, select both the button and the label together. Keep the Command button pressed, press C to copy the objects, and then press V to paste the objects.

Release the Command button and drag the two selected elements to a point below the original button and label. Line up the copy per the blue layout lines, then release the mouse button.

Double-click the button and change the text to read 2. Double-click the label and change it to read Answer 2.

Following the same process, copy and paste the button and lable, change the text of the new third button to 3, and change the label text to Answer 3. Your scene should now look like this:

Add third answer label

Build and run, and enjoy your app with all it’s cyan glory.

App with beautiful cyon

At this point, you have a good start. But there’s a lot more to a great app than just a pretty face — head on in to the next section to add the actual quiz code!

Adding the Quiz Logic

The first thing you’ll need to do is create a new class to hold your quiz info.

Go to File\New File…, choose the iOS\Cocoa Touch Class\Objective-C class template, and click Next. Name the class Quiz, make it a subclass of NSObject, click Next then Create.

You’ll start by creating the property declarations for the variables you’ll need to store your quotes. You’ll add these in the class header file, Quiz.h.

Add the following code to Quiz.h between the @interface and @end lines:

@property (nonatomic, strong) NSMutableArray * movieArray;
 
- (id)initWithQuiz:(NSString*)plistName;

The property directive above tells the compiler that the variable movieArray is a property of this class, which you can think of as a “public instance variable” in this case. Two attributes are then set on the property: nonatomic and strong.

The nonatomic attribute specifies that the property does not utilize any thread locking behavior. By default, all properties are atomic, and thereby, thread-safe. Nonatomic properties are not thread-safe, but faster. In this tutorial you won’t be accessing this property from multiple thread, so you can pick the non thread-safe (but faster) option.

The strong attribute indicates that a pointer to the specified variable will stay in memory as long as the QuoteQuizViewController object exists, using iOS’s new Automatic Reference Counting feature.

Note: To learn more about memory management and Automatic Reference Counting, check out our Automatic Reference Counting Tutorial.

Okay, now you have a class to hold your quiz info — now to load in the quiz data!

Loading the Property List

You need some sort of mechanism to store your movie quiz questions. You could hard-code the questions into your app, but it’s a better idea to store them in a property list. That way you can easily modify the property list to add or modify quiz data without cracking open your code.

First, you need to create an initialization method for your property list loader.

Add the following code to Quiz.m between @implementation and @end:

- (id)initWithQuiz:(NSString*)plistName 
{
    if ((self = [super init])) 
    {   
        NSString *plistCatPath = [[NSBundle mainBundle] pathForResource:plistName ofType:@"plist"];     
        self.movieArray = [NSMutableArray arrayWithContentsOfFile:plistCatPath];
    }
    return self;
}

The id return type means “any object that derives from NSObject”. It’s a standard return type for all object constructors. It’s also used with callbacks when the referring object type is not known.

plistName is the actual name of the property list. The benefit of using a parameter in your method to load up your property list is that this method can now load up any named property list file — and in this way, you increase the reusability of your objects!

allthethings

Note: Why should you use self when setting and getting data for a property? That’s because self is similar to the “this” keyword in other languages, and refers to the object upon which the method is running. This is now mandatory from Xcode 4.5 — don’t forget or you’ll run into build errors!

Okay, now you need to declare your new shiny class somewhere so that it can be used in your app!

Add the following code to QuoteQuizViewController.h just before @interface:

@class Quiz;

The class directive is known as a forward declaration. It informs the compiler that the Quiz class will be defined later in the compilation process. This is used to resolve circular dependences.

What are circular dependencies, you ask?

Think of it like this: class A depends on class B, but class B has some code that depends on class A. The compiler is unable to resolve this dependency, since it needs to know about both classes at the same time. The class directive tells the compiler: “Hang ten, good buddy, you don’t need to know about this class now. Just know that I’m going to be using it — the definition is coming down the road.”

This keeps the compiler happy and it’s able to continue building your code. :]

You’ll need to keep track of two things here — the Quiz object that represents the running quiz, and some sort of index counter to keep track of which question you’re on.

Add the following code to QuoteQuizViewController.h below the @interface line:

@property (nonatomic, assign) NSInteger quizIndex;
@property (nonatomic, strong) Quiz * quiz;

Now add the following line to the top of QuoteQuizViewController.m in order to import your quiz header file:

#import "Quiz.h"

The first time through your quiz, you will need to set quizIndex to 999, which will tell your app that it needs to start at the beginning. You also need to initialize an instance of Quiz with your custom init method.

Replace viewDidLoad in QuoteQuizViewController.m with the following:

- (void)viewDidLoad
{
    [super viewDidLoad];
    self.quizIndex = 999;
    self.quiz = [[Quiz alloc] initWithQuiz:@"quotes"];
}

In the code above, you instantiate Quiz with your custom init method, and then pass in the name of the property list that you want your class to load. In this case, the file is named “quotes”.

Guess you’d better get cracking and create that file so that your app can load it! :]

Creating the Property List File

Go to File\New File…, choose the iOS\Resource\Property List template, and click Next. Select the location to save your new file and name the file quotes.

Right-click on the quotes file in your Project Navigator and select Open As\Source Code. Now, add your movie quotes by copying and pasting the following code into quotes:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<!--
 Quotes
 -->
<plist version="1.0">
    <array>
 
        <dict>
            <key>category</key>  <string>classic</string>
            <key>quote</key>  <string>Frankly my dear, I don't give a damn.</string>
            <key>ans1</key>  <string>Gone with the wind</string>
            <key>ans2</key>  <string>Casablanca</string>
            <key>ans3</key>  <string>Pride and Prejudice</string>
            <key>answer</key>  <string>1</string>
            <key>tip</key>  <string>Check the weather.  Is that a North-Westerly blowing?</string>
        </dict>
 
        <dict>
            <key>category</key>  <string>classic</string>
            <key>quote</key>  <string>Here's looking at you kid.</string>
            <key>ans1</key>  <string>The rear window</string>
            <key>ans2</key>  <string>To Kill a Mockingbird</string>
            <key>ans3</key>  <string>Casablanca</string>
            <key>answer</key>  <string>3</string>
            <key>tip</key>  <string>Moroccan tea anyone?</string>
        </dict>
 
        <dict>
            <key>category</key>  <string>classic</string>
            <key>quote</key>  <string>There's no place like home.</string>
            <key>ans1</key>  <string>Vertigo</string>
            <key>ans2</key>  <string>Wizard of Oz</string>
            <key>ans3</key>  <string>Singin in the rain</string>
            <key>answer</key>  <string>2</string>
            <key>tip</key>  <string>Be aware of the wicked witch.</string>
        </dict>
 
        <dict>
            <key>category</key>  <string>classic</string>
            <key>quote</key>  <string>Elementary my dear Watson.</string>
            <key>ans1</key>  <string>Sherlock Holmes</string>
            <key>ans2</key>  <string>The African Queen</string>
            <key>ans3</key>  <string>High Noon</string>
            <key>answer</key>  <string>1</string>
            <key>tip</key>  <string>It is mystery, this one.</string>
        </dict>
 
        <dict>
            <key>category</key>  <string>classic</string>
            <key>quote</key>  <string>Fasten your seatbelts. It's going to be a bumpy night.</string>
            <key>ans1</key>  <string>The Graduate</string>
            <key>ans2</key>  <string>All about Eve</string>
            <key>ans3</key>  <string>West Side Story</string>
            <key>answer</key>  <string>2</string>
            <key>tip</key>  <string>The birds and the bees.</string>
        </dict>
 
        <dict>
            <key>category</key>  <string>classic</string>
            <key>quote</key>  <string>I have not the pleasure of understanding you.</string>
            <key>ans1</key>  <string>Pride and Prejudice</string>
            <key>ans2</key>  <string>Jane Eyre</string>
            <key>ans3</key>  <string>Rebecca</string>
            <key>answer</key>  <string>1</string>
            <key>tip</key>  <string>I have high self esteem.</string>
        </dict>
 
 
        <dict>
            <key>category</key>  <string>classic</string>
            <key>quote</key>  <string>That's, uh, quite a dress you almost have on...What holds it up?</string>
            <key>ans1</key>  <string>It happened one night</string>
            <key>ans2</key>  <string>Roman Holiday</string>
            <key>ans3</key>  <string>An American in Paris</string>
            <key>answer</key>  <string>3</string>
            <key>tip</key>  <string>Fancy a baguette?</string>
        </dict>
 
        <dict>
            <key>category</key>  <string>classic</string>
            <key>quote</key>  <string>Love, desire, ambition, faith - without them life is so simple, believe me</string>
            <key>ans1</key>  <string>Easy Rider</string>
            <key>ans2</key>  <string>Invasion of the Body Snatchers</string>
            <key>ans3</key>  <string>Wuthering Heights</string>
            <key>answer</key>  <string>2</string>
            <key>tip</key>  <string>Danger, Danger.</string>
        </dict>
 
        <dict>
            <key>category</key>  <string>modern</string>
            <key>quote</key>  <string>Go ahead make my day.</string>
            <key>ans1</key>  <string>Dirty Harry</string>
            <key>ans2</key>  <string>Star wars</string>
            <key>ans3</key>  <string>Terminator</string>
            <key>answer</key>  <string>1</string>
            <key>tip</key>  <string>Get some soap.</string>
        </dict>
 
        <dict>
            <key>category</key>  <string>modern</string>
            <key>quote</key>  <string>May the Force be with you.</string>
            <key>ans1</key>  <string>One flew of the cuckoo's nest'</string>
            <key>ans2</key>  <string>Top Gun</string>
            <key>ans3</key>  <string>Star wars</string>
            <key>answer</key>  <string>3</string>
            <key>tip</key>  <string>It's a bright moon tonight.'</string>
        </dict>
        <dict>
            <key>category</key>  <string>modern</string>
            <key>quote</key>  <string>Hasta la vista, baby</string>
            <key>ans1</key>  <string>Harry Potter</string>
            <key>ans2</key>  <string>Terminator</string>
            <key>ans3</key>  <string>Pirates of the Carribean</string>
            <key>answer</key>  <string>2</string>
            <key>tip</key>  <string>The main character is an emotionless and efficient killing machine.</string>
        </dict>
        <dict>
            <key>category</key>  <string>modern</string>
            <key>quote</key>  <string>I feel the need for speed.</string>
            <key>ans1</key>  <string>Top Gun</string>
            <key>ans2</key>  <string>Twilight</string>
            <key>ans3</key>  <string>Speed</string>
            <key>answer</key>  <string>1</string>
            <key>tip</key>  <string>Flying high.</string>
        </dict>
        <dict>
            <key>category</key>  <string>modern</string>
            <key>quote</key>  <string>She doesn't even go here.</string>
            <key>ans1</key>  <string>St Trinians</string>
            <key>ans2</key>  <string>Wild Child</string>
            <key>ans3</key>  <string>Mean Girls</string>
            <key>answer</key>  <string>3</string>
            <key>tip</key>  <string>No one likes a bully.</string>
        </dict>
        <dict>
            <key>category</key>  <string>modern</string>
            <key>quote</key>  <string>It takes a great deal of bravery to stand up to your enemies, but a great deal more to stand up to your friends.</string>
            <key>ans1</key>  <string>Pirates of the Carribean</string>
            <key>ans2</key>  <string>Harry Potter</string>
            <key>ans3</key>  <string>Toy Story</string>
            <key>answer</key>  <string>2</string>
            <key>tip</key>  <string>Cast a spell on me.</string>
        </dict>
        <dict>
            <key>category</key>  <string>modern</string>
            <key>quote</key>  <string>I solemnly swear that I am up to no good.</string>
            <key>ans1</key>  <string>Harry Potter</string>
            <key>ans2</key>  <string>Shrek</string>
            <key>ans3</key>  <string>The Lion King</string>
            <key>answer</key>  <string>1</string>
            <key>tip</key>  <string>Cast another spell on me.</string>
        </dict>
 
        <dict>
            <key>category</key>  <string>modern</string>
            <key>quote</key>  <string>You like pain? Try wearing a corset.</string>
            <key>ans1</key>  <string>Spy kids</string>
            <key>ans2</key>  <string>Shrek</string>
            <key>ans3</key>  <string>Pirates of the Carribean</string>
            <key>answer</key>  <string>3</string>
            <key>tip</key>  <string>Aye, Aye, captain.</string>
        </dict>       
 
    </array>
</plist>

Property lists can be tiresome to debug, but you can easily use Xcode to validate your property list file to make sure that you haven’t missed a closing tag somewhere.

In Xcode, select Xcode\Product\Build For -> Running. If you see no compilation errors, you can be sure that your property list is solid, and you can continue on with the road trip!

Iterating Through the Property List

Now you need to implement the code that retrieves the next quote for the quiz.

Modify Quiz.h so that it looks the following:

#import <Foundation/Foundation.h>
 
@interface Quiz : NSObject
 
@property (nonatomic, retain) NSMutableArray * movieArray; // Previous Property
@property (nonatomic, assign) NSInteger correctCount;
@property (nonatomic, assign) NSInteger incorrectCount;
@property (nonatomic, assign) NSInteger quizCount;
@property (nonatomic, readonly, strong) NSString * quote;
@property (nonatomic, readonly, strong) NSString * ans1;
@property (nonatomic, readonly, strong) NSString * ans2;
@property (nonatomic, readonly, strong) NSString * ans3;
 
-(id) initWithQuiz:(NSString*)plistName; // Previous Method
-(void) nextQuestion: (NSUInteger) idx;
-(BOOL) checkQuestion: (NSUInteger) question forAnswer: (NSUInteger) answer;
 
@end

The properties and methods declared above will support the retrieval of questions and answers in your quiz. The Quiz object also keeps track of the number of correct and incorrect questions, as well as the total number of questions that were asked.

The methods above are publicly accessible because you’ve declared them in your header file. However, if you have methods in your implementation that are private and used only internally by your implementation code, then you don’t need to declare them in your header.

Okay, now you’ll start fleshing out your implementation.

Add the following code to Quiz.m before the @implementation line:

@interface Quiz()
    @property (nonatomic, strong) NSString * quote;
    @property (nonatomic, strong) NSString * ans1;
    @property (nonatomic, strong) NSString * ans2;
    @property (nonatomic, strong) NSString * ans3;
@end

Hey, those property definitions look familiar! They’re almost the same as the ones you defined in your header file. The only difference is that these definitions don’t include the readonly keyword. What gives?

This is known as a class extension, which gives you the ability to read and write to these properties locally inside your implementation. But to the rest of the world, these properties still appear read-only.

Okay, now that all of the properties you need are in place, it’s time to put them to use!

Add the following code to Quiz.m after the @implementation line:

- (void) nextQuestion: (NSUInteger) idx 
{    
    self.quote = [NSString stringWithFormat:@"'%@'",self.movieArray[idx][@"quote"]];
 
    self.ans1 = self.movieArray[idx][@"ans1"];
    self.ans2 = self.movieArray[idx][@"ans2"];
    self.ans3 = self.movieArray[idx][@"ans3"];
 
    if (idx == 0) {
        self.correctCount = 0;
        self.incorrectCount = 0;
    }
}

nextQuestion sets the internal values of the quiz object. It reads in a quote and three possible answers from movieArray, which contains all the data loaded from your property list.

If this is the first question of the quiz, then the app resets the quiz totals just in case the user want to play through the quiz again to beat their previous high score!

Now, how are you going to determine if the answer the player gives is right or wrong? checkQuestion can do that work for you.

Add the following code to Quiz.m just before @end:

- (BOOL) checkQuestion: (NSUInteger) question forAnswer: (NSUInteger) answer
{
    NSString * ans = self.movieArray[question] [@"answer"];   
    if  ([ans intValue] == answer) {
        self.correctCount++;
        return YES;
    } else  {
        self.incorrectCount++;
        return NO;
    }
}

checkQuestion takes an input parameter (NSUInteger) question as the index of the correct question. If the answer the player gives matches the correct answer, then correctCount is incremented; otherwise incorrectCount is incremented.

Finally you need to to keep track of the number of rows in the array.

Inside Quiz.m, modify initWithQuiz by adding a line to set self.quizCount, as shown below:

if ((self = [super init]))
{
    NSString *plistCatPath = [[NSBundle mainBundle] pathForResource:plistName ofType:@"plist"];
    self.movieArray = [NSMutableArray arrayWithContentsOfFile:plistCatPath];
    self.quizCount = [self.movieArray count];
}

Providing Feedback Through the UI

It’s always a great idea to provide some feedback to the user as they navigate through your app.
When an answer button is touched you’ll change the background color of the answer label. Green is for correct answers. Red is for incorrect answers. A new question reverts the labels to the original color.

To modify the various UI elements in your app from code, you’ll first need to define some private properties so that you can access them later.

Add the following code to QuoteQuizViewController.m, just after the @interface statement:

@property (weak, nonatomic) IBOutlet UILabel * questionLabel;
@property (weak, nonatomic) IBOutlet UILabel * answer1Label;
@property (weak, nonatomic) IBOutlet UILabel * answer2Label;
@property (weak, nonatomic) IBOutlet UILabel * answer3Label;
@property (weak, nonatomic) IBOutlet UIButton * answer1Button;
@property (weak, nonatomic) IBOutlet UIButton * answer2Button;
@property (weak, nonatomic) IBOutlet UIButton * answer3Button;
@property (weak, nonatomic) IBOutlet UIImageView * movie1;
@property (weak, nonatomic) IBOutlet UIImageView * movie2;
@property (weak, nonatomic) IBOutlet UIImageView * movie3;
@property (weak, nonatomic) IBOutlet UILabel * statusLabel;
@property (weak, nonatomic) IBOutlet UIButton * startButton;
@property (weak, nonatomic) IBOutlet UIButton * infoButton;

While these properties are privately scoped, Interface Builder (the actual user interface editor) will know where to look in your class to find them. Remember that the IBOutlet keyword means that this is an object that can be linked to an interface element.

Also, you’ll need a private property to keep track of the current answer.

Add the following line to the end of the list of @property declarations in QuoteQuizViewController.m:

@property (assign, nonatomic) NSInteger answer;

You’ll need a way to handle the end-game scenario, when the player has answered all of the questions.

Add the following code to QuoteQuizViewController.m just before the final @end :

- (void)quizDone
{
    // More later
}

Hey, there’s no code in this method!

Yep — it’s just stubbed out for now to satisfy the compiler. You’ll flesh out this method later on in the tutorial, but for now you can leave it as-is.

Once the player has answered the first question, you’ll want to move on to the next question and present all the questions in sequence. To do so, you’ll need a method to get the next quote in line.

Add the following code to QuoteQuizViewController.m, just before the final @end:

- (void)nextQuizItem
{
     if (self.quizIndex == 999) {
        self.quizIndex = 0;
        self.statusLabel.text = @"";
    } else if ((self.quiz.quizCount-1) > self.quizIndex) {
        self.quizIndex++;
    } else {
        self.quizIndex = 0;
        self.statusLabel.text = @"";
    }
 
    if (self.quiz.quizCount >= self.quizIndex + 1) {
        [self.quiz nextQuestion:self.quizIndex];
        self.questionLabel.text = self.quiz.quote;
        self.answer1Label.text = self.quiz.ans1;
        self.answer2Label.text = self.quiz.ans2;
        self.answer3Label.text = self.quiz.ans3;
    } else {
        self.quizIndex = 0;
        [self quizDone];
    }
 
    // reset fields for next question
    self.answer1Label.backgroundColor = [UIColor colorWithRed:51/255.0 green:133/255.0 blue:238/255.0 alpha:1.0];
    self.answer2Label.backgroundColor = [UIColor colorWithRed:51/255.0 green:133/255.0 blue:238/255.0 alpha:1.0];
    self.answer3Label.backgroundColor = [UIColor colorWithRed:51/255.0 green:133/255.0 blue:238/255.0 alpha:1.0];
 
    self.answer1Button.hidden = NO;
    self.answer2Button.hidden = NO;
    self.answer3Button.hidden = NO;
}

You now need to make a call to nextQuizItem to start the game once your app has finished initializing.

Add the following code to the bottom of viewDidLoad in QuoteQuizViewController.m:

[self nextQuizItem];

Now you must link up each of the scene objects so that you can access them in your code.

Click on MainStoryboard.storyboard to display the Quite Quiz View Controller Scene if it’s not already displayed.

Bring up the connection dialog by right clicking on the scene name — Quote Quiz View Controller.

In the connection dialog, find the questionLabel outlet. Click the circle on the right hand side of the outlet and drag it to the question label on the screen, like so:

Setting up the outlets

Now repeat the process to hook up the following outlets to their respective controls: answer1Label, answer2Label, answer3Label, answer1Button, answer2Button, and finally answer3Button.

Your outlets dialog should look like the following:

Outlets dialog

Build and run, and your app should look like the screenshot below:

Running the app

Hm, it would be nice for the background color of the question to match that of the answers. You could do this in the Attributes Inspector, but you can also do this in code.

Open QuoteQuizViewController.m and add the following line at the bottom of viewDidLoad:

    self.questionLabel.backgroundColor = [UIColor colorWithRed:51/255.0 green:133/255.0 blue:238/255.0 alpha:1.0];

Build and run, and yep – they seem to match now:

Running for the second time

If you receive any errors, don’t panic. Compare your code with the tutorial’s code, and pay close attention to the case.

If your app does compile and run, you should see your first quote!

Okay, you’ve hit the on-ramp in your Objective-C road test, and now it’s time to merge on to the interstate! :]

Adding Actions to Your Buttons

Open QuoteQuizViewController.h add the following lines after the @interface declaration:

- (IBAction)ans1Action:(id)sender;
- (IBAction)ans2Action:(id)sender;
- (IBAction)ans3Action:(id)sender;
- (IBAction)startAgain:(id)sender;

The lines above create the action methods for the answer buttons, as well as a method to restart the quiz. The IBAction keyword allows the method to be linked to a view object.

Now you need to add the implementation of these four new methods.

Add the following code above the final @end declaration in QuoteQuizViewController.m:

- (void)checkAnswer
{
    if ([self.quiz checkQuestion:self.quizIndex forAnswer:_answer]) {
        if (self.answer == 1) {
            self.answer1Label.backgroundColor = [UIColor greenColor];
        } else if (self.answer == 2) {
            self.answer2Label.backgroundColor = [UIColor greenColor];
        } else if (self.answer == 3) {
            self.answer3Label.backgroundColor = [UIColor greenColor];
        }
    } else {
        if (self.answer == 1) {
            self.answer1Label.backgroundColor = [UIColor redColor];
        } else if (self.answer == 2) {
            self.answer2Label.backgroundColor = [UIColor redColor];
        } else if (self.answer == 3) {
            self.answer3Label.backgroundColor = [UIColor redColor];
        }
    }
 
    self.statusLabel.text = [NSString stringWithFormat:@"Correct: %d Incorrect: %d",self.quiz.correctCount, self.quiz.incorrectCount];
 
    self.answer1Button.hidden = YES;
    self.answer2Button.hidden = YES;
    self.answer3Button.hidden = YES;
 
    self.startButton.hidden = NO;
 
    [self.startButton setTitle:@"Next" forState:UIControlStateNormal];
}
 
- (IBAction)ans1Action:(id)sender 
{
    self.answer = 1;
    [self checkAnswer];
}
 
- (IBAction)ans2Action:(id)sender 
{
    self.answer = 2;
    [self checkAnswer];
}
 
- (IBAction)ans3Action:(id)sender 
{
    self.answer = 3;
    [self checkAnswer];    
}
 
- (IBAction)startAgain:(id)sender 
{    
    [self nextQuizItem];
}

The ans1Action and sibling methods set the user’s answer, then call checkAnswer to determine if the answer was correct.

checkAnswer changes the color of the corresponding label to indicate if the answer is correct or not. The user interface then updates to display the label change.

Now, you need to connect the above action methods in your code to the buttons on your interface.

Open MainStoryboard.storyboard. Select the first button, then right-click and drag to the scene name Quote Quiz View Controller, as shown below:

Connecting the IBAction to the button

A list of IBActions will appear next to the button. Select ans1Action to connect the action with the button, like so:

Select the first action

The action method will be associated with the default event of “touch-up inside”. The “touch-up inside” event initiates the action method once the user has lifted their finger off the button. This allows a user to cancel the button press by sliding their finger off the button.

Link up the rest of the buttons with their designated action method.

Then build and run your app, and test out the controls by selecting an answer from the ones presented. An incorrect answer will turn the label red, while a correct answer will turn the label green, as seen in the screenshot below:

Selecting an answer

Looks good so far, but there’s one little problem: there’s no way to move on to the next question! As well, there’s nothing to indicate the current score to the player.

To fix this, open MainStoryboard.storyboard. First, you’ll need to make some room for the player’s score.

Hold down the command button, and select all of the answer labels and buttons. Drag them as a group to leave some space between the Question label and the first answer button and label — about thirty points total.

Next, drag a new label into the space you just created and extend its width to match the width of the Question label. Change the label’s text to “Correct: Incorrect:”, as shown in the screenshot below:

Adding a new label

Next, add a button to the interface, and position it on the right hand side of the view, flush with the last answer label. Set the width of the button to be 119 points, and the height to be 44 points. Finally, change the text of the button to read “Next”, as seen here:

Adding the next button

Hook up the outlets by right-clicking the scene name Quote Quiz View Controller and drag to the “Correct: Incorrect” label. Select statusLabel from the available outlets. Do the same for the button, selecting startButton outlet.

Now, right click and drag from the button to the scene name, and select the startAgain action.

Finally, flesh out quizDone in QuoteQuizViewController.m as follows:

- (void)quizDone
{
    if (self.quiz.correctCount) {
        self.statusLabel.text = [NSString stringWithFormat:@"Quiz Done - Score %d%%", self.quiz.quizCount / self.quiz.correctCount];    
    } else {
        self.statusLabel.text = @"Quiz Done - Score: 0%";
    }
    [self.startButton setTitle:@"Try Again" forState:UIControlStateNormal];
    self.quizIndex = 999;
}

quizDone first checks if the user has correctly answered any questions. If so, it does a simple calculation and displays the number of correct answers. Otherwise, it outputs a pre-defined message, preventing any possible division by zero errors. The method then resets the button to try again, and finally resets the quiz index.

Build and run your app, and now you should now have a fully functional quiz!

Advancing to the next question

Feel free to play around with the quiz — and see how much of a movie buff you are by seeing how many answers you can get right.

With the quiz done, it’s time to put the pedal to the metal and see how fast you can really drive! :]

The user may appreciate a little help every so often, so your app should probably provide some clues to assist the user.

Adding a New View

You might have noticed that the property list has tips as one of its elements. This is where you implement the ability for the player to call up the “tip” screen and get a tip for the current question.

First, you’ll need to create the methods to keep track of the tip for each clue.

Add the following variables to Quiz.h at the end of the list of @property declarations:

@property (nonatomic, assign) NSInteger tipCount;
@property (nonatomic, strong) NSString * tip;

The first variable keeps track of the number of tips, while the second stores the current tip.

Now you’ll need to initialize tipCount to zero the first time through.

Add the following line to Quiz.m, at the end of the if statement block in initWithQuiz:

self.tipCount = 0;

You’ll also need to reset all the pertinent variables when the user moves on to the next question.

Next modify nextQuestion in Quiz.m as follows:

- (void) nextQuestion: (NSUInteger) idx 
{    
    self.quote = [NSString stringWithFormat:@"'%@'",self.movieArray[idx][@"quote"]];
 
    self.ans1 = self.movieArray[idx][@"ans1"];
    self.ans2 = self.movieArray[idx][@"ans2"];
    self.ans3 = self.movieArray[idx][@"ans3"];
    self.tip = self.movieArray[idx][@"tip"];
 
    if (idx == 0) {
        self.correctCount = 0;
        self.incorrectCount = 0;
        self.tipCount = 0;
    }
 
}

In the code above, you first obtain the current tip, then set the quiz count to zero when the user is on the first question.

If the user has used up all of their allotted tips, then the tips button should not be available to the player.

To do this, move back to QuoteQuizViewController.m and add the following code to the end of nextQuizItem:

if (self.quiz.tipCount < 3) {
    self.infoButton.hidden = NO;
} else {
    self.infoButton.hidden = YES;
}

Once tipCount exceeds the three allotted tips, then you simply set the button’s hidden property to make it unavailable.

Showing the Tips

Now that all of the supporting modules have been created, you’ll need to create a view controller to show the requested tips to the player.

To do this, go to File\New File…, choose the iOS\Cocoa Touch Class\Objective-C class template, and click Next. Name the new class QuizTipViewController and make it a subclass of UIViewController. Finally, click Next then Create.

Now you need to add a protocol to QuizTipViewController.h so that this class becomes a delegate.

Add the following code to QuizTipViewController.h just before @interface:

@protocol QuizTipViewControllerDelegate;

A protocol allows classes to share similarly defined behavior. This is helpful when classes are not related to each other, but still behave in similar ways. In this way, classes do not need to “know” about one another, which reduces dependencies throughout your code.

Notice that the protocol has the word “delegate” in it. What’s a delegate?

A delegate is an object that performs work on behalf of another object. For example, a foreman delegates the construction of a sidewalk to some of his workers in the same way an object may delegate the processing of information to another object. In this case, the class will notify another object that an event has occurred.

By using delegates with protocols, the delegator need not be concerned with the actual objects that are doing the work — so long as the work gets done! :]

Add the following code to QuizTipViewController.h between @interface and @end:

@property (nonatomic, assign) id <QuizTipViewControllerDelegate> delegate;
@property (weak, nonatomic) IBOutlet UITextView * tipView;
@property (nonatomic, copy) NSString * tipText;

When the user presses the “Done” button, QuizTipViewController will notify its delegate that the user is finished and the tip view should be dismissed. In this case, the delegate is QuizViewController which also is the presenter of QuizTipViewController.

Once QuizViewController is told that the “show is over”, it will dismiss QuizTipViewController.

Add the following line of code to QuizTipViewController.h just before @end:

- (IBAction)doneAction:(id)sender;

You’re going to add a button that says “Done” on this view controller, and here you’re declaring the method that you’ll set up to be called when the button is tapped.

Finally, declare the rest of your protocol. While protocols can be contained in their own file, it is also common for developers to define them in the header file of the related class.

Add the following code to QuizTipViewController.h just after @end:

@protocol QuizTipViewControllerDelegate
- (void)quizTipDidFinish:(QuizTipViewController *)controller;
@end

Here you say that any class that implements this protocol must implement one method, quizTipDidFinish:.

Switch to QuizTipViewController.m. At the end of viewDidLoad, add the following code:

self.tipView.text = self.tipText;

This will set the tipText field to the tip that was set in the class variable.

Next, add the following method to QuizTipViewController.m, just before the @end declaration:

- (IBAction)doneAction:(id)sender
{
   [self.delegate quizTipDidFinish:self];
}

Here you tell call the quizTipDidFinish on the delegate method when the button is tapped.

Next, let’s make the QuoteQuizViewController implement the delegate.

Switch over to QuoteQuizViewController.h and add the import line for QuizTipViewController.h directly below the import statement for UIKit. Also add <QuizTipViewControllerDelegate> to the end of the @interface declaration to mark it as implementing the protocol, as shown below:

#import <UIKit/UIKit.h>
#import "QuizTipViewController.h"
 
@class Quiz;
@interface QuoteQuizViewController : UIViewController <QuizTipViewControllerDelegate>
 
@property (nonatomic, assign) NSInteger quizIndex;
@property (nonatomic, strong) Quiz * quiz;
 
- (IBAction)ans1Action:(id)sender;
- (IBAction)ans2Action:(id)sender;
- (IBAction)ans3Action:(id)sender;
- (IBAction)startAgain:(id)sender;
 
@end

Open QuoteQuizViewController.m and create quizTipDidMethod — as defined by the protocol — by adding the code below just before the final @end:

- (void)quizTipDidFinish:(QuizTipViewController *) controller {
    [self dismissViewControllerAnimated:YES completion:^{}];
}

So when the user taps the done button, this delegate method gets called, and all you do is dismiss the view controller. You could do some more fancy work here if you wanted, like saving data.

Now it’s time to add this new view controller to your user interface. Open the MainStoryboard.storyboard and drag in a new view controller, as shown in the screenshot below:

Add a new view controller

Select the new view controller and switch to the Identity Inspector (the third tab in). Under the Custom Class section, set the class field to be QuizTipViewController, as shown below:

Changing the class

Drag a label onto the view and place it near the top of the screen. Double-click the label and modify the label’s text to be “Movie tip (3 tips only)“. Center the label in the middle of the view.

Now drag in a textview under the label. Set the width to be 280 points, and the height to be 136 points.

Place a button underneath the textview, and make it the same width as the textview. Modify the text of the button to read “Done”.

Your interface should now look like the following:

TipViewAdded

Right-click the Quiz Tip View Controller Scene, drag it to the text view, and then select the tipView outlet. Next, right-click the Done button, drag it to the Quote Tip View Controller, and select doneAction.

Now it is time to link up the two view controllers using a segue. First you will need a tip button — this will be the button that triggers the app to show the quiz tip view.

Scroll over to the previous view controller and select the “Next” button. Copy and paste the Next button, and drag this new button to the left side of the view controller. Modify the button’s text to read Tip.

AddingTipButton

Since you copied the button, you also copied the actions associated with it. Right click the button, and click the ‘X’ button next to the action associated with “Touch Up Inside” action, to remove the action, as shown below:

Delete action

We could have just dragged in a new button, but this is a common problem to run across so I wanted to show you it.

Finally, right-click Quote Quiz View Controller, drag over to the tip button, then select the infoButton outlet.

Adding a Segue Between Views

To hookup the two view controllers by a segue, select the Tip button, then right click on the button and drag over to QuizTipViewController, like so:

Connecting the two view controller with segues

From the menu selection, select modal.

You will notice a line with a slight curve is now connecting the two views. This is the segue.

Select the circle in between the view controllers, then select the Attributes Inspector. In the Identifier field, enter TipModal, like so:

Setting up the segue

Build and run your app, and click on the Tip button, and you’ll see the following:

TipViewiPhone

As you can see, the view controller shows only the empty text — and when you click the Done button, nothing happens!

You’ll need to initialize the view controller during the segue, and set the tip text accordingly.

You can manipulate a view controller or run other additional code before the view controller is presented on the screen. Your segue must have a unique name, so that you can trigger different actions based on which segue is being triggered — in this case, it’s TipModal.

To do so, add the following code to QuoteQuizViewController.m, just before the final @end:

- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
    if ([segue.identifier isEqualToString:@"TipModal"]) {
        QuizTipViewController * detailViewController = (QuizTipViewController *) segue.destinationViewController;
        detailViewController.delegate = self;
        detailViewController.tipText = self.quiz.tip;
    }
}

This is the actual segue code. First, you obtain the next view controller and since it is returned as a type id, you must cast it to your destination view controller. Then you can manipulate this view controller like it were any other object, and set the various properties as required.

Build and run your app, and when the first clue presents itself, tap the Tip button. You should see the following:

TipScreenFinished

Click the Done button to dismiss the view controller. It all works!

Congratulations, you have made a complete quiz app – and learned a ton about Objective-C and iOS development in general in the process!

Where to Go From Here?

Here is a sample project with all of the code from the above tutorial.

Well, you’ve come to the end of your road – and hopefully you’ve learned a lot of cool new things! Take a break and when you are feeling rested, gas up the car then put the pedal to the metal. There’s a whole wide world of app development just waiting for you out there! :]

The next best place to start is to check out our huge list of free tutorials, or check out some of our books and starter kits.

Also, feel free to stop by the forums if you have any questions about what you’ve done. Also, if you liked this tutorial and would like to see more in the series, please let me know!

In the meantime, good luck and I’ll see you on the road! :]

Linda is an IT Professional with 20+ years experience in the IT industry based in Melbourne, Australia. Linda has gotten back to her roots (and youth) by developing iOS apps in her spare time. She is the founder of a family based iPhone app development studio, canApps. When Linda is not developing apps by the fire, she is loving life… and thinking of new app ideas! You can follow Linda on Twitter.

User Comments

23 Comments

[ 1 , 2 ]
  • Love the Tutorial,

    I am trying to add an image to the quizzes though but am having trouble figuring out the code. Can someone please help.

    thanks
    Scubadivingfool
  • Can anyone explain me the following code-

    - (id)initWithQuiz:(NSString*)plistName;

    is initWithQuiz is a user defined function with parameter plistName
    amit199
  • Too bad you never bother to explain this part of the code. I had to go over it in my head repeatedly to finally understand it. Just find it funny how you make 0 effort to explain it let alone write a comment. Hope I don't stumble across another one of your tutorials on this site.


    - (void)checkAnswer
    {
    if ([self.quiz checkQuestion:self.quizIndex forAnswer:_answer]) {
    if (self.answer == 1) {
    self.answer1Label.backgroundColor = [UIColor greenColor];
    } else if (self.answer == 2) {
    self.answer2Label.backgroundColor = [UIColor greenColor];
    } else if (self.answer == 3) {
    self.answer3Label.backgroundColor = [UIColor greenColor];
    }
    } else {
    if (self.answer == 1) {
    self.answer1Label.backgroundColor = [UIColor redColor];
    } else if (self.answer == 2) {
    self.answer2Label.backgroundColor = [UIColor redColor];
    } else if (self.answer == 3) {
    self.answer3Label.backgroundColor = [UIColor redColor];
    }
    }
    jookjah
  • You make one of the worst tutorials on this site. You're terrible and explaining stuff. Please don't make any tutorials anymore.
    jookjah
  • This tutorial is fine but it has some errors. Did you actually run the code that is on the site? There are errors in nextQuizItem and the setting of the startButton title to Next is in the wrong place. See below:

    The setting of the startButton title is placed in method checkAnswer. But this never gets hit if the user only clicks next and never chooses an answer. A better placement is in method nextQuizItem (which is hit regardless of the user selecting an answer) in place of setting the startButton visible -- the latter being unnecessary since it is never set hidden in the first place.

    The code in nextQuizItem, in the first conditional set, sets quizIndex = 0. This causes it to NEVER be able to fall through the next conditional and take the else clause where it invokes quizDone. I made the following change and it works.

    - (void)nextQuizItem {
    if (self.quizIndex == 999) {
    // Restart
    self.quizIndex = 0;
    self.statusLabel.text = @"";
    }
    else if ((self.quiz.quizCount - 1) > self.quizIndex) {
    // Next question
    self.quizIndex++;
    }
    else {
    // Done
    self.quizIndex = 0;
    // Add invocation of quizDone
    [self quizDone];
    // Comment the next line because we don't want to clear it yet.
    // self.statusLabel.text = @"";
    }

    if (self.quiz.quizCount >= self.quizIndex + 1) {
    // Not done yet
    [self.quiz nextQuestion:self.quizIndex];
    self.questionLabel.text = self.quiz.quote;
    self.answer1Label.text = self.quiz.ans1;
    self.answer2Label.text = self.quiz.ans2;
    self.answer3Label.text = self.quiz.ans3;
    }
    // Comment out this conditional; it is unnecessary and unreachable
    // else {
    // // Done
    // self.quizIndex = 0;
    // [self quizDone];
    // }

    // reset fields for next question
    self.answer1Label.backgroundColor = [UIColor colorWithRed:51/255.0 green:133/255.0 blue:238/255.0 alpha:1.0];
    self.answer2Label.backgroundColor = [UIColor colorWithRed:51/255.0 green:133/255.0 blue:238/255.0 alpha:1.0];
    self.answer3Label.backgroundColor = [UIColor colorWithRed:51/255.0 green:133/255.0 blue:238/255.0 alpha:1.0];

    self.answer1Button.hidden = NO;
    self.answer2Button.hidden = NO;
    self.answer3Button.hidden = NO;

    }
    RodBarnes
  • There are errors in this tutorial; did you even run the code that is IN the tutorial?

    Method nextQuizItem has two conditional sets. The first one sets self.quizIndex = 0 when it reaches the end of the list. However, this causes it to NEVER fall into the else clause of the next conditional set so that it next invokes method quizDone. Below is my modification which works. Note, also, I moved the setting of the startButton title into method nextQuizItem rather than in method checkAnswer since the latter never gets called if the user doesn't select anything and only clicks [Next]. Since the program allows for this, and it is really the [Next] action that goes to the next quote and NOT the checking of an answer, this is the appropriate location.

    - (void)nextQuizItem {
    if (self.quizIndex == 999) {
    // Restart
    self.quizIndex = 0;
    self.statusLabel.text = @"";
    }
    else if ((self.quiz.quizCount - 1) > self.quizIndex) {
    // Next question
    self.quizIndex++;
    }
    else {
    // Done
    self.quizIndex = 0;
    // Add invocation of quizDone
    [self quizDone];
    // Comment the next line because we don't want to clear it yet.
    // self.statusLabel.text = @"";
    }

    if (self.quiz.quizCount >= self.quizIndex + 1) {
    // Not done yet
    [self.quiz nextQuestion:self.quizIndex];
    self.questionLabel.text = self.quiz.quote;
    self.answer1Label.text = self.quiz.ans1;
    self.answer2Label.text = self.quiz.ans2;
    self.answer3Label.text = self.quiz.ans3;
    }
    // Comment out this conditional; it is unnecessary and unreachable
    // else {
    // // Done
    // self.quizIndex = 0;
    // [self quizDone];
    // }

    // reset fields for next question
    self.answer1Label.backgroundColor = [UIColor colorWithRed:51/255.0 green:133/255.0 blue:238/255.0 alpha:1.0];
    self.answer2Label.backgroundColor = [UIColor colorWithRed:51/255.0 green:133/255.0 blue:238/255.0 alpha:1.0];
    self.answer3Label.backgroundColor = [UIColor colorWithRed:51/255.0 green:133/255.0 blue:238/255.0 alpha:1.0];

    self.answer1Button.hidden = NO;
    self.answer2Button.hidden = NO;
    self.answer3Button.hidden = NO;

    [self.startButton setTitle:@"Next" forState:UIControlStateNormal];

    }
    RodBarnes
  • Sorry for the duplicate comment folks. The first one never appeared so I re-entered it. Then they finally both appeared.
    RodBarnes
  • Hi all! i have a question! why is that in this code:

    if ([self.quiz checkQuestion:self.quizIndex forAnswer:_answer]) {

    the _answer appears and i have not created that variable and what value does it take here? maybe a stupid question but is making noise in my head! hope you can help me!
    jdecuirm
[ 1 , 2 ]

Other Items of Interest

Ray's Monthly Newsletter

Sign up to receive a monthly newsletter with my favorite dev links, and receive a free epic-length tutorial as a bonus!

Advertise with Us!

Hang Out With Us!

Every month, we have a free live Tech Talk - come hang out with us!


Coming up in July: Facebook Pop Tech Talk!

Sign Up - July

RWDevCon Conference?

We are considering having an official raywenderlich.com conference called RWDevCon in DC in early 2015.

The conference would be focused on high quality Swift/iOS 8 technical content, and connecting as a community.

Would this be something you'd be interested in?

    Loading ... Loading ...

Our Books

Our Team

Tutorial Team

... 50 total!

Update Team

  • Riccardo D'Antoni

Editorial Team

  • Ryan Nystrom

... 23 total!

Code Team

  • Orta Therox

... 1 total!

Translation Team

... 33 total!

Subject Matter Experts

  • Richard Casey

... 4 total!