Quantcast
Channel: Kodeco | High quality programming tutorials: iOS, Android, Swift, Kotlin, Unity, and more
Viewing all 4374 articles
Browse latest View live

Video Tutorial: Beginner OpenGL ES and GLKit Part 4: Vertex Array Objects and a Model Class


How to Save your Game’s Data: Part 1/2

$
0
0
Space game

Learn how to save your game’s data!

Unbelievably, in the “old days” many games lacked the ability to save data. You would literally have to beat the game in one sitting! This often caused players to experience emotions ranging from passing frustration to explosive rage, as Angry Video Game Nerd can attest.

In the great days of modern iOS, your game has no excuse because there are plenty of ways to save data, from NSUserDefaults to Core Data to iCloud and more!

In this two-part tutorial, you’re going to learn the best ways to save data in your games. Specifically, you’ll learn how to:

  • Separate your data: You’ll build a class to hold your game data and update it properly during gameplay.
  • Save your data: You’ll use NSCoding to persist your game data to the device.
  • Use iCloud: You’ll also learn how to use iCloud to allow the user to continue their game on a different device if they so desire.

By the end of both parts, you’ll have an awesome iCloud connected Space Shooter made with Sprite Kit:

sgd_01_completed

And yes, that’s a cat in the cockpit. Surely you know cats are avid pilots?

Get ready to start saving!

Getting Started

For this tutorial, you’ll build upon the game created in Tony Dahbura‘s Sprite Kit Space Shooter Tutorial. These exercises will be most useful if you go through that tutorial first, but if you’d rather not, at least scan the code so you’re familiar with how it works.

If you decide to skip the Space Shooter tutorial, you can just download the completed project.

Open up Space Shooter in Xcode, then build and run on an iOS device. You’ll need to run the game on an actual device since the accelerometer is required to steer the spaceship around all those asteroids.
sgd_02_start

To win the game just stay alive for 30 seconds. All you have to do is blast the asteroids out of your way to avoid deadly collisions. It’s not as easy as it sounds!

Game Data Class

Did you notice that the game doesn’t track the score, or the distance traveled by the ship? You’ll start by adding tracking for those two metrics so that you have something interesting to save across gameplay sessions.

In this section, you’ll create a new class to hold the game data. It’s a best practice to isolate your game’s data from the rest of the game; this abstraction makes it much easier to save the user’s progress from game to game.

In Xcode, go to File\New\File…, choose the iOS\Cocoa Touch\Objective-C class template and click Next. Name your new class RWGameData, make it a subclass of NSObject, click Next and then Create.

RWGameData will hold your game’s data, specifically, the score and distance traveled for the current game, as well as the player’s highest score and total distance flown. Open RWGameData.h and replace the default contents of this file with the following code:

#import <Foundation/Foundation.h>
 
@interface RWGameData : NSObject
 
@property (assign, nonatomic) long score;
@property (assign, nonatomic) int distance;
 
@property (assign, nonatomic) long highScore;
@property (assign, nonatomic) long totalDistance;
 
+(instancetype)sharedGameData;
-(void)reset;
 
@end

Here’s what each of those class properties will do:

  • score: The current game’s score
  • distance: The current game’s distance traveled (in space miles)
  • highScore: The player’s high score across all games (later on in this tutorial you’ll persist this high score)
  • totalDistance: The player’s total distance travelled across all games

Keep in mind that the logic necessary to keep this data separate by player and device will be more complex than just keeping everything in memory as you’re doing for now.

There are also two method definitions in your class interface. sharedGameData will give you access to this class’ singleton instance. As you may have guessed, reset will reset the current game data.

Open RWGameData.m and add the following method:

+ (instancetype)sharedGameData {
    static id sharedInstance = nil;
 
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedInstance = [[self alloc] init];
    });
 
    return sharedInstance;
}

This is a boilerplate Objective-C implementation of the singleton pattern. Using sharedGameData throughout your app will ensure that you’re always accessing the same RWGameData instance. You can use this same code any time you want to provide singleton access to one of your own classes.

Next, take care of the reset method, which will reset the game’s score and distance whenever the user starts a new game. Add this method inside RWGameData.m:

-(void)reset
{
    self.score = 0;
    self.distance = 0;
}

In this method you just set the score and distance properties to 0, so the user can start fresh.

Pimping up the HUD

Now that you have a class to store the game’s data, now you’ll modify the game to keep score with the new class.

Open MyScene.m, where the game logic resides, and add the following line at the top of the file to import your new class:

#import "RWGameData.h"

You’ll need a few labels to display progress to the user. Add the following instance variables to the @implementation section in MyScene.m:

SKLabelNode* _score;
SKLabelNode* _highScore;
SKLabelNode* _distance;

Still in MyScene.m, add the following method to initialize those labels and add them to the scene:

-(void)setupHUD
{
    _score = [[SKLabelNode alloc] initWithFontNamed:@"Futura-CondensedMedium"];
    _score.fontSize = 12.0;
    _score.position = CGPointMake(50, 7);
    _score.fontColor = [SKColor greenColor];
    [self addChild:_score];
 
    _distance = [[SKLabelNode alloc] initWithFontNamed:@"Futura-CondensedMedium"];
    _distance.fontSize = 12.0;
    _distance.position = CGPointMake(115, 7);
    _distance.fontColor = [SKColor orangeColor];
    [self addChild:_distance];
 
    _highScore = [[SKLabelNode alloc] initWithFontNamed:@"Futura-CondensedMedium"];
    _highScore.fontSize = 12.0;
    _highScore.position = CGPointMake(200, 7);
    _highScore.fontColor = [SKColor redColor];
    [self addChild:_highScore];
}

This code sets up _score, _distance, and _highScore by setting font face, size and color, and finally positioning all three labels at the bottom of the scene, out of the way of the space ship.

Next, you’ll need to call setupHUD: somewhere in your code. Add the following line to initWithSize:, just before the line that calls startBackgroundMusic:

[self setupHUD];

This will add the HUD labels to the scene just before starting the gameplay.

One final touch — at the end of startTheGame:, add the code to initialize the label’s values:

_highScore.text = [NSString stringWithFormat:@"High: %li pt", [RWGameData sharedGameData].highScore];
_score.text = @"0 pt";
_distance.text = @"";

This will set the high score label to whatever you stored last in RWGameData‘s highScore property and will reset the values of _score and _distance. You’re all ready to go! Build and run the project right now.

sgd_03_initialHUD

The HUD is now visible, but the game doesn’t keep score yet – that’s the next thing to add to the app.

Keeping Score

There’s no sense in playing the game if you’re not scoring points, right? Every time the player shoots an asteroid he or she should earn a point and the score should update.

Stay in MyScene.m, scroll to update: and look for the if statement that checks for [shipLaser intersectsNode:asteroid]. This code branch executes whenever a ship laser contacts an asteroid – the code hides the laser and the asteroid, then spawns a new explosion on the position of the impact. Boom!

This is a good spot to add the score-keeping code. Add the following just above the continue; statement at the end of this if block:

[RWGameData sharedGameData].score += 10;
_score.text = [NSString stringWithFormat:@"%li pt", [RWGameData sharedGameData].score];

First, you increase the player’s score by 10 points, then you update the label’s text with the new amount. Build and run, then shoot a few asteroids to try out the scoring system:

sgd_04_HUD_score

If you play a couple of games you’ll notice the score does not reset between games. But why does this happen? You certainly remember writing a reset method in RWGameData, right?

Long story short, you need to add code to invoke reset. Doh!

You want to reset the game data both when the user fails and completes the game. In MyScene.m, just above the line that calls removeAllActions:, add the following line of code inside endTheScene:.

[[RWGameData sharedGameData] reset];

This line will take care of resetting the per-game data every time the game is over. Build and run to see this in action. The score will now reset every time you restart the game.

Next, you’re going to update the high score at the end of every game – but you’ll need to check if the current score is higher than the stored highest score, and update the high score as needed. To make the reward more complex, you’ll need to implement a more complex reward logic that updates the highest score only when the player survives for at least 30 seconds.

The code that detects a successful completion of the game is towards the end of update: in MyScene.m. Find this line [self endTheScene:kEndReasonWin]; and add the following code above it to update the high score:

[RWGameData sharedGameData].highScore = MAX([RWGameData sharedGameData].score,
                                            [RWGameData sharedGameData].highScore);

The MAX() function compares the current score to the high score and sends back the bigger number. You just set [RWGameData sharedGameData].highScore to that number and that’s it. Adding it is easy enough when you know where to put that line in the project’s code.

Next, you’re going to track the distance the space ship travels during a game.

Still inside update: in MyScene.m, add the following code a bit above where you just updated highScore and immediately before the if statement that checks whether _lives <= 0:

static NSTimeInterval _lastCurrentTime = 0;
if (currentTime-_lastCurrentTime>1) {
  [RWGameData sharedGameData].distance++;
  [RWGameData sharedGameData].totalDistance++;
  _distance.text = [NSString stringWithFormat:@"%li miles", [RWGameData sharedGameData].totalDistance];
  _lastCurrentTime = currentTime;
}

update: and currentTime parameters hold the time the method was called. The above code compares currentTime against _lastCurrentTime to see if at least one second passed since it last updated the distance.

To update the distance, you do a few things:

  • You increase the distance traveled this game and the total distance traversed across all games.
  • You update the distance label's text. Note you're displaying the total distance across all games, so it's easy to see if this saves properly when you add that later on.
  • Finally you update _lastCurrentTime, which ensures you won't update distance again until another second has passed.

That's about it. Build and run, and enjoy keeping score and tracking your progress.

sgd_05_HUD_distance

Persisting Data Between Launches

You're doing well so far, but did you notice that every time you launch the game, the high score and the total distance resets to zero? That's not really the result you're after, but it's helpful to take baby steps as you learn how to persist data between app launches.

Your next step will be to make the RWGameData class conform to the NSCoding protocol, which is one good way to persist game data on a device. The advantage of NSCoding over alternative persistence methods like Core Data is that it's nice and easy, and is ideal for a small amount of data like what you see with this game.

The NSCoding protocol declares two required methods:

  • encodeWithCoder:: This method converts your object into a buffer of data. You can think of it as "serializing" your class.
  • initWithCoder:: This method converts a buffer of data into your object. You can think of it as "deserializing" your class.

It's quite simple really - you have to implement one method for saving and one for loading; that's all there is to it. Now, you're going to see how precise data storage can be.

Open RWGameData.h and modify the @interface line so it looks like this:

@interface RWGameData : NSObject <NSCoding>

This declares that RWGameData conforms to the NSCoding protocol.

Switch back to RWGameData.m. You'll add encodeWithCoder: (just think of it as the method to "save" data) and two constants for the key names you'll use to store the data when encoding the class. Add the following code just below the @implementation line:

static NSString* const SSGameDataHighScoreKey = @"highScore";
static NSString* const SSGameDataTotalDistanceKey = @"totalDistance";
 
- (void)encodeWithCoder:(NSCoder *)encoder
{
  [encoder encodeDouble:self.highScore forKey: SSGameDataHighScoreKey];
  [encoder encodeDouble:self.totalDistance forKey: SSGameDataTotalDistanceKey];
}

encodeWithCoder: receives an NSCoder instance as a parameter. It's up to you to use this to store all the values you need persisted. Note that you'll persist only the high score and total distance. Since the other properties reset between games, there's no need to save them.

You probably already figured out how the encoding works. You call a method called encodeXxx:forKey: and provide a value and a key name, based on the type of your data. There are methods for encoding all the primitive types, like doubles, integers or booleans.

There's also a method to encode any object that supports NSCoding (encodeObject:). Many of the built-in classes like NSString, NSArray or NSDictionary implement NSCoding. You can always implement NSCoding on your own objects, much like how you're doing it here.

Note: If your class extends anything that conforms to the NSCoding protocol, you must call [super encodeWithCoder:encoder]; to ensure all of your object's data persists.

This is everything you need to do in this method, just supply values and keys to the encoder. Actually saving to the device is a separate task on your list, which you'll take on in a moment.

Now it's time to implement the opposite process - initializing a new instance with the data from a decoder. Add the following method to RWGameData.m:

- (instancetype)initWithCoder:(NSCoder *)decoder
{
  self = [self init];
  if (self) {
    _highScore = [decoder decodeDoubleForKey: SSGameDataHighScoreKey];
    _totalDistance = [decoder decodeDoubleForKey: SSGameDataTotalDistanceKey];
  }
  return self;
}

See how you start this method much as you would any other initializer, by calling some initializer on your parent class? If your class extends anything that conforms to the NSCoding protocol, you most likely need to call [super initWithCoder:decoder], but because this class extends NSObject, calling init here is fine.

Much the same way you used encodeDouble:forKey: to store a double value for a given key, you now use decodeDoubleForKey: to retrieve a double value from the NSCoder instance passed into the method.

By implementing these two methods, you added the ability to your class to save its current state and retrieve it with ease.

sgd_06_ohyeah

I'm sure you're eager to take your new NSCoding class for a test drive. However, you'll need to hold your horses just a bit longer. You still need to save the game data to a file.

First, you need to make sure the class will create a new empty instance whenever there's no persisted data, i.e. the very first time you run the game.

Inside RWGameData.m, add this simple helper method to construct the file path to store the game data:

+(NSString*)filePath
{
  static NSString* filePath = nil;
  if (!filePath) {
    filePath = 
      [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject]
       stringByAppendingPathComponent:@"gamedata"];
  }
  return filePath;
}

This method uses NSSearchPathForDirectoriesInDomains() to obtain the path to the app's document directory, then adds to it a file name of "gamedata", thus returning a fully qualified file path.

Next, you're going to use this method to check if there's a saved game data file already, and if so, load it and create a new class instance from it. Add this new method to RWGameData.m:

+(instancetype)loadInstance
{
  NSData* decodedData = [NSData dataWithContentsOfFile: [RWGameData filePath]];
  if (decodedData) {
    RWGameData* gameData = [NSKeyedUnarchiver unarchiveObjectWithData:decodedData];
    return gameData;
  }
 
  return [[RWGameData alloc] init];
}

First you get the file path where the stored game data file should be and then you try to create an NSData instance out of the file contents.

If decodedData is not nil, then that means the file content was read successfully and converted to an NSData instance. In that case (pay attention…here comes the magic) you create an RWGameData instance by calling NSKeyedUnarchiver's class method unarchiveObjectWithData:.

