Bitfountain Objective-C for iOS 9

Although Swift is the future of iOS Dev, Objective-C is still required by employers. Learn how to read and write it.
4.4 (8 ratings)
Instead of using a simple lifetime average, Udemy calculates a
course's star rating by considering a number of different factors
such as the number of ratings, the age of ratings, and the
likelihood of fraudulent ratings.
46 students enrolled
$19
$20
5% off
Take This Course
  • Lectures 39
  • Length 3 hours
  • Skill Level Intermediate Level
  • Languages English
  • Includes Lifetime access
    30 day money back guarantee!
    Available on iOS and Android
    Certificate of Completion
Wishlisted Wishlist

How taking a course works

Discover

Find online courses made by experts from around the world.

Learn

Take your courses with you and learn anywhere, anytime.

Master

Learn and practice real-world skills and achieve your goals.

About This Course

Published 12/2015 English

Course Description

Although Swift is the future of iOS development, Objective-C is still relevant. If you're trying to get a job as an iOS developer, knowing Objective-C is required. Most legacy libraries are written in the language, and you need to know how to interact with them. Learn everything you need to know by building a pirate adventure game through the expert, heartcrafted methods used in all Bitfountain courses.

Most iOS courses these days are teaching Swift. We teach Swift in our intro course too! But we also realize that the days of Objective-C are far from over. If you have a little experience coding, we'll get you building iOS 9 apps in Objective-C quickly.

What are the requirements?

  • Basic programming skills in Swift or a similar language
  • Ability to download Xcode (a computer running OSX is usually required)

What am I going to get from this course?

  • Build iOS 9 apps in Objective-C
  • Interoperate between Swift and Objective-C
  • Read and manipulate Objective-C libraries

What is the target audience?

  • This course is not for students who are complete beginners in programming. See our iOS 9 Foundation Course if you are new to coding.

What you get with this course?

Not for you? No problem.
30 day money back guarantee.

Forever yours.
Lifetime access.

Learn on the go.
Desktop, iOS and Android.

Get rewarded.
Certificate of completion.

Curriculum

Section 1: Objective-C Intro
01:47

Welcome to the Objective-C Intro Course.

We will be learning Objective-C by creating an Adventure game.

We'll see the differences and similarities between Objective-C and Swift.

Though Swift is the future, knowing Objective-C will help make you a much better developer since you may still need to deal with it when using third-party code or even some of Apple's frameworks.

Section 2: Create the Pirate Adventure Game in Objective-C
03:29

Welcome to the Adventure Game. We will build this in Objective-C. This will give us a chance to become familiar with the language.

Create a new project: New Project-> iOS Application-> Single View Application

Product Name: Pirate Adventure Game

Language: Objective-C

Though we can mix both languages, it's better to stick to only one if possible. Sometimes you **have** to mix them, for example if you have a Swift app that uses a third-party Objective-C library.

Create a git repository when asked.

The first thing you will notice is that there are 2 files for both AppDelegate and ViewController whereas in Swift we have only 1 for each. This is because files in Objective-C always comprise a header file, with extension `.h` and an implementation file, with header `.m`.

The header file is public by default whereas the implementation file is private.

This means that any code we put into the header file will be accessible to all other files in our app, but code in the implementation file will only be accessible to that file.

You will also have to import files manually. In Swift, files get imported automatically, not so in Objective-C.

---