What unarchiveObjectWithData: does is to try to initialize a new RWGameData by invoking its initWithCoder: initializer with an NSCoder loaded with decodedData. (Try saying that aloud, three times, fast; it's a mouthful!)

In case decodedData was nil you just construct a new instance of the class by calling init.

One final touch for RWGameData.m - in sharedGameData, replace this line:

sharedInstance = [[self alloc] init];

with this one:

sharedInstance = [self loadInstance];

This will ensure that when you create the instance of the game data class it loads the contents of the previously stored file, provided one exists at the target file path.

sgd_07_sir

I don't always write a load method, but when I do I write a save method too.

That's a good piece of advice just there. Add a save method to RWGameData.m, as well:

-(void)save
{
  NSData* encodedData = [NSKeyedArchiver archivedDataWithRootObject: self];
  [encodedData writeToFile:[RWGameData filePath] atomically:YES];
}

This code is the exact reverse of what you implemented in loadInstance. First, you call [NSKeyedArchiver archivedDataWithRootObject:] to get encoded data out of your class instance; this calls encodeWithCoder: on your instance behind the scenes. Then writing the data to file is simply a matter of calling writeToFile:atomically:.

Remember to switch to RWGameData.h, and also to add the method signature inside the class interface:

-(void)save;

And that's all there is to archiving and unarchiving data using the device. Now you only need to call save every now and then, and you're good to go.

Open MyScene.m and add the following line to endTheScene:, just before the line that calls reset on the RWGameData singleton:

[[RWGameData sharedGameData] save];

This wraps up this section, so now it's time to give your game a try! Build and run, then play a few to see your high score persist between launches:

sgd_08_endlevel

Then stop the game from Xcode and launch the project again. When you start the game you'll see the high score you achieved during the previous launch. Success!

sgd_09_highscoresaved

More about NSCoding

Are you mumbling something under your breath? Did you say something to the effect of, "Yeah, when you need to save two double numbers it's all too easy"? Well, you're on the right track. There is more you can do with NSCoding, but the concept is remarkably similar to what you just did.

As I mentioned earlier, NSCoding includes helper methods to encode various types of primitives:

  • encodeBool:forKey:
  • encodeInt:forKey:
  • encodeInt32:forKey:
  • encodeInt64:forKey:
  • encodeFloat:forKey:

You can encode any object that implements NSCoding with this:

  • encodeObject:forKey:

If you can convert something to either NSData or a series of bytes, you can encode it with these methods:

  • encodeBytes:length:forKey:
  • encodeDataObject:forKey:

And finally, there are a few utility methods to ease the pain whenever you need to encode certain common structures:

  • encodeCGPoint:forKey:
  • encodeCGRect:forKey:
  • encodeCGSize:forKey:
Note:If you're curious to see more, check the headers for NSCoder and NSCoding. Also, check out our tutorial: NSCoding Tutorial for iOS: How To Save Your App Data

Say "Space Cheese"!

Are you ready to try something more interesting than storing numbers? Good! Next, you'll add the player's photo to RWGameData and save it to the device.

To work with images you'll need some extra assets unrelated to this tutorial, so download them now.

Unzip it and drag the ImageMasking folder into your Xcode project. Be sure you select both of the following: Copy items into destination group's folder (if needed) and Create groups for any added folders. You should see three new files in your project navigator:

NewFiles

The files are as follows:

  • 25_mask.png: This is a black and white mask matching the shape of the front window of the spaceship.
  • UIImage+Mask.h: This is a category on UIImage that provides a method to resize and mask a UIImage instance.
  • UIImage+Mask.m: This is the implementation of the method mentioned above.

What you're about to do is let the player take a photo, scale it, mask it and display it in the space ship's window.

Now, add the code for taking a photo. Open MyScene.m and in the file contents, close to the @implementation line, add a new instance variable under the rest of the ones you added earlier:

SKLabelNode* _takePhoto;

This instance variable will hold your new HUD button for making photos. Now scroll down to setupHUD: and add this code at the end of the method body:

_takePhoto = [[SKLabelNode alloc] initWithFontNamed:@"Futura-CondensedMedium"];
_takePhoto.name = @"TakePhotoButton";
_takePhoto.fontSize = 15.0;
_takePhoto.text = @"Pilot Photo";
_takePhoto.position = CGPointMake(self.size.width-40, self.size.height-20);
_takePhoto.fontColor = [SKColor yellowColor];
[self addChild:_takePhoto];

This will create a new label along the rest of the HUD, but this label will act like a button - just like the label that acts like a button to restart the game. You also need to add the code to handle touches.

Scroll to touchesBegan:withEvent:, and add this new chunk of code to handle touches on the Pilot photo button. Check the comments inside the code below to make sure you're adding the right code:

for (UITouch *touch in touches) {
    SKNode *n = [self nodeAtPoint:[touch locationInNode:self]];
    if (n != self && [n.name isEqual: @"restartLabel"]) {
        //[self.theParentView restartScene];
        [[self childNodeWithName:@"restartLabel"] removeFromParent];
        [[self childNodeWithName:@"winLoseLabel"] removeFromParent];
        [self startTheGame];
        return;
    }
 
    // here new code begins
    if (n != self && [n.name isEqualToString:@"TakePhotoButton"]) {
      [self takePhoto];
      return;
    }
    // new code ends
}

What this new code does is to check whether the name of the node being tapped equals to "TakePhotoButton", and if so invokes the takePhoto: of your scene, which you'll add to the class implementation now:

-(void)takePhoto
{
  //1
  self.paused = YES;
 
  //2
  UIImagePickerController* imagePickerController = [[UIImagePickerController alloc] init];
 
  //3
  [imagePickerController setSourceType:UIImagePickerControllerSourceTypeCamera];
  imagePickerController.delegate = self;
 
  //4  
  UIWindow* appWindow = [UIApplication sharedApplication].windows.firstObject;
  [appWindow.rootViewController presentViewController:imagePickerController animated:YES completion:nil];
}

This code does a number of things:

  1. First you pause the game to avoid killing the player while he or she takes a selfie.
  2. Then you create a new UIImagePickerController instance - this is a view controller that allows the user to take a photo.
  3. You set the source type for the image picker to be the camera and the delegate to be the scene class.
  4. Finally, you hold the app's window and present the image picker view controller from the window's top view controller. [TODO: Please check this last point for clarity and accuracy]

Finally, to remove those compiler warnings, you need to conform to a couple of protocols, required by UIImagePickerController.

Open MyScene.m and replace the @interface directive with the following:

@interface MyScene : SKScene<UIImagePickerControllerDelegate, UINavigationControllerDelegate>

The warnings will now disappear with the next project build.

This should be enough to see the camera view controller, so give it a try! Build and run, then tap the Pilot photo button on the top of the scene.

Note: Unfortunately, you can only run this portion of the code on a device. If you try to run it in the simulator, it will crash with a NSInvalidArgumentException since the camera is not available.

Oooops! You should see an error in the console like this:

Terminating app due to uncaught exception 'UIApplicationInvalidInterfaceOrientation', 
reason: 'Supported orientations has no common orientation with the application, 
and shouldAutorotate is returning YES'

This was (almost) unexpected. So, to find the trouble-maker, click on the project file and check the allowed screen orientations:

sgd_12_orientation

Bingo! The image picker view controller only supports portrait orientation, but the app doesn't support portrait - so this seems easy enough to correct, right? Just check the portrait checkbox and build and run.

sgd_13_orientationPort

Hmm…now when you tilt the device in a certain position the whole scene rotates and the display looks off-kilter. As it turns out, your game can't support both portrait and landscape orientations.

Now you're going to implement a little trickery to support only landscape while the player plays the game, and only portrait while they are taking a photo.

Open ViewController.h and add a new static variable above the @interface line:

static BOOL lockToPortraitOrientation = NO;

This is a static variable, which you'll set to YES when you present the image picker and set back to NO when the player wants to continue playing.

Switch to MyScene.m and add along the rest of the imports at the top of the file:

#import "ViewController.h"

Add the following as the first line of code in the takePhoto:

lockToPortraitOrientation = YES;

Most of your master plan is now completed, the only thing remaining is to make ViewController respect the value of lockToPortraitOrientation. Open ViewController.m and find the method called supportedInterfaceOrientations. Replace the contents of the method with the following:

if (lockToPortraitOrientation) {
  return UIInterfaceOrientationMaskPortrait;
} else {
  return UIInterfaceOrientationMaskLandscapeRight;
}

The code is straight forward; if lockToPortraitOrientation is YES the view controller allows only portrait orientation; otherwise it allows landscape only. Could something so simple actually work? Give it a try!

sgd_14_cameraview

The camera view shows up and you can snap a picture, nice! But did you notice there's another problem? update: doesn't pause while you take a photo. There's a quick fix for that too, just open MyScene.m and as first line of code in update: add:

if (self.paused) {
  return;
}

This stops all node actions while you're taking your mug shot. That's more like it!

Immortalizing Your Photo

Now you need to make sure the photo is useful to the player. In this section, you'll add the delegate methods to the image picker view controller to make sure your handsome face saves to the disc, along your high score and total distance flown.

First open RWGameData.h and add a new property:

@property (strong, nonatomic) UIImage* pilotPhoto;

Switch to RWGameData.m and along with the other constants near the top of the file, add this:

static NSString* const SSGameDataPilotPhotoKey = @"pilotPhoto";

This will be the key you use to send the photo to the encoder/decoder.[TODO:I replaced 'encode' with 'send' to reduce redundancy. Please confirm the word choice is suitable] Speaking of which, scroll to encodeWithCoder: and add the following to the end to encode the photo property:

if (self.pilotPhoto) {
  NSData* imageData = UIImagePNGRepresentation(self.pilotPhoto);
  [encoder encodeObject:imageData forKey: SSGameDataPilotPhotoKey];
}

This code is a tiny bit different from how you've encoded the rest of the properties.

Note how you check whether self.pilotPhoto is set, and encode that property only if it is. After all, there's no need to write anything if the user is camera-shy!

Encoding the photo is a bit different when you compare it to the rest of the properties. UIImage does not implement NSCoding, so you have to convert it to a buffer of bytes first.

To do this, you use UIImagePNGRepresentation to convert the image to NSData (which does implement NSCoding), and then you can use encodeObject:forKey: as usual.

Now scroll to initWithCoder:. Look inside the if statement, which is after the code that initializes the other properties; add this code to initialize the photo:

NSData* imageData = [decoder decodeObjectForKey: SSGameDataPilotPhotoKey];
if (imageData) {
  _pilotPhoto = [[UIImage alloc] initWithData:imageData];
}

You're reversing what you did earlier. First, you try to decode the object for the photo key. If it exists, you just create a new UIImage instance out of the data.

Implementing the image picker delegate methods in your scene is the last step to immortalizing the player's photo. Open MyScene.m and at the top, alongside the rest of the imports, add:

#import "UIImage+Mask.h"

First, create a method stub for adding the pilot. You will be adding to it later in the tutorial. Please this code at the bottom of MyScene.m, just before the @end directive.

-(void) setupPilot
{
    // Code to go here.
}

Next, create a new method underneath the previous one to handle a successful photo capture.

-(void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info
{
  //1
  lockToPortraitOrientation = NO;
 
  //2
  [picker dismissViewControllerAnimated:YES
    completion:^{
 
    //3
    UIImage* photoTaken = info[UIImagePickerControllerOriginalImage];
    UIImage* pilotImage = [photoTaken imageWithSize: CGSizeMake(25, 25) andMask:[UIImage imageNamed:@"25_mask.png"]];
 
    //4
    [RWGameData sharedGameData].pilotPhoto = pilotImage;
    [[RWGameData sharedGameData] save];
 
    //5
    [self setupPilot];
 
    //6                            
    self.paused = NO;
  }];
}

This delegate method received the captured photo, so you're good to process and use it. You do this in several steps:

  1. First you reset the screen orientation to landscape only.
  2. Then you dismiss the image picker view controller.
  3. When the view controller is dismissed, you grab the captured photo (located in info[UIImagePickerControllerOriginalImage]), then store a resized and masked version of it in pilotImage.
  4. In the next couple lines you store the photo in RWGameData and then invoke save to make sure the photo saves to the disc immediately.
  5. Then you call setupPilot, which displays the photo on screen.
  6. Finally, you un-pause the scene withsetupPilot, which you'll add in few moments

Now you'll wrap up the image picker part of the code. Add the delegate method, the one called when the user cancels the image capture:

-(void)imagePickerControllerDidCancel:(UIImagePickerController *)picker
{
    lockToPortraitOrientation = NO;
 
    [picker dismissViewControllerAnimated:YES
                               completion:^{
                                   self.paused = NO;
                               }];
}

This method just resets the orientation back to landscape, dismisses and image picker view controller, and un-pauses the scene.

You're at the last step before wrapping up! Now you just need to display the photo on the screen. Make sure setupPilot: looks like the following:

-(void)setupPilot
{
  //1
  if ([RWGameData sharedGameData].pilotPhoto) {
    //2
    UIImage* pilotImage = [[RWGameData sharedGameData].pilotPhoto imageWithSize: CGSizeMake(25, 25) andMask:[UIImage imageNamed:@"25_mask.png"]];
 
    //3
    [[_ship childNodeWithName:@"Pilot"] removeFromParent];
 
    //4
    SKTexture* pilotTexture = [SKTexture textureWithImage:pilotImage];
    SKSpriteNode* pilotSprite = [SKSpriteNode spriteNodeWithTexture: pilotTexture];
    pilotSprite.name = @"Pilot";
    pilotSprite.position = CGPointMake(28, 5);
    [_ship addChild: pilotSprite];
  }
}
  1. First, you check whether there's a pilot's photo. You're going to call this method when the game starts, so you need to check.
  2. Then you make sure the photo is properly sized and masked, because once you persist it via the encoder the masking will actually disappear.
  3. Check for an existing pilot photo node with name of "Pilot" and if found, remove it from the scene.
  4. Create a new texture from the pilot photo and create a sprite node out of the resulting texture. Finally, position the sprite node and add it as a child to the space ship.

Your app calls this method when you capture a new photo, and also when you build the scene and load the game data from a file. If the player sets his or her photo in a previous app launch, you'll need to load the photo when the game starts.

Do that in initWithSize:. Just find the line where you add the ship to the scene: [self addChild:_ship]; and add this above:

[self setupPilot];

Perfect! Now when you start the game the app will check for persisted photo, and if found, it'll display on-screen. If the player takes a new photo - it overwrites and saves the new one to the game data file.

Build and run. You should be able to take your photo and see yourself piloting that space ship! Yeah!

sgd_15_pilot_spaceship

Where To Go From Here?

Here is the example project up to this point.

Here's a recap of what you learned in the part I of this tutorial series:

  • How to encode/decode data to the disc
  • How to encode/decode more complex data (i.e. beyond primitives)
  • How to have optional game data (i.e. can be set or undefined)
  • Bonus: using dynamic photos in your game scene and advanced screen orientation handling

In part two, you'll learn how to prevent malicious users from tinkering with your game's data file and how to share the game data between devices via iCloud.

If you have any questions or comments, please share them with us in the comments below!

How to Save your Game’s Data: Part 1/2 is a post from: Ray Wenderlich

The post How to Save your Game’s Data: Part 1/2 appeared first on Ray Wenderlich.

How to Save your Game’s Data: Part 2/2

$
0
0
Space game

Learn how to save your game’s data!

Welcome to part two of this tutorial series that’s all about learning how to save data in a game!

In the first part of this series, you learned how to store your game’s data in a separate class, and save it to disk using NSCoding.

In this second and final part of the series, you’ll:

  • Take on cheaters and deal with them once and forever
  • Add iCloud capabilities to your game.

Feel free to continue coding from where you left off, or you can download the project, completed to this point.

Now enable your hacking scanners and set your ship’s auto-pilot to iCloud City!

Getting Started

So far everything is perfect in your game-data-land; you track the player achievements, store them to the disc, and you even have a pilot photo showing somewhere there!

Now, imagine this scenario coming to life before your very eyes: Peter is a top-notch Space Shooter, and he achieves a high score of 10,000 points. He then loads his game on Stan’s phone, which overwrites Stan’s game data. Since the file has no protection so now Stan’s high score is also at 10,000 points! Peter is upset because now Stan is doing a victory dance and tweeting out that he’s the king of Space Shooter.

sgd_16_whathaveyoudone

With a simple game like Space Shooter, this problem isn’t a big deal. Well, except for Peter, but he’ll muddle through somehow. However, you can probably imagine a case where this would be a big problem, for example, if you were to send the high score to a leaderboard server.

The first thing that comes to mind when you need to add a smidgen of security to an app is to store the data in the user Keychain. However, it’s designed to store short pieces of text, like passwords, and you need to store more complicated data like photos. Luckily, there is a workaround for this. By the time you’re at the end of this tutorial, you’ll be ready to start working with it in your projects.

When you use Keychain to save the game data to the disc, you’ll also store a hash of the file contents in the Keychain. What does that mean? A malicious user could possibly read the data, and for example see the high score, but they won’t be able to tinker with anything without invalidating the hash.

It won’t take too long to implement the necessary modifications. First, download the KeychainWrapper class, which will make it easier for you to work with the device Keychain storage:

Note: The KeychainWrapper comes to you from another useful tutorial on RayWenderlich.com; check it out to learn more about the Keychain: Basic Security in iOS5, by Chris Lowe. Please note there are a couple of very small modifications to the source so that it’s a better fit for this tutorial.

Extract the contents of the zip file and copy the two resulting files to your project. You should see KeychainWrapper‘s files visible, like so:

sgd_17_keychainwrapper

Open RWGameData.m. At the top file, import the Keychain helper class header file.

#import "KeychainWrapper.h"

Underneath the @implementation direction, add the following:

static NSString* const SSGameDataChecksumKey = @"SSGameDataChecksumKey";

Since you’re tracking the hash, you’ll need to update it when you save the game data. Can you guess where it will to go? Scroll down to the save method.

KeychainWrapper sports a rather handy method that will help you achieve your goal. Add this line to get the SHA256 hash of encodedData to show up at the end of save:

NSString* checksum = [KeychainWrapper computeSHA256DigestForData: encodedData];

computeSHA256DigestForData: gets in an NSData instance and returns the SHA256 as a string, which is pretty handy to store in the Keychain. Add this code:

if ([KeychainWrapper keychainStringFromMatchingIdentifier: SSGameDataChecksumKey]) {
  [KeychainWrapper updateKeychainValue:checksum forIdentifier:SSGameDataChecksumKey];
} else {
  [KeychainWrapper createKeychainValue:checksum forIdentifier:SSGameDataChecksumKey];
}

You use keychainStringFromMatchingIdentifier: to check whether a value for the given key exists, and if so, you update it with updateKeychainValue:forIdentifier:. If not, you create new value storage for the checksum key createKeychainValue:forIdentifier:.

One part of the operation is now complete. Every time you save the game data file, you also store its checksum securely in the Keychain. Next, you’ll change the code so that it loads a stored file only when the file checksum is identical to most recently saved game. Locate this piece of code in loadInstance:

if (decodedData) {
  RWGameData* gameData = [NSKeyedUnarchiver unarchiveObjectWithData:decodedData];
  return gameData;
}

Currently the class unarchives provided data into an instance and returns it straight away. Replace the whole chunk of code above with this new logic:

if (decodedData) {
  //1
  NSString* checksumOfSavedFile = [KeychainWrapper computeSHA256DigestForData: decodedData];
 
  //2
  NSString* checksumInKeychain = [KeychainWrapper keychainStringFromMatchingIdentifier: SSGameDataChecksumKey];
 
  //3
  if ([checksumOfSavedFile isEqualToString: checksumInKeychain]) {
    RWGameData* gameData = [NSKeyedUnarchiver unarchiveObjectWithData:decodedData];
    return gameData;
  }
  //4
}

That’s more like it! Here’s what you did when you added this new code:

  1. You generate the SHA256 hash of the decoded data and store it in checksumOfSavedFile
  2. Then you get the most recent SHA256 hash from the Keychain and store it in checksumInKeychain. If you get decoded data, you must’ve stored its hash in the Keychain before.
  3. Finally, you compare both checksum strings. If they are equal, you unarchive the data and return the resulting RWGameData as the result.
  4. If the checksums don’t match – you don’t do anything. The code execution continues, and the method returns a new blank RWGameData on the next line.

Your basic security is in place! Good job!

Note: How you punish the user for tinkering with their game data file is entirely in your hands. In this tutorial, you’ll learn how to take a subtle approach by quietly deleting their high score and distance flown, but hey … you can be mischievous too and do things like show a sassy meme or alert the cheat police — any punishment you feel fits the crime.

The all time winner goes to the makers of Serious Sam 3. When the game detected that it was pirated, it spawned – midgame, no less – a player hunting indestructible pink scorpion that pretty much ended the game.

Build and run the project again. Look at that! Your high score is back to zero, but you didn’t modify your game file. Can you guess what happened? Try to figure it out. If you give up, click below for the solution.

Solution Inside: Solution SelectShow>

Now, play a round or two to set a new high score, and check whether everything is working. What’s the highest score you can achieve?

sgd_18_highscore

Enabling iCloud

Unless you’ve spent the last five years living in a cave on the moon, then you know “the cloud” is all the rage these days. When you have the cloud you can: do some work on your laptop, continue working on your iPhone in the subway and then pick up where you left on your iPad during your flight; it’s all connected as long as your iOS device is connected to iCloud.

With so many iOS devices in your life that are suitable for gaming (c’mon Apple we want a gaming Apple TV already!), you’ll definitely want the ability to pick up any of your devices and continue blasting asteroids to smithereens. You’ll also want to have your pilot photo and high scores synchronized across your iOS devices, right?

Luckily, enabling iCloud for your game is straightforward. Here’s how you set your game up to take advantage of the cloud

Select the SpaceShooter project file in the project navigator and select the SpaceShooter target. Click on the Capabilities tab.

sgd_19_icloud

Click the ON/OFF switch to toggle its state to ON.

The settings will change to look like the following image:

sgd_20_icloudON

Look at the project file list to make sure Xcode completed the listed steps. Look for a new entitlements file that enables iCloud capability for the game:

sgd_21_entitlements

So far, so good! The next step is to enable key-value storage for your game; you’ll use this kind of iCloud storage to persist the game data between synchronized devices. That’s technology at its best.

sgd_22_entitlementsON

Believe it or not, Apple’s iCloud service already backs up your game.

Time to put your head in the cloud. Literally

The next question is, “How do I get my handsome mug shot synced across all my devices?”

SGD_post_image

The logic behind storing the game data is a little bit more complicated. Consider the following:

  • The user might or might not have iCloud enabled on their devices. Therefore, local storage should be primary, with iCloud as an optional secondary storage.
  • The user might have played for a while, and only used disc storage. Then one sunny day they might decide to turn iCloud on, and the game will need to handle this situation properly, i.e. not delete the locally stored high score.
  • The user could delete the game completely and then re-install it. In this scenario you need to initialize the game data from iCloud the first time it launches.

So there’s a little more to it than just syncing to iCloud. Fortunately, for each of these scenarios there is a solution, and by the end of this tutorial you’ll know what to do to manage the most common.

In the beginning of this tutorial, you read that you’ll keep some game data in the cloud and some will save to the device. This means you’ll:

  • Check if the local high score is higher than the iCloud high score, and update the one in iCloud
  • Store the total distance flown locally, which effectively keeps track of the distance the player flies the ship on a particular device
  • Share the pilot photo in iCloud and monitor for changes across synced devices. This way if you take a new selfie on your iPhone, it will change on your iPad too

Here’s a recap of the final logic for storing the game data:

score Stored only in memory; resets when a new game starts.
distance Stored only in memory; resets when a new game starts.
totalDistance Stored on disc; persists between app launches, and stored on single device.
highScore Stored on disc, and always compared to iCloud. The highest number is used.
pilotPhoto Stored on disc; stored in iCloud. The latest photo taken overwrites the one in iCloud.

So far, you have all of these in working order, except for the high score and pilot photo.

Note: If you are interested in learning more about iCloud, check out these tutorials:
Beginning iCloud in iOS 5
and iCloud and UIDocument: Beyond the Basics. Also, you should check out our book, iOS 5 by Tutorials, which has been recently updated for Xcode 5 and iOS 7.

Updating iCloud

Open RWGameData.m and add this new method anywhere inside the class body:

-(void)updateiCloud
{
  NSUbiquitousKeyValueStore *iCloudStore = [NSUbiquitousKeyValueStore defaultStore];
  long cloudHighScore = [iCloudStore doubleForKey: SSGameDataHighScoreKey];
 
}

Since you’ll need to store just a couple of key-value pairs in your iCloud bucket, you’ll automatically work with the default storage. NSUbiquitousKeyValueStore is the class that provides you access to iCloud data. You can think of NSUbiquitousKeyValueStore as an NSDictionary in the cloud :]

After you get the default NSUbiquitousKeyValueStore, you use its doubleForKey:. It’s similar to what you would use with an NSCoder to get the stored high score value. Use the same constant for name of the key when archiving/unarchiving RWGameData. In the end, why not? The key name is just a string constant!

Now that you have the high score from iCloud, it’s time to compare it to the local data. Add these few lines:

if (self.highScore > cloudHighScore) {
  [iCloudStore setDouble:self.highScore forKey: SSGameDataHighScoreKey];
  [iCloudStore synchronize];
}

The code is simple enough. If the local high score is more impressive than the one on iCloud, then it’s time to update iCloud. setDouble:forKey: sets the value for the high score key and finally a call to synchronize fires up an iCloud sync.

Note: If you have ever used NSUserDefaults you probably already feel at home. The iCloud API and device key-value storage are similar by design.

Next, scroll to the method called save and add at the end:

if([NSUbiquitousKeyValueStore defaultStore]) {
  [self updateiCloud];
}

Checking if [NSUbiquitousKeyValueStore defaultStore] returns a class instance tells you whether iCloud is enabled or not. After the game data saves to the disc you check if iCloud is enabled, and if so, you call your shiny and new updateiCloud method.

That takes care of updating the high score to iCloud. Next, you’ll sync your photo.

Consider what you’re going to do for a moment. The logic for the high score is simple enough; compare two numbers and if the local one is bigger about the game updates the data on iCloud. Can you do the same for the photo? You certainly can’t compare photo sizes. What you could do is keep the last modified date of the photo separate and compare the last modification date of both photos. That could work, but there’s a better way.

You’re going to be ultra-smart about updating the photo to iCloud because you’ll implement a custom setter for the pilotPhoto.

Add this new method to RWGameData:

-(void)setPilotPhoto:(UIImage *)pilotPhoto
{
  //1
  _pilotPhoto = pilotPhoto;
 
  //2
  if([NSUbiquitousKeyValueStore defaultStore]) {
 
    //3
    NSUbiquitousKeyValueStore *iCloudStore = [NSUbiquitousKeyValueStore defaultStore];
 
    //4
    NSData* imageData = UIImagePNGRepresentation(pilotPhoto);
    [iCloudStore setObject:imageData forKey: SSGameDataPilotPhotoKey];
    [iCloudStore synchronize];
  }
}

Here’s what the code does:

  1. First, you store the new image in the backing instance variable _pilotPhoto.
  2. Then, just as before, you check whether iCloud is enabled by probing the default store.
  3. You fetch the default store in iCloudStore.
  4. Finally, you get the PNG data out of the photo and store it in your key-value iCloud storage. You wrap up with a call to storage’s synchronize method.

Now every time the player takes a new photo it will upload to iCloud. Nice.

Fetching Data from iCloud

So far, you have the code in place to update the iCloud key-value storage with your local data. But how about fetching data from iCloud and updating your local storage?

Lucky for you iCloud shouts out whenever there are pending changes. You can simply observe the NSUbiquitousKeyValueStoreDidChangeExternallyNotification notification and update your local storage accordingly.

Since you want to start listening for iCloud changes as soon as RWGameData instance is created, the best way to do that is with a custom init.

Add the following code underneath init.

- (instancetype)init
{
  self = [super init];
  if (self) {
    //1
    if([NSUbiquitousKeyValueStore defaultStore]) {
 
      //2
      [[NSNotificationCenter defaultCenter] addObserver:self
        selector:@selector(updateFromiCloud:)
        name:NSUbiquitousKeyValueStoreDidChangeExternallyNotification
        object:nil];
    }
  }
  return self;
}
Note: The following code will produce a warning as you haven’t created updateFromiCloud: yet … so stay calm … that’s coming next :]

This is a classic init with couple of code additions:

  1. You check whether the device has enabled iCloud.
  2. You look for a NSUbiquitousKeyValueStoreDidChangeExternallyNotification in case iCloud is enabled.

This ensures that every time a change comes in from iCloud, updateFromiCloud: will fire up. For example, if you have the game open on two devices and take a photo on one of them.

Now you’ll code the observer method. Add it to the class with few initial lines of code:

-(void)updateFromiCloud:(NSNotification*) notificationObject
{
    NSUbiquitousKeyValueStore *iCloudStore = [NSUbiquitousKeyValueStore defaultStore];
    long cloudHighScore = [iCloudStore doubleForKey: SSGameDataHighScoreKey];
    self.highScore = MAX(cloudHighScore, self.highScore);
 
 
}

You’re probably already super-familiar with this kind of code. You simply fetch the default store and you get the iCloud high score in cloudHighScore. In the end, you assign the highest score from the local and iCloud data to self.highScore.

High score is all set, so now onto the pilot’s photo. Add this code to the bottom of updateFromiCloud:

if ([iCloudStore objectForKey: SSGameDataPilotPhotoKey]) {
  NSData* imageData = [iCloudStore objectForKey: SSGameDataPilotPhotoKey];
  if (imageData) {
    _pilotPhoto = [[UIImage alloc] initWithData:imageData];
  }
}

When unarchiving data from the game data file, you check for a stored photo, and if there is one you fetch the persisted NSData and use it to create a UIImage. You update RWGameData and save the image as the current pilot photo.

Do you know why you used the backing instance variable _pilotPhoto to set the photo instead of setting directly the property self.pilotPhoto? Try to figure it out. When you’re ready, click below to expand the answer.

Solution Inside: Solution SelectShow>

Now you’ve done all the necessary updates. To make sure all of the changes are persisted, also add a call to save the game data file to the disc.

[self save];

So you’ve stored data to iCloud and fetched with no (hopefully) problems. Don’t run the game just yet though.

Since iCloud is accessible via network, access and changes distribution are not instantaneous. Sometimes you’ll start the game and a few seconds into the gameplay the new photo will show up. Therefore, you need a way to update the game data and the scene changes that come in during gameplay.

sgd_23_challenge

You’ve already done half of the work, because you receive a notification when changes come in from iCloud. You just need to fire another notification when you’ve updated the game data so that MyScene knows to update the UI too.

Open RWGameData.h, and under the import statement define your notification’s name constant:

static NSString* const SSGameDataUpdatedFromiCloud = @"SSGameDataUpdatedFromiCloud";

Naturally, you’ll fire the notification just after you update the game data from iCloud. Switch back to RWGameData.m and find updateFromiCloud:. Just add this line to the end of it:

[[NSNotificationCenter defaultCenter] postNotificationName: SSGameDataUpdatedFromiCloud object:nil];

postNotificationName:object: will emit a notification through NSNotificationCenter. All you need to do is catch it in your scene class and update the on-screen high score and pilot photo.

Now open MyScene.m and scroll towards the bottom of initWithSize:. Find the [self setupHUD]; line. This is the right place to start observing for notifications about changes to the HUD. Add below that line:

[[NSNotificationCenter defaultCenter] addObserver:self
  selector:@selector(didUpdateGameData:)
  name:SSGameDataUpdatedFromiCloud
  object:nil];

You add the scene class as an observer for the SSGameDataUpdatedFromiCloud notification and instruct NSNotificationCenter to fire up didUpdateGameData: if such a notification comes in. Looking good!

Being a good programmer, you’ll also want to remove the class from NSNotificationCenter when it is destroyed. Add a dealloc to the class implementation:

-(void)dealloc
{
  [[NSNotificationCenter defaultCenter] removeObserver:self name:SSGameDataUpdatedFromiCloud object:nil];
}

This should take care to keep NSNotificationCenter sane and in balance.

And now – the grand finale! The method that will update the scene in real time! Yes, that means as changes come in!

Add this fabulous piece of code to wrap up iCloud integration:

-(void)didUpdateGameData:(NSNotification*)n
{
    _highScore.text = [NSString stringWithFormat:@"High: %li pt", [RWGameData sharedGameData].highScore];
    [self setupPilot];
}

Nothing much to discuss in there — you update the high score label and invoke setupPilot, which in turn updates the pilot photo.

That’s all there is to iCloud integration.

Now blow up some asteroids to celebrate your success!

sgd_24_victory

To test whether iCloud is working, try this on your iCloud enabled iOS device:

  1. Launch the game and take a new photo, the play the game few times
  2. Then stop the game from Xcode
  3. Delete the game from your device
  4. Now re-launch the game from within Xcode

So what happens when you launch the game after you deleted the previous installation?

You start with a clean slate:

sgd_25_cleanslate

A few seconds into the game:

sgd_26_cleanslate2

You got it working! Congrats! Time for a cold beer. :]

Where To Go From Here?

Here is the final version of the example project from this tutorial series.

You covered a lot in the second part of this tutorial by adding the following new features:

  • Keychain integration and basic security against cheaters
  • iCloud integration for your game
  • Key-value storage for the player’s high score and photo
  • Observing incoming changes from the cloud and updating the scene as they come in

Now you know a lot more about how to save your game data, but there’s always more to learn. Here are a few ideas to dive deeper into this subject:

  • You didn’t get to display the total distance flown by the user. You can add a new label for that to the scene.
  • How about keeping the last three (or more) scores for the player? This will give you an opportunity to use encodeObject:forKey: with an NSArray instance

Space Game Starter Kit

But there’s even more to the Space Shooter project than what you see here! Are you aware that Ray has developed the Space Shooter game into a full blown game starter kit? If you lay your hands on the starter kit, you get an epic, detailed tutorial, plus the code to a complete space game based on Space Shooter. Pretty cool, right?

Check out the Space Shooter starter kit – it comes with art, music, and source code; the kit extends the game by adding bad guys, power-ups, more weapons and level bosses.

If you have any questions or comments, please share in the comments below.

How to Save your Game’s Data: Part 2/2 is a post from: Ray Wenderlich

The post How to Save your Game’s Data: Part 2/2 appeared first on Ray Wenderlich.

Video Tutorial: Beginner OpenGL ES and GLKit Part 5: 3D Transformation

Reminder: Free Live Tech Talk (Reactive Cocoa) Tomorrow (Tuesday)!

$
0
0
Free live tech talk (Reactive Cocoa) tomorrow!

Free live tech talk (Reactive Cocoa) tomorrow!

This is a reminder that we are having a free live tech talk on Reactive Cocoa tomorrow (Tuesday Apr 8), and you’re all invited! Here are the details:

We hope to see some of you at the tech talk, and we hope you enjoy!

Reminder: Free Live Tech Talk (Reactive Cocoa) Tomorrow (Tuesday)! is a post from: Ray Wenderlich

The post Reminder: Free Live Tech Talk (Reactive Cocoa) Tomorrow (Tuesday)! appeared first on Ray Wenderlich.

Video Tutorial: Beginner OpenGL ES and GLKit Part 6: Texturing

ReactiveCocoa Tech Talk Video

$
0
0

The first Tuesday of each month, one of the members of the team gives a Tech Talk, and by popular request we’ve started to stream these live.

Today in our April Tech Talk, Colin Eberhardt (CTO of ShinobiControls) and Justin Spahr-Summers (co-creator of ReactiveCocoa) gave an excellent talk and Q&A on ReactiveCocoa.

Here’s the video for anyone who didn’t get a chance to watch!

Helpful Links

Here are some handy links to learn more about ReactiveCocoa:

Want to Join Us Next Month?

Thanks again Colin and Justin for giving a great talk and Q&A, having the guts to present to a live audience :] And thank you to everyone who attended – we hope you enjoyed it!

Next month, our May Tech talk will be on Procedural Level Generation in Games, with Tutorial Team member Kim Pedersen.

We will be broadcasting this talk live on Tuesday, May 6 at 2:00 PM EST, so if you want to join us sign up here! As you watch the talk, you can submit any Q&A you may have live.

Hope to see some of you there! :]

ReactiveCocoa Tech Talk Video is a post from: Ray Wenderlich

The post ReactiveCocoa Tech Talk Video appeared first on Ray Wenderlich.

Video Tutorial: Beginner OpenGL ES and GLKit Part 7: Ambient Lighting


Grand Central Dispatch In-Depth: Part 1/2

$
0
0
Learn about concurrency in this Grand Central Dispatch in-depth tutorial series.

Learn about concurrency in this Grand Central Dispatch in-depth tutorial series.

Although Grand Central Dispatch (or GCD for short) has been around for a while, not everyone knows how to get the most out of it. This is understandable; concurrency is tricky, and GCD’s C-based API can seem like a set of pointy corners poking into the smooth world of Objective-C. Learn about Grand Central Dispatch in-depth in this two part tutorial series.

In this two-part series, the first tutorial explains what GCD does and showcases several of the more basic GCD functions. In the second part, you’ll learn several of the more advanced functions GCD has to offer.

What is GCD?

GCD is the marketing name for libdispatch, Apple’s library that provides support for concurrent code execution on multicore hardware on iOS and OS X. It offers the following benefits:

  • GCD can improve your app’s responsiveness by helping you defer computationally expensive tasks and run them in the background.
  • GCD provides an easier concurrency model than locks and threads and helps to avoid concurrency bugs.
  • GCD can potentially optimize your code with higher performance primitives for common patterns such as singletons.

This tutorial assumes that you have a basic understanding of working with blocks and GCD. If you’re brand-new to GCD, check out Multithreading and Grand Central Dispatch on iOS for Beginners to learn the essentials.

GCD Terminology

To understand GCD, you need to be comfortable with several concepts related to threading and concurrency. These can be both vague and subtle, so take a moment to review them briefly in the context of GCD.

Serial vs. Concurrent

These terms describe when tasks are executed with respect to each other. Tasks executed serially are always executed one at a time. Tasks executed concurrently might be executed at the same time.

Although these terms have wide application, for the purposes of this tutorial you can consider a task to be an Objective-C block. Don’t know what a block is? Check out the How to Use Blocks in iOS 5 Tutorial. In fact, you can also use GCD with function pointers, but in most cases this is substantially more tricky to use. Blocks are just easier!

Synchronous vs. Asynchronous

Within GCD, these terms describe when a function completes with respect to another task that the function asks GCD to perform. A synchronous function returns only after the completion of a task that it orders.

An asynchronous function, on the other hand, returns immediately, ordering the task to be done but does not wait for it. Thus, an asynchronous function does not block the current thread of execution from proceeding on to the next function.

Be careful — when you read that a synchronous function “blocks” the current thread, or that the function is a “blocking” function or blocking operation, don’t get confused! The verb blocks describes how a function affects its own thread and has no connection to the noun block, which describes an anonymous function literal in Objective-C and defines a task submitted to GCD.

Critical Section

This is a piece of code that must not be executed concurrently, that is, from two threads at once. This is usually because the code manipulates a shared resource such as a variable that can become corrupt if it’s accessed by concurrent processes.

Race Condition

This is a situation where the behavior of a software system depends on a specific sequence or timing of events that execute in an uncontrolled manner, such as the exact order of execution of the program’s concurrent tasks. Race conditions can produce unpredictable behavior that aren’t immediately evident through code inspection.

Deadlock
Two (or sometimes more) items — in most cases, threads — are said to be deadlocked if they all get stuck waiting for each other to complete or perform another action. The first can’t finish because it’s waiting for the second to finish. But the second can’t finish because it’s waiting for the first to finish.

Thread Safe

Thread safe code can be safely called from multiple threads or concurrent tasks without causing any problems (data corruption, crashing, etc). Code that is not thread safe must only be run in one context at a time. An example of thread safe code is NSDictionary. You can use it from multiple threads at the same time without issue. On the other hand, NSMutableDictionary is not thread safe and should only be accessed from one thread at a time.

Context Switch

A context switch is the process of storing and restoring execution state when you switch between executing different threads on a single process. This process is quite common when writing multitasking apps, but comes at a cost of some additional overhead.

Concurrency vs Parallelism

Concurrency and parallelism are often mentioned together, so it’s worth a short explanation to distinguish them from each other.

Separate parts of concurrent code can be executed “simultaneously”. However, it’s up to the system to decide how this happens — or if it happens at all.

Multi-core devices execute multiple threads at the same time via parallelism; however, in order for single-cored devices to achieve this, they must run a thread, perform a context switch, then run another thread or process. This usually happens quickly enough as to give the illusion of parallel execution as shown by the diagram below:

Concurrency_vs_Parallelism

Although you may write your code to use concurrent execution under GCD, it’s up to GCD to decide how much parallelism is required. Parallelism requires concurrency, but concurrency does not guarantee parallelism.

The deeper point here is that concurrency is actually about structure. When you code with GCD in mind, you structure your code to expose the pieces of work that can run simultaneously, as well as the ones that must not be run simulataneously. If you want to delve more deeply into this subject, check out this excellent talk by Rob Pike.

Queues

GCD provides dispatch queues to handle blocks of code; these queues manage the tasks you provide to GCD and execute those tasks in FIFO order. This guarantees that first task added to the queue is the first task started in the queue, the second task added will be the second to start, and so on down the line.

All dispatch queues are themselves thread-safe in that you can access them from multiple threads simultaneously. The benefits of GCD are apparent when you understand how dispatch queues provide thread-safety to parts of your own code. The key to this is to choose the right kind of dispatch queue and the right dispatching function to submit your work to the queue.

In this section you’ll take a look at the two kinds of dispatch queues, the particular queues GCD offers, and then work through some examples that illustrate how to add work to the queues with the GCD dispatching functions.

Serial Queues

Tasks in serial queues execute one at a time, each task starting only after the preceding task has finished. As well, you won’t know the amount of time between one block ending and the next one beginning, as shown in the diagram below:

Serial-Queue

The execution timing of these tasks is under the control of GCD; the only thing you’re guaranteed to know is that GCD executes only one task at a time and that it executes the tasks in the order they were added to the queue.

Since no two tasks in a serial queue can ever run concurrently, there is no risk they might access the same critical section concurrently; that protects the critical section from race conditions with respect to those tasks only. So if the only way to access that critical section is via a task submitted to that dispatch queue, then you can be sure that the critical section is safe.

Concurrent Queues

Tasks in concurrent queues are guaranteed to start in the order they were added…and that’s about all you’re guaranteed! Items can finish in any order and you have no knowledge of the time it will take for the next block to start, nor the number of blocks that are running at any given time. Again, this is entirely up to GCD.

The diagram below shows a sample task execution plan of four concurrent tasks under GCD:

Concurrent-Queue

Notice how Block 1, 2, and 3 all ran quickly, one after another, while it took a while for Block 1 to start after Block 0 started. Also, Block 3 started after Block 2 but finished first.

The decision of when to start a block is entirely up to GCD. If the execution time of one block overlaps with another, it’s up to GCD to determine if it should run on a different core, if one is available, or instead to perform a context switch to a different block of code.

Just to make things interesting, GCD provides you with at least five particular queues to choose from within each queue type.

Queue Types

First, the system provides you with a special serial queue known as the main queue. Like any serial queue, tasks in this queue execute one at a time. However, it’s guaranteed that all tasks will execute on the main thread, which is the only thread allowed to update your UI. This queue is the one to use for sending messages to UIViews or posting notifications.

The system also provides you with several concurrent queues. These are known as the Global Dispatch Queues. There are currently four global queues of different priority: background, low, default, and high. Be aware that Apple’s APIs also uses these queues, so any tasks you add won’t be the only ones on these queues.

Finally, you can also create your own custom serial or concurrent queues. That means you have at least five queues at your disposal: the main queue, four global dispatch queues, plus any custom queues that you add to the mix!

And that’s the big picture of dispatch queues!

The “art” of GCD comes down to choosing the right queue dispatching function to submit your work to the queue. The best way to experience this is to work through the examples below, where we’ve provided some general recommendations along the way.

Getting Started

Since the goal of this tutorial is to optimize as well as safely call code from different threads using GCD, you’ll start with an almost-finished project named GooglyPuff.

GooglyPuff is a non-optimized, threading-unsafe app that overlays googly eyes on detected faces using Core Image’s face detection API. For the base image you can select any from the Photo Library or from a set of predefined URL images downloaded from the internet.

Download the project here.

Once you’ve downloaded the project, extract it to a convenient location, then open it up in Xcode and build and run. The app will look like the following:

Workflow

Notice when you choose the Le Internet option to download pictures, a UIAlertView pops up prematurely. You’ll fix this in the second part of this series.

There are four classes of interest in this project:

  • PhotoCollectionViewController: This is the first view controller that starts the app. It showcases all the selected photos through their thumbnails.
  • PhotoDetailViewController: This performs the logic to add googly eyes to the image and to display the resulting image in a UIScrollView.
  • Photo: This is a class cluster which instantiates photos from an instance of NSURL or from an instance of ALAsset. This class provides an image, thumbnail, and a status when downloading from a URL.
  • PhotoManager: This manages all the instances of Photo.

Handling Background Tasks with dispatch_sync

Head back to the app and add some photos from your Photo Library or use the Le Internet option to download a few.

Notice how long it takes for a new PhotoDetailViewController to instantiate after clicking on a UICollectionViewCell in the PhotoCollectionViewController; there’s a noticeable lag, especially when viewing large images on slower devices.

It’s often easy to overload UIViewController’s viewDidLoad with too much clutter; this often results in longer waits before the view controller appears. If possible, it’s best to offload some work to be done in the background if it’s not absolutely essential at load time.

This sounds like a job for dispatch_async!

Open PhotoDetailViewController and replace viewDidLoad with the following implementation:

- (void)viewDidLoad
{   
    [super viewDidLoad];
    NSAssert(_image, @"Image not set; required to use view controller");
    self.photoImageView.image = _image;
 
    //Resize if neccessary to ensure it's not pixelated
    if (_image.size.height <= self.photoImageView.bounds.size.height &&
        _image.size.width <= self.photoImageView.bounds.size.width) {
        [self.photoImageView setContentMode:UIViewContentModeCenter];
    }
 
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ // 1
        UIImage *overlayImage = [self faceOverlayImageFromImage:_image];
        dispatch_async(dispatch_get_main_queue(), ^{ // 2
            [self fadeInNewImage:overlayImage]; // 3
        });
    });
}

Here’s what’s going on in the modified code above:

  1. You first move the work off of the main thread and onto a global queue. Because this is a dispatch_async(), the block is submitted asynchronously meaning that execution of the calling thread continues. This lets viewDidLoad finish earlier on the main thread and makes the loading feel more snappy. Meanwhile, the face detection processing is started and will finish at some later time.
  2. At this point, the face detection processing is complete and you’ve generated a new image. Since you want to use this new image to update your UIImageView, you add a new block of work to the main queue. Remember – you must always access UI Kit classes on the main thread!
  3. Finally, you update the UI with fadeInNewImage: which performs a fade-in transition of the new googly eyes image.

Build and run your app; select an image and you’ll notice that the view controller loads up noticeably faster and adds the googly eyes after a short delay. This lends a nice effect to the app as you show the before and after photo for maximum impact.

As well, if you tried to load an insanely huge image, the app wouldn’t hang in the process of loading the view controller, which allows the app to scale well.

As mentioned above, dispatch_async appends a block onto a queue and returns immediately. The task will then be executed at some later time as decided by GCD. Use dispatch_async when you need to perform a network-based or CPU intensive task in the background and not block the current thread.

Here’s a quick guide of how and when to use the various queue types with dispatch_async:

  • Custom Serial Queue: A good choice when you want to perform background work serially and track it. This eliminates resource contention since you know only one task at a time is executing. Note that if you need the data from a method, you must inline another block to retrieve it or consider using dispatch_sync.
  • Main Queue (Serial): This is a common choice to update the UI after completing work in a task on a concurrent queue. To do this, you’ll code one block inside another. As well, if you’re in the main queue and call dispatch_async targeting the main queue, you can guarantee that this new task will execute sometime after the current method finishes.
  • Concurrent Queue: This is a common choice to perform non-UI work in the background.

Delaying Work with dispatch_after

Consider the UX of your app for a moment. It’s possible that users might be confused about what to do when the open the app for the first time — were you? :]

It would be a good idea to display a prompt to the user if there aren’t any photos in the PhotoManager class. However, you also need to think about how the user’s eyes will navigate the home screen: if you display a prompt too quickly, they might miss it as their eyes lingered on other parts of the view.

A one-second delay before displaying the prompt should be enough to catch the user’s attention as they get their first look at the app.

Add the following code to the stubbed-out implementation of showOrHideNavPrompt in PhotoCollectionViewController.m:

- (void)showOrHideNavPrompt
{
    NSUInteger count = [[PhotoManager sharedManager] photos].count;
    double delayInSeconds = 1.0;
    dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC)); // 1 
    dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ // 2 
        if (!count) {
            [self.navigationItem setPrompt:@"Add photos with faces to Googlyify them!"];
        } else {
            [self.navigationItem setPrompt:nil];
        }
    });
}

showOrHideNavPrompt executes in viewDidLoad and anytime your UICollectionView is reloaded. Taking each numbered comment in turn:

  1. You declare the variable that specifies the amount of time to delay.
  2. You then wait for the amount of time given in the delayInSeconds variable and then asynchronously add the block to the main queue.

Build and run the app. There should be a slight delay, which will hopefully grab the user’s attention and show them what to do.

dispatch_after works just like a delayed dispatch_async. You still have no control over the actual time of execution nor can you cancel this once dispatch_after returns.

Wondering when it’s appropriate to use dispatch_after?

  • Custom Serial Queue: Use caution when using dispatch_after on a custom serial queue. You’re better off sticking to the main queue.
  • Main Queue (Serial): This is a good choice for dispatch_after; Xcode has a nice autocomplete template for this.
  • Concurrent Queue: Use caution when using dispatch_after on custom concurrent queues; it’s rare that you’ll do this. Stick to the main queue for these operations.

Making Your Singletons Thread-Safe

Singletons. Love them or hate them, they’re as popular in iOS as cats are on the web. :]

One frequent concern with singletons is that often they’re not thread safe. This concern is well-justified given their use: singletons are often used from multiple controllers accessing the singleton instance at the same time.

Threading concerns for singletons range from initiation, to reads and writes of information.
The PhotoManager class has been implemented as a singleton — and it suffers from these issues in its current state. To see how things can go wrong really quickly, you’ll create a controlled race condition on the singleton instance.

Navigate to PhotoManager.m and find sharedManager; it will look like the code below:

+ (instancetype)sharedManager    
{
    static PhotoManager *sharedPhotoManager = nil;
    if (!sharedPhotoManager) {
        sharedPhotoManager = [[PhotoManager alloc] init];
        sharedPhotoManager->_photosArray = [NSMutableArray array];
    }
    return sharedPhotoManager;
}

The code is rather simple in its current state; you create a singleton and instantiate a private NSMutableArray property named photosArray.

However, the if condition branch is not thread safe; if you invoke this method multiple times, there’s a possibility that one thread (call it Thread-A) could enter the if block and a context switch could occur before sharedPhotoManager is allocated. Then another thread (Thread-B) could enter the if, allocate an instance of the singleton, then exit.

When the system context switches back to Thread-A, you’ll then allocate another instance of the singleton, then exit. At that point you have two instances of a singleton — which is not what you want!

To force this condition to happen, replace sharedManager in PhotoManager.m with the following implementation:

+ (instancetype)sharedManager  
{
    static PhotoManager *sharedPhotoManager = nil;
    if (!sharedPhotoManager) {
        [NSThread sleepForTimeInterval:2];
        sharedPhotoManager = [[PhotoManager alloc] init];
        NSLog(@"Singleton has memory address at: %@", sharedPhotoManager);
        [NSThread sleepForTimeInterval:2];
        sharedPhotoManager->_photosArray = [NSMutableArray array];
    }
    return sharedPhotoManager;
}

In the code above you’re forcing a context switch to happen with NSThread’s sleepForTimeInterval: class method.

Open AppDelegate.m and add the following code to the very beginning of application:didFinishLaunchingWithOptions:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
    [PhotoManager sharedManager];
});
 
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
    [PhotoManager sharedManager];
});

This creates multiple asynchronous concurrent calls to instantiate the singleton and invoke the race condition as described above.

Build and run your project; check the console output and you’ll see multiple singletons instantiated, as shown below:

NSLog-Race-Condition

Notice that there are several lines all showing different addresses of the singleton instance. That defeats the purpose of a singleton, doesn’t it? :]

This output shows you that the critical section executed several times when it should have only have executed once. Now, admittedly, you forced this situation to happen, but you can imagine how this condition could occur unintentionally as well.

Note: Based upon other system events beyond your control, a variable amount of NSLogs will show up on occasion. Threading issues can be extremely hard to debug since they tend to be hard to reproduce.

To correct this condition, the instantiation code should only execute once and block other instances from running while it is in the critical section of the if condition. This is exactly what dispatch_once does.

Replace the conditional if check with dispatch_once in the singleton initialization method as shown below:

+ (instancetype)sharedManager
{
    static PhotoManager *sharedPhotoManager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        [NSThread sleepForTimeInterval:2];
        sharedPhotoManager = [[PhotoManager alloc] init];
        NSLog(@"Singleton has memory address at: %@", sharedPhotoManager);
        [NSThread sleepForTimeInterval:2];
        sharedPhotoManager->_photosArray = [NSMutableArray array];
    });
    return sharedPhotoManager;
}

Build and run your app; check the console output and you’ll now see one and only one instantiation of the singleton — which is what you’d expect for a singleton! :]

Now that you understand the importance of preventing race conditions, remove the dispatch_async statements from AppDelegate.m and replace PhotoManager‘s singleton initialization with the following implementation:

+ (instancetype)sharedManager
{
    static PhotoManager *sharedPhotoManager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedPhotoManager = [[PhotoManager alloc] init];
        sharedPhotoManager->_photosArray = [NSMutableArray array];
    });
    return sharedPhotoManager;
}

dispatch_once() executes a block once and only once in a thread safe manner. Different threads that try to access the critical section — the code passed to dispatch_once — while a thread is already in this section are blocked until the critical section completes.

Highlander_dispatch_once

It should be noted that this just makes access to the shared instance thread safe. It does not make the class thread safe, necessarily. You could still have other critical sections in the class, for instance anything that manipulates internal data. Those would need to be made thread safe in other ways, such as synchronising access to the data, as you’ll see in the following sections.

Handling the Readers and Writers Problem

Thread-safe instantiation is not the only issue when dealing with singletons. If the singleton property represents a mutable object, then you need to consider whether that object is itself thread-safe.

If the object in question is a Foundation container class, then the answer is — “probably not”! Apple maintains a helpful and somewhat chilling list of the numerous Foundation classes which are not thread-safe. NSMutableArray, used by your singleton, is right there among the rest.

Although many threads can read an instance of NSMutableArray simultaneously without issue, it’s not safe to let one thread modify the array while another is reading it. Your singleton doesn’t prevent this condition from happening in its current state.

To see the problem, have a look at addPhoto: in PhotoManager.m, which has been reproduced below:

- (void)addPhoto:(Photo *)photo
{
    if (photo) {
        [_photosArray addObject:photo];
        dispatch_async(dispatch_get_main_queue(), ^{
            [self postContentAddedNotification];
        });
    }
}

This is a write method as it modifies a private mutable array object.

Now take a look at photos, reproduced below:

- (NSArray *)photos
{
  return [NSArray arrayWithArray:_photosArray];
}

This is termed a read method as it’s reading the mutable array. It makes an immutable copy for the caller in order to defend against the caller mutating the array inappropriately, but none of this provides any protection against one thread calling the the write method addPhoto: while simultaneously another thread calls the read method photos.

This is the classic software development Readers-Writers Problem. GCD provides an elegant solution of creating a Readers-writer lock using dispatch barriers.

Dispatch barriers are a group of functions acting as a serial-style bottleneck when working with concurrent queues. Using GCD’s barrier API ensures that the submitted block is the only item executed on the specified queue for that particular time. This means that all items submitted to the queue prior to the dispatch barrier must complete before the block will execute.

When the block’s turn arrives, the barrier executes the block and ensures that the queue does not execute any other blocks during that time. Once finished, the queue returns to its default implementation. GCD provides both synchronous and asynchronous barrier functions.

The diagram below illustrates the effect of barrier functions on various asynchronous blocks:
Dispatch-Barrier

Notice how in normal operation the queue acts just like a normal concurrent queue. But when the barrier is executing, it essentially acts like a serial queue. That is, the barrier is the only thing executing. After the barrier finishes, the queue goes back to being a normal concurrent queue.

Here’s when you would — and wouldn’t — use barrier functions:

  • Custom Serial Queue: A bad choice here; barriers won’t do anything helpful since a serial queue executes one operation at a time anyway.
  • Global Concurrent Queue: Use caution here; this probably isn’t the best idea since other systems might be using the queues and you don’t want to monopolize them for your own purposes.
  • Custom Concurrent Queue: This is a great choice for atomic or critical areas of code. Anything you’re setting or instantiating that needs to be thread safe is a great candidate for a barrier.

Since the only decent choice above is the custom concurrent queue, you’ll need to create one of your own to handle your barrier function and separate the read and write functions. The concurrent queue will allow multiple read operations simultaneously.

Open PhotoManager.m, and add the following private property to the class extension category:

@interface PhotoManager ()
@property (nonatomic,strong,readonly) NSMutableArray *photosArray;
@property (nonatomic, strong) dispatch_queue_t concurrentPhotoQueue; ///< Add this
@end

Find addPhoto: and replace it with the following implementation:

- (void)addPhoto:(Photo *)photo
{
    if (photo) { // 1
        dispatch_barrier_async(self.concurrentPhotoQueue, ^{ // 2 
            [_photosArray addObject:photo]; // 3
            dispatch_async(dispatch_get_main_queue(), ^{ // 4
                [self postContentAddedNotification]; 
            });
        });
    }
}

Here’s how your new write function works:

  1. Check that there’s a valid photo before performing all the following work.
  2. Add the write operation using your custom queue. When the critical section executes at a later time this will be the only item in your queue to execute.
  3. This is the actual code which adds the object to the array. Since it’s a barrier block, this block will never run simultaneously with any other block in concurrentPhotoQueue.
  4. Finally you post a notification that you’ve added the image. This notification should be posted from the main thread because it will do UI work, so here you dispatch another task asynchronously to the main queue for the notification.