Final code can be found on the Github Page (https://github.com/BitFountain/Objective-C-AdventureGame/archive/master.zip).

09:35

In this lecture we will be setting up the storyboard.

Download the Assets and import them into our project by dragging the files into `Assets.xcassets`.

Set the size in the storyboard to Compact/Regular.

Change the background color to: RGB 238, 215, 161.

Add a UIImageView and resize it in the Size Inspector to 374 x 667.

Set the image in the Attributes Inspector to map.

Add 4 UIImageViews to the top of the storyboard. They should be around 56 x 50 in size.

The images should be heart, fist, sword and helmet, in that order.

Add 4 labels that will reside beneath each image. The default text will be Health, Damage, Weapon, Armor.

Add another label. Make it bigger and change its background color to white and Light Grey Color for the font.

Add a button with the title Action. Change the background color to a brown that looks good with the rest of the palette. Change the font color to white.

Add an image and place it next to the button. The image should be the same height of the button.

Add an image for the compass rose and 4 buttons, one for each North, East, South, West.

Add a button for reset. Red background, white text.

Add an image at the bottom for the Bitfountain whale.

07:39

In this lecture we will add `IBOutlets` for our UI elements.

We can make connections to both the header and implementation files. But the better way to do it is to add them to the implementation file so that only that class is responsible for its view. Remember that adding them to the header file will make them public, hence accessible from any other class in your app. This might also lead to potential naming conflicts.

Option-Click the ViewController.m file so it will show side by side with the storyboard.

To create the connections you need to Ctrl-Drag from the storyboard UI element to the ViewController.m file but only between the `@interface` and `@end` block. This is where we add private properties for that class.

Outlet syntax:

`@property` means we are declaring a property.

`weak` and `nonatomic` are options of this property. `weak` means the memory retention will be weak, it is a *weak pointer*, Xcode will not need to hold on to this property in memory once its parent is released.

`nonatomic` makes this property thread-safe.

`IBOutlet` is for the compiler to know this is coming from the storyboard.

`UILabel` is the type or class of the outlet.

`*` is a pointer, then the name of the property `healthLabel`. The pointer says that it points to where the label is stored in memory. In Swift we don't have to worry about pointers, they are handled automatically for us, but in Objective-C we have to tell Xcode when we want to use a pointer.

All lines in Objective-C end with a semicolon. If you don't add them, you will get an error.

Code:

@property (weak, nonatomic) IBOutlet UILabel *healthLabel;

@property (weak, nonatomic) IBOutlet UILabel *damageLabel;

@property (weak, nonatomic) IBOutlet UILabel *weaponLabel;

@property (weak, nonatomic) IBOutlet UILabel *armorLabel;

@property (weak, nonatomic) IBOutlet UILabel *storyLabel;

@property (weak, nonatomic) IBOutlet UIButton *actionButton;

@property (weak, nonatomic) IBOutlet UIButton *northButton;

@property (weak, nonatomic) IBOutlet UIButton *westButton;

@property (weak, nonatomic) IBOutlet UIButton *southButton;

@property (weak, nonatomic) IBOutlet UIButton *eastButton;

@property (weak, nonatomic) IBOutlet UIImageView *itemImageView;

03:32

In this lecture we will setup `IBActions` for our buttons.

We will add them to the implementation file.

Notice that the `IBActions` are added below the `IBOutlets`. We add them to the implementation section of our implementation file, the block of code between `@implementation` and `@end`.

We can add `IBActions` into the public section (the header file) if you wanted it to be called by other classes outside of this file.

IBAction syntax:

IBAction is a hint to the compiler that this is a connection from the storyboard. `(IBAction)` is the same as writing `(void)` meaning that the function does not return anything.

The method begins with a dash `-` then we specify the return type inside parenthesis, then the name of the method, a colon and then the parameter type inside parenthesis as well, the pointer character `*` (asterisk) if appropriate, and then the parameter name. So, it's almost the reverse of Swift.

Code:

-(IBAction)actionButtonPressed:(UIButton *)sender {

}

-(IBAction)northButtonPressed:(UIButton *)sender {

}

-(IBAction)westButtonPressed:(UIButton *)sender {

}

-(IBAction)southButtonPressed:(UIButton *)sender {

}

-(IBAction)eastButtonPressed:(UIButton *)sender {

}

-(IBAction)resetButtonPressed:(UIButton *)sender {

}

05:34

We've got our UI set up so now let's start delving into Objective-C. We'll start real easy, we'll create a class with two attributes.

File-> New File-> iOS Source-> Cocoa Touch Class

Unlike Swift, we usually want to subclass NSObject when we are creating our own classes.

Class: Weapon

Subclass of: NSObject

Language: Objective-C

Notice that creating a new class creates both the header and the implementation file for us.

Let's put these files into a new Group named *Model*.

We will create the properties in the header file so they are public and we can get access to them from other classes.

### The class declaration syntax:

@interface Weapon : NSObject

  • `@interface` keyword
  • class name `Weapon` in this case
  • A colon to indicate that we are inheriting from the `NSObject` class

Apart from the `@interface`, the declaration is very similar to Swift, where we would replace `@interface` with `class`.

The first property we will create is the `name` for our weapon:

@property (strong, nonatomic) NSString *name;

  • We start with the `@property`, telling Xcode that this will be a property of the class.
  • Since this will be a string object, we need to declare the memory retention as `strong`
  • `nonatomic` for everything, as I said before, this relates to thread-safety
  • Now the object type, this is of type `NSString`. Pretty much everything in Objective-C will be of type `NS` something (`NS` stands for NextStep, the [Operating System](https://en.wikipedia.org/wiki/NeXTSTEP) where this language originated.)
  • The pointer character `*` and the name of the property
  • Finally, the semicolon that indicates we have finished with this line

Objective-C is a superset of C, meaning you get access to both languages simultaneously.

Let's now setup an integer property and see the difference.

@property (nonatomic) int damage;

  • No `strong` keyword
  • No pointer character
  • The type is lowercase
  • This is because `int` is not an object, so its lowercase and it doesn't require a pointer
  • `int` is a primitive
  • This is an example of a C-type declaration, whereas the `NSString` declaration is an Objective-C-type declaration

Both property declarations inside the class:

Code:

@interface Weapon : NSObject

@property (strong, nonatomic) NSString *name;

@property (nonatomic) int damage;

@end

01:42

Let's not create the Armor class now.

File-> New File-> iOS Source-> Cocoa Touch Class

Class: Armor

Subclass of: NSObject

Language: Objective-C

`Armor.h` should look like this after we add the two properties:

Code:

@interface Armor : NSObject

@property (strong, nonatomic) NSString *name;

@property (nonatomic) int health;

@end

01:16

Let's create a Boss model.

File-> New File-> iOS Source-> Cocoa Touch Class

Class: Boss

Subclass of: NSObject

Language: Objective-C

Code:

@interface Boss : NSObject

@property (nonatomic) int health;

@property (nonatomic) int damage;

@end

03:09

Let's create a Character class now.

File-> New File-> iOS Source-> Cocoa Touch Class

Class: Character

Subclass of: NSObject

Language: Objective-C

The first thing we need to do is import the `Armor` class. We'll use quotes instead of angle brackets to import the file:

import "Armor.h"

The difference between the angle brackets and the quotes is that the look up paths are different. One is the *global include path* and the other is relative to the local file. The rule of thumb is that we use angle brackets `< >` for iOS Foundations and quotes `" "` for our own files.

Also note that we import only the header file, because we only care about the public declaration of `Armor` and `Weapon`.

Also note that the header gets imported by its corresponding implementation file. So the `Character.m` file implementation you will see the header import as:

import "Character.h"

Remember that, though Swift does these imports automatically for us (imports of class declarations since there are no header files), Objective-C does not and we have to do them manually.

Code:

#import <Foundation/Foundation.h>

#import "Armor.h"

#import "Weapon.h"

@interface Character : NSObject

@property (nonatomic) Armor *armor;

@property (nonatomic) Weapon *weapon;

@property (nonatomic) int damage;

@property (nonatomic) int health;

@end

`Armor` is an object type, it's an instance of the `Armor` class we created, so it requires the `*` pointer character.

Same for `Weapon`.

`damage` and `health` are primitives.

03:24

We now need to create the Tile class. This will represent the virtual tile that we will be able to move to.

File-> New File-> iOS Source-> Cocoa Touch Class

Class: Tile

Subclass of: NSObject

Language: Objective-C

Code:

import <Foundation/Foundation.h>

import <UIKit/UIKit.h>

import "Weapon.h"

import "Armor.h"

@interface Tile : NSObject

@property (strong, nonatomic) NSString *story;

@property (strong, nonatomic) UIImage *itemImage;

@property (strong, nonatomic) NSString *actionButtonName;

@property (strong, nonatomic) Weapon *weapon;

@property (strong, nonatomic) Armor *armor;

@property (nonatomic) int healthEffect;

@end

Note that we'll need to import `UIKit` in order for the file to recognize `UIImage`. Since it's an iOS API, we import it with the global path angle brackets.

06:31

Time now to create a factory to handle the creation of the different instances we'll need.

Create a new group named Factory.

File-> New File-> iOS Source-> Cocoa Touch Class

Class: Factory

Subclass of: NSObject

Language: Objective-C

Code:

import <Foundation/Foundation.h>

import "Boss.h"

@interface Factory : NSObject

+(Boss *)createBoss;

@end

Note the `+` in the `createBoss` method. `+` is for class methods (what Swift would call Type Methods) and `-` is for instance methods.

With a class method, we do not need to create an instance of `Factory` to call `createBoss`.

This method doesn't have an implementation yet, we haven't written anything in the `.m` file so we'll do that now. In the `Factory.m`:

Code:

+(Boss *)createBoss {

Boss *boss = [[Boss alloc] init];

boss.health = 65;

return boss;

}

Instantiation syntax:

  • `Boss` is the type of instance we are creating.
  • The we have the pointer character `*` and the name of the instance `boss`
  • Then the initialization
  • Whenever we have brackets inside brackets, the innermost ones are executed first
  • `[Boss alloc]` calls the `NSObject` class method `alloc` to allocate memory to store the boss and returns a pointer
  • `init` actually calls the initializer which will create the instance and store it in the specified location
  • so `[[Boss alloc] init]` is actually calling 2 methods, first the `Boss alloc` method which creates an instance and then the `init` method on that instance. The equivalent **syntax** in Swift would be something like `Boss.alloc.init`, but Swift instances are not created like this, I'm just showing you what an equivalent **syntax might look like**
04:19

Let's create a character instance now.

Code:

import "Character.h"

@interface Factory : NSObject

+(Character *)createCharacter;

@end

We'll now need to implement this method. In `Factory.m`:

Code:

+(Character *)character

{

Character *character = [[Character alloc] init];

character.health = 100;

Armor *armor = [[Armor alloc] init];

armor.name = @"Cloak";

armor.health = 5;

character.armor = armor;

Weapon *weapon = [[Weapon alloc] init];

weapon.name = @"Fists";

weapon.damage = 10;

character.weapon = weapon;

character.damage = 0;

return character;

}

When creating a string value for a variable, we need to prepend it with the `@` sign. In Swift, we only need the quotes, in Objective-C we need `@"string literal"`.

Note that you can also import files directly into the `.m` files but if you've already imported them in the `.h` there is no need to reimport them.

07:10

In this lecture we will create a class method that will create tiles. These will be instances of the Tile class we created earlier.

We'll use the tiles to encapsulate all the data necessary to update our UI.

In `Factory.h`

Code:

+(NSArray *)createTiles;

There are two array types in Objective-C, a changeable array (we can add and remove instances from it) and a non-changeable array. The changeable array is called `NSMutableArray`.

If you don't need to add or remove items from an array, use use `NSArray` instead of `NSMutableArray`.

In `Factory.m`

Code:

import "Tile.h"

+(NSArray *)createTiles {

Tile *tile1 = [[Tile alloc] init];

tile1.story = @"As the mightiest heroine in these parts, we need you to undertake a perilous journey and defeat the Mega Boss. How about a useless, rusty sword to get started?";

tile1.itemImage = [UIImage imageNamed:@"sword"];

Weapon *bluntedSword = [[Weapon alloc] init];

bluntedSword.name = @"Rusty Sword";

bluntedSword.damage = 12;

tile1.weapon = bluntedSword;

tile1.actionButtonName = @"Take the sword";

tile1.healthEffect = 0;

// to make the error go away

NSArray *tiles = [[NSArray alloc] init];

return tiles;

}

Note that we are importing `Tile.h` in the implementation (`.m`) file and not the header file. This is because we are only using the Tile class inside this implementation file. So you want to keep your imports as clean as possible, only in the files that actually use them.

07:08

Let's create some more tiles.

Code:

Tile *tile2 = [[Tile alloc] init];

tile2.story = @"You have come across an armorer. Your flimsy moth-eaten cloak won't be much use, how about a nice set of steel armor? Don't worry, it's free, cause you're so awesome.";

tile2.itemImage = [UIImage imageNamed: @"armor.png"];

Armor *steelArmor = [[Armor alloc] init];

steelArmor.name = @"Steel armor";

steelArmor.health = 8;

tile2.armor = steelArmor;

tile2.actionButtonName = @"Take armor";

tile2.healthEffect = 0;

Tile *tile3 = [[Tile alloc] init];

tile3.story = @"There is a mysterious looking old dude in the distance. He looks shifty and momma always said not to talk to strangers, but we’re lost and our medieval GPS is broken. Better stop and ask for directions. We have our rusty sword anyway, right?";

tile3.itemImage = [UIImage imageNamed:@"druid.png"];

tile3.healthEffect = 12;

tile3.actionButtonName = @"Ask Dude";

NSMutableArray *firstColumn = [[NSMutableArray alloc] init];

[firstColumn addObject:tile1];

[firstColumn addObject:tile2];

[firstColumn addObject:tile3];

// update the tiles array

NSArray *tiles = [[NSArray alloc] initWithObjects:firstColumn, secondColumn, nil];

The `firstColumn` array will be inside the `tiles` array.

`nil` at the end of the array tells `NSArray` to stop adding items.

Creating Additional Tiles Part 2
07:58
04:31

Tiles 7 through 9 for our third column.

Code:

Tile *tile7 = [[Tile alloc] init];

tile7.story = @"What is that on the horizon? Looks like a walking Shark with a laser! Should we attack it?";

tile7.itemImage = [UIImage imageNamed: @"shark.png"];

tile7.healthEffect = 8;

tile7.actionButtonName = @"Turn It Into Sushi!";

Tile *tile8 = [[Tile alloc] init];

tile8.story = @"Oh no! The terror of the skies, the Wyvern, has seen us, it’s swooping for us. Run, run!";

tile8.itemImage = [UIImage imageNamed: @"wyvern.png"];

tile8.healthEffect = -46;

tile8.actionButtonName = @"Run Away";

Tile *tile9 = [[Tile alloc] init];

tile9.story = @"What is this, another treasure chest? Let's open that baby and reap the spoils.";

tile9.itemImage = [UIImage imageNamed: @"treasure.png"];

tile9.healthEffect = 20;

tile9.actionButtonName = @"Take Treasure";

NSMutableArray *thirdColumn = [[NSMutableArray alloc] init];

[thirdColumn addObject:tile7];

[thirdColumn addObject:tile8];

[thirdColumn addObject:tile9];

// update tiles array

NSArray *tiles = [[NSArray alloc] initWithObjects:firstColumn, secondColumn, thirdColumn, nil];

04:51

Let's create our last 3 tiles for our last column.

Code:

Tile *tile10 = [[Tile alloc] init];

tile10.story = @"A group of no-good bandits attempts to steal your hard-earned booty.";

tile10.itemImage = [UIImage imageNamed: @"bandit.png"];

tile10.healthEffect = -15;

tile10.actionButtonName = @"Show Them Who's Boss";

Tile *tile11 = [[Tile alloc] init];

tile11.story = @"You found a placid lake. Looks benign, but you never know what perils might be hiding ‘neath the quiet surface.";

tile11.itemImage = [UIImage imageNamed:@"lake"];

tile11.healthEffect = -7;

tile11.actionButtonName = @"Swim Lake";

Tile *tile12 = [[Tile alloc] init];

tile12.story = @"This is the culmination of your training and your exploring, the moment of truth, what you were made for: Face off against the Mega Chicken Boss!";

tile12.itemImage = [UIImage imageNamed: @"chicken.png"];

tile12.healthEffect = -15;

tile12.actionButtonName = @"Fight!";

NSMutableArray *fourthColumn = [[NSMutableArray alloc] init];

[fourthColumn addObject:tile10];

[fourthColumn addObject:tile11];

[fourthColumn addObject:tile12];

// update the tiles array one last time

NSArray *tiles = [[NSArray alloc] initWithObjects:firstColumn, secondColumn, thirdColumn, fourthColumn, nil];

Now that we have our tile grid set up, we will use to allow the user to navigate to these different tiles in search of adventure.

07:42

To start setting up our game, we will add a helper function to the project.

In `ViewController.m`.

Code:

import "Character.h"

import "Boss.h"

import "Factory.h"

@property (strong, nonatomic) NSArray *tiles;

@property (strong, nonatomic) Character *character;

@property (strong, nonatomic) Boss *boss;

-(void) setupGame {

self.tiles = [Factory createTiles];

self.character = [Factory createCharacter];

self.boss = [Factory createBoss];

}

We will call this method inside of `viewDidLoad` to start setting up the game.

Code:

// inside viewDidLoad()

[self setupGame];

The brackets indicate that we are sending a message, which is Objective-C's way of saying calling a method. We are sending the `setupGame` message to the `self` objects. I know this sounds confusing, but all we're really doing is calling the `setupGame` method from this instance of the view controller (`self`).

Note that we are using Class methods so that we don' have to create instances of the Factory class just to call these setup methods.

Two different ways to access properties

Note that we can call the properties with an underscore before their name (`_tile`) and this would avoid any *getters* or *setters* we might have set up for it. You are accessing the instance variable directly.

If we use `self` before the property (`self.tile`) then, if you had added *getters* and *setters*, they **would** be called.

99% of the time you want to use `self` before the variable name.

02:30

We'll use a `CGPoint` to track where the user is in our tile grid. This is because a `CGPoint` will allow us to store `X` and `Y` points so we can simulate a grid, so, for example, the grid starts at 0,0 and if the user moves 1 tile to the right that would be 1,0, etc.

Code:

@property (nonatomic) CGPoint currentPoint;

// inside setupGame()

/* Setup the initial point in our coordinate system as 0,0. */

self.currentPoint = CGPointMake(0, 0);

Note that `CGPoint` is a primitive type so we don't use a pointer to it because it doesn't create an object, it accesses the value directly in memory. `CGPoint` is actually a struct and, unlike Swift, they are not objects.

06:18

We will now add a method to Character that will take care of updating its attributes.

We will create the method inside de `Character` class because all it does is update the character instance so it makes more sense to put it in there as opposed to in ViewController or elsewhere.

In `Character.h`.

Code:

-(void) calculateAttributesForArmor:(Armor *)armor withWeapon:(Weapon *) weapon withHealthEffect:(int) healthEffect;

This method takes 3 parameters, `armor`, `weapon` and `healthEffect`. The first 2 are pointers to objects, the last one is a primitive (`int`) so it's not a pointer.

However, unlike Swift, this method's name is not `calculateAttributesForArmor`. In Objective-C, You read the method names with the parameter names. So this method would be called: `calculateAttributesForArmor withWeapon withHealthEffect`.

In `Character.m`.

Code:

-(void) calculateAttributesForArmor:(Armor *)armor withWeapon:(Weapon *) weapon withHealthEffect:(int) healthEffect {

if (armor != nil) {

self.health = self.health - self.armor.health + armor.health ;

self.armor = armor;

return;

}

if (weapon != nil) {

self.damage = weapon.damage;

self.weapon = weapon;

return;

}

if (healthEffect != 0) {

self.health = self.health + healthEffect;

return;

}

self.health = self.health + self.armor.health;

self.damage = self.damage + self.weapon.damage;

}

There are no optionals in Objective-C, we have to test for `nil`. But C-types or primitives don't have `nil` values, you need to use `null` for them, which is not the same a `nil`. So for `healthEffect` we use `0` instead of `nil`.

Also, note that we need to use parentheses to encapsulate the conditions of an if statement.

Don't be confused between `self.armor.health` and `armor.health`. The first one is the attribute or property we set up (hence the `self` keyword), and the second one is the argument passed in the method. Maybe we should have named them differently to avoid this confusion, for example, we could have named the second one `incomingArmor` or something like that.

In `ViewController.m`.

Code:

[self.character calculateAttributesForArmor:nil withWeapon:nil withHealthEffect:0];

08:48

Let's now create a helper method which will update the UI for a given tile.

Code:

import "Tile.h"

-(void) updateTile {

self.actionButton.enabled = YES;

Tile *tileModel = [[self.tiles objectAtIndex:self.currentPoint.x] objectAtIndex:self.currentPoint.y];

self.storyLabel.text = tileModel.story;

self.itemImageView.image = tileModel.itemImage;

self.healthLabel.text = [NSString stringWithFormat: @"%i", self.character.health];

self.damageLabel.text = [NSString stringWithFormat: @"%i", self.character.damage];

self.armorLabel.text = self.character.armor.name;

self.weaponLabel.text = self.character.weapon.name;

[self.actionButton setTitle:tileModel.actionButtonName forState: UIControlStateNormal];

}

// inside setupGame

[self updateTile];

In this line: `[[self.tiles objectAtIndex:self.currentPoint.x] objectAtIndex:self.currentPoint.y];` we are accessing a multidimensional array or arrays inside another array.

In this code: `[NSString stringWithFormat: @"%d", self.character.health];` we use `@"%d"` is a token. Think of it as a placeholder for the variable value that is coming next. So `%d` is for numbers. We could use `%i` which is for integers, but you'll see `%d` used more often because it will accept doubles or integers, so it's more convenient to use because we don't have to check if the value is an integer or a double.

Also note that, unlike Swift, `UIControlStateNormal` does not have a period.

Run the app and let's see what we have so far.

01:27

Let's create another helper method. We'll use this for the navigation buttons. We want our user to stay within the grid.

Code:

-(void) updateButtons {

}

// inside setupGame

[self updateButtons];

Take a stab, if you wish, as to how we could keep the user only inside our grid. We'll see how to do it in the next lecture.

05:29

Let's implement the `updateButtons` method.

We're going to create a new class to abstract our code and make it more modular.

Create a new group called Brain.

Create a new file called `GameBrain`.

File-> New File-> iOS Source-> Cocoa Touch Class

Class: GameBrain

Subclass of: NSObject

Language: Objective-C

Code:

import <UIKit/UIKit.h>

-(BOOL)tileExistsAtPoint:(CGPoint)point forTiles: (NSArray *) tiles;

// in GameBrain.m

-(BOOL)tileExistsAtPoint:(CGPoint)point forTiles: (NSArray *) tiles {

if (point.y >= 0 && point.x >= 0 && point.x < [tiles count] && point.y < [[tiles objectAtIndex:point.x] count]) {

return YES;

} else {

return NO:

}

}

This method will tell us whether a tile exists at the point we want, by returning `true` of `false`.

We use the array count to make sure we stay within the grid range.

05:13

Let's import the `GameBrain` class into `ViewController` and create a property for it.

Code:

import "GameBrain.h"

@property (strong, nonatomic) GameBrain *gameBrain;

// inside the setupGame method

self.gameBrain = [[GameBrain alloc] init];

// update the updateButtons method

-(void) updateButtons {

self.westButton.hidden = ![self.gameBrain tileExistsAtPoint:CGPointMake(self.currentPoint.x - 1, self.currentPoint.y) forTiles:self.tiles];

self.eastButton.hidden = ![self.gameBrain tileExistsAtPoint:CGPointMake(self.currentPoint.x + 1, self.currentPoint.y) forTiles:self.tiles];

self.northButton.hidden = ![self.gameBrain tileExistsAtPoint:CGPointMake(self.currentPoint.x, self.currentPoint.y + 1) forTiles:self.tiles];

self.southButton.hidden = ![self.gameBrain tileExistsAtPoint:CGPointMake(self.currentPoint.x, self.currentPoint.y - 1) forTiles:self.tiles];

}

The exclamation mark `!` negates the statement that follows it, so it's like saying: "do the opposite of this". In this case, since the `tileExistsAtPoint` method returns true or false, we will actually set the `hidden` property to that opposite of what it returns. So if the tile exists, we set hidden to false because we **don't** want to hide the button.

The tricky thing here is that we're setting the `hidden` property directly to the result of the `tileExistsAtPoint` method. We could have written the method to return false if the point existed instead of true but that would be even more confusing.

Run the app. If we've done everything correctly, the South and West buttons should be hidden because we can't go in those directions.

03:48

Let's figure out how we can navigate to different tiles.

Code:

-(IBAction)northButtonPressed:(UIButton *)sender {

self.currentPoint = CGPointMake(self.currentPoint.x, self.currentPoint.y + 1);

[self updateButtons];

[self updateTile];

}

-(IBAction)westButtonPressed:(UIButton *)sender {

self.currentPoint = CGPointMake(self.currentPoint.x - 1, self.currentPoint.y);

[self updateButtons];

[self updateTile];

}

-(IBAction)southButtonPressed:(UIButton *)sender {

self.currentPoint = CGPointMake(self.currentPoint.x, self.currentPoint.y - 1);

[self updateButtons];

[self updateTile];

}

-(IBAction)eastButtonPressed:(UIButton *)sender {

self.currentPoint = CGPointMake(self.currentPoint.x + 1, self.currentPoint.y);

[self updateButtons];

[self updateTile];

}

Run the app and make sure you can navigate in the adventure.

03:31

Let's implement the `actionButtonPressed` now.

Code:

-(IBAction)actionButtonPressed:(UIButton *)sender {

Tile *tile = [[self.tiles objectAtIndex:self.currentPoint.x] objectAtIndex:self.currentPoint.y];

/* Call the method updateCharacterStatsForArmor so that we can use the armor, weapon or health effect on our tile. */

[self.character calculateAttributesForArmor:tile.armor withWeapon:tile.weapon withHealthEffect:tile.healthEffect];

/* After we perform an action we call the method updateTile so that our view will update with a new weapon, armor or health stat. */

[self updateTile];

self.actionButton.enabled = NO;

}

Set the lines in the Story label to 0 so that they are handled automatically. And enlarge and center the other labels as needed.

02:11

Let's implement the reset button.

Code:

-(IBAction)resetButtonPressed:(UIButton *)sender {

self.character = nil;

self.boss = nil;

[self setupGame];

}

We're resetting the character and boss also though it's not technically necessary because their value changes. It's still nice to be explicit with these types of things.

Run the app and make sure the reset button works.

04:53

Let's see if we can figure out the final boss fight!

We'll add a new method to `GameBrain`.

Code:

import "Character.h"

import "Boss.h"

-(void)declareWinnerForCharacter: (Character *) character andBoss: (Boss *) boss;

// in GameBrain.m

-(void)declareWinnerForCharacter: (Character *) character andBoss: (Boss *) boss {

while (boss.health > 0) {

int bonusDamage = arc4random_uniform(5);

boss.health = boss.health - character.damage - bonuseDamage;

// We keep whittling the character's health while the boss' health is greater than 0

character.health = character.health - boss.damage;

// If the character dies before we kill the boss, we return immediately with the boss as winner

if (character.health <= 0) {

break;

} else if (boss.health <= 0){

break;

}

}

}

In `Factory.m` we need to give our boss some initial damage.

Code:

// add this line to the createBoss method

boss.damage = 15;

Next we will figure out how to make this method work since it doesn't return anything.

03:44

Let's figure out how to declare a winner by using the information from the `declareWinnerForCharacter`.

We will do this with a protocol and a delegate.

Code:

@protocol GameBrainDelegate <NSObject>

@required

-(void)playerDidWin:(BOOL)playerWon;

@end

@property (nonatomic, weak) id <GameBrainDelegate> delegate;

We need the `@required` keyword because methods can be optional in Objective-C protocols.

Note that we add `weak` to the memory allocation of `delegate`. We don't want to create a pointer for it, we just want a reference.

Note: adding `strong` to a primitive does not create a pointer to it, it adds to the retain count.

In the video it states that "adding strong will create a pointer to the delegate id". This is wrong, it will increase the retain count but will not create a pointer. A pointer is created with the use of `*` for pointers to objects. It was a little slip of the tongue :)

In `GameBrain.m`.

Code:

// the if statement should now look like this

if (character.health <= 0) {

[self.delegate playerDidWin:NO];

break;

} else if (boss.health <= 0){

[self.delegate playerDidWin:YES];

break;

}

01:42

Let's not conform to the delegate we just created.

In `ViewController.m`.

Code:

// this says that the ViewController instance now conforms to the GameBrainDelegate protocol

@interface ViewController () <GameBrainDelegate>

// inside setupGame

// we set the delegate as this instance of ViewController

self.gameBrain.delegate = self;

-(void)playerDidWin:(BOOL)playerWon{

if (playerWon == YES) {

} else {

}

}

04:25

We'll now add a helper method to show an alert for when the player dies or wins the fight against the boss.

Code:

-(void) showAlertWithTextforHeader: (NSString *) header withMessage:(NSString *) message {

UIAlertController *alert = [UIAlertController alertControllerWithTitle:header message:message preferredStyle: UIAlertControllerStyleAlert];

UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil];

[alert addAction:okAction];

[self presentViewController:alert animated:true completion:nil];

}

// update the playerDidWin method to look like this:

-(void)playerDidWin:(BOOL)playerWon{

NSLog(@"playerDidWin %i", playerWon);

if (playerWon == YES) {

[self showAlertWithTextforHeader:@"Victory Message" withMessage:@"You have defeated the evil boss"];

} else {

[self showAlertWithTextforHeader:@"Death Message" withMessage:@"You're dead! Please restart the game!"];

}

}

// inside actionButtonPressed

// if we're on the boss tile, then let's see who wins that fight

if (self.currentPoint.x == 3 && self.currentPoint.y == 2) {

[self.gameBrain declareWinnerForCharacter:self.character andBoss:self.boss];

} else {

Tile *tile = [[self.tiles objectAtIndex:self.currentPoint.x] objectAtIndex:self.currentPoint.y];

[self.character calculateAttributesForArmor:tile.armor withWeapon:tile.weapon withHealthEffect:tile.healthEffect];

/* After we perform an action we call the method updateTile so that our view will update with a new weapon, armor or health stat. */

[self updateTile];

self.actionButton.enabled = NO;

}

Run the app and let's test the game. See if you can defeat the evil boss!

Section 3: Nil vs Null
Nil vs Null Introduction
01:56
03:25

// 1

In Objective-C, `nil` and `null` are not the same thing. In some languages, they are interchangeable or one of them simply doesn't exist. Objective-C is a superset of C so it has both nil and null and there is a difference between them.

// 2

The rule of thumb is that `nil` should be used for pointers to an object and `null` for non-object pointers. But what does this mean really?

`nil` is actually 0 as an `id` which is a special type in Objective-C that can be of any type, an id is like a wildcard for data type. Sort of the equivalent of AnyObject in Swift (except id can be applied to non-objects such as primitives as well).

`null` is 0 as a void return type.

// 3

(examples)

// 4

You can further distinguish them by thinking that:

`nil` is usually used for an Objective-C object type.

`NULL` is used for c-style pointers (void *).

Yet another difference is that you can send messages to `nil`, whereas you can't send messages to `null`. However the messages sent to nil will not have any effect but you won't get an error when sending them.

Section 4: Pointers Strong vs Weak Introduction
03:20

// 1

We used to have to manage memory retention and allocation not too long ago when writing programs. This is something that we don't need to do in Swift for example, it is done for us automatically.

With `strong` and `weak`, we are telling Xcode how we want it to handle the memory retention for a particular object.

In that past, if a method started with the keywords `init`, `new` or `create`, at some point we had to issue a `release` for this object or it would hang around forever and you would get memory leaks.

As I said, we don't have to worry about it these days.

// 2

A strong reference means Xcode will hold on to this pointer in memory as long as it is not set to nil. So the object is not destroyed in memory until set to nil, either explicitly by us or by any other means. But as long as this object has a value, other than nil, there will be a space for it in memory.

// 3

With weak, the compiler knows not to hold on to this object in memory. The object is only held when another object holds a `strong` reference to it. Once that is not the case, once all objects that held a strong reference are no longer in memory, then this object is automatically destroyed.

// [All paragraphs] (slide 2)

In Objective-C we have to specify what type of memory retention scheme we want to use whenever we create objects. For example in a this `property`, which is an instance of `NSString`, meaning it's an object, we are saying we want a `strong` reference to this object, we don't want the compiler to destroy this object until we say so or until its parent is destroyed, in this case, the parent would be wherever we created the instance of this object.

// 1 (slide 3)

By contrast, when we create IBOutlets for our UI elements in the storyboard, we create them as weak and let Xcode destroy them when their controller is deallocated from memory.

// 3

This outlet is only useful as long as we are in that particular controller which is managing a view. Once that controller gets destroyed from memory or *deallocated*, then there is no need to hold on to the memory for the UI element so we declare it as `weak` and let the compiler destroy it when appropriate.

// 1 (slide 4)

There is a great analogy to this on this StackOverflow post where they use balloons to explain the difference between `weak` and `strong`.

Section 5: Thread Safety Atomic vs Nonatomic
02:06

// 1

In programming, properties need to have methods to access them. They usually need a method to `set` their value and a method to `get` their value. These are known as `setters` and `getters` respectively. In the old days, we used to have to write these methods ourselves but nowadays they are created automatically, under the hood. This is true for both Swift and Objective-C.

`atomic` and `nonatomic` refer to *thread safety*. *Thread safety* simply means that if we are running multiple threads of our application, you can think of it as multiple internal copies of the application running at the same time, then `atomic` and `nonatomic` refer to whether these properties are safe to use while running multiple threads.

// 2

By default, properties are `atomic`, this means they are safe to run on multiple threads because a lock is created whenever they are being **set** or their value is being returned (**get**) so that another thread cannot override its value at the same time. This is a good thing when we are doing lots of operations on multiple threads because you don't want a property changing it's value when you're trying to see what that value is or, worse, when you're trying to change it yourself.

// 3

Why is this important? To us it's not, really. Not right now. But we need to know what it is. Because `nonatomic` provides better performance since the compiler doesn't have to check for locks, etc. It can simply set or get the value of the property. This leads to faster code. Apple uses `nonatomic` for almost all of their properties in the iOS Frameworks. And we should do the same.

// 4

If you're an expert in threads and know when to use `atomic`, go for it. If you don't then stick with `nonatomic` for your properties.

Section 6: Object vs Primitive
03:23

// 1

In Objective-C, there is a distinction between the types we create. Some are objects and some are primitives, or *scalar types*, as they are officially known.

We are talking about distinctions between integers and strings, etc. here, what we could call basic types. Swift doesn't have this distinction at all.

// 2

If you google the difference between objects and primitives in Objective-C, you'll often see discussions about memory allocation. And this is not necessarily false, it just doesn't paint the whole picture. I will explain in a moment, but the most important distinction between them is not memory allocation really, it is their use.

Primitives cannot be passed as objects without conversion first. Now, why would we care about this? Mainly because a lot of the methods in iOS expect objects and if you pass them a primitive, you will get an error.

For example, arrays in Objective-C only take objects, they cannot store primitives. So if you have a bunch of primitive integers that you want to put into an NSArray, you will get an error unless you cover them first.

What about the memory thing? OK, so the main distinction in memory allocation is that objects require a pointer to a location in the *heap* whereas primitives require no pointer because they are allocated directly in the *stack*. But the truth is that primitives can also be allocated in the *heap* if you use *malloc*. But, seriously, we don't care about any of that right now.

If all this talk of *heap* vs *stack* makes sense to you, great, if it doesn't then don't worry about it. The important thing to understand here is the syntax and the use of each type.

// 3

Primitives cannot receive messages either, whereas objects can.

// 2 (slide 3)

When we instantiate a class or struct we are actually creating an object of that type. A simple example is `NSString` to create a string. This is an instance of the NSString class in Objective-C, so it's an object.

// 3

The rule of thumb in Objective-C is that if it begins with `NS` then it’s definitely an object because it’s coming from NextStep’s internal classes.

NextStep is the unix-based operating system that Apple bought to base OS X and iOS on. Incidentally, it's also the name of the company that Steve Jobs set up when he was forced out of Apple. Part of the deal to bring him back was that Apple buy NextStep and use it as the basis for their future software.

04:30

(refer to notes on Xcode project)

Syntax and Memory allocation when declaring property.

Section 7: Sending Messages
02:55

// 1

What do we mean by "sending messages"?

In Objective-C, when you send information to an object, for example, you have a `bike` object and you want to set its `wheels` property to 2, this is called *sending a message*.

// 3

In Swift we would set the property with `dot syntax`:

bike.wheels = 2

// 5

In Objective-C we *send a message* to that object:

[bike wheels : 2]

// 1 (slide 2)

A couple of things to note here:

1. the brackets

The brackets here are used as parentheses, to group commands together, they have nothing to do with arrays, which is where we have seen them before. But we need to use them whenever we send messages or we will get errors.

// 3

2. colon

Instead of use the equals sign to set a property, we use the colon `:` because we are actually passing an argument, we are not setting a property directly.

But where's the message? OK. So in the example above, we are messaging the `bike` object with the `wheels` message and passing `2` as a value. So `bike` is the object and `wheels` is the message which, in this case, happens to be a setter which sets its value to `2`.

Don't worry if you don't quite understand all this. The important thing is that you understand what is happening in that bit of code: we are seeing the `wheels` property to 2. But I do want you to know that this is called sending messages in Objective-C because you will come across this term in lots of documentation, tutorials, etc.

// 4

We can chain multiple commands or send multiple messages if you will, by encapsulating brackets inside of each other. When doing this, the code is executed from the inside out, that is to say that the inside brackets will always be executed first and then it expands outwards like ripples in a pond.

So in this example:

[[bike alloc] init]

We are actually calling `bike alloc` first and then we are calling the `init` method on the result of `bike alloc`.

This is equivalent in Swift of doing something like `bike.alloc.init`. But note that Swift doesn't initialize objects in this manner.

04:28
(Refer to comments in the Project)

Students Who Viewed This Course Also Viewed

  • Loading
  • Loading
  • Loading

Instructor Biography

Mr. John Omar, Designer, developer, teacher at Bitfountain

John was the lead iOS developer at Fast Society and Cameo until he started Bitfountain in 2012. The apps that John has contributed to have appeared in TechCrunch, Mashable and the New York Times. At Bitfountain, John oversees all projects from a technical and strategic perspective. At Bitfountain, John has taught over 120,000 students how to code online.

Instructor Biography

Mr. Khari Slaughter, UI/UX Designer at Bitfountain.io

Khari is a graphic artist and designer from Boston, MA. After completing his degree in Sociology at Wesleyan University he moved his life to Paris where he continues to live. A degree candidate for the MFA Design and Technology at Parsons The New School, Khari maintains a firm connection with both tactile and digital media, constantly experimenting and pushing the limits of UI/UX design.

Instructor Biography

Mr. Eliot Arntz, Master iOS Teacher

Eliot regularly teaches iOS development classes and workshops at General Assembly and guest lectures for companies and development boot camps around NYC. He also taught the inaugural class for Coalition for Queens which focused on increasing diversity in iOS development. He also coaches students in a one-on-one environment with a focus on transitioning to full time development. Eliot cofounded and organizes the iOS Office Hours meetup NYC.

In his free time he works as a contractor for startups focusing on agile development.

Find me on twitter @EliotArntz - I check my direct messages quite frequently.

Ready to start learning?
Take This Course