This takes care of the write, but you also need to implement the photos read method and instantiate concurrentPhotoQueue.

To ensure thread safety with the writer side of matters, you need to perform the read on the concurrentPhotoQueue queue. You need to return from the function though, so you can’t dispatch asynchronously to the queue because that wouldn’t necessarily run before the reader function returns.

In this case, dispatch_sync would be an excellent candidate.

dispatch_sync() synchronously submits work and waits for it to be completed before returning. Use dispatch_sync to track of your work with dispatch barriers, or when you need to wait for the operation to finish before you can use the data processed by the block. If you’re working with the second case, you’ll sometimes see a __block variable written outside of the dispatch_sync scope in order to use the processed object returned outside the dispatch_sync function.

You need to be careful though. Imagine if you call dispatch_sync and target the current queue you’re already running on. This will result in a deadlock because the call will wait to until the block finishes, but the block can’t finish (it can’t even start!) until the currently executing task is finished, which can’t! This should force you to be conscious of which queue you’re calling from — as well as which queue you’re passing in.

Here’s a quick overview of when and where to use dispatch_sync:

  • Custom Serial Queue: Be VERY careful in this situation; if you’re running in a queue and call dispatch_sync targeting the same queue, you will definitely create a deadlock.
  • Main Queue (Serial): Be VERY careful for the same reasons as above; this situation also has potential for a deadlock condition.
  • Concurrent Queue: This is a good candidate to sync work through dispatch barriers or when waiting for a task to complete so you can perform further processing.

Still working in PhotoManager.m, replace photos with the following implementation:

- (NSArray *)photos
{
    __block NSArray *array; // 1
    dispatch_sync(self.concurrentPhotoQueue, ^{ // 2
        array = [NSArray arrayWithArray:_photosArray]; // 3
    });
    return array;
}

Here’s your read function. Taking each numbered comment in turn, you’ll find the following:

  1. The __block keyword allows objects to be mutable inside a block. Without this, array would be read-only inside the block and your code wouldn’t even compile.
  2. Dispatch synchronously onto the concurrentPhotoQueue to perform the read.
  3. Store the photo array in array and return it.

Finally, you need to instantiate your concurrentPhotoQueue property. Change sharedManager to instantiate the queue like so:

+ (instancetype)sharedManager
{
    static PhotoManager *sharedPhotoManager = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        sharedPhotoManager = [[PhotoManager alloc] init];
        sharedPhotoManager->_photosArray = [NSMutableArray array];
 
        // ADD THIS:
        sharedPhotoManager->_concurrentPhotoQueue = dispatch_queue_create("com.selander.GooglyPuff.photoQueue",
                                                    DISPATCH_QUEUE_CONCURRENT); 
    });
 
    return sharedPhotoManager;
}

This initializes concurrentPhotoQueue as a concurrent queue using dispatch_queue_create. The first parameter is a reversed DNS style naming convention; make sure it’s descriptive since this can be helpful when debugging. The second parameter specifies whether you want your queue to be serial or concurrent.

Note: When searching for examples on the web, you’ll often see people pass 0 or NULL as the second parameter of dispatch_queue_create. This is a dated way of creating a serial dispatch queue; it’s always better to be specific with your parameters.

Congratulations — your PhotoManager singleton is now thread safe. No matter where or how you read or write photos, you can be confident that it will be done in a safe manner with no amusing surprises.

A Visual Review of Queueing

Still not 100% sure on the essentials of GCD? Make sure you’re comfortable with the basics by creating simple examples yourself using GCD functions using breakpoints and NSLog statements to make sure you understand what is happening.

I’ve provided two animated GIFs below to help cement your understanding of dispatch_async and dispatch_sync. The code is included above each GIF as a visual aid; pay attention to each step of the GIF showing the breakpoint in the code on the left and the related queue state on the right.

dispatch_sync Revisited

- (void)viewDidLoad
{
  [super viewDidLoad];
 
  dispatch_sync(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
 
      NSLog(@"First Log");
 
  });
 
  NSLog(@"Second Log");
}

dispatch_sync_in_action

Here’s your guide to the various states of the diagram:

  1. The main queue chugs along executing tasks in order — up next is a task to instantiate UIViewController which includes viewDidLoad.
  2. viewDidLoad executes on the main thread.
  3. The main thread is currently inside viewDidLoad and is just about to reach dispatch_sync.
  4. The dispatch_sync block is added to a global queue and will execute at a later time. Processes are halted on the main thread until the block completes. Meanwhile, the global queue is concurrently processing tasks; recall that blocks will be dequeued in FIFO order on a global queue but can be executed concurrently.
  5. The global queue processes the tasks that were already present on the queue before the dispatch_sync block was added.
  6. Finally, the dispatch_sync block has its turn.
  7. The block is done so the tasks on the main thread can resume.
  8. viewDidLoad method is done, and the main queue carries on processing other tasks.

dispatch_sync adds a task to a queue and waits until that task completes. dispatch_async does the exact same thing, but the only exception is that it doesn’t wait for the task to complete before proceeding onwards from the calling thread.

dispatch_async Revisited

- (void)viewDidLoad
{
  [super viewDidLoad];
 
  dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
 
      NSLog(@"First Log");
 
  });
 
  NSLog(@"Second Log");
}

dispatch_async_in_action

  1. The main queue chugs along executing tasks in order — up next is a task to instantiate UIViewController which includes viewDidLoad.
  2. viewDidLoad executes on the main thread.
  3. The main thread is currently inside viewDidLoad and is just about to reach dispatch_async.
  4. The dispatch_async block is added to a global queue and will execute at a later time.
  5. viewDidLoad continues to move on after adding dispatch_async to the global queue and the main thread turns its attention to the remaining tasks. Meanwhile, the global queue is concurrently processing its outstanding tasks. Remember that blocks will be dequeued in a FIFO order on a global queue but can be executed concurrently.
  6. The block of code added by dispatch_async is now executing.
  7. The dispatch_async block is done and both NSLog statements have placed their output on the console.

In this particular instance, the second NSLog statement executes, followed by the first NSLog statement. This isn’t always the case — it’s dependent on what the hardware is doing at that given time, and you have no control nor knowledge as to which statement will execute first. The “first” NSLog could be the first log to execute in some invocations.

Where to Go From Here?

In this tutorial, you learned how to make your code thread safe and how to maintain the responsiveness of the main thread while performing CPU intensive tasks.

You can download the GooglyPuff Project which contains all the improvements made in this tutorial so far. In the second part of this tutorial you’ll continue to improve upon this project.

If you plan on optimizing your own apps, you really should be profiling your work with the Time Profile template in Instruments. Using this utility is outside the scope of this tutorial, so check out How to Use Instruments for a excellent overview.

Also make sure that you profile with an actual device, since testing on the Simulator can give a very inaccurate picture of the program’s speed.

In the next tutorial you’ll dive even deeper into GCD’s API to do even more cool stuff.

If you have any questions or comments, feel free to join the discussion below!

Grand Central Dispatch In-Depth: Part 1/2 is a post from: Ray Wenderlich

The post Grand Central Dispatch In-Depth: Part 1/2 appeared first on Ray Wenderlich.

Video Tutorial: Beginner OpenGL ES and GLKit Part 8: Diffuse Lighting

Grand Central Dispatch In-Depth: Part 2/2

$
0
0
Learn about concurrency in this Grand Central Dispatch in-depth tutorial series.

Learn about concurrency in this Grand Central Dispatch in-depth tutorial series.

Welcome to the second and final part of this Grand Central Dispatch in-depth tutorial series!

In the first part of this series, you learned way more than you ever imagined about concurrency, threading, and how GCD works. You made the PhotoManager singleton thread safe for instantiating using dispatch_once and made the reading and writing of Photos thread safe using a combination of dispatch_barrier_async and dispatch_sync.

In addition to all that, you enhanced the UX of the app through the timing of a prompt with dispatch_after, and offloaded the work from the instantiation of a view controller to perform a CPU intensive task with dispatch_async.

If you have been following along, you can pick up where you left off with the sample project form Part 1. If you haven’t completed Part 1 or don’t want to reuse your project you can download the finished project from the first part of this tutorial here.

It’s time to explore some more GCD!

Correcting the Premature Popup

You may have noticed that when you try to add photos with the Le Internet option, a UIAlertView pops up well before the images have finished downloading, as shown in the screenshot below:

Premature Completion Block

The fault lies with PhotoManagers’s downloadPhotoWithCompletionBlock: which has been reproduced below:

- (void)downloadPhotosWithCompletionBlock:(BatchPhotoDownloadingCompletionBlock)completionBlock
{
    __block NSError *error;
 
    for (NSInteger i = 0; i < 3; i++) {
        NSURL *url;
        switch (i) {
            case 0:
                url = [NSURL URLWithString:kOverlyAttachedGirlfriendURLString];
                break;
            case 1:
                url = [NSURL URLWithString:kSuccessKidURLString];
                break;
            case 2:
                url = [NSURL URLWithString:kLotsOfFacesURLString];
                break;
            default:
                break;
        }
 
        Photo *photo = [[Photo alloc] initwithURL:url
                              withCompletionBlock:^(UIImage *image, NSError *_error) {
                                  if (_error) {
                                      error = _error;
                                  }
                              }];
 
        [[PhotoManager sharedManager] addPhoto:photo];
    }
 
    if (completionBlock) {
        completionBlock(error);
    }
}

Here you call the completionBlock at the end of the method — you’re assuming all of the photo downloads have completed. But unfortunately, there’s no guarantee that all the downloads have finished by this point.

The Photo class’s instantiation method starts downloading a file from a URL and returns immediately before the download completes. In other words, downloadPhotoWithCompletionBlock: calls its own completion block at the end, as if its own method body were all straight-line synchronous code and every method call that completed had finished its work.

However, -[Photo initWithURL:withCompletionBlock:] is asynchronous and returns immediately — so this approach won’t work.

Instead, downloadPhotoWithCompletionBlock: should call its own completion block only after all the image download tasks have called their own completion blocks. The question is: how do you monitor concurrent asynchronous events? You don’t know when they will complete, and they can finish in any order.

Perhaps you could write some hacky code that uses multiple BOOLs to keep track of each download, but that doesn’t scale well, and frankly, it makes for pretty ugly code.

Fortunately, this type of multiple asynchronous completion monitoring is exactly what dispatch groups were designed for.

Dispatch Groups

Dispatch groups notify you when an entire group of tasks completes. These tasks can be either asynchronous or synchronous and can even be tracked from different queues. Dispatch groups also notify you in synchronous or asynchronous fashion when all of the group’s events are complete. Since items are being tracked on different queues, an instance of dispatch_group_t keeps track of the different tasks in the queues.

The GCD API provides two ways to be notified when all events in the group have completed.

The first one, dispatch_group_wait, is a function that blocks your current thread and waits until either all the tasks in the group have completed, or until a timeout occurs. This is exactly what you want in this case.

Open PhotoManager.m and replace downloadPhotosWithCompletionBlock: with the following implementation:

- (void)downloadPhotosWithCompletionBlock:(BatchPhotoDownloadingCompletionBlock)completionBlock
{
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{ // 1
 
        __block NSError *error;
        dispatch_group_t downloadGroup = dispatch_group_create(); // 2
 
        for (NSInteger i = 0; i < 3; i++) {
            NSURL *url;
            switch (i) {
                case 0:
                    url = [NSURL URLWithString:kOverlyAttachedGirlfriendURLString];
                    break;
                case 1:
                    url = [NSURL URLWithString:kSuccessKidURLString];
                    break;
                case 2:
                    url = [NSURL URLWithString:kLotsOfFacesURLString];
                    break;
                default:
                    break;
            }
 
            dispatch_group_enter(downloadGroup); // 3
            Photo *photo = [[Photo alloc] initwithURL:url
                                  withCompletionBlock:^(UIImage *image, NSError *_error) {
                                      if (_error) {
                                          error = _error;
                                      }
                                      dispatch_group_leave(downloadGroup); // 4
                                  }];
 
            [[PhotoManager sharedManager] addPhoto:photo];
        }
        dispatch_group_wait(downloadGroup, DISPATCH_TIME_FOREVER); // 5
        dispatch_async(dispatch_get_main_queue(), ^{ // 6
            if (completionBlock) { // 7
                completionBlock(error);
            }
        });
    });
}

Taking each numbered comment in turn, you’ll see the following:

  1. Since you’re using the synchronous dispatch_group_wait which blocks the current thread, you use dispatch_async to place the entire method into a background queue to ensure you don’t block the main thread.
  2. This creates a new dispatch group which behaves somewhat like a counter of the number of uncompleted tasks.
  3. dispatch_group_enter manually notifies a group that a task has started. You must balance out the number of dispatch_group_enter calls with the number of dispatch_group_leave calls or else you’ll experience some weird crashes.
  4. Here you manually notify the group that this work is done. Again, you’re balancing all group enters with an equal amount of group leaves.
  5. dispatch_group_wait waits until either all of the tasks are complete or until the time expires. If the time expires before all events complete, the function will return a non-zero result. You could put this into a conditional block to check if the waiting period expired; however, in this case you specified for it to wait forever by supplying DISPATCH_TIME_FOREVER. This means, unsurprisingly, it’ll wait, forever! That’s fine, because the completion of the photos creation will always complete.
  6. At this point, you are guaranteed that all image tasks have either completed or timed out. You then make a call back to the main queue to run your completion block. This will append work onto the main thread to be executed at some later time.
  7. Finally, check if the completion block is nil, and if not, run the completion block.

Build and run your app, attempt to download multiple images and notice how your app behaves with the completion block in place.

Note: If the network activities occur too quickly to discern when the completion block should be called and you’re running the app on a device, you can make sure this really works by toggling some network settings in the Developer Section of the Settings app. Just go to the Network Link Conditioner section, enable it, and select a profile. “Very Bad Network” is a good choice.

If you’re are running on the Simulator, you can use a network link conditioner from GitHub to change your network speed. This is a good tool to have in your arsenal because it forces you to be conscious of what happens to your apps when connection speeds are less than optimal.

This solution is good so far, but in general it’s best to avoid blocking threads if at all possible. Your next task is to rewrite the same method to notify you asynchronously when all the downloads have completed.

Before we head on to another use of dispatch groups, here’s a brief guide on when and how to use dispatch groups with the various queue types:

  • Custom Serial Queue: This is a good candidate for notifications when a group of tasks completes.
  • Main Queue (Serial): This is a good candidate as well in this scenario. You should be wary of using this on the main queue if you are waiting synchronously for the completion of all work since you don’t want to hold up the main thread. However, the asynchronous model is an attractive way to update the UI once several long-running tasks finish such as network calls.
  • Concurrent Queue: This as well is a good candidate for dispatch groups and completion notifications.

Dispatch groups, take two

That’s all well and good, but it’s a bit clumsy to have to dispatch asynchronously onto another queue and then block using dispatch_group_wait. There’s another way…

Find downloadPhotosWithCompletionBlock: in PhotoManager.m and replace it with this implementation:

- (void)downloadPhotosWithCompletionBlock:(BatchPhotoDownloadingCompletionBlock)completionBlock
{
    // 1
    __block NSError *error;
    dispatch_group_t downloadGroup = dispatch_group_create(); 
 
    for (NSInteger i = 0; i < 3; i++) {
        NSURL *url;
        switch (i) {
            case 0:
                url = [NSURL URLWithString:kOverlyAttachedGirlfriendURLString];
                break;
            case 1:
                url = [NSURL URLWithString:kSuccessKidURLString];
                break;
            case 2:
                url = [NSURL URLWithString:kLotsOfFacesURLString];
                break;
            default:
                break;
        }
 
        dispatch_group_enter(downloadGroup); // 2
        Photo *photo = [[Photo alloc] initwithURL:url
                              withCompletionBlock:^(UIImage *image, NSError *_error) {
                                  if (_error) {
                                      error = _error;
                                  }
                                  dispatch_group_leave(downloadGroup); // 3
                              }];
 
        [[PhotoManager sharedManager] addPhoto:photo];
    }
 
    dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{ // 4
        if (completionBlock) {
            completionBlock(error);
        }
    });
}

Here’s how your new asynchronous method works:

  1. In this new implementation you don’t need to surround the method in an async call since you’re not blocking the main thread.
  2. This is the same enter method; there aren’t any changes here.
  3. This is the same leave method; there aren’t any changes here either.
  4. dispatch_group_notify serves as the asynchronous completion block. This code executes when there are no more items left in the dispatch group and it’s the completion block’s turn to run. You also specify on which queue to run your completion code, here, the main queue is the one you want.

This approach is much cleaner way to handle this particular job and doesn’t block any threads.

The Perils of Too Much Concurrency

With all of these new tools at your disposal, you should probably thread everything, right!?

Thread_All_The_Code_Meme

Take a look at downloadPhotosWithCompletionBlock in PhotoManager. You might notice that there’s a for loop in there that cycles through three iterations and downloads three separate images. Your job is to see if you can run this for loop concurrently to try and speed it up.

This is a job for dispatch_apply.

dispatch_apply acts like a for loop which executes different iterations concurrently. This function is sychronous, so just like a normal for loop, dispatch_apply returns only when all of the work is done.

Care must be taken when figuring out the optimal amount of iterations for any given amount of work inside the block, since many iterations and a small amount of work per iteration can create so much overhead that it negates any gains from making the calls concurrent. The technique known as striding helps you out here. This is where for each iteration you do multiple pieces of work.

When is it appropriate to use dispatch_apply?

  • Custom Serial Queue: A serial queue would completely negate the use of dispatch_apply; you might as well just use a normal for loop.
  • Main Queue (Serial): Just as above, using this on a serial queue is a bad idea. Just use a normal for loop.
  • Concurrent Queue: This is a good choice for concurrent looping, especially if you need to track the progress of your tasks.

Head back to downloadPhotosWithCompletionBlock: and replace it with the following implementation:

- (void)downloadPhotosWithCompletionBlock:(BatchPhotoDownloadingCompletionBlock)completionBlock
{
    __block NSError *error;
    dispatch_group_t downloadGroup = dispatch_group_create();
 
    dispatch_apply(3, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(size_t i) {
 
        NSURL *url;
        switch (i) {
            case 0:
                url = [NSURL URLWithString:kOverlyAttachedGirlfriendURLString];
                break;
            case 1:
                url = [NSURL URLWithString:kSuccessKidURLString];
                break;
            case 2:
                url = [NSURL URLWithString:kLotsOfFacesURLString];
                break;
            default:
                break;
        }
 
        dispatch_group_enter(downloadGroup);
        Photo *photo = [[Photo alloc] initwithURL:url
                              withCompletionBlock:^(UIImage *image, NSError *_error) {
                                  if (_error) {
                                      error = _error;
                                  }
                                  dispatch_group_leave(downloadGroup);
                              }];
 
        [[PhotoManager sharedManager] addPhoto:photo];
    });
 
    dispatch_group_notify(downloadGroup, dispatch_get_main_queue(), ^{
        if (completionBlock) {
            completionBlock(error);
        }
    });
}

Your loop is now running concurrently; in the code above, in the call to dispatch_apply, you supply the amount of iterations with the first parameter, the queue to perform the tasks on in the second parameter and the block action in the third parameter.

Be aware that although you have code that will add the photos in a thread safe manner, the ordering of the images could be different depending on which thread finishes first.

Build & run, then add some photos from “Le Internet”. Notice anything different?

Running this new code on the device will occasionally produce marginally faster results. But was all this work worth it?

Actually, it’s not worth it in this case. Here’s why:

  • You’ve probably created more overhead running the threads in parallel, than just running the darn for loop in the first place. You should use dispatch_apply for iterating over very large sets along with the appropriate stride length.
  • Your time to create an app is limited — don’t waste time pre-optimizing code that you don’t know is broken. If you’re going to optimize something, optimize something that is noticeable and worth your time. Find the methods with the longest execution times by profiling your app in Instruments. Check out How to Use Instruments in Xcode to learn more.
  • Typically, optimizing code makes your code more complicated for yourself and for other developers coming after you. Make sure the added complication is worth the benefit.

Remember, don’t go crazy with optimizations. You’ll only make it harder on yourself and others who have to wade through your code.

Miscellaneous GCD Fun

But wait! There’s more! Here are some extra functions that are a little farther off the beaten path. Although you won’t use these tools nearly as frequently, they can be tremendously helpful in the right situations.

Blocking – the Right Way

This might sound like a crazy idea, but did you know that Xcode has testing functionality? :] I know, sometimes I like to pretend it’s not there, but writing and running tests is important when building complex relationships in code.

Testing in Xcode is performed on subclasses of XCTestCase and runs any method in its method signature that begins with test. Testing is measured on the main thread, so you can assume that every test happens in a serial manner.

As soon as a given test method completes, XCTest methods will consider a test to be finished and move onto the next test. That means that any asynchronous code from the previous test will continue to run while the next test is running.

Networking code is usually asynchronous, since you don’t want to block the main thread while performing a network fetch. That, coupled with the fact that tests finish when the test method finishes, can make it hard to test networking code. That is, unless you block the main thread inside the test method until the networking code finishes.

Note: There are some who will say that this type of testing doesn’t fall into the preferred set of integration tests. Some will agree; some won’t. If it works for you, then do it.

Gandalf_Semaphore

Navigate to GooglyPuffTests.m and check out downloadImageURLWithString: , reproduced below:

- (void)downloadImageURLWithString:(NSString *)URLString
{
    NSURL *url = [NSURL URLWithString:URLString];
    __block BOOL isFinishedDownloading = NO;
    __unused Photo *photo = [[Photo alloc]
                             initwithURL:url
                             withCompletionBlock:^(UIImage *image, NSError *error) {
                                 if (error) {
                                     XCTFail(@"%@ failed. %@", URLString, error);
                                 }
                                 isFinishedDownloading = YES;
                             }];
 
    while (!isFinishedDownloading) {}
}

This is a naïve approach to testing the asynchronous networking code. The while loop at the end of the function waits until the isFinishedDownloading Boolean becomes true, which happens in the completion block. Let’s see what impact that has.

Run your tests by clicking on Product / Test in Xcode or use ⌘+U if you have the default key bindings.

As the tests run, pay attention to the CPU usage within Xcode on the debug navigator. This poorly designed implementation is known as a basic spinlock. It’s not practical here because you are wasting valuable CPU cycles waiting in the while loop; it doesn’t scale well either.

You may need to use the network link conditioner, as explained previously, to see this problem better. If your network is too fast then the spinning happens only for a very short period of time.

You need a more elegant, scalable solution to block a thread until a resource is available. Enter semaphores.

Semaphores

Semaphores are an old-school threading concept introduced to the world by the ever-so-humble Edsger W. Dijkstra. Semaphores are a complex topic because they build upon the intricacies of operating system functions.

If you want to learn more about semaphores, check out this link which discusses semaphore theory in more detail. If you’re the academic type, a classic software development problem that uses semaphores is the Dining Philosophers Problem.

Semaphores lets you control the access of multiple consumers into a finite amount of resources. For example, if you created a semaphore with a pool of two resources, at most only two threads could access the critical section at the same time. Other items that want to use the resource must wait in a…have you guessed it?… FIFO queue!

Let’s use semaphores!

Open GooglyPuffTests.m and replace downloadImageURLWithString: with the following implementation:

- (void)downloadImageURLWithString:(NSString *)URLString
{
    // 1
    dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
 
    NSURL *url = [NSURL URLWithString:URLString];
    __unused Photo *photo = [[Photo alloc]
                             initwithURL:url
                             withCompletionBlock:^(UIImage *image, NSError *error) {
                                 if (error) {
                                     XCTFail(@"%@ failed. %@", URLString, error);
                                 }
 
                                 // 2
                                 dispatch_semaphore_signal(semaphore);
                             }];
 
    // 3
    dispatch_time_t timeoutTime = dispatch_time(DISPATCH_TIME_NOW, kDefaultTimeoutLengthInNanoSeconds);
    if (dispatch_semaphore_wait(semaphore, timeoutTime)) {
        XCTFail(@"%@ timed out", URLString);
    }
}

Here’s how the semaphores work in your code above:

  1. Create the semaphore. The parameter indicates the value the semaphore starts with. This number is the number of things that can access the semaphore without having to have something increment it first. (Note that incrementing a semaphore is known as signalling it).
  2. In the completion block you tell the semaphore that you no longer need the resource. This increments the semaphore count and signals that the semaphore is available to other resources that want it.
  3. This waits on the semaphore, with a given timeout. This call blocks the current thread until the semaphore has been signalled. A non-zero return code from this function means that the timeout was reached. In this case, the test is failed because it is deemed that the network should not take more than 10 seconds to return — a fair point!

Run the tests again. Provided you have a working network connection, the tests should succeed in a timely manner. Pay particular attention to the CPU usage in this case, compared to the earlier spinlock implementation.

Disable your connection and run the tests again; if you are running on a device, put it in airplane mode. If you’re running on the simulator then simply turn off your connection. The tests complete with a fail result, after 10 seconds. Great, it worked!

These are rather trivial tests, but if you are working with a server team then these basic tests can prevent a wholesome round of finger-pointing of who is to blame for the latest network issue.

Working With Dispatch Sources

A particularly interesting feature of GCD is Dispatch Sources, which are basically a grab-bag of low-level functionality helping you to respond to or monitor Unix signals, file descriptors, Mach ports, VFS Nodes, and other obscure stuff. All of this is far beyond the scope of this tutorial, but you’ll get a small taste of it by implementing a dispatch source object and using it in a rather peculiar way.

First-time users of dispatch sources can get quite lost on how to use a source, so the first thing you need to understand how dispatch_source_create works. This is the function prototype for creating a source:

dispatch_source_t dispatch_source_create(
   dispatch_source_type_t type,
   uintptr_t handle,
   unsigned long mask,
   dispatch_queue_t queue);

The first parameter is dispatch_source_type_t. This is the most important parameter as it dictates what the handle and mask parameters will be. You’ll need to refer to the Xcode documentation to see what options are available for each dispatch_source_type_t parameter.

Here you’ll be monitoring for DISPATCH_SOURCE_TYPE_SIGNAL. As the documentation shows:

A dispatch source that monitors the current process for signals. The handle is a signal number (int). The mask is unused (pass zero for now).

A list of these Unix signals can found in the header file signal.h. At the top there are a bunch of #defines. From that list of signals, you will be monitoring the SIGSTOP signal. This signal is sent when a process receives an unavoidable suspend instruction. This is the same signal that’s sent when you debug your application using the LLDB debugger.

Go to PhotoCollectionViewController.m and add the following code to the top of viewDidLoad, underneath [super viewDidLoad]:

- (void)viewDidLoad
{
  [super viewDidLoad];
 
  // 1
  #if DEBUG
      // 2
      dispatch_queue_t queue = dispatch_get_main_queue();
 
      // 3
      static dispatch_source_t source = nil;
 
      // 4
      __typeof(self) __weak weakSelf = self;
 
      // 5
      static dispatch_once_t onceToken;
      dispatch_once(&onceToken, ^{
          // 6
          source = dispatch_source_create(DISPATCH_SOURCE_TYPE_SIGNAL, SIGSTOP, 0, queue);
 
          // 7
          if (source)
          {
              // 8
              dispatch_source_set_event_handler(source, ^{
                  // 9
                  NSLog(@"Hi, I am: %@", weakSelf);
              });
              dispatch_resume(source); // 10
          }
      });
  #endif
 
  // The other stuff

The code is a little involved, so step through the code one comment at a time to see what’s going on:

  1. It’s best to only compile this code while in DEBUG mode since this could give “interested parties” a lot of insight into your app. :]
  2. Just to mix things up, you create an instance variable of dispatch_queue_t instead of supplying the function directly in the parameter. When code gets long, it’s sometimes better to split things up to improve legibility.
  3. source needs to survive outside the scope of the method, so you use a static variable.
  4. You’re using weakSelf to ensure that there are no retain cycles. This isn’t completely necessary for PhotoCollectionViewController since it stays alive for the lifetime of the app. However, if you had any classes that disappeared, this would still ensure that there would be no retain cycles.
  5. Use the tried and true dispatch_once to perform the dispatch source’s one-time setup.
  6. Here you instantiate the source variable. You indicate that you’re interested in signal monitoring and provided the SIGSTOP signal as the second parameter. Additionally, you use the main queue for handling received events — you’ll discover why shortly.
  7. A dispatch source object won’t be created if you provide malformed parameters. As a result, you should make sure you have a valid dispatch source object before working on it.
  8. dispatch_source_set_event_handler invokes when you receive the signal you’re monitoring for. You then set the appropriate logic handler in the block parameter.
  9. This is a basic NSLog call that prints the object to the console.
  10. By default, all sources start in the suspended state. You must tell the source object to resume when you want to start monitoring for the events.

Build and run your app; pause in the debugger and resume the app immediately. Check out the console, and you’ll see that this function from the dark arts actually worked. You should see something like this in the debugger:

2014-03-29 17:41:30.610 GooglyPuff[8181:60b] Hi, I am: 

You app is now debugging-aware! That’s pretty awesome, but how would you use this in real life?

You could use this to debug an object and display data whenever you resume the app; you could also give your app custom security logic to protect itself (or the user’s data) when malicious attackers attach a debugger to your application.

An interesting idea is to use this approach as a stack trace tool to find the object you want to manipulate in the debugger.

What_Meme

Think about that situation for a second. When you stop the debugger out of the blue, you’re almost never on the desired stack frame. Now you can stop the debugger at anytime and have code execute at your desired location. This is very useful if you want to execute code at a point in your app that’s tedious to access from the debugger. Try it out!

I_See_What_You_Did_Meme

Put a breakpoint on the NSLog statement in viewDidLoad in the event handler you just added. Pause in the debugger, then start again.; the app will then hit the breakpoint you added. You’re now deep in the depths of your PhotoCollectionViewController method. Now you can access the instance of PhotoCollectionViewController to your heart’s content. Pretty handy!

Note: If you haven’t already noticed which threads are which in the debugger, take a look at them now. The main thread will always be the first thread followed by libdispatch, the coordinator for GCD, as the second thread. After that, the thread count and remaining threads depend on what the hardware was doing when the app hit the breakpoint.

In the debugger, type the following:
po [[weakSelf navigationItem] setPrompt:@"WOOT!"]

Then resume execution of the app. You’ll see the following:

Dispatch_Sources_Xcode_Breakpoint_Console
Dispatch_Sources_Debugger_Updating_UI

With this method, you can make updates to the UI, inquire about the properties of a class, and even execute methods — all while not having to restart the app to get into that special workflow state. Pretty neat.

Where to Go From Here?

You can download the final project here.

I hate to bang on this subject again, but you really should check out the How to Use Instruments tutorial. You’ll definitely need this if you plan on doing any optimization of your apps. Be aware that Instruments is good for profiling relative execution: comparing which areas of code takes longer in relation to other areas. If you’re trying to figure out the actual execution time of a method, you might need to come up with a more home-brewed solution.

Also check out How to Use NSOperations and NSOperationQueues, a concurrency technology that is built on top of GCD. In general, it’s best practice to use GCD if you are using simple fire-and-forget tasks. NSOperations offers better control, an implementation for handling maximum concurrent operations, and a more object-oriented paradigm at the cost of speed.

Remember, unless you have a specific reason to go lower, always try and stick with a higher level API. Only venture into the dark arts of Apple if you want to learn more or to do something really, really “interesting”. :]

Good luck and have fun! Post any questions or feedback in the discussion below!

Grand Central Dispatch In-Depth: Part 2/2 is a post from: Ray Wenderlich

The post Grand Central Dispatch In-Depth: Part 2/2 appeared first on Ray Wenderlich.

Video Tutorial: Beginner OpenGL ES and GLKit Part 9: Specular Lighting

Going Indie: The raywenderlich.com Podcast Episode 5

$
0
0
Episode 4: iOS Dev Tools

Episode 5: Going Indie

In this episode, we chat with special guest David Smith from Developing Perspective about being an independent app developer: from succeeding on the App Store to schedules to motivation and more!

[Subscribe in iTunes] [RSS Feed]

Here’s what is covered in this episode:

  • News: WWDC Ticket Lottery
  • What’s new in raywenderlich.com: Best new tutorials, tech talks, and sneak peek of coming month
  • Tech Talk: Going Indie

Links and References

Our Sponsor

News

What’s New on raywenderlich.com

Tech Talk

Contact Us

Where To Go From Here?

We hope you enjoyed this podcast! We have an episode each month, so be sure to subscribe in iTunes to get access as soon as it comes out.

We’d love to hear what you think about the podcast, and any suggestions on what you’d like to hear in future episodes. Feel free to drop a comment here, or email us anytime at podcast@raywenderlich.com!

Going Indie: The raywenderlich.com Podcast Episode 5 is a post from: Ray Wenderlich

The post Going Indie: The raywenderlich.com Podcast Episode 5 appeared first on Ray Wenderlich.

Address Book Tutorial in iOS

$
0
0
Meet our Furry Little Friends!

Meet our Furry Little Friends!

Although Objective C is an object-oriented language, not all of the frameworks you’ll use while developing for iOS are object-oriented.

Some are written in C, such as the Address Book API, which you’ll learn about in this tutorial.

The Address Book is the way you can read or modify the user’s contacts from your apps (the same contacts that show up in the Contacts app).

Because the Address Book is a C-based API, it does not use objects; instead it utilizes other types. In this Address Book tutorial, you’ll become familiar with a few of these:

  • ABRecordRef: This is a contact record. It has all the properties that can be entered into the Contacts app.
  • ABAddressBookRef: This is the collection of all the user’s contacts. You can modify its records, add new records, or delete a record.
  • ABMutableMultiValueRef: This is the mutable version of ABMultiValueRef, though it is much more convenient. It lets you set properties of the ABRecordRef that may have multiple entries, such as phone number or email.

This Address Book tutorial assumes you are familiar with the basics of iOS development, and are comfortable with the basics of C syntax. If you are new to either of these, check out some of the other tutorials on our site.

Alright, let’s dive in and get some addresses!

Getting Started

To begin this tutorial, download the starter project that has the user interface pre-made, so you can stay focused on the Address Book part of the tutorial.

Build and run, and get ready to meet our furry little friends: Cheesy, Freckles, Maxi, and Shippo!

StarterAppScreenshot

Open up ViewController.m. The starter app has four UIButtons that all call petTapped: when pressed. If you look closely in the Utilities/Attributes Inspector in Main.storyboard, notice that each UIButton has a different number in its tag. This will help you distinguish which button is the one that called petTapped:.

To use the Address Book API, you need to import the framework and headers. As of iOS 7, this is easy with the @import keyword. Add this line to the top of ViewController.m:

@import AddressBook;

In this app, the user will press the image of one of the pets, and the pet’s contact information will be added to their address book (I’m surprised that even pets have iPhones!). Using the power of the Address Book API, you can finally contact your favorite furry friends.

Asking for Permission

In 2012, there was a controversy regarding certain apps sending a copy of the user’s Address Book to its servers. Because of the public’s response to this security breach, Apple immediately implemented security features to prevent this from happening without the user’s knowledge.

So now, whenever you want to use the Address Book, you first ask the user for permission.

Let’s try this out. In ViewController.m, add the following code inside petTapped:

- (IBAction)petTapped:(UIButton *)sender {
  if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusDenied ||
      ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusRestricted){
    //1
    NSLog(@"Denied");
  } else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized){
    //2
    NSLog(@"Authorized");
  } else{ //ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined
    //3
    NSLog(@"Not determined");
  }
}

Let’s go over this section by section:

  1. This checks to see if the user has either denied your app access to the Address Book in the past, or it is restricted because of parental controls. In this case, all you can do is inform the user that you can’t add the contact because the app does not have permission.
  2. This checks to see if the user has already given your app permission to use their Address Book. In this case, you are free to modify the Address Book however you want.
  3. This checks to see if the user hasn’t decided yet whether not to give permission to your app.

Build and run, and tap the cutest pet. You should see something like the following in the console:

PetBook[832:70b] Not determined

In real life when you want something, you ask. Same thing here!

So you need to request the user for access to the address book. Insert the following in section 3:

ABAddressBookRequestAccessWithCompletion(ABAddressBookCreateWithOptions(NULL, nil), ^(bool granted, CFErrorRef error) {
  if (!granted){
    //4
    NSLog(@"Just denied");
    return;	
  }
  //5
  NSLog(@"Just authorized");
});

The first parameter of this function is an ABAddressBookRef, which you create with ABAddressBookCreateWithOptions(NULL, nil). The second argument is a block that executes once the user responds to your request.

Section 4 executes only if the user denies permission when your app asks. This should be treated the same way as when the ABAuthorizationStatus() == kABAuthorizationStatusDenied.

Section 5 executes if the user gives permission for you to use the Address Book. This is the same as kABAuthorizationStatusAuthorized.

Run the app in the simulator and tap on the weirdest looking pet. A popup will appear to request access to the Address Book:

iOS Simulator Screen shot Jan 21, 2014, 4.07.34 PM

Depending on your choice, you’ll see either “Just authorized” or “Just denied” in the console. Now, press an image again, and you’ll see the result is related to your action before: if you gave permission, it will say “Authorized”, or else it will say “Denied”.

Remove the NSLogs, they were only there so you could see the code’s behavior.

Note: To debug after each test, it might be useful to use iOS Simulator/Reset Content and Settings. This will let you see the alert that asks for permission every time you reset the settings, which will help later on.

Next, add this code into sections 1 and 4 to tell the user that you can’t add the contact because it does not have needed permissions:

UIAlertView *cantAddContactAlert = [[UIAlertView alloc] initWithTitle: @"Cannot Add Contact" message: @"You must give the app permission to add the contact first." delegate:nil cancelButtonTitle: @"OK" otherButtonTitles: nil];
[cantAddContactAlert show];

Use iOS Simulator/Reset Content and Settings to reset your simulator, build and run, tap the ugliest pet, deny permission, and verify the dialog appears. Good job, iOS citizen!

Creating the Pet’s Record

Now it’s time to move onto actually creating the records. Underneath petTapped:, create a new method called addPetToContacts:

- (void)addPetToContacts: (UIButton *) petButton{
 
}

In this method, you’ll create an ABRecordRef with the pet’s attributes, check the address book to make sure the pet does not already exist, and if the pet is not in the Address Book, add it to the user’s contacts.

Begin addPetToContacts: with the following.

NSString *petFirstName;
NSString *petLastName;
NSString *petPhoneNumber;
NSData *petImageData;
if (petButton.tag == 1){
  petFirstName = @"Cheesy";
  petLastName = @"Cat";
  petPhoneNumber = @"2015552398";
  petImageData = UIImageJPEGRepresentation([UIImage imageNamed:@"contact_Cheesy.jpg"], 0.7f);
} else if (petButton.tag == 2){
  petFirstName = @"Freckles";
  petLastName = @"Dog";
  petPhoneNumber = @"3331560987";
  petImageData = UIImageJPEGRepresentation([UIImage imageNamed:@"contact_Freckles.jpg"], 0.7f);
} else if (petButton.tag == 3){
  petFirstName = @"Maxi";
  petLastName = @"Dog";
  petPhoneNumber = @"5438880123";
  petImageData = UIImageJPEGRepresentation([UIImage imageNamed:@"contact_Maxi.jpg"], 0.7f);
} else if (petButton.tag == 4){
  petFirstName = @"Shippo";
  petLastName = @"Dog";
  petPhoneNumber = @"7124779070";
  petImageData = UIImageJPEGRepresentation([UIImage imageNamed:@"contact_Shippo.jpg"], 0.7f);
}

By looking at the button the user chose, you can determine which pet was selected. If the user pressed Shippo, then you want the user to have Shippo’s contact information. The only thing that may be unfamiliar here is UIImageJPEGRepresentation(), which takes a UIImage and returns an NSData representation of it.

Next, type this at the end of addPetToContacts:

ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions(NULL, nil);
ABRecordRef pet = ABPersonCreate();

The first line creates the ABAddressBookRef that will add the pet to the user’s contacts later. The second line creates an empty record for your app to fill with the pet’s information.
Next, set the pet’s first and last names. This code will look like this.

ABRecordSetValue(pet, kABPersonFirstNameProperty, (__bridge CFStringRef)petFirstName, nil);
ABRecordSetValue(pet, kABPersonLastNameProperty, (__bridge CFStringRef)petLastName, nil);

A quick explanation:

  • ABRecordSetValue() takes an ABRecordRef as its first parameter, and that record is pet.
  • The second parameter calls for an ABPropertyID, which is a value defined by the API. Because you want to set the first name, you pass kABPersonFirstNameProperty.
  • For the last name, similarly pass kABPersonLastNameProperty.

Does the third argument seem confusing? What it does is take a CFTypeRef, which is the broad type that includes CFStringRef and ABMultiValueRef. You want to pass a CFStringRef, but you only have an NSString!

To convert an NSString to a CFStringRef, you have to bridge it using (__bridge CFStringRef) myString;. If you are familiar with “casting the variable,” it is similar to that.

Note: For more information on the __bridge keyword, check out Chapters 2 and 3 in iOS 5 by Tutorials, Beginning and Intermediate ARC.

Phone numbers are a bit trickier. Since one contact can have multiple phone numbers (home, mobile, etc.), you have to use ABMutableMultiValueRef. This can be done by adding the following code to the end of addPetToContacts:.

ABMutableMultiValueRef phoneNumbers = ABMultiValueCreateMutable(kABPersonPhoneProperty);
 
ABMultiValueAddValueAndLabel(phoneNumbers, (__bridge CFStringRef)petPhoneNumber, kABPersonPhoneMainLabel, NULL);

When you declare the ABMutableMultiValueRef, you have to say what kind of property it will. In this case, you want it to be for the kABPersonPhoneProperty. The second line adds the pet’s phone number (which is bridged to CFTypeRef), and you have to give this phone number a label. The label kABPersonPhoneMainLabel says that this is the contact’s primary number.

Try setting the pet’s phone property yourself. If you get stuck, expand the field below!

Solution Inside: Solution SelectShow>

The last piece of information to add to the record is its picture — you definitely want to see that adorable face when they call to ask for treats!

To set the record’s image, you use this statement:

ABPersonSetImageData(pet, (__bridge CFDataRef)petImageData, nil);

To add this contact and save the address book, use the following two short lines:

ABAddressBookAddRecord(addressBookRef, pet, nil);
ABAddressBookSave(addressBookRef, nil);

As a final step, you need to call this new method in the appropriate spots. So add this line of code in sections 2 and 5 inside petTapped::

[self addPetToContacts:sender];

Use iOS Simulator/Reset Content and Settings to reset your simulator, build and run, and tap on each of the pets. If asked, give the app permission to use the Address Book.

Once you’re done, go to the home screen (use Cmd+Shift+H to do this in the simulator), and go to the Contacts app. You should see the pets!

Pets in contacts

Duplicates? No More!

There are still a few things to fix up. First, you may have noticed that if you tap a pet twice, two entries will appear in the contact list. But there can only be one Shippo! ;]

To prevent Shippo clones, you should iterate through all the contacts and make sure that the new contact’s name is not in the address book already.

Insert the following sections of code before the ABAddressBookAddRecord() and ABAddressBookSave calls.

First, add this line to get an NSArray of the Address Book’s contacts.

NSArray *allContacts = (__bridge NSArray *)ABAddressBookCopyArrayOfAllPeople(addressBookRef);

Notice how you use __bridge going to NSArray in this case. It goes both between Core Foundation –> Foundation and Foundation –> Core Foundation.

Next, add this line to iterate through the array so that you can check the name of every record.

for (id record in allContacts){
  ABRecordRef thisContact = (__bridge ABRecordRef)record;
  if (CFStringCompare(ABRecordCopyCompositeName(thisContact),
                      ABRecordCopyCompositeName(pet), 0) == kCFCompareEqualTo){
    //The contact already exists!    
  }
}

You have to use id because technically Core Foundation types can’t be in an NSArray, because they are not objects. The ABRecordRefs are disguised as id’s to avoid errors. To get the ABRecordRef, simply __bridge again!

bridge_AllTheThings

The way you use CFStringCompare here is similar to using NSString’s isEqualToString:

ABRecordCopyCompositeName gets the full name of the record by joining the contact’s first and last names.

Finally, add the following to the if statement:

UIAlertView *contactExistsAlert = [[UIAlertView alloc]initWithTitle:[NSString stringWithFormat:@"There can only be one %@", petFirstName] message:nil delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];
[contactExistsAlert show];
return;

This shows an alert to the user and stops the method so the contact isn’t added. If the loop goes through all the contacts and does not find a match, it adds the pet to the Address Book.

Run the app, and try to select one of the pets multiple times. Look for an alert that says the contact already exists to appear.

There can only be one!

It’s also helpful if you add an alert whenever the contact is added. At the end of this method, after the app adds and saves the contact, insert this confirmation alert:

UIAlertView *contactAddedAlert = [[UIAlertView alloc]initWithTitle:@"Contact Added" message:nil delegate:nil cancelButtonTitle:@"OK" otherButtonTitles: nil];
[contactAddedAlert show];

Multithreading

There is still a hidden issue with the code as it is right now. If you look at the documentation for ABAddressBookRequestAccessWithCompletion (which is called in petTapped:) the completion is called on an arbitrary queue. In other words, it may be called on a thread besides the main thread.

If there is one thing you should know about multithreading, it’s that the user interface can only be used on the main thread. You have to make sure that anything affecting the user interface (presenting UIAlertView?) is called on the main thread.

This is easy to accomplish with the following code. Insert this at the beginning of the completion of ABAddressBookRequestAccessWithCompletion.

dispatch_async(dispatch_get_main_queue(), ^{
 
});

This runs the block on the main thread so you can use the user interface. To learn more about multithreading, read this tutorial.

Cut and paste the code from inside the completion handler into the dispatch_async block, to make the call look like this:

ABAddressBookRequestAccessWithCompletion(ABAddressBookCreateWithOptions(NULL, nil), ^(bool granted, CFErrorRef error) {
  dispatch_async(dispatch_get_main_queue(), ^{
    if (!granted){ 
      //4
      UIAlertView *cantAddContactAlert = [[UIAlertView alloc] initWithTitle: @"Cannot Add Contact" message: @"You must give the app permission to add the contact first." delegate:nil cancelButtonTitle: @"OK" otherButtonTitles: nil];
      [cantAddContactAlert show];
      return;		
    }
    //5
    [self addPetToContacts:sender];
  });
});

This is the best way for the app to ask permission to use the Address Book. Note that a best practice is to ask for permission only when you actually need to use it — if you ask for permission when the app first launches, the user may be suspicious because they won’t understand why you need to use the Address Book.

One issue with ABAddressBookRequestAccessWithCompletion is that once the user gives the app permission, sometimes there is a 5-10 second delay until the completion is called. This can make it seem like the app’s frozen when it’s adding the contact. In most cases, this is not too much of an issue.

Your PetBook app is now fully functional, and I know you can’t wait to text your new furry friends right away!

Note: Sorry to disappoint all the raving Shippo fans, but the numbers listed here are fake :]

Where To Go From Here?

Here is the finished example project from this Address Book tutorial.

You can do many other cool things with the Address Book API. In this tutorial, you learned how to create a new record. As an extension of this tutorial, try modifying pre-existing contacts in the Address Book.

Along with this framework, there is also an AddressBookUI.framework that has several convenient classes for modifying the address book. What these can do is give your app functionality similar to the contacts app.

If you have any questions or comments regarding this tutorial or the Address Book API, please join the discussion in the comments below!

Address Book Tutorial in iOS is a post from: Ray Wenderlich

The post Address Book Tutorial in iOS appeared first on Ray Wenderlich.

Video Tutorial: Beginner OpenGL ES and GLKit Part 10: Importing Models


Beat ‘Em Up Game Starter Kit Second Edition (Sprite Kit Version) Now Available!

$
0
0
Fully updated for iOS 7 and Sprite Kit!

Fully updated for iOS 7 and Sprite Kit!

Good news – Allen Tan has just released a major new second edition to his Beat ‘Em Up Game Starter Kit. It is now fully updated for iOS 7 and Sprite Kit!

This was a ton of work. Since Sprite Kit is a completely different game engine, so almost every page needed to change, and the starter kit needed to go through our editing process all over again.

Most other sites would have charged you again for a major update of this nature, but we are making this completely free of charge to existing customers, to thank you for supporting all we do on this site!

At this point, all three of our starter kits are now fully updated for iOS 7 and Sprite Kit. We hope you enjoy!

How To Grab Your Copy

If you’ve previously purchased the Beat ‘Em Up Game Starter Kit, you can download the latest version for free on your My Loot page. If for some reason the book does not show up on your My Loot page, fill out this form or contact me.

If you don’t have the Beat ‘Em Up Game Starter Kit yet, it’s a great time to grab a copy!

Allen and I hope you all enjoy the second edition!

Beat ‘Em Up Game Starter Kit Second Edition (Sprite Kit Version) Now Available! is a post from: Ray Wenderlich

The post Beat ‘Em Up Game Starter Kit Second Edition (Sprite Kit Version) Now Available! appeared first on Ray Wenderlich.

Introduction to CocoaPods Tutorial

$
0
0
Make an app with several library dependencies with ease!

Make an app with several library dependencies with ease!

4/14/2014: Fully updated for CocoaPods 0.31 and iOS 7 (original post by Marcelo Fabri, update by Joshua Greene).

In this tutorial, you’ll learn how to use a popular dependency management tool called CocoaPods.

But wait! What is a dependency management tool and why do you need one?

As an iOS developer, you certainly use a lot of code made by others, in the shape of libraries. Just imagine how difficult it would be if you had to implement everything from scratch!

Without a dependency management tool, you might simply add each library’s code to your project. However, this has several disadvantages:

  • Library code is stored within your project, wasting space.
  • There’s no central place where you can see all libraries that are available.
  • It can be difficult to find and update a library to a new version, especially if several libraries need to be updated together.
  • Downloading and including libraries within your project may tempt you to make changes to the downloaded code and just leave it there (making it harder to update them later).

A dependency management tool can help you overcome these issues. It will fetch library source code, resolve dependencies between libraries, and even create and maintain the right environment to build your project with the minimum of hassles.

You’ll get hands on experience using CocoaPods for dependency management in this tutorial. Specifically, you will create an app that uses several open-source libraries to fetch and display information from a popular television and movie information site, trakt.tv.

CocoaPods will make this project much easier. Read on to see for yourself!

Getting Started

Before you begin: This tutorial assumes you are familiar with Xcode, working with the command line, using AFNetworking, and the JSON format. If you’re completely new to any of these (a basic understanding should be okay), you should refer to the other tutorials on this site first.

According to its website, CocoaPods is “the best way to manage library dependencies in Objective-C projects.” And in this case, the advertising is true!


True Dat!

Instead of downloading code from GitHub and copying it into your project (and thus making future updates difficult), you can let CocoaPods do it for you.

In this tutorial, you will be building an app that displays upcoming TV episodes using trakt.tv. If you haven’t heard of it, trakt helps you keep track of the shows that you watch. Trakt has several other features, but this is the only one that you will be using in this tutorial.

You will retrieve show information from a JSON feed provided by trakt. To simplify downloading, parsing, and showing results, you will use AFNetworking and a few other open-source libraries along the way.

To get started, you first need to install CocoaPods. CocoaPods runs on Ruby, yet that’s the only dependency it has. Fortunately, all recent versions of Mac OS X (since OS X 10.7 Lion) ship with Ruby already installed. So all you need to do is update RubyGems (just to make sure you have a recent version).

To do so, open Terminal and type the following command:

sudo gem update --system

Enter your password when requested. The Terminal output should look something like this:

Terminal Output

This update may take a little while, so be patient and give it a few minutes to complete. You can also expect some documentation in the Terminal window about the latest version; you can ignore this for now.

Next, you need to install CocoaPods. Type this command in Terminal to do so:

sudo gem install cocoapods

You may get this prompt during the install process:

rake's executable "rake" conflicts with /usr/bin/rake
Overwrite the executable? [yN]

If so, just enter y to continue. (This warning is raised because the rake gem is updated as part of the install process. You can safely ignore it.)

Lastly, enter this command in Terminal to complete the setup of CocoaPods:

pod setup

This process will likely take a while as this command clones the CocoaPods Specs repository into ~/.cocoapods/ on your computer.

Great, you’re now setup to use CocoaPods!


Oh yeah!

Introducing the “Show Tracker” App

Download the starter project for this tutorial here. This provides a basic UI to get you started – no dependencies have been added yet.

Open Main.storyboard, and you will see just one view controller:


View Controller

This is the initial view controller, simply called ViewController.

It has a UIScrollView connected via an IBOutlet to its showsScrollView property and a UIPageControl connected to showsPageControl.

It is also set as the delegate for showsScrollView and currently has a single method, pageChanged:, which is connected to the value changed event of showsPageControl.

Now, close Xcode.

Yeah, you read that right! It’s time to create your pod file.

Installing Your First Dependency

Open Terminal and navigate to the directory containing your ShowTracker project by using the cd command:

cd ~/Path/To/Folder/Containing/ShowTracker

Next enter this command:

pod init

This will create a default Podfile for your project. The Podfile is where you define the dependencies your project relies on.

Type this command to open Podfile using Xcode for editing:

open -a Xcode Podfile

Note: You shouldn’t use TextEdit to edit the pod file because TextEdit likes to replace standard quotes with more graphically appealing quotes. This can cause CocoaPods to get confused and display errors, so it’s best to just use Xcode or another programming text editor.

The default Podfile should look like this:

# Uncomment this line to define a global platform for your project
# platform :ios, "6.0"
 
target "ShowTracker" do
 
end

Replace # platform :ios, "6.0" with the following:

platform :ios, "7.0"

This tells CocoaPods that your project is targeting iOS 7.

Many libraries – AFNetworking included – have a minimum iOS version requirement. If you omit this line, CocoaPods assumes a default target version (currently iOS 4.3).

If you’ve only ever programmed in Objective-C, this may look a bit strange to you – that’s because the pod file is actually written in Ruby. You don’t need to know Ruby to use CocoaPods, but you should be aware that even minor text errors will typically cause CocoaPods to throw an error.

It’s finally time to add your first dependency using CocoaPods! Copy and paste the following into your pod file, right after target "ShowTracker" do:

pod 'AFNetworking', '2.2.1'

This tells CocoaPods that you want to include AFNetworking version 2.2.1 (the latest as of the writing of this tutorial) as a dependency for your project.

This link has more information about the Podfile format and syntax. If you want to do more complicated stuff (like specifying “any version higher than” a specific version of a library), you should definitely check it out.

Save and close the pod file.

You now need to tell CocoaPods to “install” the dependencies for your project. Enter the following command in Terminal to do so (making sure that you’re still in the directory containing the ShowTracker project and Podfile):

pod install

You should see output similar to the following:

Analyzing dependencies
Downloading dependencies
Installing AFNetworking (2.2.1)
Generating Pods project
Integrating client project

It might also tell you something like this:

[!] From now on use `ShowTracker.xcworkspace`.

If you type ls now (or browse to the project folder using Finder), you’ll see that CocoaPods created a Pods folder – where it stores all dependencies – and ShowTracker.xcworkspace.

VERY IMPORTANT!

From now on, as the command-line warning mentioned, you must always open the workspace (ShowTracker.xcworkspace) and not the project!

Close the Xcode project (if you had it open) and open ShowTracker.xcworkspace.

Register for Trakt

Before you can use the Trakt APIs, you first need to register for a free account. Don’t worry – it’s fast and easy to do!

After registering, go to the Settings -> API page to get your API key. Go ahead and leave this page open as you’ll need it soon.

Testing AFNetworking

To verify that AFNetworking was successfully added, create a new file with the iOS -> Cocoa Touch -> Objective-C class template.

Name the class TraktAPIClient, and make it a subclass of NSObject.

Make sure you add the new file to the ShowTracker project and not the Pods project – if you look at your Project Navigator, you’ll see that you now have two separate projects in your workspace.


Adding TraktAPIClient

Open TraktAPIClient.h and replace the existing code with the following:

// 1
#import <AFNetworking/AFNetworking.h>
 
// 2
extern NSString * const kTraktAPIKey;
extern NSString * const kTraktBaseURLString;
 
// 3
@interface TraktAPIClient : AFHTTPSessionManager
 
// 4
+ (TraktAPIClient *)sharedClient;
 
// 5
- (void)getShowsForDate:(NSDate *)date
               username:(NSString *)username
           numberOfDays:(int)numberOfDays
                success:(void(^)(NSURLSessionDataTask *task, id responseObject))success
                failure:(void(^)(NSURLSessionDataTask *task, NSError *error))failure;
 
@end

Here’s what you’re doing:

  1. First import the main header for AFNetworking, which is AFNetworking.h. This includes AFHTTPSessionManager, which will be the super class of TraktAPIClient, and other related networking classes.
  2. Next, declare two string constants with the extern modifier. This means these strings will be globally available.
  3. Change TraktAPIClient to extend AFHTTPSessionManager instead of NSObject, as mentioned in step 1.
  4. Declare sharedClient, which will return a singleton instance for TraktAPIClient.
  5. Lastly, declare a helper method that will make it easier to use the trakt API to get shows for a specific date, username, and number of days.

You might be wondering, “why did I create TraktAPIClient when AFHTTPSessionManager already handles HTTP requests?”

TraktAPIClient will make it easier to access the trakt API and make a lot of requests to the same base URL, using the same API key. This also encapsulates all the networking calls and responsibility into a single class.

Open TraktAPIClient.m and replace its contents with the following:

#import "TraktAPIClient.h"
 
// Set this to your Trakt API Key
NSString * const kTraktAPIKey = @"PASTE YOUR API KEY HERE";
NSString * const kTraktBaseURLString = @"http://api.trakt.tv";
 
@implementation TraktAPIClient
 
+ (TraktAPIClient *)sharedClient {
    static TraktAPIClient *_sharedClient = nil;
    static dispatch_once_t oncePredicate;
    dispatch_once(&oncePredicate, ^{
        _sharedClient = [[self alloc] initWithBaseURL:[NSURL URLWithString:kTraktBaseURLString]];
    });
    return _sharedClient;
}
 
- (instancetype)initWithBaseURL:(NSURL *)url {
 
    self = [super initWithBaseURL:url];
    if (!self) {
        return nil;
    }
 
    self.responseSerializer = [AFJSONResponseSerializer serializer];
    self.requestSerializer = [AFJSONRequestSerializer serializer];
    return self;
}
 
- (void)getShowsForDate:(NSDate *)date
               username:(NSString *)username
           numberOfDays:(int)numberOfDays
                success:(void(^)(NSURLSessionDataTask *task, id responseObject))success
                failure:(void(^)(NSURLSessionDataTask *task, NSError *error))failure
{
    NSDateFormatter* formatter = [[NSDateFormatter alloc] init];
    formatter.dateFormat = @"yyyyMMdd";
    NSString* dateString = [formatter stringFromDate:date];
 
    NSString* path = [NSString stringWithFormat:@"user/calendar/shows.json/%@/%@/%@/%d",
                      kTraktAPIKey, username, dateString, numberOfDays];
 
    [self GET:path parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) {
        if (success) {
            success(task, responseObject);
        }
    } failure:^(NSURLSessionDataTask *task, NSError *error) {
        if (failure) {
            failure(task, error);
        }
    }];
}
 
@end

This code fulfills all the promises you made in the header file:

First, you declare the string constants. Make sure you replace PASTE YOUR KEY HERE with your actual trakt API key.

Next, you implement sharedClient, which uses Grand Central Dispatch (GCD) to create a singleton instance.

You then override initWithBaseURL: and set responseSerializer and requestSerializer to the default JSON serializer for each.

Lastly, you create the helper method to get shows using the format expected by trakt.

Now open AppDelegate.m and add the following right after the existing import line:

#import <AFNetworking/AFNetworkActivityIndicatorManager.h>

Then add the following to application:didFinishLaunchingWithOptions: right before the return statement:

[AFNetworkActivityIndicatorManager sharedManager].enabled = YES;

This will automatically show the network activity indicator whenever AFNetworking is performing network requests.

Next, open ViewController.m and add the following import right after the last one:

#import "TraktAPIClient.h"

Then, replace viewDidLoad with the following:

- (void)viewDidLoad
{
    [super viewDidLoad];
 
    TraktAPIClient *client = [TraktAPIClient sharedClient];
 
    [client getShowsForDate:[NSDate date]
                   username:@"rwtestuser"
               numberOfDays:3
                    success:^(NSURLSessionDataTask *task, id responseObject) {
                        NSLog(@"Success -- %@", responseObject);
                    }
                    failure:^(NSURLSessionDataTask *task, NSError *error) {
                        NSLog(@"Failure -- %@", error);
                    }];
}

This code calls the helper method you just setup to get the shows for today and the next 3 days.

Note: this API queries for tv episodes on a trakt user’s watch list. If you want to use your own username, you will first need to add shows to your watch list from the tv shows category. If you don’t want to do this, use rwtestuser as the username, which already has shows added to it.

Build and run, and you should see output in the console similar to the following:


Get Shows NSLog

Add More Pods

Now that you’ve seen CocoaPods in action, there’s only one thing to do…


Add More Pods!
Well, actually… you should strive to include as few pods as possible in each of your projects. While CocoaPods makes it easier to update library dependencies, you’re still going to have to update your own code from time to time when you update libraries.

Keeping your dependencies under control is the best way to go in the long run.

You need to add two more dependencies for this project. Rather than opening PodFile from the command line, you can now find it in the Pods target in the workspace.

Podfile In Workspace

Open Podfile and append the following right after the AFNetworking line:

pod 'SSToolkit', '1.0.4'
pod 'Nimbus/AttributedLabel', '1.2.0'

Save the file, and install the dependencies (via pod install in Terminal, as before).

Do you notice anything different about the Nimbus/AttributedLabel pod? What is that slash in the middle of its name?

Larger pods often break up their classes into several submodules that rely on a “core” module. In this case, the slash means that AttributedLabel is a submodule of Nimbus.

In case you’re not familiar with these libraries, here’s what they are:

SSToolkit is a handy collection of solutions to common issues faced by iOS developers. In the ShowTracker app, you’ll use it to check if the device has a Retina display.

Nimbus, short for NimbusKit, is another collection of useful categories and classes for iOS. You’ll use it mainly for its AttributedLabel component in this app.

Just to verify everything still works, go ahead and build the app. However, you should now see several warning messages!

“But wait a minute, I haven’t changed any of my project’s code!”, you might be thinking.

Whenever you add a dependency via CocoaPods, the pod’s source files are included within your project’s workspace. And whenever you compile your project, they’re compiled too.

So, if the pod you’re using has a lot of warnings (or worse, doesn’t compile at all), it may be an indicator that the library is no longer maintained.

In such a case, you can look for an alternative library on the CocoaPods website by using the search field. You can find detailed information about pods there too, including links to pods’ websites, documentation, and more.

In this case, however, the warnings are mainly due to recently deprecated methods in iOS 7 (mostly calls to sizeWithFont:), and you can safely ignore them.

Add the following to the top of Podfile to tell CocoaPods to silence all warnings from pod dependencies:

inhibit_all_warnings!

Run pod install again, build your project, and you shouldn’t see any warnings anymore.

Completing Show Tracker

Great, you now have all the dependencies you’ll need to complete Show Tracker, so let’s do it!

Open ViewController.m and add the following imports, just after the last one:

#import <AFNetworking/UIKit+AFNetworking.h>
#import <Nimbus/NIAttributedLabel.h>
#import <SSToolkit/UIScreen+SSToolkitAdditions.h>

Next, add the following properties right after @interface ViewController ():

@property (nonatomic, strong) NSArray *jsonResponse;
@property (nonatomic) BOOL pageControlUsed;
@property (nonatomic) NSInteger previousPage;

These will be used to store downloaded shows information.

Create a method called loadShow:, at the bottom of the file:

- (void)loadShow:(NSInteger)index 
{
    // empty for now...
}

This method will be responsible for presenting a page to the user that displays information about a selected TV show episode.

Now you need to modify viewDidLoad so that it does something useful with the response object instead of simply logging it. First add the following right after [super viewDidLoad];:

self.previousPage = -1;

Then replace the contents of the success block with the following:

// Save response object
self.jsonResponse = responseObject;
 
// Get the number of shows
NSInteger shows = 0;
for (NSDictionary *day in self.jsonResponse)
    shows += [day[@"episodes"] count];
 
// Set up page control
self.showsPageControl.numberOfPages = shows;
self.showsPageControl.currentPage = 0;
 
// Set up scroll view
self.showsScrollView.contentSize = CGSizeMake(CGRectGetWidth(self.view.bounds) * shows, CGRectGetHeight(self.showsScrollView.frame));
 
// Load first show
[self loadShow:0];

Now the responseObject JSON is stored, the page control and scroll view are setup, and loadShow: is called to load the first show on success.

You can now present information about a show by replacing loadShow: with the following:

- (void)loadShow:(NSInteger)index
{
    // 1 - Find the show for the given index
    NSDictionary *show = nil;
    NSInteger shows = 0;
 
    for (NSDictionary *day in self.jsonResponse) {
        NSInteger count = [day[@"episodes"] count];
 
        // 2 - Did you find the right show?
        if (index < shows + count) {
            show = day[@"episodes"][index - shows];
            break;
        }
 
        // 3 - Increment the shows counter
        shows += count;
    }
 
    // 4 - Load the show information
    NSDictionary *showDict = show[@"show"];
 
    // 5 - Display the show title
    UILabel *titleLabel = [[UILabel alloc] initWithFrame:CGRectMake(index * CGRectGetWidth(self.showsScrollView.bounds) + 20, 40, CGRectGetWidth(self.showsScrollView.bounds) - 40, 40)];
    titleLabel.text = showDict[@"title"];
    titleLabel.backgroundColor = [UIColor clearColor];
    titleLabel.textColor = [UIColor whiteColor];
    titleLabel.font = [UIFont boldSystemFontOfSize:18];
    titleLabel.textAlignment = NSTextAlignmentCenter;
 
    // 6 - Add to scroll view
    [self.showsScrollView addSubview:titleLabel];
}

Build and run, and you will see something like this:


Title Only Screenshot

Cool, tonight a new episode of The Walking Dead will air! But which episode?

Add the following to loadShow: just before section #6:

// 5.1 - Create formatted airing date
static NSDateFormatter *formatter = nil;
if (!formatter) {
    formatter = [[NSDateFormatter alloc] init];
    formatter.dateStyle = NSDateFormatterLongStyle;
    formatter.timeStyle = NSDateFormatterShortStyle;
    formatter.timeZone = [NSTimeZone timeZoneWithAbbreviation:@"PST"];
}
 
NSDictionary *episodeDict = show[@"episode"];
 
NSTimeInterval showAired = [episodeDict[@"first_aired_localized"] doubleValue];
NSString *showDate = [formatter stringFromDate:[NSDate dateWithTimeIntervalSince1970: showAired]];
 
// 5.2 - Create label to display episode info
UILabel *episodeLabel = [[UILabel alloc] initWithFrame:CGRectMake(index * CGRectGetWidth(self.showsScrollView.bounds),
                                                         360, CGRectGetWidth(self.showsScrollView.bounds), 40)];
NSString* episode  = [NSString stringWithFormat:@"%02dx%02d - \"%@\"",
                      [[episodeDict valueForKey:@"season"] intValue],
                      [[episodeDict valueForKey:@"number"] intValue],
                      [episodeDict objectForKey:@"title"]];
episodeLabel.text = [NSString stringWithFormat:@"%@\n%@", episode, showDate];
episodeLabel.numberOfLines = 0;
episodeLabel.textAlignment = NSTextAlignmentCenter;
episodeLabel.textColor = [UIColor whiteColor];
episodeLabel.backgroundColor = [UIColor clearColor];
 
CGSize size = [episodeLabel sizeThatFits:CGSizeMake(CGRectGetWidth(self.view.frame),
                                          CGRectGetHeight(self.view.frame) - CGRectGetMinY(episodeLabel.frame))];
CGRect frame = episodeLabel.frame;
frame.size.width = self.view.frame.size.width;
frame.size.height = size.height;
episodeLabel.frame = frame;
 
[self.showsScrollView addSubview:episodeLabel];

This creates a label containing info about the episode, including season, episode number, title and a human-readable date and time it’s going to be aired.

Build and run, and you can now see that The Walking Dead is already in its 4th season!


Title Details Screenshot

Doesn’t it feel as if there’s a giant void at the center of the screen that’s calling out to be filled? What if you put an image there?

Still in loadShow:, add the following code just before section #6:

// 5.3 - Get image
NSString *posterUrl = showDict[@"images"][@"poster"];
if ([[UIScreen mainScreen] isRetinaDisplay]) {
    posterUrl = [posterUrl stringByReplacingOccurrencesOfString:@".jpg" withString:@"-300.jpg"];
} else {
    posterUrl = [posterUrl stringByReplacingOccurrencesOfString:@".jpg" withString:@"-138.jpg"];
}
 
// 5.4 - Display image using image view
UIImageView *posterImage = [[UIImageView alloc] init];
posterImage.frame = CGRectMake(index * CGRectGetWidth(self.showsScrollView.bounds) + 90, 105, 150, 225);
[self.showsScrollView addSubview:posterImage];
 
// 5.5 - Asynchronously load the image
[posterImage setImageWithURL:[NSURL URLWithString:posterUrl] placeholderImage:[UIImage imageNamed:@"placeholder.png"]];

This code first determines the image to download based on whether the device has a retina display (since there is no need to download a larger image on a non-retina device). Note that isRetinaDisplay is a helper method included from SSToolkit.

After that, a UIImageView is created and added to the scroll view.

Finally, the image is downloaded and set on the image view, using the convenience method provided by AFNetworking. This method also sets a placeholder image that is displayed until the download is finished.

Build and run, and you will see a beautiful poster of the show.


Title, Details, and Poster Screenshot

Great! You now have information displayed about the first show but what about the rest?

You first need to show information about the other shows as the view is paged via the showsPageControl.

Replace pageChanged: with the following:

- (IBAction)pageChanged:(id)sender
{
    // Set flag
    self.pageControlUsed = YES;
 
    // Get previous page number
    NSInteger page = self.showsPageControl.currentPage;
    self.previousPage = page;
 
    // Call loadShow for the new page
    [self loadShow:page];
 
    // Scroll scroll view to new page
    CGRect frame = self.showsScrollView.frame;
    frame.origin.x = frame.size.width * page;
    frame.origin.y = 0;
    [UIView animateWithDuration:.5 animations:^{
        [self.showsScrollView scrollRectToVisible:frame animated:NO];
    } completion:^(BOOL finished) {
        self.pageControlUsed = NO;
    }];
}

Here pageControlUsed is set to YES, indicating that the new page will be shown because the user selected it with the page control and not by scrolling the scroll view. (You’ll need this later when you handle scrolling using the scroll view.)

Then, the new page is loaded and its frame calculated. Finally, the new page is displayed on the screen.

Build and run. You can now navigate through pages using the page control. Simply tap on the circles in the page control and the screen will navigate to the next or previous page. Cool, isn’t it?

But, something’s missing… What about changing pages by scrolling? If you try to do that, you’ll notice that you get blank pages, unless you’d already scrolled to that page using the page control. Let’s fix that.

Paste the following UIScrollViewDelegate methods at the end of the file:

- (void)scrollViewDidScroll:(UIScrollView *)sender
{
    // Was the scrolling initiated via page control?
    if (self.pageControlUsed)
        return;
 
    // Figure out page to scroll to
    CGFloat pageWidth = sender.frame.size.width;
    NSInteger page = floor((sender.contentOffset.x - pageWidth / 2) / pageWidth) + 1;
 
    // Do not do anything if we're trying to go beyond the available page range
    if (page == self.previousPage || page < 0 || page >= self.showsPageControl.numberOfPages)
        return;
    self.previousPage = page;
 
    // Set the page control page display
    self.showsPageControl.currentPage = page;
 
    // Load the page
    [self loadShow:page];
}
 
- (void)scrollViewDidEndDecelerating:(UIScrollView *)scrollView
{
    self.pageControlUsed = NO;
}

Note the use of self.pageControlUsed. That’s because when the scroll view offset is changed in pageChanged (when you change the page using the page control), scrollViewDidScroll: is called, but if you handled the scrolling at that point, you’d be loading the same page twice. So you need to detect when scrolling is initiated via the page control and do nothing.

Build and run, and you can now navigate through the shows by scrolling too!

But what if the user wants to know more about the show? Trakt has a page for every show registered, and its URL is in the information the app has already received.

You can use NIAttributedLabel to present it.

At the top of ViewController.m, replace @interface ViewController () with the following:

@interface ViewController () <NIAttributedLabelDelegate>

This declares that ViewController conforms to the NIAttributedLabelDelegate protocol.

Now add the following NIAttributedLabelDelegate method at the bottom of ViewController.m:

- (void)attributedLabel:(NIAttributedLabel *)attributedLabel didSelectTextCheckingResult:(NSTextCheckingResult *)result atPoint:(CGPoint)point
{
    [[UIApplication sharedApplication] openURL:result.URL];
}

This delegate method is called when a link is selected. It simply requests UIApplication to open the URL, which will result in Safari being launched to open it.

But how can one add a link to the label displayed in the app? Simple, just change the implementation of loadShow: in section #5 so that the label displaying the show title is an NIAttributedLabel instead of a UILabel.

Replace the // 5 - Display the show title section with the following:

// 5 - Display the show title
NIAttributedLabel *titleLabel = [[NIAttributedLabel alloc] initWithFrame:CGRectMake(index * CGRectGetWidth(self.showsScrollView.bounds), 40, CGRectGetWidth(self.showsScrollView.bounds), 40)];
titleLabel.text = showDict[@"title"];
titleLabel.backgroundColor = [UIColor clearColor];
titleLabel.linkColor = [UIColor redColor];
titleLabel.font = [UIFont systemFontOfSize:18];
titleLabel.textAlignment = NSTextAlignmentCenter;
[titleLabel addLink: [NSURL URLWithString:showDict[@"url"]] range:NSMakeRange(0, titleLabel.text.length)];
titleLabel.delegate = self;

The main difference is the existence of linkColor, and addLink:. It’s also important to set the label’s delegate to self, which represents the current instance of the ViewController.

Build and run, and you will see the show title in red. If you click it, Safari will open with the show page from trakt!

Where To Go From Here?

You can download the completed project from here.

Now that you know the basics of using CocoaPods, you can read through the CocoaPods Guides to learn the finer details of using CocoaPods, including how to create your own pod spec and submit it to the official repository.

To check if CocoaPods already supports a specific library, you can search the CocoaPods website.

Here are some of the libraries I think are most useful:

  • AFNetworking: Network connectivity and response handling.
  • CorePlot: Graphing and plotting framework.
  • MagicalRecord: Super Awesome Easy Fetching for Core Data 1!!!11!!!!1!
  • MBProgressHUD: An activity indicator that can be added to your views.
  • Nimbus: The iOS framework that grows only as fast as its documentation.

You should also check out the trakt API. It’s well-documented, and you can do a lot of things with it.

Regarding the sample application, there are many improvements you could make, such as:

  • What happens if there isn’t a TV show for the given date range?
  • What if there are a lot of shows? None of the pages that are loaded are thrown away, and this can quickly consume a lot of memory!
  • There’s more information to be displayed – like a synopsis, the channel airing the show, etc.

I hope you enjoyed the tutorial, and I look forward to reading your comments in the forums.

Know of another useful library? Tell us about it! Have you created a pod spec? Let us know about that too!

Introduction to CocoaPods Tutorial is a post from: Ray Wenderlich

The post Introduction to CocoaPods Tutorial appeared first on Ray Wenderlich.

We’re Hiring: Editor-In-Chief at raywenderlich.com

$
0
0
Join the raywenderlich.com Team!

Apply to be Editor-in-Chief!

As you may know, our primary focus at raywenderlich.com is developing high quality programming tutorials. Our goal is to take the coolest and most challenging topics and make them easy for everyone to learn – so we can all make great apps.

Thanks to the support of our amazing readers, we have been able to grow and improve this site over the years. Starting from my personal blog, we now have an international team of over 100 authors, editors, translators, and forum subject matter experts teaming up to make some amazing tutorials for the community.

Today, I am happy to announce our next big step forward – we are looking to hire an official Editor-in-Chief at raywenderlich.com!

This is a full time position with a competitive salary for an experienced iOS developer. It is a unique and special opportunity that you won’t find anywhere else. Keep reading to find out more about the job – and to see if it’s for you (or for someone you may know)! :]

The Job

As Editor-in-Chief, you would be the primary person responsible for making sure we have high quality content coming out on this site each week. This includes:

  • Coordinating the team. You’ll assign tutorial topics to authors and editors, and make sure everything is moving smoothly. From time to time, you will step in to perform editing or give feedback on the process.
  • Making editorial decisions. You’ll choose what topics we publish on the site, and choose when they should be scheduled. What makes a good tutorial topic, and what makes a bad one? You’ll decide! :]
  • Growing the team. We’re always looking for advanced-level developers for the tutorial team – another part of your job will be recruiting and trying out these folks, and bringing them on-board the team. You’ll also be ramping up entirely new teams – like the code team, or the update team.
  • Make video tutorials and work on books. Along with Brian and myself, you’ll regularly make video tutorials for the site like this one, and work on books like this one. We will be looking for advanced level subjects, so you’ll need a lot of experience for this.
  • Staying up-to-date. In order to be a good editor-in-chief and video tutorial maker, you will need to be constantly learning, and keeping up-to-date with the latest APIs, news, and techniques. To be successful, you’ll need a passion for this.
  • App development. If time permits, you will also be doing some app development – but the above tasks take priority.

The Benefits

As I mentioned, this is a unique opportunity you won’t find anywhere else. Here are the top 10 reasons this job is so special:

  1. Competitive Salary. We are offering a competitive salary for this position, including health and dental benefits and contributions toward an IRA.
  2. Work from Home. One of the cool things about this position is you can work from home! So it doesn’t matter where you live, as long as you’re authorized to work in the US. (Sorry to our readers who are abroad – hiring people outside the US is a bit complicated for a small company like us.)
  3. A Completely Unique Opportunity. I believe this is a once in a lifetime opportunity. Think to yourself – how many other sites are building a community centered on high-quality developer education like this, that have grown organically, without any outside investors? And how many of those do you have the chance to join so early, where you can make such a huge impact?
  4. Help Us Set the Course. As Editor-in-Chief, your decisions will directly steer where this site heads in the next few years. You’ll decide which topics we cover, who gets on the team, and what new projects we take on. If you like to make challenging and interesting decisions like this, this is the job for you!
  5. Get Paid to Learn. If you love to learn, you’ll fit right in. Part of your job will be to learn new things and make tutorials about them, and you’ll also learn by editing others’ tutorials. You will constantly be developing your skills!
  6. Personal Growth. In addition to improving your technical skills, you will also be getting tons of experience with “soft skills” like leadership, writing, editing, and public speaking. You know how good the speakers are at TED talks, or that feeling when you read a clearly-written technical book on a tricky subject? We want that to be you.
  7. Be a Huge Part of the Community. As Editor-in-Chief, you will become a leader in the iOS development community. You will speak at conferences. You will write books and make video tutorials. You’ll make friends with some of the coolest guys and gals in the industry. People will recognize you and come up to introduce themselves. People will throw you parades. (OK, maybe not that last one, but 4/5 isn’t bad!) ;]
  8. Be An Indie – Without the Risk. This is such a small company that it’s pretty much the same thing as being an indie developer – but without the risks of being on your own! You’ll get to set your own hours, work from home, work on small projects that you can complete quickly, and work on small apps. But you’ll have a steady paycheck – and benefits like health insurance, vacation, and an IRA!
  9. Make a Difference. In this job, you will be making a significant positive impact in people’s lives. We get emails every day about how our tutorials have helped developers learn something that they previously struggled with, and about how our tutorials have helped them make an app or get a job. This is what makes all the hard work worth it, and is incredibly rewarding!
  10. This Job is Fun! I’ve never had a job as cool as this one. It’s so much fun to be constantly learning, working with a bunch of talented developers, making cool stuff, and at the end of the day, helping a lot of people! Every day is a fresh and interesting batch of challenges, and I think you will love it too! :]

The Requirements

The requirements are very high for this position. Here are what I consider the Five Essential Qualities:

  1. Advanced-level iOS developer. You must be an advanced-level iOS developer for this position. We are looking for someone with 3+ years full-time iOS experience (and 5+ years full-time overall programming experience), that has worked on several apps in the App Store and is up-to-date with their iOS skills.
  2. A good leader. You must have demonstrated experience as a leader to be successful in this position. You are probably the kind of person who is a team lead in your current job, and who is friendly and patient when working with others.
  3. Love for both apps and games. Remember, at raywenderlich.com we are focused on both app and game tutorials. So to be successful, you need to have an interest in both sides. Ideally, you are a gamer at heart – video games, board games, and/or RPG games.
  4. Well read. To be successful as the Editor-in-Chief, you must be the type of person who stays up-to-date with the latest iOS development news. You probably read a lot of blogs, check out a lot of open source libraries, read a lot of books, and listen to a lot of podcasts.
  5. In for the long haul. You are going to be one of the first full-time members at Razeware LLC, beyond Vicki, Brian, and myself. As such, you will be incredibly important with respect to how this company and this website fare in the long run. I am looking for someone who really believes in what we’re trying to accomplish, and who wants to be part of this for the long haul as this company grows.

How To Apply

Apply Now!

Apply Now!

If this sounds like a job for you, please contact me and let me know:

  1. Are you authorized to work in the U.S.?
  2. Why are you interested in the Editor-in-Chief position?
  3. Why do you think you have the right qualifications for the position?
  4. Include links to any apps you’ve made (or web sites, portfolio, etc).
  5. Include links to anything you’ve written (articles, books, etc).
  6. Include a link to your resume.

Even if this position isn’t for you, please help spread the word! Finding the right person for Editor-in-Chief is really important for the future of this site.

Thank you for your consideration of this position, and/or for sharing the word! I’m looking forward to seeing this site continue to develop and grow. :]

We’re Hiring: Editor-In-Chief at raywenderlich.com is a post from: Ray Wenderlich

The post We’re Hiring: Editor-In-Chief at raywenderlich.com appeared first on Ray Wenderlich.

Video Tutorial: Beginner OpenGL ES and GLKit Part 11: Making a Simple 3D Game (Part 1)

How to Make a Line Drawing Game with Sprite Kit

$
0
0
Learn how to make a line drawing game like Flight Control!

Learn how to make a line drawing game like Flight Control!

In 2009, Firemint introduced the line drawing game to the world when they released the incredibly popular Flight Control.

In a line drawing game, you trace a line with your finger and then sprites follow the line that you drew.

In this tutorial, you’ll learn how to write your own line drawing game with Sprite Kit. Instead of being a mere clone of Flight Control, you will create a game called “Hogville” where your goal is to bring some cute and tired pigs to food and shelter.

This tutorial assumes you have some experience with Sprite Kit. While you don’t have to be an expert, you should know the basics, like how to create sprites and run actions on them. If you’ve got a big question mark in place of that knowledge, take some time to work through Ray’s Sprite Kit Tutorial for Beginners before proceeding.

Getting Started

To get started, download the starter project.

I created this starter project using the Sprite Kit Game template and set it to run in landscape mode. I also added all the artwork you’ll need – and a big thanks to Vicki Wenderlich for providing the art!

Build and run, and you should see a blank screen in landscape as a starting point:

StarterProject

Now you can get right to the business of adding your game elements and developing the line drawing gameplay.

Adding the Background… and a Pig!

After a long day of being a pig, all you want is some food and a bed—a pile of hay will do! It’s hard work rolling in the mud all day. In Hogville, it will be your player’s job to give the pigs what they want by drawing the lines to steer them home.

Before you start drawing lines, though, you need a pig to follow them. Your pig would be a bit unhappy floating in a black void, so you’ll also add a background to give the poor pig some familiar surroundings.

Open MyScene.m and find initWithSize:. Inside the if statement, add the following code:

SKSpriteNode *bg = [SKSpriteNode spriteNodeWithImageNamed:@"bg_2_grassy"];
bg.anchorPoint = CGPointZero;
[self addChild:bg];
 
SKSpriteNode *pig = [SKSpriteNode spriteNodeWithImageNamed:@"pig_1"];
pig.position = CGPointMake(self.size.width / 2.0f, self.size.height / 2.0f);
[self addChild:pig];

This adds the background image and a pig sprite. You place the lower-left corner of the background image in the lower-left corner of the scene by setting bg‘s anchorPoint to (0, 0) and using its default position.

Build and run your game and behold your plump, rosy pig in the middle of a sunny, green field.

First_Run

Moving the Sprite

Next you need to create a class for the pig sprite. This will contain the path which the pig should follow, along with methods to make the pig follow this path over time.

To create a class for the pig sprite, go to File\New\File…, choose the iOS\Cocoa Touch\Objective-C class template and click Next. Name the class Pig, make it a subclass of SKSpriteNode, click Next and then Create.

Open Pig.h and add the following two method declarations to the interface:

- (void)addPointToMove:(CGPoint)point; 
- (void)move:(NSNumber *)dt;

Now open Pig.m and add the following variable before the @implemenation section:

static const int POINTS_PER_SEC = 80;

This constant defines the speed of the pig as 80 points per second.

Next, declare two instance variables by adding the following code immediately after the @implementation line:

{
  NSMutableArray *_wayPoints;
  CGPoint _velocity;
}

_wayPoints will do what its name suggests and store all the points along which the pig should move. _velocity will store the pig’s current speed and direction.

Next, implement initWithImageNamed: and initialize _waypoints inside it:

- (instancetype)initWithImageNamed:(NSString *)name {
  if(self = [super initWithImageNamed:name]) {
    _wayPoints = [NSMutableArray array];       
  }
 
  return self;
}

Now that you’ve initialized _wayPoints, you need a method to add waypoints to it. Implement addPointToMove: by adding the following code to Pig.m:

- (void)addPointToMove:(CGPoint)point {
  [_wayPoints addObject:[NSValue valueWithCGPoint:point]];
}

This method simply adds the given point to the _wayPoints array. In order to store a CGPoint in an NSArray, you use NSValue‘s valueWithCGPoint method to store the CGPoint in an object.

Now begin implementing move: by adding the following code to Pig.m:

- (void)move:(NSNumber *)dt {
  CGPoint currentPosition = self.position;
  CGPoint newPosition;
 
  //1
  if([_wayPoints count] > 0) {
    CGPoint targetPoint = [[_wayPoints firstObject] CGPointValue];
 
    //2 TODO: Add movement logic here
 
    //3
    if(CGRectContainsPoint(self.frame, targetPoint)) {
      [_wayPoints removeObjectAtIndex:0];
    }
  }
}

You will call this method each frame to move the pig a little bit along its path. Here’s how this part of the method works:

  1. First you check to ensure there are waypoints left in the array. For the moment, the pig stops moving when it reaches the final point of the path. Later, you’ll make the pig a little smarter so it continues walking in its last direction even when no waypoints remain.
  2. This comment marks where you’ll put the code that updates the pig’s position. You’ll add that code next.
  3. Finally, you check if the pig has reached the waypoint by seeing if the pig’s frame contains the targetPoint. In this case, you remove the point from the array so that your next call to move: will use the next point. Note that it’s important to check if the frame contains the target point (rather than checking if the position equals the target point), effectively stopping the pig when he’s “close enough”. That makes some of the calculations later a bit easier.

You added that final if statement in the above code because the pig isn’t guaranteed to reach the waypoint in just one call to move:. That makes sense, because the pig needs to move at a constant speed, a little each frame.

Why? Let’s assume you have the first waypoint in the upper-left corner at (0, 50) and the second point at (300, 50). Something like this can happen if the player moves their finger very fast over the screen.

If you took the simple approach of setting the position to the first point in the array and then to the second point in the array, your pig would appear to teleport from one waypoint to the next. Have you ever seen a teleporting pig? I’m sure even Captain Kirk can’t make that claim.

Beam me up, piggy!

With the logic to process the waypoints in place, it’s time to add the code that calculates and updates the pig’s new position along the path between the waypoints. In move:, replace the //2 TODO: Add movement logic here comment with the following code:

//1
CGPoint offset = CGPointMake(targetPoint.x - currentPosition.x, targetPoint.y - currentPosition.y);
CGFloat length = sqrtf(offset.x * offset.x + offset.y * offset.y);
CGPoint direction = CGPointMake(offset.x / length, offset.y / length);
_velocity = CGPointMake(direction.x * POINTS_PER_SEC, direction.y * POINTS_PER_SEC);
 
//2
newPosition = CGPointMake(currentPosition.x + _velocity.x * [dt doubleValue], 
                          currentPosition.y + _velocity.y * [dt doubleValue]);
self.position = newPosition;

Here’s what you’re doing with the code you just added:

  1. You calculate a vector that points in the direction the pig should travel and has a length representing the distance the pig should move in dt seconds.

    To calculate the vector, first you find the difference between the pig’s current location and the next waypoint and store it as offset, a CGPoint representing the differences in both the x and y directions.

    As you can see in the following image, the distance between the two points is the length of the hypotenuse of the right triangle formed between the pig’s current position and the waypoint.

    velocity

    You divide offset‘s components by length to create a normalized vector (a vector of length 1) that points in the direction of the waypoint and you store it in direction.

    Finally, you multiply direction by POINTS_PER_SEC and store it in _velocity, which now represents a vector pointing in the direction the pig should travel, with a length that is the distance the pig should travel in one second.

  2. You calculate the pig’s new position by multiplying _velocity by dt and adding the result to the pig’s current position. Because _velocity stores the distance the pig should travel in one second and dt holds the number of seconds that have passed since the last call to move:, multiplying the two results in the distance the pig should travel in dt seconds.

You’re done here for the moment. It’s time to use your new class and move the pig.

Responding to Touches

Open MyScene.m and add the following import so the scene can access your new class:

#import "Pig.h"

Find this line in initWithSize::

SKSpriteNode *pig = [SKSpriteNode spriteNodeWithImageNamed:@"pig_1"];

Replace the above line with the following:

Pig *pig = [[Pig alloc] initWithImageNamed:@"pig_1"];
pig.name = @"pig";

You have simply replaced SKSpriteNode with your new subclass, Pig, and given it a name. You will use this name when you process new touches to identify pig nodes.

Add the following instance variables to MyScene, just below the @implementation line:

{
  Pig *_movingPig;
  NSTimeInterval _lastUpdateTime;
  NSTimeInterval _dt;
}

_movingPig will hold a reference to the pig the user wants to move. _lastUpdateTime will store the time of the last call to update: and _dt will store the time elapsed between the two most recent calls to update:.

A few steps remain before you get to see your pig move. Add the following code inside touchesBegan:withEvent::

CGPoint touchPoint = [[touches anyObject] locationInNode:self.scene];
SKNode *node = [self nodeAtPoint:touchPoint];
 
if([node.name isEqualToString:@"pig"]) {
  [(Pig *)node addPointToMove:touchPoint];
  _movingPig = (Pig *)node;
}

What happens here? First, you find the location of the touch within the scene. After that, you use nodeAtPoint: to identify the node at that location. The if statement uses the node’s name to see if the user touched a pig or something else, such as the background.

Note: You use the name property of SKNode to check for the pig. This is like UIView‘s tag property: a simple way to identify a node without needing to store a reference. Later, you’ll see another use case for the name property.

If the user touched a pig, you add touchPoint as a waypoint and set _movingPig to the touched node. You’ll need this reference in the next method to add more points to the path.

To draw a path, after the first touch the user needs to move their finger while continuously touching the screen. Add the following implementation of touchesMoved:withEvent: to add more waypoints:

- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
  CGPoint touchPoint = [[touches anyObject] locationInNode:self.scene];    
  if(_movingPig) {
    [_movingPig addPointToMove:touchPoint];
  }
}

This is a simple method. You get the next position of the user’s finger and if you found a pig in touchesBegan:withEvent:, as indicated by a non-nil _movingPig value, you add the position to this pig as the next waypoint.

So far, you can store a path for the pig—now let’s make the pig follow this path. Add the following code to update: inside MyScene.m:

_dt = currentTime - _lastUpdateTime;
_lastUpdateTime = currentTime;
 
[self enumerateChildNodesWithName:@"pig" usingBlock:^(SKNode *pig, BOOL *stop) {
  [(Pig *)pig move:@(_dt)];
}];
  1. First, you calculate the time since the last call to update: and store it in _dt. Then, you assign currentTime to _lastUpdateTime so you have it for the next call.
  2. Here is the other use case for the name property. You use SKScene‘s method enumerateChildNodesWithName:usingBlock: to enumerate over all nodes with the name pig. On these nodes, you call move:, passing _dt as the argument. Since SKNode has no method called move:, you cast it to Pig to make Xcode and the compiler happy.

Now build and run, and let the pig follow your finger as you draw a path.

Pig following mouse

The pig doesn’t face in the direction it’s moving, but otherwise this is a good result!

But wait a minute—isn’t this a line drawing game? So where is the line?

Drawing Lines

Believe it or not, there is only one important step left to complete a line drawing game prototype that you can expand. Drawing the lines!

At the moment, only the pig knows the path it wants to travel, but the scene also needs to know this path to draw it. The solution to this problem is a new method for your Pig class.

Open Pig.h and add the following method declaration to the interface:

- (CGPathRef)createPathToMove;

Now open Pig.m and implement this new method as follows:

- (CGPathRef)createPathToMove {
  //1
  CGMutablePathRef ref = CGPathCreateMutable();
 
  //2
  for(int i = 0; i < [_wayPoints count]; ++i) {
    CGPoint p = [_wayPoints[i] CGPointValue];
 
    //3
    if(i == 0) {
      CGPathMoveToPoint(ref, NULL, p.x, p.y);
    } else {
      CGPathAddLineToPoint(ref, NULL, p.x, p.y);
    }
  }
 
  return ref;
}
  1. First, you create a mutable CGPathRef so you can add points to it.
  2. This for loop iterates over all the stored waypoints to build the path.
  3. Here you check if the path is just starting, indicated by an i value of zero. If so, you move to the point’s location; otherwise, you add a line to the point. If this is confusing, think about how you would draw a path with pen and paper. CGPathMoveToPoint() is the moment you put the pen on the paper after moving it to the starting point, while CGPathAddLineToPoint() is the actual drawing with the pen on the paper.
  4. At the end, you return the path.
Note: Are you thinking about memory leaks? You’re correct that ARC does not support CGPath objects, so you need to call CGPathRelease() when you’re done with your path. You’ll do that soon!

Open MyScene.m and add this method to draw the pig’s path:

- (void)drawLines {
  //1
  [self enumerateChildNodesWithName:@"line" usingBlock:^(SKNode *node, BOOL *stop) {
    [node removeFromParent];
  }];
 
  //2
  [self enumerateChildNodesWithName:@"pig" usingBlock:^(SKNode *node, BOOL *stop) {
    //3
    SKShapeNode *lineNode = [SKShapeNode node];
    lineNode.name = @"line";
    lineNode.strokeColor = [SKColor grayColor];
    lineNode.lineWidth = 1.0f;
    //4
    CGPathRef path = [(Pig *)node createPathToMove];
    lineNode.path = path;
    CGPathRelease(path);
    [self addChild:lineNode];
  }];
}

Here’s what’s happening:

  1. You’ll redraw the path every frame, so first you remove any old line nodes. To do so, you enumerate over all nodes with the name “line” and remove them from the scene.
  2. After that, you enumerate over all the pigs in your scene using the same method.
  3. For each pig, you create an SKShapeNode and name it “line”. Next you set the stroke color of the shape to gray and the width to 1. You can use any color you want, but I think gray will be visible on the most backgrounds.
  4. You use the method you just added to Pig to create a new path and assign it to lineNode‘s path property. Then you call CGPathRelease to free the path’s memory. If you forgot to do that, you would create a memory leak that would eventually crash your app. Finally, you add lineNode to your scene so that the scene will render it.

At last, to draw the path, add this line at the end of update: in MyScene.m:

[self drawLines];

Build and run, ready your finger and watch as the game draws your path onscreen—and hopefully, your pig follows it!

Drawn_Lines

Continuous Movement

Actually, the pig stops moving after it reaches the last waypoint. For Hogville, it makes more sense and will be more challenging if the pigs keep moving. Otherwise, you could simply draw a path to the pigs straight to their pens and the game would be over!

Let’s correct this. Open Pig.m and add the following code at the end of move::

else {
  newPosition = CGPointMake(currentPosition.x + _velocity.x * [dt doubleValue],
                            currentPosition.y + _velocity.y * [dt doubleValue]);
}

This code simply continues to move the pig with its most recently calculated velocity when it runs out of waypoints. Remember, velocity includes both direction and speed.

Now find this line inside move::

self.position = newPosition;

Move the above line to the end of the method so that after the if-else statements.

Build and run, and watch your pig moving and moving and moving… until you can’t see it anymore. This little piggy went off screen!

Moving_Offscreen

What you need is a method to check if the pig is about to leave the screen and if so, switch its direction.

Also in Pig.m, add this method:

- (CGPoint)checkBoundaries:(CGPoint)point {
  //1
  CGPoint newVelocity = _velocity;
  CGPoint newPosition = point;
 
  //2
  CGPoint bottomLeft = CGPointZero;
  CGPoint topRight = CGPointMake(self.scene.size.width, self.scene.size.height);
 
  //3
  if (newPosition.x <= bottomLeft.x) {
    newPosition.x = bottomLeft.x;
    newVelocity.x = -newVelocity.x;
  } 
  else if (newPosition.x >= topRight.x) {
    newPosition.x = topRight.x;
    newVelocity.x = -newVelocity.x;
  }
 
  if (newPosition.y <= bottomLeft.y) {
    newPosition.y = bottomLeft.y;
    newVelocity.y = -newVelocity.y;
  }
  else if (newPosition.y >= topRight.y) {
    newPosition.y = topRight.y;
    newVelocity.y = -newVelocity.y;
  }
 
  //4
  _velocity = newVelocity;
 
  return newPosition;
}

This looks more complicated than it is, so let’s look more closely:

  1. First, you assign the current velocity and point to local variables.
  2. Here you define the important points on the screen. You can use bottomLeft to check if the pig is moving off screen from the left or bottom sides and topRight to check the top and right sides of the screen. You perform these checks inside the following if statements, one for each side of the screen.
  3. The first if statement checks the x value of newPosition. If this value is zero or less, the pig is leaving the screen from the left side. To avoid this, you set the pig’s x-position to the left boundary—zero—and reverse the x-component of the velocity so the pig starts moving in the opposite direction. The other if statements do the same for the remaining three bounds of the screen.
  4. At the end, you change _velocity to whatever value you calculated and then return newPosition.

To make use of this new method, change the last line in move: from this:

self.position = newPosition;

To this:

self.position = [self checkBoundaries:newPosition];

Build and run your project again and watch the pig bounce off of the screen’s borders. You’ve got yourself a pig pen!

Bouncing_Pig

Rotating the Sprite

Before adding the actual gameplay, there are some minor improvements you should make. First, the pig doesn’t rotate at all, which looks a bit weird. Like most animals, pigs normally walk facing forward rather than backward or to the side!

Rotate the pig so that it faces the direction it’s moving by adding the following line to the end of move: in Pig.m:

self.zRotation = atan2f(_velocity.y, _velocity.x) + M_PI_2;

atan2f returns the angle between the x-axis and the given point. You add M_PI_2 because the pig image you use faces down and the value you receive from atan2f assumes the image faces to the right. Therefore, by adding M_PI_2, you rotate the pig by 90° counterclockwise.

A rotating pig

Note: If this is too much math for you, check out our open source SKTUtils library. The library includes a ton of helper functions that abstracts common math like this; for example it includes a CGPointToAngle method that would be helpful here.

Animating the Sprite

Now that the pig faces the direction it’s moving, it’s time to add a pretty animation. Add the following new instance variable in Pig.m‘s @implementation section:

SKAction *_moveAnimation;

Next, go to initWithImageNamed: and add the following lines directly after the line that creates _wayPoints:

SKTexture *texture1 = [SKTexture textureWithImageNamed:@"pig_1"];
SKTexture *texture2 = [SKTexture textureWithImageNamed:@"pig_2"];
SKTexture *texture3 = [SKTexture textureWithImageNamed:@"pig_3"];
_moveAnimation = [SKAction animateWithTextures:@[texture1, texture2, texture3] timePerFrame:0.1];

This creates three textures, each of which represents a frame of the animation. The last line creates an SKAction that animates the pig’s movement using the three textures and stores the action in _moveAnimation. The game will show each frame for 0.1 seconds, resulting in a nice walking animation for the pig.

The last step is to run the animation. In Pig.m, go to move: and add the following lines at the beginning of the method:

if(![self actionForKey:@"moveAction"]) {
  [self runAction:_moveAnimation withKey:@"moveAction"];
}

First, you check if an animation named “moveAction” is already running and add it if there is no such animation. This ensures that the game adds the animation only once. Without this check, you wouldn’t see the animation because it would start from the beginning every time you called move:.

Build and run, and watch your pig animate around the screen as you direct it.

Move_Animation1

Your Gameplay Strategy

Let’s pause to think about how the gameplay will work and what you need to achieve it.

The basic idea behind Hogville is that pigs will appear at random positions on the left side of the screen and from there, move in random directions. The time between the appearances of each new pig will decrease until it reaches a threshold defined by you.

The player must herd all of these pigs to a trough of food where they can eat and after that, bring them to a barn where they can sleep. If any two pigs collide, the game is over.

Here are the needed steps:

  1. Add sprites for the food trough and barn.
  2. Spawn pigs over time.
  3. Add collision handling.
  4. Add logic that controls when a pig needs food or is ready to sleep.
  5. Handle win/lose conditions.

You will go step by step through this list to finish your game. Don’t panic—this tutorial is here to guide you!

Completing the Scene

To complete the first step, open MyScene.h and add the following property to the interface:

@property (nonatomic, strong) SKSpriteNode *homeNode;

homeNode will display an image of a barn and act as the final goal for your pigs. You will need to access this property later in the Pig class, so you make it a public property instead of a private variable.

Then open MyScene.m and add the following instance variable to the @implementation section:

NSTimeInterval _currentSpawnTime;

You’ll use _currentSpawnTime to track the wait time between pig spawns. After each new pig appears, you’ll reduce this value to speed up the next spawn.

Add this new method in MyScene.m to set up the sprites in your scene:

- (void)loadLevel {
  //1
  SKSpriteNode *bg = [SKSpriteNode spriteNodeWithImageNamed:@"bg_2_grassy"];
  bg.anchorPoint = CGPointZero;
  [self addChild:bg];
 
  //2
  SKSpriteNode *foodNode = [SKSpriteNode spriteNodeWithImageNamed:@"trough_3_full"];
  foodNode.name = @"food";
  foodNode.zPosition = 0;
  foodNode.position = CGPointMake(250.0f, 200.0f);
  // More code later
 
  [self addChild:foodNode];
 
  //3
  self.homeNode = [SKSpriteNode spriteNodeWithImageNamed:@"barn"];
  self.homeNode.name = @"home";
  self.homeNode.zPosition = 0;
  self.homeNode.position = CGPointMake(380.0f, 20.0f);
  [self addChild:self.homeNode];
  _currentSpawnTime = 5.0;
}

Let’s take a tour of this code:

  1. This is simply the code to create the background that you already have in initWithSize:. You’ll remove it from that method soon.
  2. Here you create an SKSpriteNode for the trough and give it the name “food”. You place the node near the center of the screen and add it to the scene.
  3. This creates the barn and positions it in the lower-right corner of your scene. Finally, you set the current spawn time to five seconds. You could make the game easier or harder by increasing or decreasing the initial spawn time.

Now replace all of the code within the if statement of initWithSize: with the following call to loadLevel:

[self loadLevel];

Build and run, and you’ll see the stage is now set:

Background added

But no pigs are anywhere to be found! Let’s bring in some grunts.

Spawning Pigs

To spawn some pigs, add the following new method to MyScene.m:

- (void)spawnAnimal {   
  //1
  _currentSpawnTime -= 0.2;
 
  //2
  if(_currentSpawnTime < 1.0) {
    _currentSpawnTime = 1.0;
  }
 
  //3
  Pig *pig = [[Pig alloc] initWithImageNamed:@"pig_1"];
  pig.position = CGPointMake(20.0f, arc4random() % 300);
  pig.name = @"pig";
  pig.zPosition = 1;
 
  [self addChild:pig];
 
  //4
  [self runAction:
   [SKAction sequence:@[[SKAction waitForDuration:_currentSpawnTime], 
                        [SKAction performSelector:@selector(spawnAnimal) onTarget:self]]]];
}

Here’s a step-by-step breakdown of the code above:

  1. This decreases the time between spawns by 0.2 seconds every time the game spawns a pig.
  2. You ensure the spawn time never falls below one second, because anything faster than that would make the game too difficult, and if it hit zero, things would probably break.
  3. Here you create a pig and add it to the scene like you did before in initWithSize:. Now you set the pig’s position with a fixed x-value of 20 and a random y-value that ranges between zero and 299. Setting the pig’s zPosition to 1 makes sure the pig renders on top of the lines in the scene, which you’ve added with the default zPosition of zero.
  4. This runs an action sequence. A sequence performs a set of actions in order, one at a time. The result is that the performSelector action calls spawnAnimal again after waitForDuration waits for _currentSpawnTime seconds. Because you reduce _currentSpawnTime each time you call this method, you end up calling spawnAnimal with less and less of a delay.

Now add a call to your method in MyScene.m in initWithSize:, immediately after the call to [self loadLevel];:

[self spawnAnimal];

Build and run, and watch the pigs appear faster and faster over time.

Spawning_Pigs

Detecting Collisions

As you can see in the image above, the pigs move through the trough, barn and even other pigs.

pigs_gone_wild

This is a sign your game needs collision detection! Fortunately, you don’t have to create it from scratch. Sprite Kit includes a physics engine that you can use for collision detection.

Note: This tutorial assumes you have some experience using Sprite Kit’s built-in physics engine. If you don’t, work your way through this Sprite Kit tutorial for beginners before proceeding. You’ll also find a good introduction in the book iOS Games by Tutorials, as well as more advanced chapters about the built-in physics engine.

First, add some physics categories to your scene. Open MyScene.h and add the following typedef above the @interface part:

typedef NS_OPTIONS(uint32_t, LDPhysicsCategory) {
  LDPhysicsCategoryAnimal = 1 << 0,
  LDPhysicsCategoryFood = 1 << 1,
};

This creates two categories, one for each type of physics body you will have. You can use these categories to detect collisions between different physics bodies. There are two types of collisions that can occur in this game, those between two pigs and those between a pig and the food trough.

Now open MyScene.m and add a category extension to your scene.

@interface MyScene () <SKPhysicsContactDelegate>
 
@end

This tells the compiler that your scene is implementing the SKPhysicsContactDelegate protocol.

Now find initWithSize: and add this right before the call to loadLevel:

self.physicsWorld.gravity = CGVectorMake(0.0f, 0.0f);
self.physicsWorld.contactDelegate = self;

This configures your physics world. The first line disables gravity in your scene and the second registers your scene as the contact delegate of the physics world. Sprite Kit notifies this delegate whenever two appropriately configured physics bodies begin to touch or stop touching.

To process these collision events, add the following method to MyScene.m:

- (void)didBeginContact:(SKPhysicsContact *)contact {
  //1
  SKNode *firstNode = contact.bodyA.node;
  SKNode *secondNode = contact.bodyB.node;
 
  //2
  uint32_t collision = firstNode.physicsBody.categoryBitMask | secondNode.physicsBody.categoryBitMask;
 
  //3
  if(collision == (LDPhysicsCategoryAnimal | LDPhysicsCategoryAnimal)) {
    NSLog(@"Animal collision detected");
  } else if(collision == (LDPhysicsCategoryAnimal | LDPhysicsCategoryFood)) {
    NSLog(@"Food collision detected.");
  } else {
    NSLog(@"Error: Unknown collision category %d", collision);
  }
}

Let’s break down what’s happening above:

  1. These two lines give you the nodes that just collided. There is no specific order for the nodes, so you have to check the objects yourself if you care which is which.
  2. You perform a bitwise-OR of the categories of the two collided nodes and store it in collision.
  3. Here you figure out what kind of collision occurred by comparing collision with the bit mask for an animal/animal or animal/food collision. For the moment, you simply log the detected collision to the console.

There’s something essential missing, though. Can you guess what it is?

Adding Physics Bodies

To detect collisions, you have to configure a physics body on any sprite you want to engage in collisions. Start by configuring the trough in MyScene.m by replacing the comment //More code later in loadLevel with the following code:

foodNode.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:foodNode.size];
foodNode.physicsBody.categoryBitMask = LDPhysicsCategoryFood;
foodNode.physicsBody.dynamic = NO;

Your physics body needs geometry to define its shape. In this case, a rectangle with the node’s size will suffice. Since this is the food trough, you assign the corresponding category to it. The last line tells the physics engine not to move this object. That is, things can bounce off of the object, but no force will affect the object itself.

Note: In this game, collisions will not affect the motion of any object—you will use the physics engine only for collision detection.

The second node that needs a physics body is the pig. Open Pig.m and import the scene in order to access the category enum:

#import "MyScene.h"

Then add the following to initWithImageNamed:, after the line that initializes _moveAnimation:

self.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:self.size.width / 2.0f];
self.physicsBody.categoryBitMask = LDPhysicsCategoryAnimal;
self.physicsBody.contactTestBitMask = LDPhysicsCategoryAnimal | LDPhysicsCategoryFood;
self.physicsBody.collisionBitMask = kNilOptions;

This is similar to what you did for the trough, except you use a circular physics shape because it fits the pig better than a rectangle. You set the category to indicate the object is an animal and you set contactTestBitMask to indicate that this node should produce contact notifications whenever it touches any other physics body that belongs to any category inside the bit mask.

You also set the object’s collisionBitMask to kNilOptions (which is simply zero) to indicate to the physics engine that forces generated during collisions should not affect the pigs. Basically, you allow pigs to pass right through other physics objects.

Challenge: What kinds of nodes will generate contact notifications when a pig touches them?

Solution Inside: Solution SelectShow>

Build and run, and let a pig collide with the trough and with another pig. Have a look at the console output and check that the correct logs appear.

Collision_Log

Feeding the Pigs

With your collision detection working, you can make it possible to feed your pigs. Inside Pig.m, add the following instance variables to the @implementation section:

BOOL _hungry;
BOOL _eating;

These flags will keep track of the pig’s current state.

To begin with a hungry pig, add the following line inside initWithImageNamed:, just after the lines that set up the physics body:

_hungry = YES;

Still inside Pig.m, change move: by wrapping its current contents inside an if statement that checks to make sure the pig is not eating, as shown below:

if(!_eating) {
  //existing move: code goes here.
}

This prevents your pig from moving while it’s eating. As your mother always said, “Don’t eat and run at the same time!”

eat_run_mom

When a pig is finished eating, you want it to start walking again. Add the following method to Pig.m to send the pig off in a random direction:

- (void)moveRandom {
  //1
  [_wayPoints removeAllObjects];
 
  //2
  int width = (int)CGRectGetWidth(self.scene.frame);
  int height = (int)CGRectGetHeight(self.scene.frame);
  //3
  CGPoint randomPoint = CGPointMake(arc4random() % width,  arc4random() % height);
  [_wayPoints addObject:[NSValue valueWithCGPoint:randomPoint]];
}

This method has three simple steps:

  1. First, you remove all existing waypoints to make the path truly random.
  2. Then, you get the width and height of the scene to have a range for the random numbers.
  3. With these values, you create a random CGPoint inside your scene and add it as a waypoint. This new waypoint is enough to get the pig moving again.

Now add the following method, which you’ll call when the pig gets to the trough:

- (void)eat {
  //1
  if(_hungry) {
    //2
    [self removeActionForKey:@"moveAction"];
    _eating = YES;
    _hungry = NO;
 
    //3
    SKAction *blockAction = [SKAction runBlock:^{
      _eating = NO;
      [self moveRandom];
    }];
 
    [self runAction:[SKAction sequence:@[[SKAction waitForDuration:1.0], blockAction]]];
  }
}

Here’s how you feed a pig:

  1. When the pig is hungry, it will start eating.
  2. You remove the walking animation and set _eating to YES. Your pig will stand still on the trough and eat. Once it finishes eating it is no longer hungry, so you set _hungry to NO.
  3. Like everything in life, eating takes time, so you run a sequence action that waits for a second and then executes blockAction, which sets _eating to NO and calls the method you just added to start the pig walking again. You could decrease the eating time to make the game easier.

You want to call eat from your scene, so add the declaration to the interface in Pig.h:

- (void)eat;

Now open MyScene.m and find didBeginContact:. Replace the NSLog statement that logs “Food collision detected” with the following code:

if([firstNode.name isEqualToString:@"pig"]) {
  [(Pig *)firstNode eat];
} else {
  [(Pig *)secondNode eat];
}

You know this collision is between the pig and the trough, so you simply figure out which node is the pig and call eat on it.

Build and run, and guide the pig to a trough. He will stop to eat, then move off to a random direction.

HappyPig

Can’t you just imagine that pig squealing with delight? ;]

Finishing the Game

Your game is almost complete, but you still need win and lose conditions. This is an “endless game” in that the player keeps going as long as they can until they lose.

However, you need to make it so that pigs who have eaten can be guided to the barn, at which point they will be removed from the scene. A good place for this check is the move: method of Pig.

Open Pig.m and add the following instance variable to the @implementation:

BOOL _removing;

You’ll use this flag to mark pigs while they are in the process of leaving the game. This is because you will make them fade off the screen when they’re leaving the game, and you want to have a flag that prevents you from running this animation twice.

Still in Pig.m, add the following new method:

- (void)checkForHome {
  //1
  if(_hungry || _removing) {
    return;
  }
 
  //2
  SKSpriteNode *homeNode = ((MyScene *)self.scene).homeNode;
  CGRect homeRect = homeNode.frame;
 
  //3
  if(CGRectIntersectsRect(self.frame, homeRect)) {
    _removing = YES;
    [_wayPoints removeAllObjects];
    [self removeAllActions];
    //4
    [self runAction:
     [SKAction sequence:
      @[[SKAction group:
         @[[SKAction fadeAlphaTo:0.0f duration:0.5],
           [SKAction moveTo:homeNode.position duration:0.5]]],
        [SKAction removeFromParent]]]];
  }
}

What’s happening here?

  1. Hungry pigs won’t go to sleep, so you first check if the pig is hungry or is already set to be removed from the game.
  2. Here you get the frame rectangle of the homeNode.
  3. You then check if the pig’s frame overlaps the barn’s. If that’s the case, you set the pig’s _removing flag to YES and clear its waypoints and any running actions.
  4. Here you run another sequence of actions that first runs a group of actions simultaneously, and when those are done it removes the pig from the scene. The group of actions fades out the pig’s sprite while it moves the pig to the center of the barn.

Now call this method by adding the following line to move:, right after the line that sets the pig’s zRotation:

[self checkForHome];

Build and run, and guide those pigs home!

GoHome

Game Over: When Pigs Collide

When two pigs collide, the game will show a “Game Over!” message and then give the player the chance to start a new round.

Open MyScene.m and add a BOOL instance variable to the @implementation section to indicate when the game is over:

BOOL _gameOver;

You don’t want the pigs moving or spawning when the game is over, so wrap the current contents of update: inside an if statement, as follows:

if(!_gameOver) {
  // existing code here
}

Also, add the following code at the beginning of spawnAnimal to make sure new pigs don’t appear after the player has lost:

if(_gameOver) {
  return;
}

Now add this method, which you’ll call when two pigs collide:

- (void)handleAnimalCollision {
  _gameOver = YES;
 
  SKLabelNode *gameOverLabel = [SKLabelNode labelNodeWithFontNamed:@"Thonburi-Bold"];
  gameOverLabel.text = @"Game Over!";
  gameOverLabel.name = @"label";
  gameOverLabel.fontSize = 35.0f;
  gameOverLabel.position = CGPointMake(self.size.width / 2.0f, self.size.height / 2.0f + 20.0f);
  gameOverLabel.zPosition = 5;
 
  SKLabelNode *tapLabel = [SKLabelNode labelNodeWithFontNamed:@"Thonburi-Bold"];
  tapLabel.text = @"Tap to restart.";
  tapLabel.name = @"label";
  tapLabel.fontSize = 25.0f;
  tapLabel.position = CGPointMake(self.size.width / 2.0f, self.size.height / 2.0f - 20.0f);
  tapLabel.zPosition = 5;
  [self addChild:gameOverLabel];
  [self addChild:tapLabel];
}

Here you first set _gameOver to YES, which ends the game. You add two labels centered on the screen telling the user that the game is over and that they can tap the screen to restart. You set each label’s zPosition to five to ensure they are in front of all other nodes.

Now call this new method in didBeganContact:. Replace the NSLog statement that logs “Animal collision detected” with the following line:

[self handleAnimalCollision];

You told the player that they can tap the screen to restart, but right now that isn’t true. Add the following method to MyScene.m to handle restarting:

- (void)restartGame {
  [self enumerateChildNodesWithName:@"line" usingBlock:^(SKNode *node, BOOL *stop) {
    [node removeFromParent];
  }];
 
  [self enumerateChildNodesWithName:@"pig" usingBlock:^(SKNode *node, BOOL *stop) {
    [node removeFromParent];
  }];
 
  [self enumerateChildNodesWithName:@"label" usingBlock:^(SKNode *node, BOOL *stop) {
    [node removeFromParent];
  }];
 
  _currentSpawnTime = 5.0f;
  _gameOver = NO;
  [self spawnAnimal];
}

restartGame removes all lines, pigs and labels from the scene, sets the spawn time back to five seconds, sets _gameOver to NO and begins spawning animals.

You need to call this method if the user taps the screen while the game is over, so add the following lines to the top of touchesBegan:withEvent::

if(_gameOver) {
  [self restartGame];
}

All right, then! Build and run once more, and enjoy the line drawing game you created. See if you’re a natural-born swineherd.

Game_Over

Adding Polish

You may have noticed some places where the game behaves strangely. For example, look at the behavior of spawning pigs—they appear to walk in place! You can fix this easily by calling moveRandom on the pigs when you spawn them.

You’ll need to access this method from your scene, so add the following declaration to the interface in Pig.h:

- (void)moveRandom;

Then, inside MyScene.m, call this method in spawnAnimal, just after the line that adds the pig to the scene:

[pig moveRandom];

Build and run, and now the pigs move right away:

EagerPigs

Now let’s take a closer look at the way your game currently handles touches. Run the app and add a path to a pig, but before the pig arrives at the endpoint, try to change the path by drawing a new one.

As you can see, the pig initially ignores your second command and continues along the first path. Then, after reaching the end point, it moves back to the place where you tried to change direction and follows the second path. Every new path is just tacked onto the old one!

It would be much better if the game replaced the old path with a new path. To make this happen, first open Pig.h and declare a new method:

- (void)clearWayPoints;

Next, open Pig.m and implement this method as follows:

- (void)clearWayPoints {
  [_wayPoints removeAllObjects];
}

This method simply removes all objects from _wayPoints.

Now go to MyScene.m and add the following line to touchesBegan:withEvent:, just above the line that calls addPointToMove::

[(Pig *)node clearWayPoints];

You call clearWayPoints every time the player touches a pig.

Build and run, and now the pigs listen to your every command!

ListeningPigs

Challenge: Add a HUD to the scene showing the player how many pigs they have removed. See if you can accomplish this by yourself before checking the solution below.

Solution Inside: Tips SelectShow>

Where to Go From Here?

Here is the final project with all the code from the above tutorial.

There is still a lot of room for improvement in your game. But that’s OK—it gives you plenty of opportunities to practice your skills, old and new!

  • You could reduce the area searched when removing waypoints, which would make the pig look like it was on top of the line rather than having the line always just out of the pig’s reach.
  • If you move your finger slowly while creating a line, it currently adds far too many points, producing an unpleasant visual experience. You could apply a smoothing algorithm to the points, such as the basic one described here, to reduce redundant points and improve the quality of both the line rendering and the pig’s motion.
  • It’s currently possible for a new pig to spawn with no warning right on top of an existing pig, ending the game immediately. How would you fix this problem?
  • You could add an indicator to warn the player of incoming pigs.
  • You could add different levels that you load from a plist, as well as different animals.
  • You could use the additional trough images provided with the starter project to make the trough appear empty after a certain amount of feeding, and require the player to refill the trough by tapping on it.
  • To tell you the truth, I really don’t know why the game should end when two animals collide. It would be funnier if the animals began running in random directions for a short time after a collision. Because everything must come to an end (this tutorial, too) you could add a counter that ends the game after five collisions.

Whether or not you implement any of these suggestions, I’m sure you have your own excellent ideas, and now you know more of what you need to bring them to the App Store. Make sure to drop me a note when your game is available.

How to Make a Line Drawing Game with Sprite Kit is a post from: Ray Wenderlich

The post How to Make a Line Drawing Game with Sprite Kit appeared first on Ray Wenderlich.

Viewing all 4374 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>