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

Introduction to Unity Scripting

$
0
0
We’re releasing this tutorial as part of our Unity Games by Tutorials launch celebration. The book is still in early release — check it out here. We hope you enjoy this excellent introduction to Unity Scripting in C#!

demo

Much of the power of Unity is in its rich scripting language. You can use it to handle user input, move objects in the scene, detect collisions, make use of Prefab objects, and cast directional rays around the scene to help with your game logic. It might sound intimidating, but Unity exposes well-documented APIs that make these tasks a breeze – even for novice developers!

In this tutorial, you’ll create a top-down shooter that uses Unity scripting to handle the spawning of enemies, player control, firing projectiles and other important aspects of the gameplay.

Note: This tutorial assumes you have experience with C# or similar programming languages. If you need a refresher on Unity’s interface or navigation, check out our detailed Introduction to Unity tutorial.

This tutorial was written for Unity 5.3 or greater. If necessary, you can download the latest version from here.

Getting Started

Download the BlockBuster starter project and unzip it. To open the starter project in Unity, click Open from the Start Up Wizard, then navigate to the project folder. Alternatively, you can open the Main.unity scene located in BlockBuster/Assets/Scenes if you wish.

This is how your scene should look:

StarterScene

Take a look around in the Scene view. There’s a small arena, which will be the battleground for the game, and a camera that will follow the Player as they move around. If your layout is different than in the screenshot, select the top-right drop down menu and change it to 2 by 3.

Screen Shot 2016-03-28 at 2.38.37 PM

What’s a game without a hero? Your first task is to create a game object to represent the Player in the scene.

Creating the Player

In the Hierarchy, click the Create button and select Sphere from the 3D section. Position the Sphere at (0, 0.5, 0) and name it Player:

PlayerSphere

From now on, you’ll reference this Sphere as the Player object.

Unity uses a component system to build its game objects; this means all objects in a scene can be created from any combination of components such as Transform, which describes the position of an object; Mesh Filter, which contains graphic geometry; or any number of Scripts.

The Player object will need to respond to collisions with other objects in the scene.

To make this happen, select the Player in the Hierarchy. From the Inspector tab, press the Add Component button. In the Physics category, select the Rigidbody component; this will put the Player object under the control of Unity’s physics engine.

Change the Rigidody’s values as follows:

  1. Drag: 1
  2. Angular Drag: 0
  3. Constraints: Freeze Position: Y

RigidBodySetup

Creating the Player Movement Script

Now that you have a Player object, it’s time to create the script that will take input from the keyboard and move the Player around.

In the Project Browser, click the Create button and select Folder. Name the new folder Scripts and create a subfolder within named Player. Inside the Player folder, click the Create button and select C# Script. Name your new script PlayerMovement. The sequence looks like this:

FolderStructure

Note: The Player object will contain multiple scripts, each responsible for different parts of its behavior. Keeping all related scripts in a separate folder makes project navigation easier and reduces clutter.

Double-click the PlayerMovement.cs script. On a Mac, this will open MonoDevelop, the IDE packaged with Unity on the Mac; on Windows, it should open Visual Studio. This tutorial assumes you’re using MonoDevelop.

Inside the class declare the following two public variables:

public float acceleration;
public float maxSpeed;

acceleration describes how the Player's speed increases over time. maxSpeed is the “speed limit”. Making a variable public exposes it in the Inspector so you can set its value through Unity’s interface and tweak it as needed.

Just below that, declare the following variables:

private Rigidbody rigidBody;
private KeyCode[] inputKeys;
private Vector3[] directionsForKeys;

Private variables can’t be set through the Inspector; It’s the programmer’s responsibility to initialize them at the appropriate time.

Change Start() to look like the following:

void Start () {
  inputKeys = new KeyCode[] { KeyCode.W, KeyCode.A, KeyCode.S, KeyCode.D };
  directionsForKeys = new Vector3[] { Vector3.forward, Vector3.left, Vector3.back, Vector3.right };
  rigidBody = GetComponent<Rigidbody>();
}

The inputKeys array contains the key codes you’ll use to move the Player. directionsForKeys contains the corresponding directions for each key, e.g. pressing W moves the object forward. As for the last line – do you remember the Rigidbody you added earlier? This is one way you can obtain a reference to that component.

To move the Player, you’ll have to handle input from the keyboard.

Rename Update() to FixedUpdate() and add to it the following code:

// 1
void FixedUpdate () {
  for (int i = 0; i < inputKeys.Length; i++){
    var key = inputKeys[i];
 
    // 2
    if(Input.GetKey(key)) {
      // 3
      Vector3 movement = directionsForKeys[i] * acceleration * Time.deltaTime;
    }
  }
}

There are couple of important things going on here:

  1. FixedUpdate() is frame rate independent and should be used when working with Rigidbodies.
  2. This loop checks to see if any of the input keys were pressed.
  3. Here you get the direction for the pressed key, multiply it by the acceleration and the number of seconds it took to complete the last fixed frame. This produces a vector that you’ll use to move the Player object.

Hmm…what other secret methods like FixedUpdate are available?

When you create a new script in Unity, you’re in fact creating a new MonoBehaviour object. If you come from the iOS world, this object is the equivalent of a UIViewController; that is, you can use this object to respond to events inside of Unity and to access your own data objects.

MonoBehaviours have lots of different methods, and they respond to a variety of events. For instance, if you want to initialize some variables when the MonoBehaviour is instanced, then you can implement Awake(). To run code when the MonoBehaviour is disabled, you can implement OnDisable().

For a full list of events, check out Unity’s documentation.

If you are new to game programming, you might ask yourself why you have to multiply by Time.deltaTime. The general rule is when you perform an action in every fixed time frame, you need to multiply by Time.deltaTime. In this case you want to accelerate in the direction of the key press proportional to the fixed update time.

Add the following method below FixedUpdate():

void movePlayer(Vector3 movement) {
  if(rigidBody.velocity.magnitude * acceleration > maxSpeed) {
    rigidBody.AddForce(movement * -1);
  } else {
    rigidBody.AddForce(movement);
  }
}

The above method applies force to the ridigbody, causing it to move. If the current speed exceeds maxSpeed, the force goes in the opposite direction…it’s kind of like a speed limit.

In FixedUpdate(), before the closing brace of the if-statement, add the following line::

movePlayer(movement);

Perfect! Return to Unity; in the Project Browser, drag the PlayerMovement script onto the Player object inside the Hierarchy. Use the Inspector to set Acceleration to 625 and Max Speed to 4375:

AssignScript

Run the scene and move the Player around with the WASD keys:

The ball is able to move around by player control

That’s a pretty good result for only a few lines of code! :]

However, there is one obvious issue – the player can quickly move out of sight, which makes it a little difficult to fight the bad guys.

Creating the Camera Script

In the Scripts folder, create a new script called CameraRig and attach it to the Main Camera. Need a bit of help to figure out the steps? You can check the hint below for the solution.

Solution Inside SelectShow>

Now create the following variables inside the newly created CameraRig class:

public float moveSpeed;
public GameObject target;
 
private Transform rigTransform;

As you might have guessed, moveSpeed is the speed with which the camera will follow the target – which can be any game object inside the scene.

Inside of Start(), add the following line:

rigTransform = this.transform.parent;

This code gets a reference to the parent Camera object’s transform in the scene hierarchy. Every object in a scene has a Transform, which describes the position, rotation and scale of an object.

CameraRigHierarchy

In the same script, add the method below:

void FixedUpdate () {
  if(target == null){
    return;
  }
 
  rigTransform.position = Vector3.Lerp(rigTransform.position, target.transform.position,
    Time.deltaTime * moveSpeed);
}

The CameraRig movement code is a bit simpler than the one in PlayerMovement. This is because you don’t need a Rigidbody; simply interpolating between the positions of the rigTransform and target is enough.

Vector3.Lerp() takes two points in space and a float in the range of [0, 1], which describes a point along the two endpoints. The left endpoint is 0, and the right endpoint is 1. Passing in 0.5 to Lerp() would return a point exactly between both endpoints.

This moves the rigTransform closer to the target position with a little bit of easing. In short – the camera follows the player.

Return to Unity. Make sure the Main Camera is still selected in the Hierarchy. In the Inspector, set the Move Speed to 8 and the Target to the Player:

RigHookup

Run the game and move around the scene; the camera should follow the target transform smoothly wherever it goes.

The camera follows the player around.

Creating an Enemy

A shooter game without enemies would be easy to beat, but boring. :] Create an enemy cube by clicking GameObject\3D Object\Cube from the top menu. Rename your Cube to Enemy and add a Rigidbody component.

In the Inspector, first set the Cube’s Transform to (0, 0.5,4). In the Constraints section of the Rigidbody component, check the Y checkbox in the Freeze Position category.

The properties that need to be updated are highlighted in the screenshot.

Excellent – now to make your enemies move around in a menacing manner. Create a script named Enemy in the Scripts folder. You should be a pro at this by now, but if not, check the instructions earlier in the tutorial for reference.

Next, add the following public variables inside the class:

public float moveSpeed;
public int health;
public int damage;
public Transform targetTransform;

You probably can figure out what those variables represent without too much difficulty. You used moveSpeed earlier to create the camera rig, and it has the same effect here. health and damage help determine when an enemy should die and how much their death will hurt the Player. Finally, targetTransform references the Player's transform.

Speaking of the Player, you’ll need to create a class to represent all the Player goodness that the enemy wants to destroy.

In the Project Browser, select Player folder and create a new script named Player; this script will react to collisions and keep track of the Player’s health. Double-click the script to edit it.

Add the following public variable to store the Player’s the health:

public int health = 3;

This provides a default value for the health, but it can be modified in the Inspector as well.

To handle collisions, add the following methods:

 
void collidedWithEnemy(Enemy enemy) {
  // Enemy attack code
  if(health <= 0) {
    // Todo 
  }
}
 
void OnCollisionEnter (Collision col) {
    Enemy enemy = col.collider.gameObject.GetComponent<Enemy>();
    collidedWithEnemy(enemy);
}

OnCollisionEnter() triggers when two rigidbodies with colliders touch. The Collision argument contains information about such things as contact points and impact velocities. In this case, you’re only interested in the Enemy component of the colliding object, so you can call collidedWithEnemy() and execute the attack logic – which you’ll add next.

Switch back to Enemy.cs and add the following methods:

void FixedUpdate () {
  if(targetTransform != null) {
    this.transform.position = Vector3.MoveTowards(this.transform.position, targetTransform.transform.position, Time.deltaTime * moveSpeed);
  }
}
 
public void TakeDamage(int damage) {
  health -= damage;
  if(health <= 0) {
    Destroy(this.gameObject);
  }
}
 
public void Attack(Player player) {
  player.health -= this.damage;
  Destroy(this.gameObject);
}

You’re already familiar with FixedUpdate(), the slight difference is you’re using MoveTowards() instead of Lerp(). This is because the Enemy should move at the same speed all the time and not ease-in as it approaches the target. When an enemy is hit with a projectile, TakeDamage() is called; when the Enemy gets to 0 health it will destroy itself. Attack() is similar – it applies damage to the Player and then the enemy destroys itself.

Switch back to Player.cs and in collidedWithEnemy(), replace the Enemy attack code comment with the following:

enemy.Attack(this);

The player will take damage and the enemy will self destruct in the process.

Switch back to Unity. Attach the Enemy script to the Enemy object and in the Inspector, set the following values on the Enemy:

  1. Move Speed: 5
  2. Health: 2
  3. Damage: 1
  4. Target Transform: Player

By now you should be able to do all of this yourself. Try it on your own, and then compare the result with the GIF below:

Solution Inside SelectShow>

In the game, an Enemy collising with the Player constitutes a valid enemy attack. Detecting collisions with Unity’s physics is almost a trivial task.

Finally, attach the Player script to the Player in the Hierarchy.

Run the game, and keep an eye on the console:

Enemy collisions create console errors

When the enemy reaches the Player, it performs the attack successfully and reduces the Player’s health variable to 2. However there is a NullReferenceException thrown in the console, pointing to the Player script:

CollisionException

Aha – the Player can collide not only with enemies, but also other parts of the game world, such as the Arena. These game objects don’t have an Enemy script and therefore GetComponent() returns null.

Open Player.cs. In OnCollisionEnter(), wrap collidedWithEnemy() in an if statements:

if(enemy) {
  collidedWithEnemy(enemy);
}

No more nulls!

Working with Prefabs

Simply running around and avoiding enemies is a pretty one-sided game. Its time to arm the Player for combat.

Click the Create button in the Hierarchy and select 3D Object/Capsule. Name it Projectile and give it the following transform values:

  1. Position: (0, 0, 0)
  2. Rotation: (90, 0, 0)
  3. Scale: (0.075, 0.246, 0.075)

Setting default values

Each time the Player shoots, it will fire an instance of Projectile. To make this happen, you need to create a Prefab. Unlike the objects you already have in the scene, Prefabs are created on-demand by the game logic.

Create a new folder under Assets, called Prefabs. Now drag the Projectile object into this folder. That’s it: you have a Prefab!

CreatingPrefab

Your Prefab will need a bit of script. Create a new script inside the Scripts folder named Projectile and add to it the following class variables:

public float speed;
public int damage;
 
Vector3 shootDirection;

Just like any moving object so far in this tutorial, this one will also have speed and damage variables, since it’s part of the combat logic. The shootDirection vector determines where the Projectile will go.

Put that vector to work by implementing the following methods inside the class:

// 1
void FixedUpdate () {
  this.transform.Translate(shootDirection * speed, Space.World);
}
 
// 2
public void FireProjectile(Ray shootRay) {
  this.shootDirection = shootRay.direction;
  this.transform.position = shootRay.origin;
}
 
// 3
void OnCollisionEnter (Collision col) {
  Enemy enemy = col.collider.gameObject.GetComponent<Enemy>();
  if(enemy) {
    enemy.TakeDamage(damage);
  }
  Destroy(this.gameObject);
}

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

  1. The Projectile moves differently than everything else in this game. It doesn’t have a target, or some force applied to it over time; instead, it moves in a predetermined direction for its entire lifecycle.
  2. Here you set the starting position and direction of the Prefab. This Ray argument seems pretty mysterious, but you’ll soon learn how it’s calculated.
  3. If a projectile collides with an enemy, it calls TakeDamage() and destroys itself.

In the scene Hierarchy, attach the Projectile script to the Projectile GameObject. Set the Speed to 0.2 and Damage to 1, then click the Apply button located near the top of the Inspector. This will apply the changes you just made to all instances of this prefab.

Setting values in the Inspector

Remove the Projectile object from the scene Hierarchy – you don’t need it anymore.

Firing Projectiles

Now that you have a prefab that can move and apply damage, you’re ready to start shooting.

Inside the Player folder, create a new script named PlayerShooting and attach it to the Player game object in the scene. Inside the class, declare the following variables:

public Projectile projectilePrefab;
public LayerMask mask;

The first variable will contain a reference to the Projectile Prefab you created earlier. Every time your Player fires a projectile, you’ll create a new instance from this Prefab. The mask variable is used to filter GameObjects.

Wait, casting Rays? What is this sorcery?

No, there’s no black magic afoot – there are times in your game when you’ll need to know if a collider exists in a particular direction. To do this, Unity can cast an invisible ray from a certain point in a direction you specify. You’ll likely encounter a lot of GameObjects that intersect with the ray, so using a mask lets you filter out any unwanted objects.

Raycasts are incredibly useful and can used for a variety of purposes. They’re commonly used to test if another player has been struck by a projectile, but you can also use them to test if there is any geometry underneath a mouse pointer. To learn more about Raycasts, check out this Unity live training video on the Unity site.

The image below shows a ray cast from a cube to a cone. Since the ray has a iconsphere mask on it, it ignores that GameObect and reports a hit on the cone:

Demonstration of a raycast in action

Now it’s time for you to fire your own rays.

Add the following to PlayerShooting.cs:

void shoot(RaycastHit hit){
  // 1
  var projectile = Instantiate(projectilePrefab).GetComponent<Projectile>();
  // 2
  var pointAboveFloor = hit.point + new Vector3(0, this.transform.position.y, 0);
 
  // 3
  var direction = pointAboveFloor - transform.position;
 
  // 4
  var shootRay = new Ray(this.transform.position, direction);
  Debug.DrawRay(shootRay.origin, shootRay.direction * 100.1f, Color.green, 2);
 
  // 5
  Physics.IgnoreCollision(GetComponent<Collider>(), projectile.GetComponent<Collider>());
 
  // 6
  projectile.FireProjectile(shootRay);
}

Here’s what the above code does:

  1. Instantiates a projectile Prefab and gets its Projectile component so it can be initialized.
  2. This point always looks like (x, 0.5, z). X and Z are coordinates on the floor where the ray cast from the mouse click position hits. This calculation is important, because the projectile has to be parallel to the floor – otherwise you’d be shooting downward, and only amateurs shoot towards the ground. :]
  3. Calculates the direction from the Player GameObject to pointAboveFloor.
  4. Creates a new ray that describes the projectile trajectory by its origin and direction.
  5. This line tells Unity’s physics engine to ignore collisions between the Player collider and Projectile collider. Otherwise OnCollisionEnter() in the Projectile script would be called before it had the chance to fly off.
  6. Finally, it sets the trajectory for the projectile.
Note: Using Debug.DrawRay() when ray casting is invaluable as it can help you visualize how the ray looks and what it hits.

With the firing logic in place, add the following methods to let the player actually pull the trigger:

// 1
void raycastOnMouseClick () {
  RaycastHit hit;
  Ray rayToFloor = Camera.main.ScreenPointToRay(Input.mousePosition);
  Debug.DrawRay(rayToFloor.origin, rayToFloor.direction * 100.1f, Color.red, 2);
 
  if(Physics.Raycast(rayToFloor, out hit, 100.0f, mask, QueryTriggerInteraction.Collide)) {
    shoot(hit);
  }
}
 
// 2
void Update () {
  bool mouseButtonDown = Input.GetMouseButtonDown(0);
  if(mouseButtonDown) {
    raycastOnMouseClick();
  }
}

Taking each numbered comment in turn:

  1. This method casts a ray from the camera to the point where the mouse clicked. Then it checks to see if this ray intersects a game object with the given LayerMask.
  2. On every update, the script checks for a left mouse button press. If it finds one, it calls raycastOnMouseClick().

Return back to Unity and set up the following variables in the Inspector:

  • Projectile Prefab: reference the Projectile from the prefab folder
  • Mask: Floor

ShootingScriptSetup

Note: Unity comes with a limited amount of predefined masks which are also known as layers.

You can create your own by clicking the Layer dropdown of a GameObject and selecting Add Layer:

The layer location

You can also assign masks to GameObjects by selecting a layer from that Layer dropdown:

Selecting a layer

For more information about layers, check out Unity’s Layers documentation.

Run the project and fire at will! The projectiles are fired in the desired direction, but something seems a bit off, doesn’t it?

NoProjectileRotation2

It would be much cooler if the projectiles were pointing in the direction of travel. To fix this, open up the Projectile.cs script and add the following method:

void rotateInShootDirection() {
  Vector3 newRotation = Vector3.RotateTowards(transform.forward, shootDirection, 0.01f, 0.0f);
  transform.rotation = Quaternion.LookRotation(newRotation);
}
Note: RotateTowards is very similar to MoveTowards, but it treats vectors as directions instead of positions. Also, you don’t need to change rotation over time so using a step close to zero will suffice. Transform rotations in Unity are expressed using quaternions, which are beyond the scope of this tutorial. All you need to know for this tutorial is that they have advantages over vectors when making calculations that involve rotations in 3D.

Interested in learning more about quaternions and why they’re useful? Check out this excellent article: How I learned to Stop Worrying and Love Quaternions

At the end of FireProjectile(), add a call to rotateInShootDirection(). FireProjectile() should now look like the following:

public void FireProjectile(Ray shootRay) {
  this.shootDirection = shootRay.direction;
  this.transform.position = shootRay.origin;
  rotateInShootDirection();
}

Run the game once again and fire in a few different directions; this time the projectiles will point in the direction they are shot:

ProjectileRotation

Remove the Debug.DrawRay calls as you won’t need them further.

Generating More Bad Guys

Having only one enemy isn’t terribly challenging. But now that you know about Prefabs, you can spawn all the adversaries you want! :]

To keep the player guessing, you can randomize the health, speed and location of each Enemy.

Creating an empty game object – GameObject\Create Empty. Name it EnemyProducer and add a Box Collider component. Set the values in the Inspector as follows:

  1. Position: (0, 0, 0)
  2. Box Collider:
    • Is Trigger: true
    • Center: (0, 0.5, 0)
    • Size: (29, 1, 29)

Setting box collider values

The collider you attached defines a particular 3D space inside the Arena. To see this, select the Enemy Producer GameObject in the Hierarchy and look inside the Scene view:

The green wire outlines represent a collider.

The green wire outlines represent a collider.

You are about to write a script that picks a random location in this space along the X and Z axis and instantiates an enemy Prefab.

Create a new script called EnemyProducer and attach it to the EnemyProducer GameObject. Inside the newly set up class, add the following instance members:

public bool shouldSpawn;
public Enemy[] enemyPrefabs;
public float[] moveSpeedRange;
public int[] healthRange;
 
private Bounds spawnArea;
private GameObject player;

The first variable enables and disables spawning. The script will pick a random enemy prefab from enemyPrefabs and instantiate it. The next two arrays will specify a minimal and maximal value of speed and health. The spawn area is that green box you saw in the Scene view. Finally, you’ll need a reference to the Player and pass it as target to the bad guys.

Inside the script, define the following methods:

public void SpawnEnemies(bool shouldSpawn) {
  if(shouldSpawn) {
    player = GameObject.FindGameObjectWithTag("Player");
  }
  this.shouldSpawn = shouldSpawn;
}
 
void Start () {
  spawnArea = this.GetComponent<BoxCollider>().bounds;
  SpawnEnemies(shouldSpawn);
  InvokeRepeating("spawnEnemy", 0.5f, 1.0f);
}

SpawnEnemies() gets a reference of a game object with tag Player and determines whether an enemy should spawn.

Start() initializes the spawn area and schedules the invocation of a method 0.5 seconds after the game starts. It will get called repeatedly every second. Besides acting as a setter method, SpawnEnemies() also gets a reference of a game object with tag Player.

The Player game object isn’t tagged yet – you’ll do that now. Select the Player object from the Hierarchy and then in the Inspector tab, choose Player from the Tag dropdown menu:

Screen Shot 2016-05-26 at 12.45.49 PM

Now, you need to write the actual spawning code for an individual enemy.

Open up the Enemy script and add the method below:

public void Initialize(Transform target, float moveSpeed, int health) {
  this.targetTransform = target;
  this.moveSpeed = moveSpeed;
  this.health = health;
}

This simply acts as a setter for creating the object. Next up: the code to spawn your leagues of enemies. Open EnemyProducer.cs and add the following methods:

 
Vector3 randomSpawnPosition() {
  float x = Random.Range(spawnArea.min.x, spawnArea.max.x);
  float z = Random.Range(spawnArea.min.z, spawnArea.max.z);
  float y = 0.5f;
 
  return new Vector3(x, y, z);
}
 
void spawnEnemy() {
  if(shouldSpawn == false || player == null) {
    return;
  }
 
  int index = Random.Range(0, enemyPrefabs.Length);
  var newEnemy = Instantiate(enemyPrefabs[index], randomSpawnPosition(), Quaternion.identity) as Enemy;
  newEnemy.Initialize(player.transform,
      Random.Range(moveSpeedRange[0], moveSpeedRange[1]),
      Random.Range(healthRange[0], healthRange[1]));
}

All that spawnEnemy() does is pick a random enemy prefab, instantiate it at a random position and initialize the Enemy script public variables.

EnemyProducer.cs is almost ready to go!

Return back to Unity. Create an Enemy prefab by dragging the Enemy object from the Hierarchy to the Prefabs folder. Remove the enemy object from the scene – you don’t need it anymore. Next set the Enemy Producer script public variables like so:

  1. Should Spawn: True
  2. Enemy Prefabs:
    • Size: 1
    • Element 0: Reference the enemy prefab
  3. Move Speed Range:
    • Size: 2
    • Element 0: 3
    • Element 1: 8
  4. Health Range:
    • Size: 2
    • Element 0: 2
    • Element 1: 6

EnemyProducerSetup

Run the game and check it out – an endless stream of bad guys!

SpawningEnemies

Okay, those cubes don’t look terribly frightening. Time to spice things up.

Create a 3D Cylinder and Capsule in the scene. Name them Enemy2 and Enemy3 respectively. Just as you did earlier with the first enemy, add a Rigidbody component and the Enemy script to both of them. Select Enemy2 and change its configuration in the Inspector like so:

  1. Scale: (0, 0.5, 0)
  2. Rigidbody:
    • Use Gravity: False
    • Freeze Position: Y
    • Freeze Rotation: X, Y, Z
  3. Enemy Component:
    • Move Speed: 5
    • Health: 2
    • Damage: 1
    • Target Transform: None

Now do the same for Enemy3, but set its Scale to 0.7:

OtherEnemiesPrefabSettings

Next, turn them into Prefabs, just as you did with the original Enemy, and reference all of them in the Enemy Producer. The values in the Inspector should look like this:

  • Enemy Prefabs:
    • Size: 3
    • Element 0: Enemy
    • Element 1: Enemy2
    • Element 2: Enemy3

EnemyPrefabs

Run the game; you’ll see different prefabs spawn inside the Arena.

SpawningDifferentEnemies

It won’t take long before you realize that you’re invincible! As awesome as that is, you need to level the playing field a bit.

Implementing the Game Controller

Now that you have shooting, movement and enemies in place, you’ll implement a basic game controller. It will restart the game once the Player is “dead”. But first, you’ll have to create a mechanism to notify any interested parties that the Player has reached 0 health.

Open the Player script and add the following above the class declaration:

using System;

Inside the class add the following new public event:

public event Action<Player> onPlayerDeath;

An event is a C# language feature that lets you broadcast changes in objects to any listeners. To learn how to use events, check out Unity’s live training on events..

Edit collidedWithEnemy() to look like the code below:

void collidedWithEnemy(Enemy enemy) {
  enemy.Attack(this);
  if(health <= 0) {
    if(onPlayerDeath != null) {
      onPlayerDeath(this);
    }
  }
}

Events provide a neat way for objects to signal state changes between themselves. A game controller would be very interested in the event declared above. In the Scripts folder, create a new script called GameController. Double-click the file to edit it, and add to it the following variables:

public EnemyProducer enemyProducer;
public GameObject playerPrefab;

The script will need to have some control over the enemy production, as it doesn’t make sense to spawn enemies once the Player has perished. Also, restarting the game means you will have to recreate the Player which means…that’s right, it will become a Prefab.

Add the following methods:

void Start () {
  var player = GameObject.FindGameObjectWithTag("Player").GetComponent<Player>();
  player.onPlayerDeath += onPlayerDeath;
}
 
void onPlayerDeath(Player player) {
  enemyProducer.SpawnEnemies(false);
  Destroy(player.gameObject);
 
  Invoke("restartGame", 3);
}

In Start(), the script gets a reference to the Player script and subscribes for the event you created earlier. Once the Player’s health reaches 0 onPlayerDeath() will be called, stoping enemy production, removing the Player object from the scene and invoking restartGame() method after 3 seconds.

Finally, add the implementation of the restart game action:

void restartGame() {
  var enemies = GameObject.FindGameObjectsWithTag("Enemy");
  foreach (var enemy in enemies)
  {
    Destroy(enemy);
  }
 
  var playerObject = Instantiate(playerPrefab, new Vector3(0, 0.5f, 0), Quaternion.identity) as GameObject;
  var cameraRig = Camera.main.GetComponent<CameraRig>();
  cameraRig.target = playerObject;
  enemyProducer.SpawnEnemies(true);
  playerObject.GetComponent<Player>().onPlayerDeath += onPlayerDeath;
}

Here you’re doing a bit of cleanup: you destroy all enemies in the scene and create a new Player object. You then reassign the camera rig’s target to this instance, resume enemy production, and subscribe Game Controller to the player death event.

Now return to Unity, open the Prefabs folder and change the tag of all Enemy prefabs to Enemy. Next, make the Player game object into a Prefab by dragging it into the Prefabs folder. Create an empty game object, name it GameController and attach the script you just created. Hookup all the required references in Inspector.

By now you’re pretty familiar with this pattern. Try placing the references by yourself and then check your results against the illustration hidden below:

Solution Inside SelectShow>

Run the game again to see the game controller in action.

GameCycle

That’s it –  you’ve scripted your first Unity game! Congratulations! :]

Where to Go From Here?

You can download the completed project here.

By now you should have a good understanding of what it takes to bring together a simple action game. Making games is not a simple task; it definitely takes a lot of work and scripting is only one of the elements needed to bring a project to life. To add a good level of polish, you will also need to add animations and UI to your games. For this reason, I highly recommend checking out our other tutorials on these subjects:

If you’re keen to learn how to design, code and publish your own games built in Unity, check out Unity Games by Tutorials.

The book teaches you everything you need to know about building games in Unity, whether you’re a beginner or a more experienced game developer. In the book, you’ll build four great games:

  • A 3D twin-stick shooter
  • A classic 2D platformer
  • A 3D tower-defense game (with virtual reality mode!)
  • A first-person shooter

The book is still in development, but here’s a preview of the first game you’ll build in the book:

I hope you enjoyed this tutorial and found inspiration to take a crack at that game you always wanted to create. Questions or comments? Join the discussion below!

The post Introduction to Unity Scripting appeared first on Ray Wenderlich.


iOS 10 Screencast: Dynamic Xcode 8 Extensions

Unity Tutorial Part 3: Components

$
0
0

Unity-1-feature

Welcome to the third and final part of this Unity mini-series! In this tutorial, you’ll learn all about Components in Unity while you give your hero free reign to blast the landscape with bullets!

This tutorial continues on from the previous tutorial, Unity Games, Part 2: GameObjects.

At this point, you’ve accomplished your goal of having all the main actors ready, but it’s essentially an empty stage. Sure, you might win some avant-garde awards for a poignant play on the lack of free will, but that’s not going to pay the bills.

In this tutorial, you’ll add some interactivity to your game through the use of Components, which are fundamental to Unity game development. If you were to think of a GameObject as a noun, then a component would be a verb: a component does something on behalf of a GameObject.

You’ll learn about several types of components, including the Rigidbody component, the script component, and more. By the end of this tutorial, your marine will be running and gunning!

Getting Started

You can continue on with your completed project from the last tutorial, or if you would prefer to start over fresh, you can download the starter project for this tutorial here.

Currently, the space marine only knows how to stand like a statue. He needs to move around if he’s going to avoid being some alien’s snack.

The first thing you’ll add is a Rigidbody component, which opts the GameObject into the physics engine. By adding it, you’ll enable the GameObject to collide with other GameObjects.

You’ll learn much more about rigidbodies and collisions when you reach the chapter on physics. For now, just take it at face value and move forward.

Adding the Rigidbody Component

In the Hierarchy, select the SpaceMarine GameObject and click the Add Component button in the Inspector.

add-component

You’ll see many different categories. When you know which component you need, you can simply search by name. Otherwise, select one of the categories and pick the best option.

categories

Click the Physics category then select Rigidbody.

rigidbody

You’ll see that a Rigidbody component was attached to the GameObject. Congratulations! You’ve added your first component.

rigidbody-component

Each component has its own set of properties, values and so forth. These properties can be changed in the Inspector or in code. Components also have their own icon to make it easy to determine their type at a glance.

icons

You’ll notice a couple of icons in the right-hand corner of each component, like this:

component-icons

The first icon is the Reference icon. Click it. It’ll open another window with the documentation page for that component. If you installed the documentation, this page is on your computer, and yes, the search bar works.

documentation

You’ll also see a gear icon. Click it and you’ll see the following dialog:

component-options

Here are the most important options:

  • Reset will reset the component to its default values.
  • Move to Front and Move to Back are for adjusting the ordering of sprites in 2D games.
  • Remove Component will delete the component from the GameObject — you can undo this action.
  • Copy Component allows you to copy a component from one GameObject and paste it onto another.
  • Paste Component as New will paste a copied component to a GameObject.
  • Paste Component Values allows you to overwrite the values of the current component from a copied component. Specifically, you can copy the values of a component while your game is being played. When you stop the game, you can paste those values onto another component. This is quite useful because sometimes it’s useful to tweak things as you play to see what works in practice.

By adding the Rigidbody component to the space marine, you’ve made it so that he now can respond to collision events and respond to gravity. However, you don’t want him bouncing off enemies or walls. You definitely want to know about those collisions events, but you don’t want the hero to fly out of control just because he bumped into a column.

In the Rigidbody component, check the IsKinematic checkbox. Also, uncheck the Use Gravity option.

rigidbody-tweaks

The isKinematic property tells the physics engine that you’re manually controlling the marine, rather than letting the physics engine move the marine for you. But the physics engine is still aware of where the marine is and when it collides with something. This is helpful so that when the marine collides with an alien, the physics engine will notify you so you can make something happen — like the space marine getting chomped!

By unchecking the Use Gravity option, the space marine won’t be affected by gravity. Again, you don’t need this because you’ll be moving the marine manually and are only using the Rigidbody for collision detection.

Now comes the fun part: it’s time to make that marine dance! Or move. That’s fine too.

Unity provides some built-in components to make movement easy, and you’ll use them in Chapter 4, “Physics”. But for learning purposes, in this tutorial you’ll do everything manually through the power of scripting.

Introducing Scripting

For the non-programmer, at best scripting is a small barrier to entry. At worse, scripting is the devil that never sleeps.

Coming from an arts background, I can say that scripting is not as hard as you might imagine. Like anything, it just requires some time and patience to get.

In this book, you’ll write scripts in C#, which is a popular language developed by Microsoft that works for both mobile and desktop apps. It’s feature-rich and fully versatile. Unfortunately, I can’t teach it all within the covers of this book.

Thankfully, you can learn C# at raywenderlich.com where there’s a free course that teaches the language from the ground up, for complete beginners.

screencast

It’s designed for non-programmers and taught within the context of Unity. If you’re a beginner to programming, I’d recommend you have a go at this course before you dive deep into scripting.

If you’re an experienced developer in another language, or a particularly brave beginner, feel free to continue along since everything in this book is presented in easy-to-follow steps. But you may encounter some things that will trip you up without a C# primer.

Why not Javascript?

Believe it or not, Unity supports other programming languages such as JavaScript and Boo. Some people consider JavaScript to be “easy” to learn, so you may wonder why we chose C#.

The truth of the matter is that Unity’s implementation of JavaScript IS NOT JavaScript. It’s actually called UnityScript and considerably different than the JavaScript widely used on the web. While you can certainly be productive with it in Unity, there are no viable use cases for it outside of the engine.

C#, on the other hand, is completely compatible with the outside world. Skills that you learn in Unity can easily be applied outside of game development. For instance, if you find yourself disliking game development (or having a hard time making a living) but enjoying the language, you can transition those skills into a C# development job, creating desktop or mobile apps, or even developing backend server apps.

Finally, most professional Unity developers write in C#, so by taking the time to learn it, you’ll be in a better position to take advantage of game development opportunities as they come up.

The way coding works in Unity is that you create scripts. Scripts are simply another type of component that you attach to GameObjects; ones you get to write the code for.

A script derives from a class called MonoBehavior, and you can override several methods to get notified upon certain events:

  • Update(): This event occurs at every single frame. If your game runs at sixty frames per second, Update() will be called sixty times per second. Needless to say, you don’t want to do any heavy processing in this method.
  • OnEnable(): This is called when a GameObject is enabled, and also when an inactive GameObject suddenly reactivates. Typically, you deactivate GameObjects when you don’t need them for a while but will have a need at a later point in time.
  • Start(): This is called once in the script’s lifetime and before Update() is called. It’s a good place to do set up and initialization.
  • Destroy(): This is called right before the object goes to the GameObject afterlife. It’s is a good place to do clean up such as shutting down network connections.

There are many other events that you’ll discover throughout this book. To see a complete listing, check out the MonoBehavior reference on Unity’s site:

https://docs.unity3d.com/ScriptReference/MonoBehaviour.html

Creating Your First Script

It’s showtime!

You have many options for creating a script. You _could_ click the Add Component button and then select New Script.

But I’d like you to try it this way: select the Scripts folder in the Project Browser, and then click the Create button. Select C# Script from the drop-down and name it PlayerController.

You’ll see your new script in the Scripts folder. Drag it from the Scripts folder onto the SpaceMarine GameObject.

You should now see the script listed as one of the components on the Space Marine:

script-added

You’ve added your first custom component! Granted, it doesn’t do anything…yet.

You’ll change that in just a moment, but before you do, you need to learn about the Input Manager.

Managing Input

Inputs are the game’s controls, and if you’re developing a game for a desktop computer, your users will expect the ability to rebind keys. Unity’s Input Manager makes this easy, and is the preferred way for your game to deal with user input.

To get access to the Input Manager, click Edit\Project Settings\Input.

The Inspector will look pretty empty. Click the disclosure triangle next to the word Axes.

input-manager

Once expanded, you’ll see all the pre-configured inputs that are available to you.

input-manager-closed

The first property is Size, and it’s the number of inputs your game uses. You can decrease the number to decrease the amount and increase it if you want more inputs. The current amount of inputs is more than enough for this game.

Click the disclosure triangle next to Horizontal. Here you can configure the input for the horizontal axis, i.e., left or right.

expanded-input-manager

Here’s a breakdown of the key fields:

  • Horizontal is the name Unity gives the input, but you can name it anything you like. This is also the name you reference in code, which you’ll see in a moment.
  • Descriptive Name and Negative Name are the names presented to the user in the Unity game launcher if they want to remap the keys. You can disable the Unity game launch and provide your own key mapping interface if you’d like, so these aren’t required properties.
  • Negative and Positive Buttons are the actual keys being used. Unity allows buttons to have negative or opposite keys. For instance, the right arrow key is positive while the left arrow key is negative. You don’t need to provide a negative for all keys — it wouldn’t make sense to provide a negative key for a use action.
  • Alt Negative and Alt Positive Buttons are alternative keys. In this case, instead of left and right arrow keys, you enable the a and d keys.

The other fields mostly relate to the functionality of analog sticks. For simplicity, this game will only use keyboard input. If you wanted to make the game a bona fide twin-stick shooter, these are the options you’d tweak to create a tight control scheme.

Accessing input from code

Now to actually implement the control scheme. In the Project Browser, double-click the PlayerController script to launch the editor.

Note: If you’ve never set up or used a code editor before, prepare to see MonoDevelop launch. MonoDevelop is part of Unity’s default installation.

However, you can use other editors. If you’re running Unity on Windows and want to run with Visual Studio, check out the chapter “Unity and Visual Studio” in the appendix. If you’re on a Mac, or you’re happy using MonoDevelop and would like to learn more about how it works, read through the chapter “Unity and MonoDevelop” also found in the appendix.

When the code editor opens, you’ll see your first script. Every new script contains empty implementations for Start() and Update().

Look for a blank line below the first { (aka “curly bracket”) — that’s the class definition. Add the following code there:

public float moveSpeed = 50.0f;
Note: If you are new to programming languages, it’s critical that you copy everything exactly as it is written. Any deviation will produce errors. Programming languages are very precise and become grumpy when you use the incorrect case or syntax.

If your script throws an error, carefully review the code to make sure you didn’t miss, forget or mess up any of your code.

moveSpeed is a variable that determines how fast the hero moves around in the arena. You set it to a default value of 50 that you can change later in Unity’s interface.

Now, to write the actual moving code. In Update(), add the following:

Vector3 pos = transform.position;

This bit of code simply gets the current position of the current GameObject — in this case, the space marine, since that is what this script is attached to — and stores it in a variable named pos. In Unity, a Vector3 encapsulates the x, y and z of a GameObject, i.e., the object’s point in 3D space.

Now comes the tricky part. Add the following after the previous line:

pos.x += moveSpeed * Input.GetAxis("Horizontal")
  * Time.deltaTime;
pos.z += moveSpeed * Input.GetAxis("Vertical")
  * Time.deltaTime;

When you move the object, it’ll only be on z- and x-axes because y represents up and down. Because there are two different input sources (Horizontal for left and right, and Vertical for up and down), you need to calculate values for each axis separately.

Input.GetAxis("Horizontal") retrieves a value from the Horizontal component of the Input Manager. The value returned is either 1 or -1, with 1 indicating that a positive button in the Input Manager is being pressed.

According to the settings you saw defined earlier, this is either the right arrow or d keys. Similarly, a value of -1 indicates that a negative button is being pressed, meaning it was either the left arrow or a key.

arrows

Whatever the returned value may be, it’s then multiplied by the moveSpeed and added to the current x position of the GameObject, effectively moving it in the desired direction.

The same thing happens with Input.GetAxis("Vertical"), except it retrieves a value from the vertical component of the Input Manager (indicating the s, down, w or up keys), multiplies this (1 or -1) value by the moveSpeed and adds it to the z position of the GameObject.

So what’s with Time.deltaTime? That value indicates how much time has passed since the last Update(). Remember, Update() is called with every frame, so that time difference must be taken into account or the space marine would move too fast to be seen.

TL;DR: Time.deltaTime ensures movement is in sync with the frame rate.

What do these numbers mean?

By default, Unity considers 1 point to be equal to 1 meter, but you don’t have to follow this logic. For instance, you may be making a game about planets, and that scale would be way too small. For the purposes of simplicity in this book, we have sized our models to follow Unity’s default of one point per meter.

Now that you’ve altered the location, you have to apply it to the space marine. Add the following after the previous line:

transform.position = pos;

This updates the space marine’s position with the new position.

Save the script and switch back to Unity. You may be tempted to run your game, but there’s a slight problem. The camera is not positioned correctly.

In the Hierarchy, select the Main Camera, and in the Inspector, set Position to (X:-9.7, Y:53.6, Z:-56.1) and Rotation to (X:30, Y:0, Z:0). I figure out these values by manually moving the camera around and looking at the Camera preview in the lower right until I was happy with the result.

In the Camera component, set Field of View to 31. This effectively “zooms in” the view a bit.

camera-settings

Now it’s time to give your game a test run. Look for the play controls at the center-top of the editor. Click the play button.

play-controls

Note: You’ll notice the controls give you two more options: pause and pause stepper. Pause allows you to, well, pause your game in motion. The stepper allows you to step through the animation one frame at a time and is especially useful for debugging animation issues.

first-play

Now, look at the Game window and move your character by pressing the arrow keys or WASD keys. Behold…life!

The Game Window

The Game window is where you actually play the game. There are two life-and-death details to keep in mind as you play.

First, when you start playing, the interface becomes darker to give a visual queue that you’re in play mode.

Second, when you play your game you can change anything about it (including changing values on components in the inspector), but when you stop playing the game, all your changes will be lost. Let me repeat that — all your changes will be lost. This is both a blessing and a curse. The blessing is that you have the ability to tweak the game without consequence. The curse is that sometimes you forget you’re in play mode, continue working on your game, then for some reason the game stops. Poof! Buh-bye changes!

Thankfully, you can (and should) make play mode really obvious. Select Edit\Preferences on PC or Unity\Preferences on Mac to bring up a list of options.

Select the Colors section. Here you can change the colors used throughout the editor. Look for Playmode tint. Click the color box next to it, and then give it an unmistakable color — I prefer red. Now play your game to see if it’s conspicuous enough.

Camera movement

There’s only one problem with the space marine’s movement: he will slip off the screen. You want the camera to follow the hero around the arena, so he doesn’t get away from you.

With a little scripting, you can keep the marine in focus.

In the Hierarchy, click the Create button and select Create Empty. Name it CameraMount.

The basic idea is you want CameraMount to represent the position the camera should focus on, and have the camera be relative to this position.

Initially you want the camera to focus where the space marine is, so let’s configure the CameraMount to be at the exact same position as the space marine.

To do this, select the space marine, click on the gear button to the upper right of the Transform component, and select Copy Component:

copy-component

Then select the CameraMount, click on the gear button to the upper right of the Transform component, and select Paste Component Values:

paste-component

Next, drag the Main Camera GameObject into the CameraMount GameObject.

camera-mount

Great! Now as you move the player around, you can move the CameraMount to move with the player, and the camera will track the player. You just need to write a script to do this.

With the CameraMount selected, click the Add Component button in the Inspector and select New Script. Call it CameraMovement and set the language to C Sharp.

Note: When you make a new script by clicking the Add Component button, Unity will create the script in the top level of your assets folder. Get into the habit of moving assets into their respective folders the moment you make or see them.

Drag your new file from the top level of the assets folder into the Scripts folder.

Double-click the CameraMovement script to open it in your code editor. Underneath the class definition, add the following variables:

public GameObject followTarget;
public float moveSpeed;

followTarget is what you want the camera to follow and moveSpeed is the speed at which it should move. By creating these as public variables, Unity will allow you to set these within the Unity editor itself, so you can set the followTarget to the space marine and fiddle with the moveSpeed to your heart’s content, as you’ll see shortly.

Now add the following to Update():

if (followTarget != null) {
  transform.position = Vector3.Lerp(transform.position, followTarget.transform.position, Time.deltaTime * moveSpeed);
}

This code checks to see if there is a target available. If not, the camera doesn’t follow.

Next, Vector3.Lerp() is called to calculate the required position of the CameraMount.

Lerp() takes three parameters: a start position in 3D space, an end position in 3D space, and a value between 0 and 1 that represents a point between the starting and ending positions. Lerp() returns a point in 3D space between the start and end positions that’s determined by the last value.

For example, if the last value is set to 0 then Lerp() will return the start position. If the last value is 1, it returns the end position. If the last value is 0.5, then it returns a point half-way between the start and end positions.

In this case, you will supply the camera mount position as the start and the player position as the end. Finally, you multiply the time since the last frame rate by a speed multiplier to get a suitable value for the last parameter. Effectively, this makes the camera mount position smoothly move to where the player is over time.

Save your code and return to Unity. If you look in the Inspector now, you’ll see two new fields named Follow Target and Move Speed. As mentioned earlier, these were automatically derived by Unity from the public variables you just added to the script. These variables need some values.

With the CameraMount still selected in the Hierarchy, drag SpaceMarine to the Follow Target field and set the Move Speed to 20.

mount-settings

Play your game to see what’s changed.

running-free

The marine is a real superstar now, complete with a personal camera crew. Granted, he can’t turn, and he walks right through objects just like Kitty Pryde, but these are easily solvable issues that you’ll tackle in Chapter 4.

Note: The bigger the move speed, the faster the camera mount will move to the player. The smaller, the more the camera will “lag” behind the player’s position, letting the player “jump ahead” of the camera. Try changing the move speed to a smaller value like 2 and see what happens for yourself!

Adding Gunplay

Unfortunately, the finer parts of diplomacy are lost on the flesh-eating antagonists of this game. It’s best you give the hero some firepower so he can protect himself on his terribly relaxing (terrible?) vacation.

First, you need to create a bullet. In the Hierarchy, click the Create button. From the drop-down, select 3D Object\Sphere to create a sphere in the Scene view.

create-sphere

Give it the name Projectile. With it still selected, check out the Inspector. You’ll notice a bunch of new components.

sphere-components

The three new components are:

  1. The Mesh Filter is a component that contains data about your model’s mesh and passes it to a renderer.
  2. The Mesh Renderer displays the mesh. It contains a lot of information about lighting, such as casting and receiving shadows.
  3. Finally, you’ll notice the sphere contains a Sphere Collider. This component serves as the GameObject’s boundaries. You’ll learn cover colliders in Chapter 4.

Since you want the bullet to participate in Unity’s physics, it needs a rigidbody.

Luckily, you’ve done this before. Click the Add Component button, and select Rigidbody from the Physics category. Make sure to uncheck Use Gravity.

Since the marine will burn through lots of projectiles, drag it from the Hierarchy to the Prefabs folder in the Project Browser. Delete the projectile from the Hierarchy because you don’t need it now that it’s gone on to be a prefab.

At this point, you need to create a script to launch the projectile. In the Project Browser, select the Scripts folder then click the Create button. Choose C# Script and name it Gun. Double-click the file to launch the code editor.

This file needs a few properties underneath the class definition. Add the following:

  public GameObject bulletPrefab;
  public Transform launchPosition;

Again when you create a public variable on a script, Unity exposes these variables in the editor. You will set the bulletPrefab to the bullet prefab you just created, and you will set the launchPosition to the position of the barrel of the Space Marine’s gun.

Next, add the following method:

void fireBullet() {
  // 1
  GameObject bullet = Instantiate(bulletPrefab) as GameObject;
  // 2
  bullet.transform.position = launchPosition.position;
  // 3
  bullet.GetComponent<Rigidbody>().velocity =
    transform.parent.forward * 100;
}

Let’s review this section by section:

  1. Instantiate() is a built-in method that creates a GameObject instance for a particular prefab. In this case, this will create a bullet based on the bullet prefab. Since Instantiate() returns a type of Object, the result must be cast into a GameObject.
  2. The bullet’s position is set to the launcher’s position — you’ll set the launcher as the barrel of the gun in just a moment.
  3. Since the bullet has a rigidbody attached to it, you can specify its velocity to make the bullet move at a constant rate. Direction is determined by the transform of the object to which this script is attached — you’ll soon attach it to the body of the space marine, thus ensuring the bullet travels in same the direction as the marine is facing.

Save and switch back to Unity.

In the Hierarchy, expand the SpaceMarine GameObject and select the BobbleMarine-Body GameObject. In the Inspector, click the Add Component button and near the bottom of the list of components, select Scripts. From the list of scripts, choose Gun.

add-gun-script

You’ll see that your Gun script component has been added to the body of the marine. You’ll also notice there are two new fields: Bullet Prefab and Launch Position. Do those sound familiar?

Click the circle next to Bullet Prefab. Select Projectile from the resulting asset list. Now you have loaded the bullet and just need to set the launch position.

add-prefab

In the Hierarchy, hold the Alt key on PC or Option on Mac and click the disclosure triangle next to the BobbleMarine-Body. You’ll see a large list of child GameObjects. Look for Gun.

expanded-gameobjects

Select that GameObject and click the Create button. Choose Create Empty Child and rename it to Launcher. This new GameObject lives in the center of the gun’s barrel and represents where bullets will spawn from – feel free to move it around in the scene editor if you’d like to tweak the spawn position.

create-empty-child

Keep all the GameObjects expanded and select BobbleMarine-Body so that the Inspector shows all the components. Drag the new Launcher GameObject into the Gun component’s Launch Position field.

Notice that when you add the GameObject to a transform field, Unity finds and references the attached transform.

It’s official! The marine’s gun is locked and loaded. All that’s left is the firing code. Thankfully, it’s pretty easy.

marine

Switch back to your code editor and open Gun.cs.

The gun should fire when the user presses the mouse button and stop when the user releases it.

You could simply check to see if the button is pressed in Update() and call fireBullet() if so, but since Update() is called every frame, that would mean your space marine would shoot up to 60 times per second! Our space marine can shoot fast, but not that fast.

What you need is a slight delay between when you shoot bullets. To do this, add the following to Update():

if (Input.GetMouseButtonDown(0)) {
  if (!IsInvoking("fireBullet")) {
    InvokeRepeating("fireBullet", 0f, 0.1f);
  }
}

First, you check with the Input Manager to see if the left mouse button is held down.

Note: If you wanted to check the right mouse button, you’d pass in 1, and for the middle mouse button, you’d pass in 2.

If the mouse is being held down, you check if fireBullet() is being invoked. If not, you call InvokeRepeating(), which repeatedly calls a method until you call CancelInvoke().

InvokeRepeating() needs a method name, a time to start and the repeat rate. InvokeRepeating() is a method of MonoBehaviour.

After that bit of code, add the following:

if (Input.GetMouseButtonUp(0)) {
  CancelInvoke("fireBullet");
}

This code makes it so the gun stops firing once the user releases the mouse button. Save your work and return to Unity, then play the game.

firing

Hold down the mouse button. You have bullets for days!

Where to Go From Here?

You can download the completed project from this chapter here.

At this point, you should be feeling more comfortable with Unity. You have a walking space marine with a functioning weapon.

As you made these two things happen, you learned about:

  • Components and how they give your GameObjects behavior.
  • Scripting and how to use scripts to create custom behavior.
  • The Input Manager and how to access it from code.
  • The Game window and how to test your games.

This was a thick, heavy tutorial, but you made it to the end. Congratulations! You’ve come a long way.

There’s still a lot more to do. Your poor marine is a sitting duck because he can’t turn around to see what’s sneaking up behind him. At the same time, he has nothing to worry about because there are no aliens hordes attacking him.

If you’ve enjoyed this tutorial series and want to learn how to finish off your game (and build more amazing games in Unity), check out Unity Games by Tutorials.

The book teaches you everything you need to know about building games in Unity, whether you’re a beginner or a more experienced game developer. In the book, you’ll build four great games:

  • A 3D twin-stick shooter
  • A classic 2D platformer
  • A 3D tower-defense game (with virtual reality mode!)
  • A first-person shooter

The book is still in development, but here’s a preview of what’s inside:

If you have questions or comments on this tutorial, please leave them in the discussion below!

The post Unity Tutorial Part 3: Components appeared first on Ray Wenderlich.

Unity Games by Tutorials Giveaway Winners!

$
0
0

Unity-giveaway-feature

Thank you for being a part of our early access launch of Unity Games by Tutorials!

It’s been a really fun (and busy!) week:

Phew. I feel like a space marine who’s been battling bobblehead aliens all week!

buddy-cop-movie

But before we close out this week, we have two important pieces of news to share with you!

Unity Games by Tutorials Giveaway Winners

As part of our Unity celebrations this week, we had a giveaway for three free copies of Unity Games by Tutorials. All you had to do to enter was leave a comment on the announcement post, telling us what you’re most looking forward to in the book.

We had some great comments, ranging from people who want to immortalize the raywenderlich.com team in a game:

comment1

…to people who are looking forward to working from our classic written tutorials:

comment2

…to people who are looking to make learning Unity a family event:
comment3
comment4

They were all great comments — and we drew three random ones to win a free copy of Unity Games by Tutorials.

And now it’s time to announce the three lucky winners… drum roll please!

The three lucky book winners are: ivantro, chaupt, and h-max. Congratulations! :]

Each winner will receive a free PDF copy of Unity Games by Tutorials, or a PDF of their choice if they already have a copy. We will be in touch with the winners directly via email.

Grab the Early Access Discount!

I want to remind you that we still have the early access discount active on Unity Games by Tutorials. Grab your copy today and take advantage of $10 off the normal book price!

Thanks to everyone who entered the Unity Games by Tutorials giveaway, bought the book, or simply read these posts. We really appreciate each and every one of you, and we thank you for your support and encouragement. :]

We can’t wait to see what you create in Unity!

The post Unity Games by Tutorials Giveaway Winners! appeared first on Ray Wenderlich.

iOS Metal Tutorial with Swift Part 5: Switching to MetalKit

$
0
0

MetalKit-feature

Welcome the 5th part of our iOS Metal tutorial series!

In the 1st part, you learned how to get started with Metal and render a simple 2D triangle.

In the 2nd part, you learned how to setup a series of transformations to move from a triangle to a full 3D cube.

In the 3rd part, you learned how to add a texture to the cube.

In the 4th part, you learned how to add light to the scene.

In this 5th part, you’ll learn how to update your app to take advantage of the MetalKit framework. In addition, you’ll also be updating the app to use the SIMD (pronounced “sim-dee”) framework for 3D-related math.

To get the most out of this tutorial, you should have a basic understanding of 3D graphics with either Metal or OpenGL. If this is the first time you’re learning about Metal, you should go back and complete the previous parts of the series.

Without further ado, let’s get into it!

Note: The iOS Simulator can’t access your computer’s GPU, so you’ll need to test your Metal apps on a real 64-bit device. Additionally, the sample code for this tutorial is written in Swift 3.0 using Xcode 8 beta 6.

Getting Started

Start by downloading the starter project for this tutorial. This tutorial starts essentially where you ended with the previous tutorial. Also, make sure to download the additional resources required to complete this tutorial.

Do a quick build and run, just to make sure the starter project works. The result should resemble something like this:

IMG_5921

Getting Started with MetalKit

metal_icon_small

Apple presented MetalKit at WWDC 2015 as a gateway to Metal. The framework gives you utilities that reduce the amount of boilerplate code you have to write in order to get an app running on Metal.

MetalKit provides three major pieces of functionality:

  • Texture loading: Allows you to easily load image assets into Metal textures using a MTKTextureLoader.
  • View management: Reduces the amount of code you need to get Metal to render something on-screen via MTKView.
  • Model I/O integration: Allows you to efficiently load model assets into Metal buffers and manage mesh data using built-in containers.

In this tutorial, you’ll be focusing on texture loading and view management. Model I/O integration will be the subject of a future part in the series.

Switching to SIMD

The SIMD framework provides many common data types and functions that help when dealing with vector and matrix math. When this tutorial series first started, there were issues in Swift that prevented you from using C data types like the ones found in the SIMD and GLKit frameworks.

To overcome those issues, you ended up having to write an Objective-C wrapper to represent a 4×4 matrix and perform various GLKMath operations. It served its purpose, but it didn’t look that nice, because you needed to use a bridging header to bring it to Swift.

163fmh

Thankfully, the problem was fixed in Swift 2 and you can now take full advantage of the SIMD framework, which means you can now remove all the remaining Objective-C code from the project!

Deleting the Objective-C Wrapper

Open the project and take a look at the Matrix4 class. You’re currently using this class to store your matrix data; it also provides helper methods for a couple of matrix math operations.

To get started, select Matrix.h, Matrix.m and HelloMetal-Bridging-Header.h and delete them from your project. This will understandably cause a lot of errors to show up in your project. Fear not, because you’ll be working on getting the app back to a runnable state.

Even though you deleted the bridging header file, you still need to unlink it from your project settings. Go to Build Settings and search for bridging to find the Objective-C Bridging Header setting. Highlight it and press the Delete key to clear the setting:

Screen Shot 2016-06-19 at 7.13.09 PM

At this point, Objective-C is no longer part of this project. Hooray!

Replacing Matrix4 With a SIMD Data Type

Next, you’re going to replace all of your Matrix4 instances with float4x4 instances in your app. A float4x4 is a SIMD data type representing a 4×4 matrix of floats.

Since you’ll replace code throughout your entire project, you can perform a rare case of blind search and replace. Open the find navigator, click the Find text and select Replace from the dropdown. Put in Matrix4 in the search field and float4x4 in the replace field. To enable the Replace All button, you actually have to perform the search first, so press the enter-key with the search field selected:

Screen Shot 2016-06-19 at 7.43.25 PM

Click the Replace All button to replace all occurrences of Matrix4 with float4x4.

Now, since float4x4 is part of the SIMD library, you’ll need to import it everywhere you use float4x4.

At the top of these four files:

  • BufferProvider.swift
  • MetalViewController.swift
  • MySceneViewController.swift
  • Node.swift

Add the following line:

import simd

There are still lots of errors, because float4x4 doesn’t have some of the methods that Matrix4 had; specifically, methods for applying transformations, creating a projection matrix and returning the number of elements in matrix.

Fixing the Remaining Errors

To fix the remaining errors, find the float4x4+Extensions.swift file under the additional resources you downloaded and add it to your project. This file contains an extension to float4x4, which adds a Swift version of those helper methods.

You’ll notice there are a few errors left; don’t panic, you’ll take care of them next.

Fixing Issues in BufferProvider.swift

Open BufferProvider.swift and under nextUniformsBuffer(_:modelViewMatrix:light:) find the following code:

memcpy(bufferPointer, modelViewMatrix.raw(), MemoryLayout<Float>.size*float4x4.numberOfElements())
    memcpy(bufferPointer + MemoryLayout<Float>.size*float4x4.numberOfElements(), projectionMatrix.raw(), MemoryLayout<Float>.size*float4x4.numberOfElements())
    memcpy(bufferPointer + 2*MemoryLayout<Float>.size*float4x4.numberOfElements(), light.raw(), Light.size())

Replace that code with:

// 1
var projectionMatrix = projectionMatrix
var modelViewMatrix = modelViewMatrix
 
// 2
memcpy(bufferPointer, &modelViewMatrix, MemoryLayout<Float>.size*float4x4.numberOfElements())
memcpy(bufferPointer + MemoryLayout<Float>.size*float4x4.numberOfElements(), &projectionMatrix, MemoryLayout<Float>.size*float4x4.numberOfElements())
memcpy(bufferPointer + 2*MemoryLayout<Float>.size*float4x4.numberOfElements(), light.raw(), Light.size())

The two main differences between these blocks of code are:

  1. Matrices are now Swift structs and you need to mark them as mutable when you pass them by reference.
  2. To pass matrix data to memcpy, you simply need to get a pointer to it.

Fixing Issues in Node.swift

Now, open Node.swift and find the following code under render(_ commandQueue: MTLCommandQueue, pipelineState: MTLRenderPipelineState, drawable: CAMetalDrawable, parentModelViewMatrix: float4x4, projectionMatrix: float4x4, clearColor: MTLClearColor?):

let nodeModelMatrix = self.modelMatrix()

Replace that code with:

var nodeModelMatrix = self.modelMatrix()

Under modelMatrix(), find:

let matrix = float4x4()

And replace that code with:

var matrix = float4x4()

Also, remove the question marks and the exclamation mark right below it.

The various helper methods from the float4x4 extension are modifying the struct, therefore the variables must be declared as var instead of let.

Your project should now be error free. Time for another build and run. The result should look exactly the same as before, which is to be expected!

IMG_5924

The main difference is that you’ve now removed all the Objective-C code, and you’re now using the new SIMD data type float4x4 instead of that old Matrix4.

Exploring float4x4+Extensions.swift

Open float4x4+Extensions.swift and take a look at the methods. As you can see, this file still calls math functions from GLKMath under the hood in order to use well-written and well-tested code instead of reinventing the wheel.

68950697

This change might not seem worth it, but it’s important to use SIMD’s float4x4 because it’s a standardized solution for 3D graphics and it will allow easier integration with third-party code.

At the end of the day, it doesn’t really matter how the matrix math is done. You can use GLKit, a 3rd party extension or perhaps Apple will release their own solution down the road someday. The important thing is to have your matrices represented in the same format as the rest of them, out there in the wild! :]

MetalKit Texture Loading

Before you take a look at the functionality that MetalKit offers, open MetalTexture.swift and review how it currently loads the texture in loadTexture(_ device: MTLDevice, commandQ: MTLCommandQueue, flip: Bool):

  1. First, you load the image from a file.
  2. Next, you extract the pixel data from that image into raw bytes.
  3. Then, you ask the MTLDevice to create an empty texture.
  4. Finally, you copy the bytes data into that empty texture.

Lucky for you, MetalKit provides a great API that helps you with loading textures. Your main interaction with it will be through the MTKTextureLoader class.

You might be asking, “How much code can this API save me from writing?” The answer is pretty much everything in MetalTexture!

To switch texture loading to MetalKit, delete MetalTexture.swift from your project. Again, this will cause some errors; you’ll fix these shortly.

Fixing Issues in Cube.swift

First, open Cube.swift and find the following at the top of the file:

import Metal

Then, replace it with this:

import MetalKit

Next, add a parameter to the initializer. Find this line of code:

init(device: MTLDevice, commandQ: MTLCommandQueue) {

Then, replace it with the following:

init(device: MTLDevice, commandQ: MTLCommandQueue, textureLoader :MTKTextureLoader) {

Scroll down to the end of this initializer and find the following code:

let texture = MetalTexture(resourceName: "cube", ext: "png", mipmaped: true)
texture.loadTexture(device, commandQ: commandQ, flip: true)
 
super.init(name: "Cube", vertices: verticesArray, device: device, texture: texture.texture)

Now, replace it with the following:

let path = Bundle.main.path(forResource: "cube", ofType: "png")!
let data = NSData(contentsOfFile: path) as! Data
let texture = try! textureLoader.newTexture(with: data, options: [MTKTextureLoaderOptionSRGB : (false as NSNumber)])
 
super.init(name: "Cube", vertices: verticesArray, device: device, texture: texture)

Here’s a recap of what you’ve just done:

  • You added a MTKTextureLoader parameter to the cube’s initializer.
  • Then, after converting the image into NSData, you used newTextureWithData(_:options:) on the textureLoader to directly load the image into a MTLTexture.

Fixing Issues in MetalViewController.swift

Now you need to pass a texture loader to the cube when you create it.

Open MetalViewController.swift and find the following at the top of the file:

import Metal

Replace it with this:

import MetalKit

Next, add the following new property to MetalViewController:

var textureLoader: MTKTextureLoader! = nil

Finally, initialize this property by adding the line below, right after the point where you create the default device in viewDidLoad():

textureLoader = MTKTextureLoader(device: device)

Fixing Issues in MySceneViewController.swift

Now that you’ve got a default instance of a texture loader, you need to update MySceneViewController.swift to pass it to the cube.

Go to viewDidLoad() in MySceneViewController.swift and find this code:

objectToDraw = Cube(device: device, commandQ:commandQueue)

Now, replace the call to Cube() with this:

objectToDraw = Cube(device: device, commandQ: commandQueue, textureLoader: textureLoader)

Build and run the app, and you should have the exact same result as before. Again, this is the expected result.

IMG_5923

Although the result didn’t change, you’re making positive changes to your app, under the hood. You’re now using MTKTextureLoader from MetalKit to load a texture. Compared to before, where you had to write a whole bunch of code yourself to achieve the same result.

Switching to MTKView

The idea behind MTKView is simple. In iOS, it’s a subclass of UIView, and it allows you to quickly connect a view to the output of a render pass. A MTKView will help you do the following:

  • Configure the CAMetalLayer of the view.
  • Control the timing of the draw calls.
  • Quickly manage a MTLRenderPassDescriptor.
  • Handle view resizes easily.

To use a MTKView, you can either implement a delegate for it or you can subclass it to provide the draw updates for the view. For this tutorial, you’ll go with the first option.

First, you need to change the main view’s class to be a MTKView.

Open Main.storyboard, select the view controller view, then change the class to MTKView in the Identity Inspector:

Screen Shot 2016-06-19 at 10.13.51 PM

An instance of MTKView, by default, will ask for redraws periodically. So you can remove all the code that sets up a CADisplayLink.

Removing Redundant Code From MetalViewController.swift

Open MetalViewController.swift, scroll to the end of viewDidLoad() and remove the following:

timer = CADisplayLink(target: self, selector: #selector(MetalViewController.newFrame(_:)))
timer.add(to: RunLoop.main, forMode: RunLoopMode.defaultRunLoopMode)

After that, you can also remove both newFrame(_:) and gameloop(_:) functions.

Now you need to remove the code that sets up the Metal layer since the MTKView will handle that for you.

Again, in viewDidLoad(), remove the following:

metalLayer = CAMetalLayer()
metalLayer.device = device
metalLayer.pixelFormat = .bgra8Unorm
metalLayer.framebufferOnly = true
view.layer.addSublayer(metalLayer)

Adding the MTKViewDelegate Protocol

To make your MetalViewController responsible for the draw updates, it must conform to MTKViewDelegate.

Add this extension to the end of the file to implement the protocol methods:

// MARK: - MTKViewDelegate
extension MetalViewController: MTKViewDelegate {
 
  // 1
  func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
    projectionMatrix = float4x4.makePerspectiveViewAngle(float4x4.degrees(toRad: 85.0),
      aspectRatio: Float(self.view.bounds.size.width / self.view.bounds.size.height),
      nearZ: 0.01, farZ: 100.0)
  }
 
  // 2
  func draw(in view: MTKView) {
    render(view.currentDrawable)
  }
 
}

Taking a look at the two protocol methods:

  1. mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) runs whenever the MTKView resizes. Here, you reset the projectionMatrix based on the new size.
  2. draw(in view: MTKView) is called when you need to draw a new frame to the view.

Since you’ve changed the way you call render(), you need to update the method. Find the following code:

func render() {
  if let drawable = metalLayer.nextDrawable() {
    self.metalViewControllerDelegate?.renderObjects(drawable)
  }
}

Then, replace it with this:

func render(_ drawable: CAMetalDrawable?) {
  guard let drawable = drawable else { return }
  self.metalViewControllerDelegate?.renderObjects(drawable)
}

Now that you’re responding to size changes using the delegate, you can remove the viewDidLayoutSubviews() function too.

To connect the view delegate to the view controller, add the following code to the MetalViewController class after the list of properties:

@IBOutlet weak var mtkView: MTKView! {
  didSet {
    mtkView.delegate = self
    mtkView.preferredFramesPerSecond = 60
    mtkView.clearColor = MTLClearColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 1.0)
  }
}

This is a property observer and will connect the view’s delegate to the view controller whenever the outlet is set.

To make the actual connection from the storyboard to the outlet, you need to open up Main.storyboard and have MetalViewController.swift open in the assistant editor. Drag from the unconnected outlet (denoted by the empty circle in the gutter) to the view in the storyboard. When you release, they should be connected:

Outlet to MTKView

All that’s left to do is set the device property of the MTKView. To do that, first find these two lines in viewDidLoad():

device = MTLCreateSystemDefaultDevice()
textureLoader = MTKTextureLoader(device: device)

Below those two lines, add the following line:

mtkView.device = device

Finally, remove the following properties from the top of the class:

var metalLayer: CAMetalLayer! = nil
var timer: CADisplayLink! = nil
var lastFrameTimestamp: CFTimeInterval = 0.0

Also, find the following lines of code and remove them:

metalLayer = CAMetalLayer()
metalLayer.device = device
metalLayer.pixelFormat = .bgra8Unorm
metalLayer.framebufferOnly = true
view.layer.addSublayer(metalLayer)

You’re all done, build and run your app!

IMG_5926 2

The cube still looks exactly like before, but now it’s running on MetalKit! Huzzah!

Note: The texture loading seems to work differently for different iOS beta versions. If, for some reason, you can’t see all cube sides then, most likely, the texture is loaded upside down. Try flipping the image vertically to fix this issue.

OK, you might feel a little disappointed because it looks like you ended up right where you started, and that nothing’s changed, right? But don’t fret. Just remember this ancient Chinese proverb: “The journey to Model I/O starts with the single step of porting to MetalKit.” Er, or something like that! :]

Where to Go From Here?

Here is the final example project from this iOS Metal tutorial.

Take a moment to review what you’ve done:

  • You switched to using SIMD’s float4x4.
  • You removed all Objective-C code from the project.
  • You loaded a texture using MTKTextureLoader.
  • You integrated MTKView into the project, removing lots of boilerplate code.
  • And best of all, you didn’t break anything!

You made it! You totally deserve some rest. :]

Feel like you’re up for more Metal? We’re looking to create more Metal tutorials in the future, but in the meantime, be sure to check out some of the great resources below:

Also, tune into the OpenGL ES video tutorials on this site and learn as Ray explains — in depth — how many of these similar concepts work in OpenGL ES.

Thank you for joining me on this tour through Metal. As you can see, it’s a powerful technology that’s relatively easy to implement once you understand how it works.

If you have any questions, comments or Metal discoveries to share, please leave them in the comments below!

The post iOS Metal Tutorial with Swift Part 5: Switching to MetalKit appeared first on Ray Wenderlich.

UICollectionView Tutorial: Getting Started

$
0
0
Create your own grid-based photo browsing app with collection views!

Create your own grid-based photo browsing app with collection views!

Note: This tutorial was updated for Swift and iOS 9 by Bradley Johnson. Original post by Brandon Trebitowski.

The iOS Photos app has a stylish way of displaying photos via a multitude of layouts. You can view your photos in a nice grid view:

iOS Photos App

Or you can view your albums as stacks:

iOS Photos app in album view

You can even transition between the two layouts with a cool pinch gesture. “Wow, I want that in my app!”, you may think.

UICollectionView makes adding your own custom layouts and layout transitions (like those in the Photos app) simple to build.

You’re by no means limited to stacks and grids, because collection views are extremely customizable. You can use them to make circle layouts, cover-flow style layouts, Pulse news style layouts – almost anything you can dream up!

The good news is, if you’re familiar with UITableView, you’ll have no problem picking up collection views – using them is very similar to the table view data source and delegate pattern.

In this tutorial, you’ll get hands-on experience with UICollectionView by creating your own grid-based photo browsing app. By the time you are done with this tutorial, you will know the basics of using collection views and will be ready to start using this amazing technology in your apps!

Anatomy of a UICollectionView

Let’s go right to an example of the finished project. UICollectionView contains several key components, as you can see below:

rw cv 1

Take a look at these components one-by-one:

  1. UICollectionView – the main view in which the content is displayed, similar to a UITableView. Like a table view, a collection view is a UIScrollView subclass.
  2. UICollectionViewCell – similar to a UITableViewCell in a table view. These cells make up the content of the view and are added as subviews to the collection view. Cells can be created programmatically or inside Interface Builder.
  3. Supplementary Views – if you have extra information you need to display that shouldn’t be in the cells but still somewhere within the collection view, you should use supplementary views. These are commonly used for headers or footers.

Note:
Collection views can also have Decoration Views – if you want to add some extra views to enhance the appearance of the collection view (but don’t really contain useful data), you should use decoration views. Background images or other visual embellishments are good examples of decoration views. You won’t be using decoration views in this tutorial as it requires you to write a custom layout class.

In addition to the above visual components, a collection view has a layout object which is responsible for the size, position and several other attributes of the content. Layout objects are subclasses of UICollectionViewLayout. Layouts can be swapped out during runtime and the collection view can even automatically animate switching from one layout to another!

You can subclass UICollectionViewLayout to create your own custom layouts, but Apple has graciously provided developers with a basic “flow-based” layout called UICollectionViewFlowLayout. It lays elements out one after another based on their size, quite like a grid view. You can use this layout class out of the box, or subclass it to get some interesting behavior and visual effects.

You will learn more about these elements in-depth throughout this tutorial and the next. But for now, it’s time for you to get your hands into the mix with a project!

Introducing FlickrSearch

In the rest of this tutorial, you are going to create a cool photo browsing app called FlickrSearch. It will allow you to search for a term on the popular photo sharing site Flickr, and it will download and display any matching photos in a grid view, as you saw in the screenshot earlier.

Ready to get started? Fire up Xcode and go to File\New\Project… and select the iOS\Application\Single View Application template.

rw cv 2

This template will provide you with a simple UIViewController and storyboard to start out with, and nothing more. It’s a good “almost from scratch” point to start from.

Click Next to fill out the information about the application. Set the Product Name to FlickrSearch, the device type to iPad and the language to Swift. Click Next to select the project location, and then click Create.

rw cv 3

The view controller subclass and storyboard that come with the single view application template aren’t any use to you – you’re going to use a UICollectionViewController, which, like a UITableViewController, is a specialized view controller subclass designed to host a collection view. Delete ViewController.swift and remove the empty view controller from Main.storyboard. Now you’ve got a really blank slate :].

Open AppDelegate.swift and add a constant to hold a delightful shade I call Wenderlich Green. Add the following underneath the import UIKit line:

let themeColor = UIColor(red: 0.01, green: 0.41, blue: 0.22, alpha: 1.0)

Use Wenderlich Green as a theme color for the whole app. Update application(_:didFinishLaunchingWithOptions:) to the following:

func application(application: UIApplication!, didFinishLaunchingWithOptions
  launchOptions: [NSObject: AnyObject]?) -> Bool {
 
  window?.tintColor = themeColor
  return true
}

Starting your collection

Open Main.storyboard and drag in a Collection View Controller. Go to Editor\Embed in\Navigation Controller to create a navigation controller and automatically set the collection view controller as the root.

You should now have a layout like this in the storyboard:

rw cv4

Next, select the Navigation Controller you installed and make it the initial view controller in the Attributes Inspector:

rw cv 5

Next, focusing on the collection view controller, select the UICollectionView inside and change the background color to White Color:

rw cv10

Note: Wondering what the Scroll Direction property does? This property is specific to UICollectionViewFlowLayout, and defaults to Vertical. A vertical flow layout means the layout class will place items from left to right across the top of the view until it reaches the view’s right edge, at which point it moves down to the next line. If there are too many elements to fit in the view at once, the user will be able to scroll vertically to see more.

Conversely, a horizontal flow layout places items from top to bottom across the left edge of the view until it reaches the bottom edge. Users would scroll horizontally to see items that don’t fit on the screen. In this tutorial, you’ll stick with the more common Vertical collection view.

Select the single cell in the collection view and set the Reuse Identifier to FlickrCell using the attributes inspector. This should also be familar from table views – the data source will use this identifier to dequeue or create new cells.

Drag in a text field to the center of the navigation bar above the collection view. This will be where the user enters their search text. Set the Placeholder Text of the search field to Search and the Return Key to Search in the attributes inspector:

rw cv6

Next, control-drag from the text field to the collection view controller and choose the delegate outlet:

rw cv7

UICollectionViewController does a lot, but you generally need to make a subclass. Do this now. Go to File\New\File…, choose iOS\Source\Cocoa Touch Class template and click Next. Name the new class FlickrPhotosViewController, making it a subclass of UICollectionViewController. There’s a lot of code added by the template, but the best way to understand what this class does is to start from scratch. Open FlickrPhotosViewController.swift replace the contents of the file with the below code:

import UIKit
 
final class FlickrPhotosViewController: UICollectionViewController {
 
  // MARK: - Properties
  private let reuseIdentifier = "FlickrCell"
}

Next, add a constant for section insets (which you’ll use later) right below the reuseIdentifier constant:

private let sectionInsets = UIEdgeInsets(top: 50.0, left: 20.0, bottom: 50.0, right: 20.0)

You’ll be filling in the rest of the gaps as you progress through the tutorial.

Go back to Main.storyboard, select the collection view controller and in the identity inspector, set the Class to FlickrPhotosViewController to match your new class:

rw cv 9

Fetching Flickr Photos

You first task for this section is to say the section title ten times fast. OK, just kidding.

Flickr is a wonderful image sharing service that has a publicly accessible and dead- simple API for developers to use. With the API you can search for photos, add photos, comment on photos, and much more.

To use the Flickr API, you need an API key. If you are doing a real project, I recommend you sign up for one here: http://www.flickr.com/services/api/keys/apply/.

However, for test projects like this, Flickr has a sample key they rotate out every so often that you can use without having to sign up. Simply perform any search at: http://www.flickr.com/services/api/explore/?method=flickr.photos.search and copy the API key out of the URL at the bottom – it follows the “&api_key=” all the way to the next “&”. Paste it somewhere in a text editor for later use.

For example, if the URL is:

http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=6593783 efea8e7f6dfc6b70bc03d2afb&format=rest&api_sig=f24f4e98063a9b8ecc8b522b238 d5e2f

Then the API key is: 6593783efea8e7f6dfc6b70bc03d2afb

Note: If you use the sample API key, note that it is changed nearly every day. So if you’re doing this tutorial over the course of several days, you might find that you have to get a new API key often. For this reason it might be easier to get an API key of your own from Flickr if you think you’re going to spend several days on this project.

Since this tutorial is about UICollectionView and not the Flickr API, a set of classes has been created for you that abstracts the Flickr search code. You can download them here.

Unzip and drag the files into your project, making sure that the box Copy items into destination group’s folder (if needed) is checked, and click Finish.

The file contains two classes and a struct:

  • FlickrSearchResults: A struct which wraps up a search term and the results found for that search.
  • FlickrPhoto: Data about a photo retrieved from Flickr – its thumbnail, image, and metadata information such as its ID. There are also some methods to build Flickr URLs and some size calculations. FlickrSearchResults contains an array of these objects.
  • Flickr: Provides a simple block-based API to perform a search and return a FlickrSearchResult

Feel free to take a look at the code – it’s pretty simple and might inspire you to make use of Flickr in your own projects!

Before you can search Flickr, you need to enter an API key. Open Flickr.swift and replace the value of apiKey with the API key you obtained earlier. It should look something like this:

let apiKey = "hh7ef5ce0a54b6f5b8fbc36865eb5b32"

When you’re ready to go, move on to the next section – it’s time to do a little prep work before hooking into Flickr.

Preparing Data Structures

You’re going to build this project so that after each time you perform a search, it displays a new section in the collection view with the results (rather than simply replacing the previous section). In other words, if you search for “ninjas” and then “pirates”, there will be a section of ninjas and a section of pirates in the collection view. Talk about a recipe for disaster!

To accomplish this, you’re going to need a data structure so you can keep the data for each section separate. An array of FlickrSearchResults will do the trick nicely.

Open FlickrPhotosViewController.swift and the following properties below the sectionInsets constant:

private var searches = [FlickrSearchResults]()
private let flickr = Flickr()

searches is an array that will keep track of all the searches made in the app, and flickr is a reference to the object that will do the searching for you. Next, add the following private extension to the bottom of the file:

// MARK: - Private
private extension FlickrPhotosViewController {
  func photoForIndexPath(indexPath: NSIndexPath) -> FlickrPhoto {
    return searches[indexPath.section].searchResults[indexPath.row]
  }
}

photoForIndexPath is a convenience method that will get a specific photo related to an index path in your collection view. You’re going to access a photo for a specific index path a lot, and you don’t want to repeat code.

Getting Good Results

You are now ready to get your Flickr search on! You want to trigger a search when the user hits Search after typing in a query. You already connected the text field’s delegate outlet to your collection view controller, now you can do something about it.

Open FlickrPhotosViewController.swift and add an extension to hold the text field delegate methods:

extension FlickrPhotosViewController : UITextFieldDelegate {
  func textFieldShouldReturn(textField: UITextField) -> Bool {
    // 1
    let activityIndicator = UIActivityIndicatorView(activityIndicatorStyle: .Gray)
    textField.addSubview(activityIndicator)
    activityIndicator.frame = textField.bounds
    activityIndicator.startAnimating()
 
    flickr.searchFlickrForTerm(textField.text!) {
      results, error in
 
 
      activityIndicator.removeFromSuperview()
 
 
      if let error = error {
        // 2
        print("Error searching : \(error)")
        return
      }
 
      if let results = results {
        // 3
        print("Found \(results.searchResults.count) matching \(results.searchTerm)")
        self.searches.insert(results, atIndex: 0)
 
        // 4
        self.collectionView?.reloadData()
      }
    }
 
    textField.text = nil
    textField.resignFirstResponder()
    return true
  }
}

Here is an explanation of the code:

  1. After adding an activity view, use the Flickr wrapper class I provided to search Flickr for photos that match the given search term asynchronously. When the search completes, the completion block will be called with a the result set of FlickrPhoto objects, and an error (if there was one).
  2. Log any errors to the console. Obviously, in a production application you would want to display these errors to the user.
  3. The results get logged and added to the front of the searches array
  4. At this stage, you have new data and need to refresh the UI. You’re using the reloadData() method, which works just like it does in a table view.

Go ahead and run your app. Perform a search in the text box, and you should see a log message in the console indicating the number of search results, similar to this:

Found 20 matching bananas

Note that the results are limited to 20 by the Flickr class to keep load times down.

Unfortunately, you don’t see any photos in your collection view! Just like a table view, a collection view doesn’t do much unless you implement the relevant data source and delegate methods.

Feeding the UICollectionView

As you probably already know, when you use a table view you have to set a data source and a delegate in order to provide the data to display and handle events (like row selection).

Similarly, when you use a collection view you have to set a data source and a delegate as well. Their roles are the following:

  • The data source (UICollectionViewDataSource) returns information about the number of items in the collection view and their views.
  • The delegate (UICollectionViewDelegate) is notified when events happen such as cells being selected, highlighted, or removed.

UICollectionViewFlowLayout also has a delegate protocol – UICollectionViewDelegateFlowLayout. It allows you to tweak the behaviour of the layout, configuring things like the cell spacing, scroll direction, and more.

In this section, you’re going to implement the required UICollectionViewDataSource and UICollectionViewDelegateFlowLayout methods on your view controller, so you are all set up to work with your collection view. The UICollectionViewDelegate methods aren’t needed for this part, but you’ll be using them in part 2.

UICollectionViewDataSource

Open FlickrPhotosViewController.swift, add the following extension to the file for the UICollectionViewDataSource protocol:

// MARK: - UICollectionViewDataSource
extension FlickrPhotosViewController {
  //1
  override func numberOfSectionsInCollectionView(collectionView: UICollectionView) -> Int {
    return searches.count
  }
 
  //2
  override func collectionView(collectionView: UICollectionView,
                               numberOfItemsInSection section: Int) -> Int {
    return searches[section].searchResults.count
  }
 
  //3
  override func collectionView(collectionView: UICollectionView,
                               cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
    let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier, forIndexPath: indexPath)
    cell.backgroundColor = .blackColor()
    // Configure the cell
    return cell
  }
}

These methods are pretty straightforward:

  1. There’s one search per section, so the number of sections is the count of the searches array.
  2. The number of items in a section is the count of the searchResults array from the relevant FlickrSearch object.
  3. This is a placeholder method just to return a blank cell – you’ll be populating it later. Note that collection views require you to have registered a cell with a reuse identifier, or a runtime error will occur.

Build and run again, and perform a search. You should see 20 new cells, albeit looking a little dull at the moment:

rw cv11

UICollectionViewFlowLayoutDelegate

As I mentioned early in the section, every collection view has an associated layout. You’re using the pre-made flow layout for this project, since it’s nice and easy to use and gives you the grid-view style you’re looking for.

Still in FlickrPhotosViewController.swift, add the following constant below your flickr constant:

private let itemsPerRow: CGFloat = 3

Next, add the UICollectionViewDelegateFlowLayout extension to allow the view controller to conform to the flow layout delegate protocol:

extension FlickrPhotosViewController : UICollectionViewDelegateFlowLayout {
  //1
  func collectionView(collectionView: UICollectionView,
                      layout collectionViewLayout: UICollectionViewLayout,
                             sizeForItemAtIndexPath indexPath: NSIndexPath) -> CGSize {
    //2
    let paddingSpace = sectionInsets.left * (itemsPerRow + 1)
    let availableWidth = view.frame.width - paddingSpace
    let widthPerItem = availableWidth / itemsPerRow
 
    return CGSize(width: widthPerItem, height: widthPerItem)
  }
 
  //3
  func collectionView(collectionView: UICollectionView,
                      layout collectionViewLayout: UICollectionViewLayout,
                             insetForSectionAtIndex section: Int) -> UIEdgeInsets {
    return sectionInsets
  }
 
  // 4
  func collectionView(collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAtIndex section: Int) -> CGFloat {
    return sectionInsets.left
  }
}
  1. collectionView(_:layout:sizeForItemAtIndexPath:) is responsible for telling the layout the size of a given cell
  2. Here, you work out the total amount of space taken up by padding. There will be n + 1 evenly sized spaces, where n is the number of items in the row. The space size can be taken from the left section inset. Subtracting this from the view’s width and dividing by the number of items in a row gives you the width for each item. You then return the size as a square
  3. collectionView(_:layout:insetForSectionAtIndex:) returns the spacing between the cells, headers, and footers. A constant is used to store the value.
  4. This method controls the spacing between each line in the layout. You want this to match the padding at the left and right.

Build and run again, and perform a search. Behold! Black squares that are bigger than before!

rw cv12

With this infrastructure in place, you are now ready to actually display some photos on screen!

Creating custom UICollectionViewCells

One of the great things about UICollectionView is that, like table views, it is easy to set up collection views visually in the Storyboard editor. You can drag and drop collection views into your view controller, and design the layout for your cells right from within the Storyboard editor! Let’s see how it works.

Open Main.storyboard and select the collection view. Give yourself a bit of room to work by setting the cell size to 200×200 in the size inspector:

rw cv13

Note: Setting this size doesn’t affect the cells in your app, because you’ve implemented the delegate method to give a size for each cell, which overwrites anything set in the storyboard.

Drag an image view onto the cell and stretch it so it perfectly takes up the entire cell. With the image view still selected, open the pin menu, uncheck Constrain to margins and add constraints of 0 points all the way round:

rw cv14

With the image view still selected, change its Mode to Aspect Fit, so the images are not cropped or stretched in any way:

rw cv 18

UICollectionViewCell doesn’t allow for much customization beyond changing the background color. You will almost always want to create your own subclass, to allow you to easily access any content subviews you have added.

Choose File\New\File… and choose iOS\Source\Cocoa Touch Class template and click Next. Name the new class FlickrPhotoCell, making it a subclass of UICollectionViewCell.

Open Main.storyboard and select the cell. In the identity inspector, set the cell’s class to FlickrPhotoCell:

rw cv15

Open the Assistant editor, making sure it is displaying FlickrPhotoCell.swift and control-drag from the image view to the class to add a new outlet, naming it imageView:

Connect Image Outlet

Now you have a custom cell class with an image view. It’s time to put a photo on it! Open FlickrPhotosViewController.swift and replace collectionView(_:cellForItemAtIndexPath:) with the following:

override func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
  //1
  let cell = collectionView.dequeueReusableCellWithReuseIdentifier(reuseIdentifier,
               forIndexPath: indexPath) as! FlickrPhotoCell
  //2
  let flickrPhoto = photoForIndexPath(indexPath)
  //3
  cell.imageView.image = flickrPhoto.thumbnail
 
  return cell
}

This is a little different from the placeholder method you defined earlier.

  1. The cell coming back is now a FlickrPhotoCell
  2. You need to get the FlickrPhoto representing the photo to display, using the convenience method from earlier
  3. You populate the image view with the thumbnail

Build and run, perform a search and you’ll finally see the pictures you’ve been searching for!

rw cv19

Yes! Success!

Note: If your view doesn’t look like this or the photos are acting weird, it likely means your Auto Layout settings aren’t correct. If you get stuck, try comparing your settings to the solution for this project.

At this point, you’ve now got a complete working (and quite cool) example of UICollectionView – give yourself a pat on the back! Here’s a link to the finished project: FlickrSearch

Where To Go From Here?

But there’s more! Stay tuned for part 2 of this tutorial, where you will learn:

  • How to add custom headers to collection views
  • How to easily move cells by dragging them
  • How to implement single cell selection to bring up a detail view
  • How to implement multi-cell selection too!

In the meantime, if you have any questions or comments on what you’ve learned so far, please join the forum discussion below!

The post UICollectionView Tutorial: Getting Started appeared first on Ray Wenderlich.

UICollectionView Tutorial: Reusable Views, Selection, and Reordering

$
0
0
Create your own grid-based photo browsing app with collection views!

Create your own grid-based photo browsing app with collection views!

Note: This UICollectionView tutorial was updated for Swift and iOS 9 by Bradley Johnson. Original post by Brandon Trebitowski.

In the first part of this tutorial, you saw how to use a UICollectionView to display a grid of photos.

In this second and final part of the tutorial, you will continue the journey and learn how to interact with a collection view as well as customize it a bit further with headers. You’ll continue working where you left off in part 1 so open your project or FlickrSearch from part 1 and start from there (though you’ll still need to get a new API key as shown in part 1. If it’s been a while since you did part 1, you will also need a new API key).

Adding a header

The app has one section per set of search results. It would be nice to add a header before each set of search results, to give the user a bit more context about the photos.

You will create this header using a class called UICollectionReusableView. This class is kind of like a collection view cell (in fact, cells inherit from this class), but used for other things like headers or footers.

This view can be built inside of your storyboard and connected to its own class. Choose File\New\File…, choose the iOS\Source\Cocoa Touch Class template and click Next. Name the class FlickrPhotoHeaderView making it a subclass of UICollectionReusableView. Click Next and then Create to save the file.

Open MainStoryboard.storyboard and click on the collection view inside of the Scene Inspector on the left (you might need to drill down a couple of levels from the main view first). Open the Attributes Inspector and check the Section Header box under Accessories:

rw cv2 2

If you look at the scene inspector on the left, a “Collection Reusable View” has automatically been added under the Collection View. Click on the Collection Reusable View to select it, and you can begin adding subviews.

To give you a little more space to work with, click the white handle at the bottom of the view and drag it down, making the view 90 pixels tall. (Or, you can set the size for the view explicitly via the Size Inspector.)

Drag a label into the header view and center it using the guides. Change its Font to System 32.0, then go to the alignment menu and pin it to the horizontal and vertical centers of the container, and update the frame:

rw cv2 4

Select the header view itself, open the identity inspector and set the Class to FlickrPhotoHeaderView:

rw cv2 5

Open the Attributes Inspector and set the Background to Group Table View Background Color, which is a nice light grey, and set the Identifier to FlickrPhotoHeaderView. This is the identifier that will be used when dequeuing this view.

Open the Assistant editor, making sure FlickrPhotoHeaderView.swift is open, and control-drag from the label to the class to make a new outlet, naming it label:

class FlickrPhotoHeaderView: UICollectionReusableView {
  @IBOutlet weak var label: UILabel!
}

If you build and run the app at this point, you still won’t see a header (even if it is just a blank one with the word “Label”). There’s another datasource method you need to implement. Open FlickrPhotosViewController.swift and add the following method to the UICollectionViewDataSource extension below the collectionView(_:numberOfItemsInSection:) method:

override func collectionView(collectionView: UICollectionView,
                             viewForSupplementaryElementOfKind kind: String,
                             atIndexPath indexPath: NSIndexPath) -> UICollectionReusableView {
  //1
  switch kind {
  //2
  case UICollectionElementKindSectionHeader:
    //3
    let headerView = collectionView.dequeueReusableSupplementaryViewOfKind(kind,
                       withReuseIdentifier: "FlickrPhotoHeaderView",
                       forIndexPath: indexPath) as! FlickrPhotoHeaderView
    headerView.label.text = searches[indexPath.section].searchTerm
    return headerView
  default:
    //4
    assert(false, "Unexpected element kind")
  }
}

This method is similar to cellForItemAtIndexPath, but for supplementary views. Here’s a step-by-step explanation of the code:

  1. The kind parameter is supplied by the layout object and indicates which sort of supplementary view is being asked for.
  2. UICollectionElementKindSectionHeader is a supplementary view kind belonging to the flow layout. By checking that box in the storyboard to add a section header, you told the flow layout that it needs to start asking for these views. There is also a UICollectionElementKindSectionFooter, which you’re not currently using. If you don’t use the flow layout, you don’t get header and footer views for free like this.
  3. The header view is dequeued using the identifier added in the storyboard. This works just like cell dequeuing. The label’s text is then set to the relevant search term.
  4. An assert is placed here to make it clear to other developers (including future you!) that you’re not expecting to be asked for anything other than a header view.

This is a good spot to build and run. You will see that your UI is mostly complete. If you do multiple searches, you’ll get nice section headers dividing up your results. As a bonus, try rotating the device – notice how the layout, including the headers, adapts perfectly, without any extra work required :]

rw cv2 7

Mmmmmmmmmmm Pizza

Interacting With Cells

In this final section of the tutorial you will learn some ways to interact with collection view cells. You’ll take three different approaches. The first will display a larger version of the image. The second will demonstrate how to support multiple selection in order to share images. And lastly, you will learn how to allow the user to rearrange the images by dragging them.

Single selection

Collection views can animate changes to their layout. Your first task is to show a larger version of a photo when it is tapped.

First, you need to add a property to keep track of the tapped cell. Open FlickrPhotosViewController.swift and add the following property definition below the itemsPerRow property:

//1
var largePhotoIndexPath: NSIndexPath? {
  didSet {
    //2
    var indexPaths = [NSIndexPath]()
    if let largePhotoIndexPath = largePhotoIndexPath {
      indexPaths.append(largePhotoIndexPath)
    }
    if let oldValue = oldValue {
      indexPaths.append(oldValue)
    }
    //3
    collectionView?.performBatchUpdates({
      self.collectionView?.reloadItemsAtIndexPaths(indexPaths)
    }) { completed in
      //4
      if let largePhotoIndexPath = self.largePhotoIndexPath {
        self.collectionView?.scrollToItemAtIndexPath(
          largePhotoIndexPath,
          atScrollPosition: .CenteredVertically,
          animated: true)
      }
    }
  }
}

Here’s the step by step breakdown:

  1. largePhotoIndexPath is an optional that will hold the index path of the tapped photo, if there is one.
  2. Whenever this property gets updated, the collection view needs to be updated. a didSet property observer is the safest place to manage this. There may be two cells that need reloading, if the user has tapped one cell then another, or just one if the user has tapped the first cell, then tapped it again to shrink.
  3. performBatchUpdates will animate any changes to the collection view performed inside the block. You want it to reload the affected cells.
  4. Once the animated update has finished, it’s a nice touch to scroll the enlarged cell to the middle of the screen

What enlarged cell?, I hear you asking. You’ll get to that in a minute!

Tapping a cell will make the collection view select it. You want to know a cell has been tapped, so you can set the largeIndexPath property, but you don’t actually want to select it, because that might get confusing later on when you’re doing multiple selection. UICollectionViewDelegate has you covered. The collection view asks its delegate if it’s OK to select a specific cell. Still in FlickrPhotosViewController.swift, add the following extension below your UICollectionViewDataSource extension:

// MARK: - UICollectionViewDelegate
extension FlickrPhotosViewController {
 
  override func collectionView(collectionView: UICollectionView,
                               shouldSelectItemAtIndexPath indexPath: NSIndexPath) -> Bool {
 
    largePhotoIndexPath = largePhotoIndexPath == indexPath ? nil : indexPath
    return false
  }
}

This method is pretty simple. If the tapped cell is already the large photo, set the largePhotoIndexPath property to nil, otherwise set it to the index path the user just tapped. This will then call the property observer you added earlier and cause the collection view to reload the affected cell(s).

To make the tapped cell appear larger, you need to modify the sizeForItemAtIndexPath flow layout delegate method. Add the following code to the beginning of the method:

// New code
if indexPath == largePhotoIndexPath {
  let flickrPhoto = photoForIndexPath(indexPath)
  var size = collectionView.bounds.size
  size.height -= topLayoutGuide.length
  size.height -= (sectionInsets.top + sectionInsets.right)
  size.width -= (sectionInsets.left + sectionInsets.right)
  return flickrPhoto.sizeToFillWidthOfSize(size)
}

This calculates the size of the cell to fill as much of the collection view as possible whilst maintaining its aspect ratio.

There’s not much point in making a bigger cell unless you have a larger photo to show in it.

Open Main.storyboard and drag an activity indicator into the image view in the collection view cell. In the Attributes inspector, set the Style to Large White and check the Hides When Stopped box. Drag the indicator into the middle of cell (let the guides show you where that is) then, using the alignment menu, horizontally and vertically center the indicator in its container.

rw cv2 8

Open the assistant editor and control-drag from the activity indicator to FlickrPhotoCell.swift to add an outlet – call it activityIndicator:

@IBOutlet weak var activityIndicator: UIActivityIndicatorView!

Also in FlickrPhotoCell.swift, add the following code to give the cell control of its background color. You’ll need this later on:

// MARK: - Properties
override var selected: Bool {
  didSet {
    imageView.layer.borderWidth = selected ? 10 : 0
  }
}
 
// MARK: - View Life Cycle
override func awakeFromNib() {
  super.awakeFromNib()
  imageView.layer.borderColor = themeColor.CGColor
  selected = false
}

Finally, open FlickrPhotosViewController.swift and replace the collectionView(_:cellForItemAtIndexPath:) method with the following:

override func collectionView(collectionView: UICollectionView,
                             cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
 
  let cell = collectionView.dequeueReusableCellWithReuseIdentifier(
               reuseIdentifier, forIndexPath: indexPath) as! FlickrPhotoCell
  var flickrPhoto = photoForIndexPath(indexPath)
 
  //1
  cell.activityIndicator.stopAnimating()
 
  //2
  guard indexPath == largePhotoIndexPath else {
    cell.imageView.image = flickrPhoto.thumbnail
    return cell
  }
 
  //3
  guard flickrPhoto.largeImage == nil else {
    cell.imageView.image = flickrPhoto.largeImage
    return cell
  }
 
  //4
  cell.imageView.image = flickrPhoto.thumbnail
  cell.activityIndicator.startAnimating()
 
  //5
  flickrPhoto.loadLargeImage { loadedFlickrPhoto, error in
 
    //6
    cell.activityIndicator.stopAnimating()
 
    //7
    guard loadedFlickrPhoto.largeImage != nil && error == nil else {
      return
    }
 
    //8
    if let cell = collectionView.cellForItemAtIndexPath(indexPath) as? FlickrPhotoCell
                  where indexPath == self.largePhotoIndexPath  {
      cell.imageView.image = loadedFlickrPhoto.largeImage
    }
  }
 
  return cell
}

This is quite a long method now, so here’s the step-by-step:

  1. Always stop the activity spinner – you could be reusing a cell that was previously loading an image
  2. This part is as before – if you’re not looking at the large photo, just set the thumbnail and return
  3. If the large image is already loaded, set it and return
  4. By this point, you want the large image, but it doesn’t exist yet. Set the thumbnail image and start the spinner going. The thumbnail will stretch until the download is complete
  5. Ask for the large image from Flickr. This loads the image asynchronously and has a completion block
  6. The load has finished, so stop the spinner
  7. If there was an error or no photo was loaded, there’s not much you can do.
  8. Check that the large photo index path hasn’t changed while the download was happening, and retrieve whatever cell is currently in use for that index path (it may not be the original cell, since scrolling could have happened) and set the large image.

Build and run, perform a search and tap a nice-looking photo – it grows to fill the screen, and the other cells move around to make space!

rw cv2 9

Tap the cell again, or try scrolling and tapping a different cell. You didn’t have to write any code to move or animate those cells, the collection view and its layout object did all the hard work for you!

Multiple selection

Your next task for this tutorial is to let the user select multiple photos and share them with a friend. The process for multi-selection on a collection view is very similar to that of a table view. The only trick is to tell the collection view to allow multiple selection.

The process for selection works in the following way:

  1. The user taps the Share button to tell the UICollectionView to allow multi- selection and set the sharing property to YES.
  2. The user taps multiple photos that they want to share, adding them to an array.
  3. The user taps the Share button again, which brings up the sharing interface.
  4. When the user finishes sharing the images or taps Cancel, the photos are deselected and the collection view goes back to single selection mode.

First, open FlickrPhotosViewController.swift and add the following properties below the itemsPerRow property:

private var selectedPhotos = [FlickrPhoto]()
private let shareTextLabel = UILabel()

Next, add the following method to the private extension below the photoForIndexPath(_:) method:

func updateSharedPhotoCount() {
  shareTextLabel.textColor = themeColor
  shareTextLabel.text = "\(selectedPhotos.count) photos selected"
  shareTextLabel.sizeToFit()
}

The selectedPhotos array will keep track of the photos the user has selected, and the shareTextLabel will provide feedback to the user on how many photos have been selected. You will call updateSharedPhotoCount to keep shareTextLabel up to date.

Next, add the property following property below the largePhotoIndexPath property:

var sharing: Bool = false {
  didSet {
    collectionView?.allowsMultipleSelection = sharing
    collectionView?.selectItemAtIndexPath(nil, animated: true, scrollPosition: .None)
    selectedPhotos.removeAll(keepCapacity: false)
 
    guard let shareButton = self.navigationItem.rightBarButtonItems?.first else {
      return
    }
 
    guard sharing else {
      navigationItem.setRightBarButtonItems([shareButton], animated: true)
      return
    }
 
    if let _ = largePhotoIndexPath  {
      largePhotoIndexPath = nil
    }
 
    updateSharedPhotoCount()
    let sharingDetailItem = UIBarButtonItem(customView: shareTextLabel)
    navigationItem.setRightBarButtonItems([shareButton,sharingDetailItem], animated: true)
  }
}

sharing is a Bool with another property observer, similar to largePhotoIndexPath above. In this observer, you toggle the multiple selection status of the collection view, clear any existing selection and empty the selected photos array. You also update the bar button items to include and update the shareTextLabel.

Open Main.storyboard and drag a UIBarButtonItem to the right of the navigation bar above the collection view controller. In the Attributes Inspector, set the System Item to Action to give it the familiar sharing icon. Open the assistant editor, making sure FlickrPhotosViewController.swift is open, and control-drag from the bar button into the class to create a new action. Call the action share: and set the sender to be of type UIBarButtonItem.

Fill in the action method as shown:

@IBAction func share(sender: UIBarButtonItem) {
  guard !searches.isEmpty else {
    return
  }
 
  guard !selectedPhotos.isEmpty else {
    sharing = !sharing
    return
  }
 
  guard sharing else  {
    return
  }
  //TODO actually share photos!
}

At the moment, all this method does is some checking to make sure the user has actually searched for something, and has selected photos to share.

You actually want to allow the user to select cells now. Add the following code to the top of the collectionView(_:shouldSelectItemAtIndexPath:) method:

guard !sharing else {
  return true
}

This will allow selection in sharing mode.

Next, add the following method to the UICollectionViewDelegate extension below the collectionView(_:shouldSelectItemAtIndexPath:) method:

override func collectionView(collectionView: UICollectionView,
                             didSelectItemAtIndexPath indexPath: NSIndexPath) {
  guard sharing else {
    return
  }
 
  let photo = photoForIndexPath(indexPath)
  selectedPhotos.append(photo)
  updateSharedPhotoCount()
}

This method allows adding selected photos to the shared photos array and updates the shareTextLabel.
Finally, add the following method below the collectionView(_:didSelectItemAtIndexPath:) method:

override func collectionView(collectionView: UICollectionView,
                             didDeselectItemAtIndexPath indexPath: NSIndexPath) {
 
  guard sharing else {
    return
  }
 
  let photo = photoForIndexPath(indexPath)
 
  if let index = selectedPhotos.indexOf(photo) {
    selectedPhotos.removeAtIndex(index)
    updateSharedPhotoCount()
  }
}

This method removes a photo from the shared photos array and updates the shareTextLabel.

Build and run, and perform a search. Tap the share button to go into sharing mode and select different photos. The label will update and the selected cells will get a fetching Wenderlich Green border.

rw cv2 10

If you tap the share button again, everything just gets deselected, and you go back into non-sharing mode, where tapping a single photo enlarges it.

Of course, this share button isn’t terribly useful unless there’s actually a way to share the photos! Replace the TODO comment in your share(_:) method with the following code:

var imageArray = [UIImage]()
for selectedPhoto in selectedPhotos {
  if let thumbnail = selectedPhoto.thumbnail {
    imageArray.append(thumbnail)
  }
}
 
if !imageArray.isEmpty {
  let shareScreen = UIActivityViewController(activityItems: imageArray, applicationActivities: nil)
  shareScreen.completionWithItemsHandler = { _ in
    self.sharing = false
  }
  let popoverPresentationController = shareScreen.popoverPresentationController
  popoverPresentationController?.barButtonItem = sender
  popoverPresentationController?.permittedArrowDirections = .Any
  presentViewController(shareScreen, animated: true, completion: nil)
}

First, this code creates an array of UIImage objects from the FlickrPhoto‘s thumbnails. The UIImage array is much more convenient, as we can simply pass it to a UIActivityViewController. The UIActivityViewController will show the user any image sharing services or actions available on the device: iMessage, Mail, Print, etc. You simply present your UIActivityViewController from within a popover (because this is an iPad app), and let the user take care of the rest!

Build and run, enter sharing mode, select some photos and hit the share button again. Your share dialog will appear!

rw cv2 11

Note: Testing exclusively on a simulator? You will find the simulator has far fewer sharing options than on device. If you are having trouble confirming that your share screen is properly sharing your images, try the Save (x) Images option. Whether on device or on simulator, this will save the selected images to Photos app, where you can review them to ensure everything worked.

Cell Reordering

Prior to iOS 9, Collection View was sorely missing one feature that its cousin Table View has had for a long time: easy cell reordering. Thankfully, this is no longer the case, and it will be especially easy for you to implement it in this project because you are using a Collection View Controller.

Collection View Controllers now have a property called installsStandardGestureForInteractiveMovement, which by default is set to true. This property controls whether or not a gesture recognizer is installed onto the collection view to allow cell reordering. The only other thing you have to do to get cell reordering to work is override a single method from the UICollectionViewDataSource protocol. Open FlickrPhotosViewController.swift, add the following method below the collectionView(_:cellForItemAtIndexPath:) method in your UICollectionViewDataSource extension:

override func collectionView(collectionView: UICollectionView,
                             moveItemAtIndexPath sourceIndexPath: NSIndexPath,
                             toIndexPath destinationIndexPath: NSIndexPath) {
 
  var sourceResults = searches[sourceIndexPath.section].searchResults
  let flickrPhoto = sourceResults.removeAtIndex(sourceIndexPath.row)
 
  var destinationResults = searches[destinationIndexPath.section].searchResults
  destinationResults.insert(flickrPhoto, atIndex: destinationIndexPath.row)
}

This is a pretty straight forward method to implement. You simply removed the moving item from it’s spot in the results array, and then reinserted it into the array in its new position. That’s it.

Give it a run, press/click and hold on an image and try moving it around to a new spot in the grid:

rw cv2 12

Not bad for 4 lines of code!

Where To Go From Here?

Here is the completed project that you developed in the tutorial series.

Congratulations, you have finished creating your very own stylish Flickr photo browser, complete with a cool UICollectionView based grid view!

In the process, you learned how to make custom UICollectionViewCells, create headers with UICollectionReusableView, detect when rows are tapped, implement multi-cell selection, and much more!

If you have any questions or comments about UICollectionViews or this tutorial, please join the forum discussion below!

The post UICollectionView Tutorial: Reusable Views, Selection, and Reordering appeared first on Ray Wenderlich.

iOS 10 Screencast: Creating a SiriKit Extension


RWDevCon 2016 Inspiration Talk – Going Deep by Ken Yarmosh

$
0
0

Note from Ray: At our recent RWDevCon tutorial conference, in addition to hands-on tutorials, we also had a number of “inspiration talks” – non-technical talks with the goal of giving you a new idea, some battle-won advice, and leaving you excited and energized.

We recorded these talks so that you can enjoy them, even if you didn’t get to attend the conference. Here’s our next talk – Going Deep by Ken Yarmosh – I hope you enjoy!

Transcript

Today’s work environment is broken. We’ve trained ourselves to seek out interruption, to covet being busy, and to let technology run our day and life.

Screen Shot 2016-07-21 at 12.14.26 AM

I know you’re feeling very inspired right now, even just with the material you’ve heard today. You’ve got another great day tomorrow.

Fast forward to Monday with me for a second. You get back into the office. You fire up your machine. You’re excited to start applying some of the things that you learned about today. You’ve got a new feature you’re ready to work on. You start working on that feature.

It’s 9:05 am. You get this nice little Slack message, and that Slack message says, “Hey, I gave you a Polar request last week. I need you to go ahead and get that done so we can merge this branch into Master.” You can see where I’m going with this.

You then start responding, and all of a sudden you have Tweetbot running in the background because we have to have Twitter open when we work, and we see a tweet. It’s getting a lot of attention.

You see it’s got a number of re-tweets, and it’s about an app that’s actually competitive to your app. The app happens to have the feature that you were about to start working on a moment ago. That app, as you do a little bit more research, you see it going up ProductCon.

You then find out, it has 1.5 million dollars in investment. It has a whole team of people working on it. You’re an independent developer.

Hours later, you don’t know who you are.

Screen Shot 2016-07-21 at 12.14.38 AM

You don’t know what you were doing, and you come back ready to work on what you were about to work on, but guess what? You got to do that Polar request still and you still have to fix your app that’s crashing.

This might seem like a “whoa is me” mentality, but we’ve actually come to embrace this digital addiction to some extent. We regularly now use technology to magnify the worse parts of our human nature.

  • We use technology to procrastinate.
  • We use it to distract us, and
  • we use it as an excuse.

Being reactive to the problems of technology is not a new mentality.

Throughout history, people have resisted all sorts of things:

  • electricity,
  • cars,
  • radios,
  • TV’s,
  • computers,
  • the Internet,
  • mobile devices.

Screen Shot 2016-07-21 at 12.14.47 AM

Everything, except Candy Crush. Candy Crush is something that we just had to hold on to. The amount of money people spend on that – let’s not even get started there. The point is though it’s harder than ever to avoid technology. Those of us that do what we do, that in and of itself is just not possible.

What do we have to do here? We have to come up with an approach and a mindset, if you will, to operate in this environment, to operate in an environment where technology is more interesting than ever, and society really has embraced this instant on-demand, 24/7 culture.

Screen Shot 2016-07-21 at 12.14.57 AM

It’s not easy, but the benefits can be amazing. Clear thinking, less stress, and doing work that we’re incredibly proud of. This is the essence of what going deep is really all about.

Some Examples Of Going Deep

I want to provide you a couple of examples to make this a little bit more tangible. Keep in mind that when I outline some of these examples, it may not be what going deep looks like for you, and we’re going to talk about that as we go through this talk, but they should help solidify the concept a little bit more.

  1. I didn’t even know that he was going to talk about it, but an Apple Design Award. Pretty good example of doing this going deep mentality or doing deep work. It doesn’t just happen overnight. The amount of time that someone like Jeremy spent working on his app, strategy, marketing, design, development, to the fact that it gets all the way up the ladder into Apple, and the editors decide that this is going to be something that a handful of people get every single year.
  2. Another example might be a library that we use, and a lot of us use these kinds of libraries. Almost in every single app you open up, you see one of these libraries. That’s a good example of people that have taken a lot of time and attention and thought about a problem, and then really focused on that to build a great solution.

    I almost thought about saying Xcode, but I decided not to. Another great example, we all like Xcode, but we have those days where we have to restart it 100 times.

  3. Something like Sketch. Again, a lot of people here are probably doing development, but Sketch is a user interface design tool. It came out of nowhere. Now, it’s something that a lot of the top designers use today.

    We use it at Savvy Apps for both user experience and visual designs, and as an example of someone that looked at a problem and really spent a lot of time focusing on building something better and now has a great amount of adoption. There’s all these plugins, so on and so forth.

  4. Then, a little nod to the people here that got us here today. When you want to find how to solve a problem, what is the place that you turn to? The tutorial team does awesome work.

    Even in terms of how they actually did this conference. Ray had all of us working and reworking our talks and gave great feedback, and this is the kind of approach that really is just exemplifying going deep.

It Takes Time, But Is Worth The Investment

If you start to subscribe to this, and this sounds like an interesting concept, especially as we talk through this a little bit more, just realize that it does take time if you do this.

Again, you don’t wake up one day and just win an Apple Design Award. I saw Jeremy go through that process. I’ve known him for a long time now, and I can tell you those blog posts, all the things that he mentioned, it took a long time to get to that point and to where he is now.

Screen Shot 2016-07-21 at 12.15.12 AM

If you go down this path, many habits have to be broken and retrained. Depending on research you look at, it could take 21-66 days to form a new habit, but then take the better part of a year to break some of your bad habits.

If you’re constantly going through your day and letting all these different notifications and all this different technology drive you, expect that it’s going to take some time to start really being able to go deep.

Eight Things to Consider That Will Help You To Go Deep

I want to talk with you now about eight different considerations for setting up your environment, your work, so that you can start doing these things.

Screen Shot 2016-07-21 at 12.15.26 AM

It doesn’t mean that if you follow all these eight things, you’re going to win an Apple Design Award or create a library that every single person in the community’s going to use, but these are the kinds of traits and things that you’ll see out there for those who actually do go deep.

1) Define Deep For You

The first one ties in nicely actually to what Jamie talked about. If you think about “Why” being your higher level guiding principles, the next level is to think about what really matters most to you and what you want to accomplish, more specifically, in your goals.

A number of weeks ago, I got to hear a New York Times bestselling author. His name’s Lewis Hobbs. He talks about doing great work, and he wasn’t someone that started out this way. It’s, again, someone that just started on a journey, started a podcast, started having people listen to him. He talked about what he does.

One of the things he does is he frames his vision, literally. He takes it and hangs it up on his wall and he said, “This is going to be my certificate of achievement in the next 6-12 months.” He’s very, very specific. Something that we also heard earlier in another talk. This grounds him in the distraction-filled environment and this is the place that we need to think about.

Screen Shot 2016-07-21 at 12.15.38 AM

We also took the time to do something similar to what Jamie talked about, tying these two things again together at Savvy Apps, called our Guiding Principles.

Why do we do this? So that we have the more tangible, quarterly or yearly goals, but we also have these things that help us that we can reflect back to on a regular basis. This is actually something that we’ve even published on our website.

2) Foster a Creative Environment

Having an inspiring work environment is another element that has a significant impact on going deep.

If you look across at the people who are the most creative, the most prolific, you often see they have these great environments, things that they keep around them to refer back to. It even talked about Lewis’ certificate of achievement that he puts up on his wall in his office.

But if you look at someone like Mark Twain, as an example, there is a really beautiful place that he got to work out of.

It’s one of his writing spots, a writing hut or writing gazebo. It had these beautiful scenic views and a very focused workspace, and it’s very likely, from what we know in history, that he probably wrote both Tom Sawyer and Huckleberry Finn in that spot specifically.

Screen Shot 2016-07-21 at 12.16.07 AM

For us specifically at Savvy, it’s taken us a long time to get to this spot, but we now have a really fun work environment that we can feel proud to come into every single day.

We have very minimal kind of design. We have places where we can collaborate. We have places where people can work that’s not a traditional work spot.

I want you to think about this, even if you actually work at home or work in a shared space. It doesn’t mean it’s just only applicable to the office environment. If you work at home, make that an inspiring environment. Don’t have your personal bills laying around your desk. Don’t have laundry a couple of doors down. As you walk to the bathroom, you’ll see the laundry and think, “i’ve got to get to that later.” Think about this as it relates even to those kinds of environments.

Also, in the shared workspace. If you’re working out of a coffee shop or in a co-working spot, I’m sure it’s not distracting you. You need to be able to have a focused, inspiring environment when it comes to being able to set up to go deep.

3) Be Present

If you think about the creative, physical workspace as one part of it, another element of going deep is really your mental aspect of this. Being present for me is about focusing on those who are around me. It’s about focusing on my family members. It’s about focusing and allowing my mind to wander and to just be alone with my thoughts.

Believe it or not, it actually really starts with my iPhone, my mobile device. How many times now do you see people sitting somewhere and there on a public transportation, they’re just scrolling through everything. They’re crossing the street, they’re scrolling through everything. Everyone’s always checking something on their phone.

We’re all guilty of it. I won’t call you out if you’re doing it right now. It’s totally fine.

Screen Shot 2016-07-21 at 12.16.36 AM

What I’ve done with my own mobile device is actually set it up so that I have no reason to want to grab my phone and check it. I don’t put any news apps on there. I don’t have social media things going on. I don’t have any work applications. I don’t have email hooked up to it.

It’s really just a communication device to some extent. I even have a lot of things muted. I have notifications turned off, especially for those special family threads that we’re all on. Some of them are not the ones that we can unsubscribe to unfortunately. Really what it comes down to is, I’m even on the weekends often putting my device away and just being present.

What happens when you do this? You let your mind recharge, refresh. You actually come back to work ready to really do work. Just generally speaking, I think I’ve overall felt more fulfilled and just even a lot of times gotten ideas that I might not have gotten if I was going on my device and playing with it.

4) Regulate “News” Intake

This ties into that last point, but another common trait of those who are prolific and very creative is that they’re smart with the news that they actually consume. Take a look at someone like Adele. She actually just broke all these records for her last album and she had a nice little interview that I thought was relevant for this.

She talks about social media and how she doesn’t participate in it because she wants to spend time writing music. She wants to spend time in the studio. She wants to spend time with her family, which is a really inspiring thing for her.

Screen Shot 2016-07-21 at 12.16.47 AM

I think it really stands on itself here. This doesn’t mean, again, that you should just completely get off those things, but I think you should be smarter about it because if you think about it, it really is the food for your mind in many ways.

I also want you to think about the fact that often time news is depressing. It’s sensational. It’s rumors. It’s things that we can’t actually act on.

I like to think about this. Is the human mind actually built to take in information that’s happening 1,000 miles away that’s tragic? This is why some people who often look and read news all the time are depressed.

Our mind’s just not really built to take in all this information. Again, I’m not advocating that you’re ignorant, but just be selective with what it is that you put into your head every single day.

5) Now Versus Later

Another thing that we’re guilty of is the tyranny of the urgent. We have this propensity to have to respond to everything that happens at every single moment. Someone mentions us on Twitter, we have to respond. If we get an email, we have to respond. It goes down the line there.

What I want you to do as you go back next week to work, consider if a task requires immediate attention. If it doesn’t, deal with it later. Allocate specific times for conversations and collaboration with your team members, and especially when something comes in that requires thought, give yourself the time to have that thought.

Screen Shot 2016-07-21 at 12.17.05 AM

Step away from things when you’re emotionally charged. This one actually does require a little bit of team buy in, so if you are out there and you’re in this environment where everyone has to be urgent and immediate, you might have to be the champion for this one specifically.

6) Use The Right Tools

For tools itself, we have tool overkill today. There’s just a million things out there. I mentioned some of the ones today already, but how often do you get a text message or a Slack message that says, “I just sent you an email.”

Your response back to it is “I’m not sure if you understand how this works. I got a notification the first time. I didn’t need a second notification about this first notification because that’s what the purpose of the first notification really was.”

What do we have? Some ideas or guidelines for how we use the different tools at Savvy Apps, and it really comes down to just being smart about the right tool.

If we’re working on something right now, it’s a ticket that’s in progress or a card that’s in progress, we’ll discuss that thing in Slack, but we don’t want to spend all day managing Slack. If you’re talking about something, and I’m saying Slack. You could use HipChat, whatever it is that you might use, but we don’t want to spend all day messaging back and forth on these things.

Screen Shot 2016-07-21 at 12.17.15 AM

If you’re spending more than 10 minutes talking about something, jump onto Hangout, walk over to someone’s desk, whatever is the relevant thing, but again, we don’t need to just message the whole time about this.

If it’s something in the future, use the relevant tool. Again, Trello, Pivotal Tracker, JIRA, whatever it is that you use, but put the context on that future item. Those real-time messaging things, they don’t maintain context very well. If you’re talking about something in the future in a channel, guess what? You’re not going to have that context on the future item because it’s going to be left in the channel somewhere. You have to go dig it up later on, so use the relevant tool instead.

Mention it to someone. Assign it to them. When they get through their cue, they’ll get to that item. Wouldn’t that be nice to not be interrupted every five seconds? This is something that we do in our own workflow.

If something needs a lot more attention and thought, you really need to lay out the pros and cons, advantages and disadvantages, use something like a document base. Use Quip, Google Docs, whatever it may be.

Screen Shot 2016-07-21 at 12.17.27 AM

Amazon actually goes to the extent of saying that people in certain kinds of meetings need to write 5-page memos before they step into that meeting. The reason why they do that is they just want people to really think through the problem before they start having lots of conversations about it.

Finally, if something is much more focused on a private exchange, an HR issue, compensation, whatever it is, we actually prefer email even versus a direct message in Slack, and we’ll use email to have those kinds of conversations.

You can see here just thinking about the right tool can really help you so you’re not interrupted 100 times a day because that’s not going to be helpful for you doing this deeper work.

7) Automate Whenever Possible

It may seem counterintuitive, but actually having a process automation can actually help you focus on the most creative parts of what you do.

Instead of continuing to ask questions over and over again about the way to setup an Xcode project, how to ship an app update, how to have a new person join the team, you can instead focus on the special parts of your app, the thing that you are real excited about and that fulfills you.

Screen Shot 2016-07-21 at 12.17.48 AM

At Savvy, we use Trello a lot. We have a bunch of different Trello templates. They’re just literally templates that we can take and copy into a new board.

We have a new team member join, they literally get put into here, and they start going through all the different items to help get them up to speed, and we have a lot of documentation. We have crazy amounts of documentation.

They read through that relevant documentation, and then they’re able to understand how we work at Savvy, and they can start focusing on contributing to the team almost immediately.

8) Allocate Deep Time

Enacting many of these suggestions will considerably free up your time. They’ll free up your brain cycles, and they’ll give you the opportunity to find deep time, and that’s what this is really all about.

My personal approach is that I like to get into the office really early. I get into the office at 7 am every day. I make some super snobby coffee, and I am to do my deep work, my deep time, 1-2 hours before any of my team comes into the office.

Screen Shot 2016-07-21 at 12.18.17 AM

By the time a lot of the team’s rolling in, I’m feeling fulfilled. I’m feeling like I have done the thing that’s going to have the biggest impact on Savvy Apps that day. I then can be more available to my team and help on different things.

I hope that I gave you some different ideas here. As you go forward, I really hope that you consider going deep, to find that for yourself, and I think you’re going to have a much more fulfilling 2016.

About the Speaker: Ken Yarmosh is the Founder & CEO of Savvy Apps. He’s the creator of more than 20 featured apps, including an Editor’s Choice selection and Starbucks Pick of the Week. An O’Reilly author, Ken regularly speaks about application design & development, as well as the future of technology at outlets ranging from Bloomberg TV to Google.

Note from Ray: If you enjoyed this talk, you should join us at RWDevCon 2017! We’ve sold out for the past two years, so don’t miss your chance.

The post RWDevCon 2016 Inspiration Talk – Going Deep by Ken Yarmosh appeared first on Ray Wenderlich.

Unit Testing on macOS: Part 1/2

$
0
0

Unit-Testing-macOS

Unit testing is one of those things that we all know deep down we should be doing, but it seems too difficult, too boring, or too much like hard work.

It’s so much fun creating code that does exciting things; why would anyone want to spend half the time writing code that just checks things?

The reason is confidence! In this Unit testing on macOS tutorial, you’ll learn how to test your code and you will gain confidence that your code is doing what you want it to do, confidence that you can make major changes to your code and confidence that you won’t break anything.

Getting Started

This project uses Swift 3 and requires, at a minimum, Xcode 8 beta 6. Download the starter project and open it in Xcode.

If you have done any other tutorials here at raywenderlich.com, you are probably expecting to build and run at this stage, but not this time — you are going to test. Go to the Product menu and choose Test. Note the shortcut — Command-U — you’ll be using it a lot.

When you run the tests, Xcode will build the app and you will see the app window appear a couple of times before you get a message saying “Test Succeeded”. In the Navigator pane on the left, select Test navigator.

TestNavigator2

This shows you the three tests added by default; each one has a green tick beside it, showing that the test passed. To see the file containing those tests, click on the second line in the Test Navigator where it says High RollerTests preceded by an uppercase T icon.

DefaultTests3

There are a few important things to note here:

  • The imports: XCTest is the testing framework provided by Xcode. @testable import High_Roller is the import that gives the testing code access to all the code in the High_Roller module. Every test file will need these two imports.
  • setup() and tearDown(): these are called before and after every single test method.
  • testExample() and testPerformanceExample(): actual tests. The first one tests functionality, and the second one tests performance. Every test function name must begin with test so that Xcode can recognize it as a test to perform.

What Is Unit Testing?

Before you get into writing your own tests, it’s time for a brief discussion about unit testing, what it actually is and why you should use it.

A unit test is a function that tests a single piece — or unit — of your code. It doesn’t get included in the code of your application, but is used during development to check that your code does what you expected.

A common first reaction to unit tests is: “Are you telling me I should write twice as much code? One function for the app itself and another to test that function?” Actually, it can be worse than that — some projects end up with more testing code than production code.

At first, this seems like a terrible waste of time and effort — but wait until a test catches something that you didn’t spot, or alerts you to a side-effect of re-factoring. That’s when you realize what an amazing tool this is. After a while, any project without unit tests feels very fragile, and you’ll hesitate to make any changes because you cannot be sure what will happen.

Test Driven Development

Test Driven Development (TDD) is a branch of unit testing where you start with the tests and only write code as required by the tests. Again, this seems like a very strange way to proceed at first and can produce some very peculiar code as you’ll see in a minute. The upshot is that this process really makes you think about the purpose of the code before coding begins.

Test Driven Development has three repeating steps:

  1. Red: Write a failing test.
  2. Green: Write the minimum code needed to make the test pass.
  3. Refactor: Optional; if any app or test code can be re-factored to make it better, do it now.

This sequence is important and the key to effective TDD. Fixing a failing test gives you a clear indication you know exactly what your code is doing. If your test passes the first time, without any new code being written, then you have not correctly pin-pointed the next stage of development.

To start, you’ll write a series of tests and the accompanying code using TDD.

dog

The Test Project

This project is a dice rolling utility for board gamers. Ever sit down to play a game with the family and discover that the dog ate the dice? Now your app can come to the rescue. And if anyone says “I don’t trust a computer not to cheat!” you can proudly say that the app has been unit tested to prove that it works correctly. That’s bound to impress the family — and you will have saved games night. :]

The model for this app will have two main object types: Dice, which will have a value property and a method for generating a random value, and Roll, which will be a collection of Dice objects with methods for rolling them all, totaling the values and so on.

This first test class is for the Dice object type.

The Dice Test Class

In Xcode go to the File Navigator and select the High RollerTests group. Select File\New\File… and choose macOS\Unit Test Case Class. Click Next and name the class DiceTests. Make sure the language is set to Swift. Click Next and Create.

Select all the code inside the class and delete it. Add the following statement to DiceTests.swift just under the import XCTest line:

@testable import High_Roller

Now you can delete HighRollerTests.swift as you don’t need the default tests any longer.

The first thing to test is whether a Dice object can be created.

Your First Test

Inside the DiceTests class, add the following test function:

  func testForDice() {
    let _ = Dice()
  }

This gives a compile error before you can even run the test: "Use of unresolved identifier 'Dice'". In TDD, a test that fails to compile is considered a failing test, so you have just completed step 1 of the TDD sequence.

To make this test pass with the minimum of code, go to the File Navigator and select the Model group in the main High Roller group. Use File\New\File… to create a new Swift file and name it Dice.swift.

Add the following code to the file:

struct Dice {
 
}

Go back to DiceTests.swift; the error will still be visible until the next build. However, you can now run the test in several different ways.

If you click the diamond in the margin beside the test function, only that single test will run. Try that now, and the diamond will turn into a green checkmark symbol, showing that the test has passed.

You can click a green symbol (or a red symbol that shows a failed test) at any time to run a test. There will now be another green symbol beside the class name. Clicking this will run all the tests in the class. At the moment, this is the same as running the single test, but that will soon change.

The final way to test your code is to run all the tests.

RunningTests

Press Command-U to run all the tests and then go to the Test Navigator where you will see your single test in the High RollerTests section; you may need to expand the sections to see it. The green checkmark symbols appear beside every test. If you move the mouse pointer up and down the list of tests, you will see small play buttons appear which you can use to run any test or set of tests.

In the Test Navigator, you can see that the High RollerUITests ran as well. The problem with UI Tests are that they’re slow. You want your tests to be fast as possible so that there is no drawback to testing frequently. To solve this problem, edit the scheme so that the UI Tests don’t run automatically.

Go to the scheme popup in the toolbar and select Edit scheme…. Click Test in the pane on the left and un-check High RollerUITests. Close the scheme window and run your tests again with Command-U. The UI Tests are faded out in the Test Navigator, but they can still be run manually.

TurnOffUITests

Choosing Which Tests to Run

So which method should you use for running your tests? Single, class or all?

If you are working on a test, it is often useful to test it on its own or in its class. Once you have a passing test, it is vital to check that it hasn’t broken anything else, so you should do a complete test run after that.

To make things easier as you progress, open DiceTests.swift in the primary editor and Dice.swift in the assistant editor. This is a very convenient way to work as you cycle through the TDD sequence.

That completes the second step of the TDD sequence; since there is no refactoring to be done, it’s time to go back to step 1 and write another failing test.

Testing for nil

Every Dice object should have a value which should be nil when the Dice object is instantiated.

Add the following test to DiceTests.swift:

  // 1
  func testValueForNewDiceIsNil() {
    let testDie = Dice()
 
    // 2
    XCTAssertNil(testDie.value, "Die value should be nil after init")
  }

Here’s what this test does:

  1. The function name starts with 'test', and the remainder of the function name expresses what the test checks.
  2. The test uses one of the many XCTAssert functions to confirm that the value is nil. The second parameter of XCTAssertNil() is an optional string that provides the error message if the test fails. I generally prefer to use descriptive function names and leave this parameter blank in the interests of keeping the actual test code clean and easy to read.

This test code produces a compile error: "Value of type 'Dice' has no member 'value'".

To fix this error, add the following property definition to the Dice struct within Dice.swift:

  var value: Int?

In DiceTests.swift, the compile error will not disappear until the app is built. Press Command-U to build the app and run the tests which should pass. Again there is nothing to re-factor.

Each Dice object has to be able to “roll” itself and generate its value. Add this next test to DiceTests.swift:

  func testRollDie() {
    var testDie = Dice()
    testDie.rollDie()
 
    XCTAssertNotNil(testDie.value)
  }

This test uses XCTAssertNotNil() instead of XCTAssertNil() from the previous test.

As the Dice struct has no rollDie() method, this will inevitably cause another compile error. To fix it, switch back to the Assistant Editor and add the following to Dice.swift:

  func rollDie() {
 
  }

Run the tests; you’ll see a warning about using var instead of let along with a note that XCTAssert has failed this time. That makes sense, since rollDie() isn’t doing anything yet. Change rollDie() as shown below:

  mutating func rollDie() {
    value = 0
  }

Now you are seeing how TDD can produce some odd code. You know that eventually the Dice struct has to produce random dice values, but you haven’t written a test asking for that yet, so this function is the minimum code need to pass the test. Run all the tests again to prove this.

Developing to Tests

Put your thinking cap on — these next tests are designed to shape the way your code comes together. This can feel backwards at first, but it’s a very powerful way to make you focus on the true intent of your code.

You know that a standard die has six sides, so the value of any die after rolling should be between 1 and 6 inclusive. Go back to DiceTests.swift and add this test, which introduces two more XCTAssert functions:

  func testDiceRoll_ShouldBeFromOneToSix() {
    var testDie = Dice()
    testDie.rollDie()
 
    XCTAssertTrue(testDie.value! >= 1)
    XCTAssertTrue(testDie.value! <= 6)
    XCTAssertFalse(testDie.value == 0)
  }

one-sided_dice2

Run the tests; two of the assertions will fail. Change rollDie() in Dice.swift so that it sets value to 1 and try again. This time all the tests pass, but this dice roller won’t be of much use! :]

Instead of testing a single value, what about making the test roll the die multiple times and count how many of each number it gets? There won’t be a perfectly even distribution of all numbers, but a large enough sample should be close enough for your tests.

Time for another test in DiceTests.swift:

  func testRollsAreSpreadRoughlyEvenly() {
    var testDie = Dice()
    var rolls: [Int: Double] = [:]
 
    // 1
    let rollCounter = 600.0
 
    for _ in 0 ..< Int(rollCounter) {
      testDie.rollDie()
      guard let newRoll = testDie.value else {
        // 2
        XCTFail()
        return
      }
 
      // 3
      if let existingCount = rolls[newRoll] {
        rolls[newRoll] = existingCount + 1
      } else {
        rolls[newRoll] = 1
      }
    }
 
    // 4
    XCTAssertEqual(rolls.keys.count, 6)
 
    // 5
    for (key, roll) in rolls {
      XCTAssertEqualWithAccuracy(roll,
                                 rollCounter / 6,
                                 accuracy: rollCounter / 6 * 0.3,
                                 "Dice gave \(roll) x \(key)")
    }
  }

Here’s what’s going on in this test:

  1. rollCounter specifies how many times the dice will be rolled. 100 for each expected number seems like a reasonable sample size.
  2. If the die has no value at any time during the loop, the test will fail and exit immediately. XCTFail() is like an assertion that can never pass, which works very well with guard statements.
  3. After each roll, you add the result to a dictionary.
  4. This assertion confirms that there are 6 keys in the dictionary, one for each of the expected numbers.
  5. The test uses a new assertion: XCTAssertEqualWithAccuracy() which allows inexact comparisons. Since XCTAssertEqualWithAccuracy() is called numerous times, the optional message is used to show which part of the loop failed.

Run the test; as you would expect, it fails as every roll is 1. To see the errors in more detail, go to the Issue Navigator where you can read what the test results were, and what was expected.

IssueNavigator

It is finally time to add the random number generator to rollDie(). In Dice.swift, change the function as shown below:

  mutating func rollDie() {
    value = Int(arc4random_uniform(UInt32(6))) + 1
  }

This uses arc4random_uniform() to produce what should be a number between 1 and 6. It looks simple, but you still have to test! Press Command-U again; all the tests pass. You can now be sure that the Dice struct is producing numbers in roughly the expected ratios. If anyone says your app is cheating, you can show them the test results to prove it isn’t!

Job well done! The Dice struct is complete, time for a cup of tea…

Until your friend, who plays a lot of role-playing games, has just asked if your app could support different types of dice: 4-sided, 8-sided, 12-sided, 20-sided, even 100-sided…

Dice

Modifying Existing Code

You don’t want to ruin your friend’s D&D nights, so head back to DiceTests.swift and add another test:

  func testRollingTwentySidedDice() {
    var testDie = Dice()
    testDie.rollDie(numberOfSides: 20)
 
    XCTAssertNotNil(testDie.value)
    XCTAssertTrue(testDie.value! >= 1)
    XCTAssertTrue(testDie.value! <= 20)
  }

The compiler complains because rollDie() doesn’t take any parameters. Switch over to the assistant editor and in Dice.swift change the function declaration of rollDie() to expect a numberOfSides parameter:

  mutating func rollDie(numberOfSides: Int) {

But that will make the old test fail because they don’t supply a parameter. You could edit them all, but most dice rolls are for 6-sided dice (no need to tell your role-playing friend that). How about giving the numberOfSides parameter a default value?

Change the rollDie(numberOfSides:) definition to this:

  mutating func rollDie(numberOfSides: Int = 6) {

All the tests now pass, but you are in the same position as before: the tests don’t check that the 20-sided dice roll is really producing values from 1 to 20.

Time to write another test similar to testRollsAreSpreadRoughlyEvenly(), but only for 20-sided dice.

  func testTwentySidedRollsAreSpreadRoughlyEvenly() {
    var testDie = Dice()
    var rolls: [Int: Double] = [:]
    let rollCounter = 2000.0
 
    for _ in 0 ..< Int(rollCounter) {
      testDie.rollDie(numberOfSides: 20)
      guard let newRoll = testDie.value else {
        XCTFail()
        return
      }
 
      if let existingCount = rolls[newRoll] {
        rolls[newRoll] = existingCount + 1
      } else {
        rolls[newRoll] = 1
      }
    }
 
    XCTAssertEqual(rolls.keys.count, 20)
 
    for (key, roll) in rolls {
      XCTAssertEqualWithAccuracy(roll,
                                 rollCounter / 20,
                                 accuracy: rollCounter / 20 * 0.3,
                                 "Dice gave \(roll) x \(key)")
    }
  }

This test gives seven failures: the number of keys is only 6, and the distribution isn’t even. Have a look in the Issue Navigator for all the details.

IssueNavigator2

You should expect this: rollDie(numberOfSides:) isn’t using the numberOfSides parameter yet.

Replace the 6 in the arc4random_uniform() function call with numberOfSides and Command-U again.

Success! All the tests pass — even the old ones that call the function you just changed.

Refactoring Tests

For the first time, you have some code worth re-factoring. testRollsAreSpreadRoughlyEvenly() and testTwentySidedRollsAreSpreadRoughlyEvenly() use very similar code, so you could separate that out into a private function.

Add the following extension to the end of the DiceTests.swift file, outside the class:

extension DiceTests {
 
  fileprivate func performMultipleRollTests(numberOfSides: Int = 6) {
    var testDie = Dice()
    var rolls: [Int: Double] = [:]
    let rollCounter = Double(numberOfSides) * 100.0
    let expectedResult = rollCounter / Double(numberOfSides)
    let allowedAccuracy = rollCounter / Double(numberOfSides) * 0.3
 
    for _ in 0 ..< Int(rollCounter) {
      testDie.rollDie(numberOfSides: numberOfSides)
      guard let newRoll = testDie.value else {
        XCTFail()
        return
      }
 
      if let existingCount = rolls[newRoll] {
        rolls[newRoll] = existingCount + 1
      } else {
        rolls[newRoll] = 1
      }
    }
 
    XCTAssertEqual(rolls.keys.count, numberOfSides)
 
    for (key, roll) in rolls {
      XCTAssertEqualWithAccuracy(roll,
                                 expectedResult,
                                 accuracy: allowedAccuracy,
                                 "Dice gave \(roll) x \(key)")
    }
  }
 
}

This function name doesn’t start with test, as it’s never run on its own as a test.

Go back to the main DiceTests class and replace testRollsAreSpreadRoughlyEvenly() and testTwentySidedRollsAreSpreadRoughlyEvenly() with the following:

  func testRollsAreSpreadRoughlyEvenly() {
    performMultipleRollTests()
  }
 
  func testTwentySidedRollsAreSpreadRoughlyEvenly() {
    performMultipleRollTests(numberOfSides: 20)
  }

Run all the tests again to confirm that this works.

Using #line

To demonstrate another useful testing technique, go back to Dice.swift and undo the 20-sided dice change you made to rollDie(numberOfSides:): replace numberOfSides with 6 inside the arc4random_uniform() call. Now run the tests again.

testTwentySidedRollsAreSpreadRoughlyEvenly() has failed, but the failure messages are in performMultipleRollTests(numberOfSides:) — not a terribly useful spot.

Xcode can solve this for you. When defining a helper function, you can supply a parameter with a special default value — #line — that contains the line number of the calling function. This line number can be used in the XCTAssert function to send the error somewhere useful.

In the DiceTests extension, change the function definition of performMultipleRollTests(numberOfSides:) to the following:

fileprivate func performMultipleRollTests(numberOfSides: Int = 6, line: UInt = #line) {

And change the XCTAsserts like this:

XCTAssertEqual(rolls.keys.count, numberOfSides, line: line)
 
for (key, roll) in rolls {
  XCTAssertEqualWithAccuracy(roll,
                             expectedResult,
                             accuracy: allowedAccuracy,
                             "Dice gave \(roll) x \(key)",
                             line: line)
}

You don’t have to change the code that calls performMultipleRollTests(numberOfSides:line:) because the new parameter is filled in by default. Run the tests again, and you’ll see the error markers are on the line that calls performMultipleRollTests(numberOfSides:line:) — not inside the helper function.

Change rollDie(numberOfSides:) back again by putting numberOfSides in the arc4random_uniform() call, and Command-U to confirm that everything works.

Pat yourself on the back — you’ve learned how to use TDD to develop a fully-tested model class.

victory

Adding Unit Tests to Existing Code

TDD can be great when developing new code, but often you’ll have to retrofit tests into existing code that you didn’t write. The process is much the same, except that you’re writing tests to confirm that existing code works as expected.

To learn how to do this, you’ll add tests for the Roll struct. In this app, the Roll contains an array of Dice and a numberOfSides property. It handles rolling all the dice as well as totaling the result.

Back in the File Navigator, select Roll.swift. Delete all the placeholder code and replace it with the following code:

struct Roll {
 
  var dice: [Dice] = []
  var numberOfSides = 6
 
  mutating func changeNumberOfDice(newDiceCount: Int) {
    dice = []
    for _ in 0 ..< newDiceCount {
      dice.append(Dice())
    }
  }
 
  var allDiceValues: [Int] {
    return dice.flatMap { $0.value}
  }
 
  mutating func rollAll() {
    for index in 0 ..< dice.count {
      dice[index].rollDie(numberOfSides: numberOfSides)
    }
  }
 
  mutating func changeValueForDie(at diceIndex: Int, to newValue: Int) {
    if diceIndex < dice.count {
      dice[diceIndex].value = newValue
    }
  }
 
  func totalForDice() -> Int {
    let total = dice
      .flatMap { $0.value }
      .reduce(0) { $0 - $1 }
    return total
  }
 
}

(Did you spot the error? Ignore it for now; that’s what the tests are for. :])

Select the High RollerTests group in the File Navigator and use File\New\File… to add a new macOS\Unit Test Case Class called RollTests. Delete all the code inside the test class.

Add the following import to RollTests.swift:

@testable import High_Roller

Open Roll.swift in the assistant editor, and you’ll be ready to write more tests.

First, you want to test that a Roll can be created, and that it can have Dice added to its dice array. Arbitrarily, the test uses five dice.

Add this test to RollTests.swift:

  func testCreatingRollOfDice() {
    var roll = Roll()
    for _ in 0 ..< 5 {
      roll.dice.append(Dice())
    }
 
    XCTAssertNotNil(roll)
    XCTAssertEqual(roll.dice.count, 5)
  }

Run the tests; so far, so good — the first test passes. Unlike TDD, a failing test is not an essential first step in a retrofit as the code should (theoretically) already work properly.

Next, use the following test to check that the total is zero before the dice are rolled:

  func testTotalForDiceBeforeRolling_ShouldBeZero() {
    var roll = Roll()
    for _ in 0 ..< 5 {
      roll.dice.append(Dice())
    }
 
    let total = roll.totalForDice()
    XCTAssertEqual(total, 0)
  }

Again this succeeds, but it looks like there is some refactoring to be done. The first section of each test sets up a Roll object and populates it with five dice. If this was moved to setup() it would happen before every test.

Not only that, but Roll has a method of its own for changing the number of Dice in the array, so the tests might as well use and test that.

Replace the contents of the RollTests class with this:

  var roll: Roll!
 
  override func setUp() {
    super.setUp()
 
    roll = Roll()
    roll.changeNumberOfDice(newDiceCount: 5)
  }
 
  func testCreatingRollOfDice() {
    XCTAssertNotNil(roll)
    XCTAssertEqual(roll.dice.count, 5)
  }
 
  func testTotalForDiceBeforeRolling_ShouldBeZero() {
    let total = roll.totalForDice()
    XCTAssertEqual(total, 0)
  }

As always, run the tests again to check that everything still works.

With five 6-sided dice, the minimum total is 5 and the maximum is 30, so add the following test to check that the total falls between those limits:

  func testTotalForDiceAfterRolling_ShouldBeBetween5And30() {
    roll.rollAll()
    let total = roll.totalForDice()
    XCTAssertGreaterThanOrEqual(total, 5)
    XCTAssertLessThanOrEqual(total, 30)
  }

Run this test — it fails! It looks like the tests have discovered a bug in the code. The problem must be in either rollAll() or totalForDice(), since those are the only two functions called by this test. If rollAll() was failing, the total would be zero. However, returned total is a negative number, so have a look at totalForDice() instead.

There’s the problem: reduce is subtracting instead of adding the values. Change the minus sign to a plus sign:

func totalForDice() -> Int {
  let total = dice
    .flatMap { $0.value }
    // .reduce(0) { $0 - $1 }       // bug line
    .reduce(0) { $0 + $1 }          // fixed
  return total
}

Run your tests again — everything should run perfectly.

Where to Go From Here?

You can download the sample project here with all the tests from this part of the tutorial in it.

Carry on to the second half of this Unit testing on macOS tutorial for more goodness, including interface testing, network testing, performance testing and code coverage. Hope to see you there! :]

If you have any questions or comments about this part of the tutorial, please join the discussion below!

The post Unit Testing on macOS: Part 1/2 appeared first on Ray Wenderlich.

Unit Testing on macOS: Part 2/2

$
0
0

Unit-Testing-macOS

In Part 1 of this Unit testing tutorial, you learned how to use Test Driven Development to test new code and how to add unit tests to existing code. In this part, you’ll learn how to test the user interface, how to test networking code and learn about a couple of Xcode tools to help your testing.

If you haven’t completed Part 1, or want a fresh start, download the completed project from Part 1 project here. This project uses Swift 3 and requires, at a minimum, Xcode 8 beta 6. Open it in Xcode and press Command-U to run all the tests to confirm that everything is working as expected.

Testing the Interface

As you saw in part 1, Xcode includes the ability to run UITests. While these can be useful, it is much faster to test views and view controllers programmatically. Instead of having Xcode run the app and send fake clicks to interface objects, get your tests to create a new instance of your view or view controller and work with that directly. You can get and set properties, call methods — including IBAction methods — and test the results much more quickly.

Select the High RollerTests group in the File Navigator and use File\New\File… to create a new macOS\Unit Test Case Class named ViewControllerTests. Delete all the code and add the following import to the top of the file:

@testable import High_Roller

Insert the following code into the ViewControllerTests class:

// 1
var vc: ViewController!
 
override func setUp() {
  super.setUp()
 
  // 2
  let storyboard = NSStoryboard(name: "Main", bundle: nil)
  vc = storyboard.instantiateController(withIdentifier: "ViewController") as! ViewController
 
  // 3
  _ = vc.view
}

So what’s going on here?

  1. The entire class will have access to a ViewController property names vc. It’s OK to make this non-optional because if it crashes, that’s still a useful test result.
  2. This view controller is instantiated from the storyboard setup().
  3. To trigger the view lifecycle, get the view controller’s view property. You don’t need to store it; the act of getting it makes the view controller create it correctly.

Instantiating a view controller this way will only work if the view controller has a storyboard ID. Open Main.storyboard, select ViewController and show the Identity Inspector on the right. Set the Storyboard ID to ViewController.

Add-Storyboard-ID-for-ViewController

The first test will confirm that the ViewController was created properly. Go back to ViewControllerTests.swift and add the following test function:

func testViewControllerIsCreated() {
  XCTAssertNotNil(vc)
}

Run the tests; if this test fails or crashes, go back to Main.storyboard and check that you set the storyboard ID correctly.

Build and run the app to have a look at the interface. All the controls are functional, so click the Roll button to roll the dice. Change the settings and roll again; notice that the number of dice can be set using a test field or a stepper and that you set the number of sides for the dice using a popup.

BuildRun2

Before testing that the controls operate as expected, you first need to confirm the interface elements start off with the expected values.

Add this test to ViewControllerTests:

func testControlsHaveDefaultData() {
  XCTAssertEqual(vc.numberOfDiceTextField.stringValue, String(2))
  XCTAssertEqual(vc.numberOfDiceStepper.integerValue, 2)
  XCTAssertEqual(vc.numberOfSidesPopup.titleOfSelectedItem, String(6))
}

Run the tests to make sure the initial setup is valid.

Once that is confirmed, you can test what happens when you change the parameters through the interface. When you change the number in the text field, the value of the stepper should change to match and vice versa.

If you were using the app and clicked on the up or down arrows to change the stepper, the IBAction method numberOfDiceStepperChanged(_:) would be called automatically. Similarly, if you edited the text field, numberOfDiceTextFieldChanged(_:) would be called. When testing, you have to call the IBAction methods manually.

Insert the following two tests into ViewControllerTests:

func testChangingTextFieldChangesStepper() {
  vc.numberOfDiceTextField.stringValue = String(4)
  vc.numberOfDiceTextFieldChanged(vc.numberOfDiceTextField)
 
  XCTAssertEqual(vc.numberOfDiceTextField.stringValue, String(4))
  XCTAssertEqual(vc.numberOfDiceStepper.integerValue, 4)
}
 
func testChangingStepperChangesTextField() {
  vc.numberOfDiceStepper.integerValue = 10
  vc.numberOfDiceStepperChanged(vc.numberOfDiceStepper)
 
  XCTAssertEqual(vc.numberOfDiceTextField.stringValue, String(10))
  XCTAssertEqual(vc.numberOfDiceStepper.integerValue, 10)
}

Run the tests to see the result. You should also test the view controller’s variables and confirm that they are being changed as expected by events from the interface elements.

The view controller has a Roll object which has its own properties. Add the following test to check that the Roll object exists and has the expected default properties:

func testViewControllerHasRollObject() {
  XCTAssertNotNil(vc.roll)
}
 
func testRollHasDefaultSettings() {
  XCTAssertEqual(vc.roll.numberOfSides, 6)
  XCTAssertEqual(vc.roll.dice.count, 2)
}

Next, you need to confirm that changing a setting using one of the interface elements actually changes the setting in the Roll object. Add the following tests:

    func testChangingNumberOfDiceInTextFieldChangesRoll() {
      vc.numberOfDiceTextField.stringValue = String(4)
      vc.numberOfDiceTextFieldChanged(vc.numberOfDiceTextField)
 
      XCTAssertEqual(vc.roll.dice.count, 4)
    }
 
    func testChangingNumberOfDiceInStepperChangesRoll() {
      vc.numberOfDiceStepper.integerValue = 10
      vc.numberOfDiceStepperChanged(vc.numberOfDiceStepper)
 
      XCTAssertEqual(vc.roll.dice.count, 10)
    }
 
    func testChangingNumberOfSidesPopupChangesRoll() {
      vc.numberOfSidesPopup.selectItem(withTitle: "20")
      vc.numberOfSidesPopupChanged(vc.numberOfSidesPopup)
 
      XCTAssertEqual(vc.roll.numberOfSides, 20)
    }

These three tests operate the text field, the stepper and the popup. After each change, they check that the roll property has changed to match.

Open ViewController.swift in the assistant editor and look at rollButtonClicked(_:). It does three things:

  1. Makes sure that any ongoing edit in the number of dice text field is processed.
  2. Tells the Roll struct to roll all the dice.
  3. Displays the results.

You have already written tests to confirm that rollAll() works as expected, but displayDiceFromRoll(diceRolls:numberOfSides:) needs to be tested as part of the interface tests. The display methods are all in ViewControllerDisplay.swift, which is a separate file containing an extension to ViewController. This is just an organizational split to keep ViewController.swift smaller and to keep the display functions all collected in one place.

Look in ViewControllerDisplay.swift and you’ll see a bunch of private functions and one public function: displayDiceFromRoll(diceRolls:numberOfSides:), This clears the display, fills in the textual information and then populates a stack view with a series of sub-views, one for each die.

As with all testing, it is important to start in the right place. The first test to write is one that checks that the results text view and stack view are empty at the start.

Go to ViewControllerTests.swift and add this test:

func testDisplayIsBlankAtStart() {
  XCTAssertEqual(vc.resultsTextView.string, "")
  XCTAssertEqual(vc.resultsStackView.views.count, 0)
}

Run this test to confirm that the display starts off as expected.

Next, add the test below to check if data appears after the Roll button is clicked:

func testDisplayIsFilledInAfterRoll() {
  vc.rollButtonClicked(vc.rollButton)
 
  XCTAssertNotEqual(vc.resultsTextView.string, "")
  XCTAssertEqual(vc.resultsStackView.views.count, 2)
}

Since the default setting for the number of dice is 2, it’s safe to check that the stack view has two views. But if you don’t know what the settings might be, you can’t test to see whether the data displayed is correct.

Look back at rollButtonClicked(_:) in ViewController.swift. See how it rolls the dice and then displays the result? What if you called displayDiceFromRoll(diceRolls:numberOfSides:) directly with known data? That would allow exact checking of the display.

Add the following test to ViewControllerTests.swift:

func testTextResultDisplayIsCorrect() {
  let testRolls = [1, 2, 3, 4, 5, 6]
  vc.displayDiceFromRoll(diceRolls: testRolls)
 
  var expectedText = "Total rolled: 21\n"
  expectedText += "Dice rolled: 1, 2, 3, 4, 5, 6 (6 x 6 sided dice)\n"
  expectedText += "You rolled: 1 x 1s,  1 x 2s,  1 x 3s,  1 x 4s,  1 x 5s,  1 x 6s"
 
  XCTAssertEqual(vc.resultsTextView.string, expectedText)
}

Run it to confirm that the test result is as expected for a roll with 6 six-sided dice showing one of each possible sides.

The stack view shows the results in a more graphical way using dice emojis if possible. Insert this test into ViewControllerTests.swift:

func testGraphicalResultDisplayIsCorrect() {
  let testRolls = [1, 2, 3, 4, 5, 6]
  vc.displayDiceFromRoll(diceRolls: testRolls)
 
  let diceEmojis = ["\u{2680}", "\u{2681}", "\u{2682}", "\u{2683}", "\u{2684}", "\u{2685}" ]
 
  XCTAssertEqual(vc.resultsStackView.views.count, 6)
 
  for (index, diceView) in vc.resultsStackView.views.enumerated() {
    guard let diceView = diceView as? NSTextField else {
      XCTFail("View \(index) is not NSTextField")
      return
    }
    let diceViewContent = diceView.stringValue
    XCTAssertEqual(diceViewContent, diceEmojis[index], "View \(index) is not showing the correct emoji.")
  }
}

Run the tests again to check that the interface is acting as you expect. These last two tests demonstrate a very useful technique for testing, by supplying known data to a method and checking the result.

If you’re feeling keen, it looks like there is some re-factoring that could be done here! :]

UI Testing

Time to move on to the UITests. Close the assistant editor and open High_RollerUITests.swift. The default code is very similar to the testing code you’ve been using so far, with just a couple of extra lines in setup().

One interesting thing about UITests is their ability to record interface interactions. Remove the comments from inside testExample(), place the cursor on a blank line inside the function and click the red dot at the bottom left of the edit pane to start recording:

Record-UI-Test

When the app starts, follow this sequence of interactions, pausing after each step to let Xcode write at least one new line:

  1. Click the up arrow on the “Number of dice” stepper.
  2. Click the up arrow on the “Number of dice” stepper again.
  3. Double-click inside the “Number of dice” text field.
  4. Type 6 and press Tab.
  5. Open the “Number of sides” popup and select 12.
  6. Click the “Roll” button.

Click the record button again to stop recording.

Xcode will have filled in the function with details of your actions, but you will see one odd thing where you selected 12 from the popup. Xcode can’t quite decide what option to use, so it shows you a number of possibilities. In a complex interface, this might be important to distinguish between different controls, but in this case the first option is sufficient for your needs.

Record-UI-Test2

Click on the down arrow beside menuItems[“12”] to see the popup. Choosing the one to use is easy enough, but convincing Xcode of your choice is not so straightforward.

Select the first option in the list which will dismiss the popup. Then click on the item which will still have a pale blue background. When it’s selected, the background will have a slightly darker shade of blue; you can then press Return to accept this choice, which will leave your code looking like this:

  func testExample() {
 
    let highRollerWindow = XCUIApplication().windows["High Roller"]
    let incrementArrow = highRollerWindow.steppers.children(matching: .incrementArrow).element
    incrementArrow.click()
    incrementArrow.click()
 
    let textField = highRollerWindow.children(matching: .textField).element
    textField.doubleClick()
    textField.typeText("6\t")
    highRollerWindow.children(matching: .popUpButton).element.click()
    highRollerWindow.menuItems["12"].click()
    highRollerWindow.buttons["Roll"].click()
 
  }

The main use for recording is to show the syntax for accessing the interface elements. The unexpected thing is that you aren’t getting NSButton or NSTextField references; you’re getting XCUIElements instead. This gives you the ability to send messages and test a limited number of properties. value is an Any that will usually hold the most important content of the XCUIElement.

Using the information in the recording to work out how to access the elements, this test function checks to see that editing the number of dice using the stepper also changes the text field:

func testIncreasingNumberOfDice() {
  let highRollerWindow = XCUIApplication().windows["High Roller"]
 
  let incrementArrow = highRollerWindow.steppers.children(matching: .incrementArrow).element
  incrementArrow.click()
  incrementArrow.click()
 
  let textField = highRollerWindow.children(matching: .textField).element
  let textFieldValue = textField.value as? String
 
  XCTAssertEqual(textFieldValue, "4")
}

Save the file and run the test by clicking in the little diamond in the margin beside it. The app will run, the mouse pointer will be moved to the stepper’s up arrow and it will click twice. This is fun, like having a robot operate your app!

robot

It’s a lot slower than the previous tests, though, so UITesting is not for everyday use.

Network and Asynchronous Tests

So far, everyone is happy. Family games night is going ahead, your role-playing friends have the option to roll all their weird dice, your tests prove that everything is working correctly…. but there is always someone who causes trouble:

“I still don’t trust your app to roll the dice. I found a web page that generates dice rolls using atmospheric noise. I want your app to use that instead.”

Sigh. Head to Random.org to see how this works. If the URL contains a num parameter, the page shows the results of rolling that many 6-sided dice. Inspecting the source code for the page, it looks like this is the relevant section:

<p>You rolled 2 dice:</p>
<p>
<img src="dice6.png" alt="6" />
<img src="dice1.png" alt="1" />
</p>

So you could parse the data returned and use that data for the roll. Check out WebSource.swift and you’ll see this is exactly what it does. But how do you test this?

The first thing is to make a WebSourceTests.swift test file. Select the High RollerTests group in the File Navigator and use File\New\File… to make a new macOS\Unit Test Case Class and name it WebSourceTests.

Delete the contents of the class and add the following import statement:

@testable import High_Roller

Open WebSource.swift in the assistant editor.

Look at findRollOnline(numberOfDice:completion:) in WebSource.swift. This function creates a URLRequest and a URLSession and then combines them into a URLSessionDataTask which tries to download the web page for the selected number of dice.

If data arrives, it parses the result and calls the completion handler with the dice results or an empty array.

As a first attempt at testing, try adding the following to WebSourceTests.swift:

func testDownloadingOnlineRollPage() {
  let webSource = WebSource()
  webSource.findRollOnline(numberOfDice: 2) { (result) in
    XCTAssertEqual(result.count, 2)
  }
}

When you run this test, it passes suspiciously fast. Click in the margin to add a breakpoint to the XCTAssertEqual() line.

breakpoint

Run the test again, and your breakpoint never gets triggered. The test is completing without waiting for the results to come back. This is a bit of a trap, as you could have erroneously assumed that the test passed. Never worry, XCTests has the solution to this: expectations!

Replace the previous test with this one:

func testDownloadingPageUsingExpectation() {
  // 1
  let expect = expectation(description: "waitForWebSource")
  var diceRollsReceived = 0
 
  let webSource = WebSource()
  webSource.findRollOnline(numberOfDice: 2) { (result) in
    diceRollsReceived = result.count
    // 2
    expect.fulfill()
  }
 
  // 3
  waitForExpectations(timeout: 10, handler: nil)
  XCTAssertEqual(diceRollsReceived, 2)
}

There are several new things to look at here:

  1. Create an XCTestExpectation with a human-readable description.
  2. When the closure is called after the data has been returned, fulfill this expectation by indicating whatever it’s been waiting for has now happened.
  3. Set up a timeout for the test function to wait until the expectation has been fulfilled. In this case, if the web page hasn’t returned the data within 10 seconds, the expectation will timeout.

This time, put a breakpoint on the XCTAssertEqual() line, and it should trigger and the test will pass for real. If you want to see what happens when an expectation times out, set the timeout to something really small (0.1 works for me) and run the test again.

Now you know how to test asynchronously, which is really useful for network access and long background tasks. But what if you want to test your network code and you don’t have access to the internet, or the site is down, or you just want your tests to run faster?

In this case, you can use a testing technique called mocking to simulate your network call.

Mocking

In the real code, URLSession was used to start a URLSessionDataTask which returned the response. Since you don’t want to access the internet, you can test that the URLRequest is configured correctly, that the URLSessionDataTask is created and that the URLSessionDataTask is started.

You’re going to create mock versions of the classes involved: MockURLSession and MockURLSessionDataTask, which you can use instead of the real classes.

At the bottom of the WebSourcesTests.swift file, outside the WebSourceTests class, add the following two new classes:

class MockURLSession: URLSession {
  var url: URL?
  var dataTask = MockURLSessionTask()
 
  override func dataTask(with request: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> MockURLSessionTask {
      self.url = request.url
      return dataTask
  }
}
 
class MockURLSessionTask: URLSessionDataTask {
  var resumeGotCalled = false
 
  override func resume() {
    resumeGotCalled = true
  }
}

MockURLSession sub-classes URLSession, supplying an alternative version of dataTask(with:completionHandler:) that stores the URL from the supplied URLRequest and returns a MockURLSessionTask instead of a URLSessionDataTask.

MockURLSessionTask sub-classes URLSessionDataTask and when resume() is called, does not go online but instead sets a flag to show that this has happened.

Add the following to the WebSourceTests class and run the new test:

func testUsingMockURLSession() {
  // 1
  let address = "https://www.random.org/dice/?num=2"
  guard let url = URL(string: address) else {
    XCTFail()
    return
  }
  let request = URLRequest(url: url)
 
  // 2
  let mockSession = MockURLSession()
  XCTAssertFalse(mockSession.dataTask.resumeGotCalled)
  XCTAssertNil(mockSession.url)
 
  // 3
  let task = mockSession.dataTask(with: request) { (data, response, error) in }
  task.resume()
 
  // 4
  XCTAssertTrue(mockSession.dataTask.resumeGotCalled)
  XCTAssertEqual(mockSession.url, url)
}

What’s going on in this test?

  1. Construct the URLRequest as before.
  2. Create a MockURLSession and confirm the initial properties.
  3. Create the MockURLSessionTask and call resume().
  4. Test that the properties have changed as expected.

This test checks the first part of the process: the URLRequest, the URLSession and the URLSessionDataTask, and it tests that the data task is started. What is missing is any test for parsing the returned data.

There are two test cases you need to cover here: if the data returns matches the expected format, and if it does not.

Add these two tests to WebSourcesTests.swift and run them:

func testParsingGoodData() {
  let webSource = WebSource()
  let goodDataString = "<p>You rolled 2 dice:</p>\n<p>\n<img src=\"dice6.png\" alt=\"6\" />\n<img src=\"dice1.png\" alt=\"1\" />\n</p>"
  guard let goodData = goodDataString.data(using: .utf8) else {
    XCTFail()
    return
  }
 
  let diceArray = webSource.parseIncomingData(data: goodData)
  XCTAssertEqual(diceArray, [6, 1])
}
 
func testParsingBadData() {
  let webSource = WebSource()
  let badDataString = "This string is not the expected result"
  guard let badData = badDataString.data(using: .utf8) else {
    XCTFail()
    return
  }
 
  let diceArray = webSource.parseIncomingData(data: badData)
  XCTAssertEqual(diceArray, [])
}

Here you have used expectations to test the network connection, mocking to simulate the networking to allow tests independent of the network and a third-party web site, and finally supplied data to test the data parsing, again independently.

Performance Testing

Xcode also offers performance testing to check how fast your code executes. In Roll.swift, totalForDice() uses flatMap and reduce to calculate the total for the dice, allowing for the fact that value is an optional. But is this the fastest approach?

To test performance, select the High RollerTests group in the File Navigator and use File\New\File… to create a new macOS\Unit Test Case Class named PerformanceTests.

Delete the contents of the class and — you guessed it — add the following import as you’ve done before:

@testable import High_Roller

Insert this test function:

  func testPerformanceTotalForDice_FlatMap_Reduce() {
    // 1
    var roll = Roll()
    roll.changeNumberOfDice(newDiceCount: 20)
    roll.rollAll()
 
    // 2
    self.measure {
      // 3
      _ = roll.totalForDice()
    }
  }

The sections of this function are as follows:

  1. Set up a Roll with 20 Dice.
  2. self.measure defines the timing block.
  3. This is the code being measured.

Run the test and you will see a result like this:

PerformanceTest

As well as getting the green checkmark symbol, you will see a speed indicator which in my test shows “Time: 0.000 sec (98% STDEV)”. The standard deviation (STDEV) will indicate if there are any significant changes from the previous results. In this case, there is only one result — zero — so STDEV is meaningless. Also meaningless is a result of 0.000 seconds, so the test needs to be longer. The easiest way to do this is to add a loop that repeats the measure block enough times to get an actual time.

Replace the test with the following:

  func testPerformanceTotalForDice_FlatMap_Reduce() {
    var roll = Roll()
    roll.changeNumberOfDice(newDiceCount: 20)
    roll.rollAll()
 
    self.measure {
      for _ in 0 ..< 10_000 {
        _ = roll.totalForDice()
      }
    }
  }

Run the test again; the result you get will depend on your processor, but I get about 0.2 seconds. Adjust the loop counter from 10_000 until you get around 0.2.

Here are three other possible ways of adding up the total of the dice. Open Roll.swift in the assistant editor and add them as follows:

  func totalForDice2() -> Int {
    let total = dice
      .filter { $0.value != nil }
      .reduce(0) { $0 + $1.value! }
    return total
  }
 
  func totalForDice3() -> Int {
    let total = dice
      .reduce(0) { $0 + ($1.value ?? 0) }
    return total
  }
 
  func totalForDice4() -> Int {
    var total = 0
    for d in dice {
      if let dieValue = d.value {
        total += dieValue
      }
    }
    return total
  }

And here are the matching performance tests which you should add to PerformanceTests.swift:

  func testPerformanceTotalForDice2_Filter_Reduce() {
    var roll = Roll()
    roll.changeNumberOfDice(newDiceCount: 20)
    roll.rollAll()
 
    self.measure {
      for _ in 0 ..< 10_000 {
        _ = roll.totalForDice2()
      }
    }
  }
 
  func testPerformanceTotalForDice3_Reduce() {
    var roll = Roll()
    roll.changeNumberOfDice(newDiceCount: 20)
    roll.rollAll()
 
    self.measure {
      for _ in 0 ..< 10_000 {
        _ = roll.totalForDice3()
      }
    }
  }
 
  func testPerformanceTotalForDice4_Old_Style() {
    var roll = Roll()
    roll.changeNumberOfDice(newDiceCount: 20)
    roll.rollAll()
 
    self.measure {
      for _ in 0 ..< 10_000 {
        _ = roll.totalForDice4()
      }
    }
  }

Run these tests and work out which option is the fastest. Did you guess which one would win? I didn’t!

tortoise

Code Coverage

The final Xcode test tool to discuss is code coverage, which is the measure of how much of your code is covered during a series of tests. It’s turned off by default. To turn it on, select Edit Scheme… in the schemes popup at the top of the window. Select Test in the column on the left and then check Gather coverage data.

Turn-On-Code-Coverage

Close that window and press Command-U to re-run all the tests. Once the tests are complete, go to the Report Navigator and select the latest entry.

You’ll see the test report showing a series of green checkmarks, plus some timings for the performance tests. If you don’t see this, make sure both All toggles are selected at the top left.

Click on Coverage at the top of this display and mouse over the top of the blue bars to see that your tests cover nearly 80% of your code. Amazing work! :]

Code-Coverage-4

The two model objects (Dice and Roll) are very well covered. If you are only going to add some tests, the model is the best place to start.

There is another good, fast way to improve code coverage: delete code that isn’t being used. Look at the coverage for AppDelegate.swift, it’s at 50%.

Go to the AppDelegate.swift file. On the gutter on the right-hand side, mouse up and down and you’ll see it shows green for methods called during the tests, and red for methods that are not called.

Uncovered-code

In this case, applicationWillTerminate(_:) is not used at all; it’s dramatically decreasing the code coverage for this file. Since the app is not using this function, delete it. Now run all the tests again and AppDelegate.swift has jumped to 100% coverage.

This may seem to be cheating the system, but it is actually good practice to remove any dead code that is cluttering up your app. Xcode tries to be helpful when you make a new file and supplies lots of boilerplate code by default, but if you don’t need any of this, delete it.

Note: If you find the code coverage gutter and flashes of red and green distracting, turn them off by selecting Hide Code Coverage from the Editor menu. This doesn’t stop Xcode gathering the code coverage data, but stops it being displayed while you edit.

Now for the warning about code coverage: it is a tool, not a goal! Some developers and employers treat it as a goal and insist on a certain percentage. But it is possible to get a good percentage without testing meaningfully. Tests have to be well thought out and not just added for the sake of increasing your code coverage.

Tests may call numerous functions in your code without actually checking the result. While a high code coverage number is probably better than a low one, it doesn’t say anything about the quality of the tests.

Where to Go From Here?

You can download the final version of the sample project here.

Apple has a set of docs about Testing with Xcode with links to relevant WWDC videos.

NSHipster has a useful summary of the various assertions and what you really need to know to write tests.

For information about Test Driven Development, check out Uncle Bob’s excellent site.

Interested in learning more about UITests? Check out the Joe Masilotti’s excellent cheat sheet.

I hope you enjoyed this Unit testing tutorial; if you have any questions or comments, please join the forum discussion below!

The post Unit Testing on macOS: Part 2/2 appeared first on Ray Wenderlich.

Screencast: Beginning C# Part 10: Variable Scope

An iOS 10 Surprise Coming Tomorrow!

$
0
0

We’ve got a special iOS 10 surprise coming for you tomorrow to coincide with the release of iOS 10:

surprise

Are there any sharp readers out there who can guess what it is? :]

Check back on Tuesday to see what we’ve been cooking up — and make sure to come hungry!

The post An iOS 10 Surprise Coming Tomorrow! appeared first on Ray Wenderlich.

Geofencing Tutorial with Core Location

$
0
0
Let's get geofencing!

Let’s get geofencing!

Update Note: This tutorial has been updated for Xcode 8 / Swift 3.

Geofencing notifies your app when its device enters or leaves geographical regions you set up. It lets you make cools that can trigger a notification whenever you leave home, or greet users with the latest and greatest deals whenever favorite shops are nearby. In this geofencing tutorial, you’ll learn how to use region monitoring in iOS with Swift – using the Region Monitoring API in Core Location.

In particular, you’ll create a location-based reminder app called Geotify that will let the user create reminders and associate them with real-world locations. Let’s get started!

Getting Started

Download the starter project. The project provides a simple user interface for adding/removing annotation items to/from a map view. Each annotation item represents a reminder with a location, or as I like to call it, a geotification. :]

Build and run the project, and you’ll see an empty map view.

Interface of Geotify

Tap on the + button on the navigation bar to add a new geotification. The app will present a separate view, allowing you to set up various properties for your geotification.

For this tutorial, you will add a pin on Apple’s headquarters in Cupertino. If you don’t know where it is, open this google map in a separate tab and use it to hunt the right spot. Be sure to zoom in to make the pin nice and accurate!

Note: To pinch to zoom on the simulator, hold down option, then hold shift temporarily to move the pinch center, then release shift and click-drag to pinch.

iOS Simulator Screen Shot 20 Feb 2015 10.18.19 pm

The radius represents the distance in meters from the specified location, at which iOS will trigger the notification. The note can be any message you wish to display during the notification. The app also lets the user specify whether it should trigger the reminder upon either entry or exit of the defined circular geofence, via the segmented control at the top.

Enter 1000 for the radius value and Say Hi to Tim! for the note, and leave it as Upon Entry for your first geotification.

Click Add once you’re satisfied with all the values. You’ll see your geotification appear as a new annotation pin on the map view, with a circle around it denoting the defined geofence:

A Geotification

Tap on the pin and you’ll reveal the geotification’s details, such as the reminder note and the event type you specified earlier. Don’t tap on the little cross unless you want to delete the geotification!

Feel free to add or remove as many geotifications as you want. As the app uses NSUserDefaults as a persistence store, the list of geotifications will persist between relaunches.

Setting Up a Location Manager and Permissions

At this point, any geotifications you’ve added to the map view are only for visualization. You’ll fix this by taking each geotification and registering its associated geofence with Core Location for monitoring.

Before any geofence monitoring can happen, though, you need to set up a Location Manager instance and request the appropriate permissions.

Open GeotificationsViewController.swift and declare a constant instance of a CLLocationManager near the top of the class, as shown below:

class GeotificationsViewController: UIViewController {
 
  @IBOutlet weak var mapView: MKMapView!
 
  var geotifications = [Geotification]()
  let locationManager = CLLocationManager() // Add this statement
 
  ...
}

Next, replace viewDidLoad() with the following code:

override func viewDidLoad() {
  super.viewDidLoad()
  // 1
  locationManager.delegate = self
  // 2
  locationManager.requestAlwaysAuthorization()
  // 3
  loadAllGeotifications()
}

Let’s run through this method step by step:

  1. You set the view controller as the delegate of the locationManager instance so that the view controller can receive the relevant delegate method calls.
  2. You make a call to requestAlwaysAuthorization(), which invokes a prompt to the user requesting for Always authorization to use location services. Apps with geofencing capabilities need Always authorization, due to the need to monitor geofences even when the app isn’t running. Info.plist has already been setup with a message to show the user when requesting the user’s location under the key NSLocationAlwaysUsageDescription.
  3. You call loadAllGeotifications(), which deserializes the list of geotifications previously saved to NSUserDefaults and loads them into a local geotifications array. The method also loads the geotifications as annotations on the map view.

When the app prompts the user for authorization, it will show NSLocationAlwaysUsageDescription, a user-friendly explanation of why the app requires access to the user’s location. This key is mandatory when you request authorization for location services. If it’s missing, the system will ignore the request and prevent location services from starting altogether.

Build and run the project, and you’ll see a user prompt with the aforementioned description that’s been set:

Requesting Always Authorization

You’ve set up your app to request the required permission. Great! Click or tap Allow to ensure the location manager will receive delegate callbacks at the appropriate times.

Before you proceed to implement the geofencing, there’s a small issue you have to resolve: the user’s current location isn’t showing up on the map view! This feature is disabled, and as a result, the zoom button on the top-left of the navigation bar doesn’t work.

Fortunately, the fix is not difficult — you’ll simply enable the current location only after the app is authorized.

In GeotificationsViewController.swift, add the following delegate method to the CLLocationManagerDelegate extension:

extension GeotificationsViewController: CLLocationManagerDelegate {
  func locationManager(_ manager: CLLocationManager, didChangeAuthorization status: CLAuthorizationStatus) {
    mapView.showsUserLocation = (status == .authorizedAlways)
  }
}

The location manager calls locationManager(_:didChangeAuthorizationStatus:) whenever the authorization status changes. If the user has already granted the app permission to use Location Services, this method will be called by the location manager after you’ve initialized the location manager and set its delegate.

That makes this method an ideal place to check if the app is authorized. If it is, you enable the map view to show the user’s current location.

Build and run the app. If you’re running it on a device, you’ll see the location marker appear on the main map view. If you’re running on the simulator, click Debug\Location\Apple in the menu to see the location marker:

view-location-on-simulator

In addition, the zoom button on the navigation bar now works. :]

zoom-in-simulator

Registering Your Geofences

With the location manager properly configured, the next order of business is to allow your app to register user geofences for monitoring.

In your app, the user geofence information is stored within your custom Geotification model. However, Core Location requires each geofence to be represented as a CLCircularRegion instance before it can be registered for monitoring. To handle this requirement, you’ll create a helper method that returns a CLCircularRegion from a given Geotification object.

Open GeotificationsViewController.swift and add the following method to the main body:

func region(withGeotification geotification: Geotification) -> CLCircularRegion {
  // 1
  let region = CLCircularRegion(center: geotification.coordinate, radius: geotification.radius, identifier: geotification.identifier)
  // 2
  region.notifyOnEntry = (geotification.eventType == .onEntry)
  region.notifyOnExit = !region.notifyOnEntry
  return region
}

Here’s what the above method does:

  1. You initialize a CLCircularRegion with the location of the geofence, the radius of the geofence and an identifier that allows iOS to distinguish between the registered geofences of a given app. The initialization is rather straightforward, as the Geotification model already contains the required properties.
  2. The CLCircularRegion instance also has two Boolean properties, notifyOnEntry and notifyOnExit. These flags specify whether geofence events will be triggered when the device enters and leaves the defined geofence, respectively. Since you’re designing your app to allow only one notification type per geofence, you set one of the flags to true while you set the other to false, based on the enum value stored in the Geotification object.

Next, you need a method to start monitoring a given geotification whenever the user adds one.

Add the following method to the body of GeotificationsViewController:

func startMonitoring(geotification: Geotification) {
  // 1
  if !CLLocationManager.isMonitoringAvailable(for: CLCircularRegion.self) {
    showAlert(withTitle:"Error", message: "Geofencing is not supported on this device!")
    return
  }
  // 2
  if CLLocationManager.authorizationStatus() != .authorizedAlways {
    showAlert(withTitle:"Warning", message: "Your geotification is saved but will only be activated once you grant Geotify permission to access the device location.")
  }
  // 3
  let region = self.region(withGeotification: geotification)
  // 4
  locationManager.startMonitoring(for: region)
}

Let’s walk through the method step by step:

  1. isMonitoringAvailableForClass(_:) determines if the device has the required hardware to support the monitoring of geofences. If monitoring is unavailable, you bail out entirely and alert the user accordingly. showSimpleAlertWithTitle(_:message:viewController) is a helper function in Utilities.swift that takes in a title and message and displays an alert view.
  2. Next, you check the authorization status to ensure that the app has also been granted the required permission to use Location Services. If the app isn’t authorized, it won’t receive any geofence-related notifications.

    However, in this case, you’ll still allow the user to save the geotification, since Core Location lets you register geofences even when the app isn’t authorized. When the user subsequently grants authorization to the app, monitoring for those geofences will begin automatically.

  3. You create a CLCircularRegion instance from the given geotification using the helper method you defined earlier.
  4. Finally, you register the CLCircularRegion instance with Core Location for monitoring.

With your start method done, you also need a method to stop monitoring a given geotification when the user removes it from the app.

In GeotificationsViewController.swift, add the following method below startMonitoringGeotificiation(_:):

func stopMonitoring(geotification: Geotification) {
  for region in locationManager.monitoredRegions {
    guard let circularRegion = region as? CLCircularRegion, circularRegion.identifier == geotification.identifier else { continue }
    locationManager.stopMonitoring(for: circularRegion)
  }
}

The method simply instructs the locationManager to stop monitoring the CLCircularRegion associated with the given geotification.

Now that you have both the start and stop methods complete, you’ll use them whenever you add or remove a geotification. You’ll begin with the adding part.

First, take a look at addGeotificationViewController(_:didAddCoordinate) in GeotificationsViewController.swift.

The method is the delegate call invoked by the AddGeotificationViewController upon creating a geotification; it’s responsible for creating a new Geotification object using the values passed from AddGeotificationsViewController, and updating both the map view and the geotifications list accordingly. Then you call saveAllGeotifications(), which takes the newly-updated geotifications list and persists it via NSUserDefaults.

Now, replace the method with the following code:

func addGeotificationViewController(controller: AddGeotificationViewController, didAddCoordinate coordinate: CLLocationCoordinate2D, radius: Double, identifier: String, note: String, eventType: EventType) {
  controller.dismiss(animated: true, completion: nil)
  // 1
  let clampedRadius = min(radius, locationManager.maximumRegionMonitoringDistance)
  let geotification = Geotification(coordinate: coordinate, radius: clampedRadius, identifier: identifier, note: note, eventType: eventType)
  add(geotification: geotification)
  // 2
  startMonitoring(geotification: geotification)
  saveAllGeotifications()
}

You’ve made two key changes to the code:

  1. You ensure that the value of the radius is clamped to the maximumRegionMonitoringDistance property of locationManager, which is defined as the largest radius in meters that can be assigned to a geofence. This is important, as any value that exceeds this maximum will cause monitoring to fail.
  2. You add a call to startMonitoringGeotification(_:) to ensure that the geofence associated with the newly-added geotification is registered with Core Location for monitoring.

At this point, the app is fully capable of registering new geofences for monitoring. There is, however, a limitation: As geofences are a shared system resource, Core Location restricts the number of registered geofences to a maximum of 20 per app.

While there are workarounds to this limitation (See Where to Go From Here? for a short discussion), for the purposes of this tutorial, you’ll take the approach of limiting the number of geotifications the user can add.

Add a line to updateGeotificationsCount(), as shown in the code below:

func updateGeotificationsCount() {
  title = "Geotifications (\(geotifications.count))"
  navigationItem.rightBarButtonItem?.isEnabled = (geotifications.count < 20)  // Add this line
}

This line disables the Add button in the navigation bar whenever the app reaches the limit.

Finally, let’s deal with the removal of geotifications. This functionality is handled in mapView(_:annotationView:calloutAccessoryControlTapped:), which is invoked whenever the user taps the “delete” accessory control on each annotation.

Add a call to stopMonitoring(geotification:) to mapView(_:annotationView:calloutAccessoryControlTapped:), as shown below:

func mapView(_ mapView: MKMapView, annotationView view: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
  // Delete geotification
  let geotification = view.annotation as! Geotification
  stopMonitoring(geotification: geotification)   // Add this statement
  removeGeotification(geotification)
  saveAllGeotifications()
}

The additional statement stops monitoring the geofence associated with the geotification, before removing it and saving the changes to NSUserDefaults.

At this point, your app is fully capable of monitoring and un-monitoring user geofences. Hurray!

Build and run the project. You won’t see any changes, but the app will now be able to register geofence regions for monitoring. However, it won’t be able to react to any geofence events just yet. Not to worry—that will be your next order of business!

These_eyes

Reacting to Geofence Events

You’ll start by implementing some of the delegate methods to facilitate error handling – these are important to add in case anything goes wrong.

In GeotificationsViewController.swift, add the following methods to the CLLocationManagerDelegate extension:

func locationManager(_ manager: CLLocationManager, monitoringDidFailFor region: CLRegion?, withError error: Error) {
  print("Monitoring failed for region with identifier: \(region!.identifier)")
}
 
func locationManager(_ manager: CLLocationManager, didFailWithError error: Error) {
  print("Location Manager failed with the following error: \(error)")
}

These delegate methods simply log any errors that the location manager encounters to facilitate your debugging.

Note: You’ll definitely want to handle these errors more robustly in your production apps. For example, instead of failing silently, you could inform the user what went wrong.

Next, open AppDelegate.swift; this is where you’ll add code to properly listen and react to geofence entry and exit events.

Add the following line at the top of the file to import the CoreLocation framework:

import CoreLocation

Ensure that the AppDelegate has a CLLocationManager instance near the top of the class, as shown below:

class AppDelegate: UIResponder, UIApplicationDelegate {
  var window: UIWindow?
 
  let locationManager = CLLocationManager() // Add this statement
  ...
}

Replace application(_:didFinishLaunchingWithOptions:) with the following implementation:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool {
  locationManager.delegate = self
  locationManager.requestAlwaysAuthorization()
  return true
}

You’ve set up your AppDelegate to receive geofence-related events. But you might wonder, “Why did I designate the AppDelegate to do this instead of the view controller?”

Geofences registered by an app are monitored at all times, including when the app isn’t running. If the device triggers a geofence event while the app isn’t running, iOS automatically relaunches the app directly into the background. This makes the AppDelegate an ideal entry point to handle the event, as the view controller may not be loaded or ready.

Now you might also wonder, “How will a newly-created CLLocationManager instance be able to know about the monitored geofences?”

It turns out that all geofences registered by your app for monitoring are conveniently accessible by all location managers in your app, so it doesn’t matter where the location managers are initialized. Pretty nifty, right? :]

Now all that’s left is to implement the relevant delegate methods to react to the geofence events. Before you do so, you’ll create a method to handle a geofence event.

Add the following method to AppDelegate.swift:

func handleEvent(forRegion region: CLRegion!) {
  print("Geofence triggered!")
}

At this point, the method takes in a CLRegion and simply logs a statement. Not to worry—you’ll implement the event handling later.

Next, add the following delegate methods in the CLLocationManagerDelegate extension of AppDelegate.swift, as well as a call to the handleRegionEvent(_:) function you just created, as shown in the code below:

extension AppDelegate: CLLocationManagerDelegate {
 
  func locationManager(_ manager: CLLocationManager, didEnterRegion region: CLRegion) {
    if region is CLCircularRegion {
      handleEvent(forRegion: region)
    }
  }
 
  func locationManager(_ manager: CLLocationManager, didExitRegion region: CLRegion) {
    if region is CLCircularRegion {
      handleEvent(forRegion: region)
    }
  }
}

As the method names aptly suggest, you fire locationManager(_:didEnterRegion:) when the device enters a CLRegion, while you fire locationManager(_:didExitRegion:) when the device exits a CLRegion.

Both methods return the CLRegion in question, which you need to check to ensure it’s a CLCircularRegion, since it could be a CLBeaconRegion if your app happens to be monitoring iBeacons, too. If the region is indeed a CLCircularRegion, you accordingly call handleRegionEvent(_:).

Note: A geofence event is triggered only when iOS detects a boundary crossing. If the user is already within a geofence at the point of registration, iOS won’t generate an event. If you need to query whether the device location falls within or outside a given geofence, Apple provides a method called requestStateForRegion(_:).

Now that your app is able to receive geofence events, you’re ready to give it a proper test run. If that doesn’t excite you, it really ought to, because for the first time in this tutorial, you’re going to see some results. :]

The most accurate way to test your app is to deploy it on your device, add some geotifications and take the app for a walk or a drive. However, it wouldn’t be wise to do so right now, as you wouldn’t be able to verify the print logs emitted by the geofence events with the device unplugged. Besides, it would be nice to get assurance that the app works before you commit to taking it for a spin.

Fortunately, there’s an easy way do this without leaving the comfort of your home. Xcode lets you include a hardcoded waypoint GPX file in your project that you can use to simulate test locations. Lucky for you, the starter project includes one for your convenience. :]

Open up TestLocations.gpx, which you can find in the Supporting Files group, and inspect its contents. You’ll see the following:

<?xml version="1.0"?>
<gpx version="1.1" creator="Xcode">
  <wpt lat="37.422" lon="-122.084058">
    <name>Google</name>
  </wpt>
  <wpt lat="37.3270145" lon="-122.0310273">
    <name>Apple</name>
  </wpt>
</gpx>

The GPX file is essentially an XML file that contains two waypoints: Google’s Googleplex in Mountain View and Apple’s Headquarters in Cupertino.

To begin simulating the locations in the GPX file, build and run the project. When the app launches the main view controller, go back to Xcode, select the Location icon in the Debug bar and choose TestLocations:

Test Locations

Back in the app, use the Zoom button on the top-left of the navigation bar to zoom to the current location. Once you get close to the area, you’ll see the location marker moving repeatedly from the Googleplex to Apple, Inc. and back.

Test the app by adding a few geotifications along the path defined by the two waypoints. If you added any geotifications earlier in the tutorial before you enabled geofence registration, those geotifications will obviously not work, so you might want to clear them out and start afresh.

For the test locations, it’s a good idea to place a geotification roughly at each waypoint. Here’s a possible test scenario:

  • Google: Radius: 1000m, Message: “Say Bye to Google!”, Notify on Exit
  • Apple: Radius: 1000m, Message: “Say Hi to Apple!”, Notify on Entry

Screenshot of two geofences

Once you’ve added your geotifications, you’ll see a log in the console each time the location marker enters or leaves a geofence. If you activate the home button or lock the screen to send the app to the background, you’ll also see the logs each time the device crosses a geofence, though you obviously won’t be able to verify that behavior visually.

Geofence triggered

Note: Location simulation works both in iOS Simulator and on a real device. However, the iOS Simulator can be quite inaccurate in this case; the timings of the triggered events do not coincide very well with the visual movement of the simulated location in and out of each geofence. You would do better to simulate locations on your device, or better still, take the app for a walk!

Notifying the User of Geofence Events

You’ve made a lot of progress with the app. At this point, it simply remains for you to notify the user whenever the device crosses the geofence of a geotification—so prepare yourself to do just that.

To obtain the note associated with a triggering CLCircularRegion returned by the delegate calls, you need to retrieve the corresponding geotification that was persisted in NSUserDefaults. This turns out to be trivial, as you can use the unique identifier you assigned to the CLCircularRegion during registration to find the right geotification.

In AppDelegate.swift, add the following helper method at the bottom of the class:

func note(fromRegionIdentifier identifier: String) -> String? {
  let savedItems = UserDefaults.standard.array(forKey: PreferencesKeys.savedItems) as? [NSData]
  let geotifications = savedItems?.map { NSKeyedUnarchiver.unarchiveObject(with: $0 as Data) as? Geotification }
  let index = geotifications?.index { $0?.identifier == identifier }
  return index != nil ? geotifications?[index!]?.note : nil
}

This helper method retrieves the geotification note from the persistent store, based on its identifier, and returns the note for that geotification.

Now that you’re able to retrieve the note associated with a geofence, you’ll write code to trigger a notification whenever a geofence event is fired, using the note as the message.

Add the following statements to the end of application(_:didFinishLaunchingWithOptions:), just before the method returns:

application.registerUserNotificationSettings(UIUserNotificationSettings(types: [.sound, .alert, .badge], categories: nil))
UIApplication.shared.cancelAllLocalNotifications()

The code you’ve added prompts the user for permission to enable notifications for this app. In addition, it does some housekeeping by clearing out all existing notifications.

Next, replace the contents of handleRegionEvent(_:) with the following:

func handleEvent(forRegion region: CLRegion!) {
  // Show an alert if application is active
  if UIApplication.shared.applicationState == .active {
    guard let message = note(fromRegionIdentifier: region.identifier) else { return }
    window?.rootViewController?.showAlert(withTitle: nil, message: message)
  } else {
    // Otherwise present a local notification
    let notification = UILocalNotification()
    notification.alertBody = note(fromRegionIdentifier: region.identifier)
    notification.soundName = "Default"
    UIApplication.shared.presentLocalNotificationNow(notification)
  }
}

If the app is active, the code above simply shows an alert controller with the note as the message. Otherwise, it presents a location notification with the same message.

Build and run the project, and run through the test procedure covered in the previous section. Whenever your test triggers a geofence event, you’ll see an alert controller displaying the reminder note:

Say Bye to Google!

Send the app to the background by activating the Home button or locking the device while the test is running. You’ll continue to receive notifications periodically that signal geofence events:

Geotify Notifications on Lock screen

And with that, you have a fully functional, location-based reminder app in your hands. And yes, get out there and take that app for a spin!

Note: When you test the app, you may encounter situations where the notifications don’t fire exactly at the point of boundary crossing.

This is because before iOS considers a boundary as crossed, there is an additional cushion distance that must be traversed and a minimum time period that the device must linger at the new location. iOS internally defines these thresholds, seemingly to mitigate the spurious firing of notifications in the event the user is traveling very close to a geofence boundary.

In addition, these thresholds seem to be affected by the available location hardware capabilities. From experience, the geofencing behavior is a lot more accurate when Wi-Fi is enabled on the device.

Where to Go From Here?

You can download the complete project for this tutorial here.

Congratulations—you’re now equipped with the basic knowledge you need to build your own geofencing-enabled apps!

Geofencing is a powerful technology with many practical and far-reaching applications in such realms as marketing, resource management, security, parental control and even gaming—what you can achieve is really up to your imagination.

If you’re looking for additional challenges or learning opportunities, consider the following:

  • Allow the user to edit an existing geotification.
  • Enable the app to handle more than 20 geofences by selectively monitoring geofences that are in the user’s vicinity. One way to achieve this is by monitoring for significant location changes and selecting the 20 geofences closest to the user’s current location.

I hope you’ve enjoyed this tutorial, and I really look forward to seeing how you use geofencing in your apps. Feel free to leave a comment or question below!

The post Geofencing Tutorial with Core Location appeared first on Ray Wenderlich.

Introducing the iOS 10 Feast!

$
0
0

It’s the iOS 10 release date, and you know what that means: time to party!

This year marks our 6th annual iOS release celebration: the iOS 10 Feast.

iOS10Feast

The iOS 10 Feast consists of 10 courses:

  • Appetizer: Learning Swift 3
  • First Course: iOS Apprentice Fifth Edition
  • Second course: tvOS Apprentice Second Edition
  • Third course: watchOS by Tutorials Second Edition
  • Fourth course: Core Data by Tutorials Third Edition
  • Fifth course: 2D Apple Games by Tutorials Second Edition
  • Sixth course: 3D Apple Games by Tutorials Second Edition
  • Seventh course: iOS Animations by Tutorials Third Edition
  • Eighth course: iOS 10 by Tutorials
  • Ninth Course: Unity Apprentice
  • Tenth Course: iOS 10 Feast Giveaway

Plus, we have over $30,000 in giveaways for lucky readers – our biggest giveaway yet!

And right now, we’re offering 10% off anything in the raywenderlich.com store with coupon code IOS10FEAST!

Let’s take a closer look at what’s inside.

Appetizer: Learning Swift 3

swifty-1

Apple unveiled a lot of changes in iOS, tvOS, watchOS, and macOS this year, but there’s one thing that will affect every developer: Swift 3.

Swift 3 includes massive changes: to the Swift language, and the Cocoa APIs it uses. This means that if you’re porting your code to Swift 3, you’re going to need to make a ton of changes!

You may have found it hard to keep track of what’s going on with Swift 3 due to its constant evolution, but don’t worry: we’ve got you covered.

So let’s get this feast started with a tasty treat: Learning Swift 3, raywenderlich.com style!

If you are a beginner to Swift

If you are a beginner to Swift, now is a great time to learn: you can learn Swift 3 from the ground up!

There are two ways to learn Swift 3 at raywenderlich.com:

  • If you prefer to learn by books, you should check out the Swift Apprentice. We’re happy to announce that we’ve updated the book for Swift 3, and it is 100% complete and available for download today!

SA-store

  • If you prefer to learn by video, you should check out our new video course Beginning Swift, which walks you through the basics of Swift 3 development. All episodes are available today for raywenderlich.com subscribers.

Beginning Swift 3

If you are an experienced Swift developer

If you’re an experienced Swift developer, we understand your time is valuable.

So how about learning Swift 3 in just 3 minutes?

UNLOCKED! Click to play - Sam Davies and "Swift 3 in 3 Minutes"

UNLOCKED! Click to play – Sam Davies and “Swift 3 in 3 Minutes”

We were planning on releasing this screencast, and three more Swift 3 screencasts, to raywenderlich.com subscribers tomorrow. But how would you like to view them right now?

All you have to do is tweet this post to help get the word out! If you can help us get 199 retweets, we’ll immediately open the doors and let you view those screencasts for free.

Update: Congratulations, you did it! By helping to spread the word about the iOS 10 Feast, you unlocked Swift 3 screencasts for free. Click on the video still above to view. We hope you enjoy!

Jump-start your Swift 3 learning and click the button below:


First Course: iOS Apprentice Fifth Edition

Is your appetite whetted? Great, then it’s time to start the first course: the iOS Apprentice Fifth Edition!

IA1234-store
The iOS Apprentice is our book for complete beginners to iOS development. It has a series of epic-length tutorials that each show you how to build four complete apps from scratch.

In this update, Matthijs has completely updated the text and illustrations for Xcode 8, iOS 10, and Swift 3. This will be a free update for existing PDF customers, to thank you for supporting this site.

Did you know that iOS Apprentice was first written for iOS 5, and Matthijs has updated it for every version of iOS since then for free? You can’t beat that value! :]

“Over the years, I have read iOS books/ebooks by Dave Mark, Big Nerd Ranch, Wei-Ming Lee, Neil Smythe, Matt Neuburg, many RW tutorials and probably several others, but Matthijs Hollemans’ tutorials absolutely tower over the rest. . . .Matthijs’s knowledge is profound and his presentations are flawless, but his detailed explanations are pure dev gold.” –chicago in a recent forum post

The iOS Apprentice Fifth Edition will launch on Wednesday, Sep. 21. Enjoy!

[Order Now]

Second Course: tvOS Apprentice Second Edition

TVT-store

Guess what: we updated the tvOS Apprentice too!

In the tvOS Apprentice Second Edition, you learn how to make tvOS apps from the ground up, with a series of tutorials for complete beginners. You’ll learn how to make tvOS apps in two different ways: via the traditional method using UIKit, and via the new Client-Server method using TVML.

You’ll learn how to download video and other content and display it in your app, built great-looking interfaces with UI elements designed for the Apple TV experience, monetize your app with in-app purchases, add animations, and much, much more!

The second edition includes some brand new new content:

  • A new chapter on PhotoKit (introduced in tvOS 10) to show great Photo Library integrations.
  • A new chapter on Multipeer Connectivity (also introduced in tvOS 10) to show how tvOS and iOS apps can better communicate with one another.
  • Inclusion of Dark Mode APIs in the UI chapters to show how your interface can better display in low light interfaces.

This is also a free update for existing PDF customers, and will be released Wednesday, Sep 28.

We can’t wait to show you the new edition of the book!

[Order Now]

Third Course: watchOS by Tutorials, Second Edition

W2T-store

There’s yet another update to announce — the second edition of watchOS by Tutorials!

watchOS continues to evolve rapidly, and the latest version — watchOS 3 — is no exception. We’ve updated the entire book to show you how to take advantage of all the great stuff in watchOS 3, including working with the digital crown, handling gestures, persist and retrieve data from CloudKit, and more.

We’ve included four new chapters in the second edition:

  • Designing Great Watch Apps: Apple has repeatedly emphasized glanceable, actionable, and responsive as the design goal of watchOS 3 apps. From icon design to the new interactivity APIs, make your apps stand out from the rest.
  • CloudKit: Learn how to persist and retrieve data with CloudKit and keep your Watch and iPhone synchronized — even when they’re not in range of each other.
  • Snapshot API: Glances are out, snapshots are in. Learn how to make your app appear in the new Dock — and update the icon dynamically!
  • Digital Crown and Gesture Recognizers: Explore the rich set of physical interactions with the Watch, including the Digital Crown, pan gestures, and force touch!

This is also a free update for existing PDF customers, and will be released on Monday, Oct 3.

We hope you enjoy! :]

[Order Now]

Fourth Course: Core Data by Tutorials, Third Edition

CD-store

Marching right along, we’re pleased to bring you the third edition of Core Data by Tutorials!

Core Data in iOS 10 is all making Core Data easier to learn and use:

  • Say goodbye to screenfuls of code just for setting up the Core Data “stack”.
  • Say goodbye to generating and maintaining managed object subclasses. Say goodbye to having to tell Swift what type of managed object you’re dealing with.
  • Say goodbye to single readers at the persistent store level.
  • And say goodbye (a bit sadly) to iCloud support for Core Data, which has been deprecated. Well, it can’t all be good news. ;]

Core Data by Tutorials teaches you everything you need to know about Core Data, starting with the basics like setting up your own Core Data Stack and moving all the way to advanced topics like migration, performance, multithreading, and more.

This is also a free update for existing PDF customers, and will be released on Wednesday, Oct 5. We hope you enjoy! :]

[Order Now]

Fifth Course: 2D Apple Games by Tutorials, Second Edition

igt1-store

At WWDC 2016, Apple announced a lot of cool new features to both SpriteKit and Xcode, including built-in tile map support and the ability to run SpriteKit on watchOS. Yes, you read that correctly! :]

These changes were so significant that we decided it would be better to completely revamp 2D Games by Tutorials (again!) — we even included new games!

Here are the highlights of this edition:

  • Zombie Conga: Fully updated for Swift 3 and iOS 10. We’ve moved the detail about porting to tvOS to a brand-new section — “Section V: Other Platforms” — to give it a more in-depth treatment.
  • Cat Nap: Fully updated for Swift 3 and iOS 10. Again, we’ve moved the tvOS detail to Section V.
  • (New!) Pest Control: First introduced in iOS Games by Tutorials, this new and improved version uses the new tile maps features in SpriteKit and covers saving and loading game data.
  • Drop Charge: Fully updated for Swift 3 and iOS 10. We’ve taken the GameplayKit features and migrated them out of this version so we can cover them in depth in a future book.
  • (New!) Zombie Piranhas: A completely new game specifically designed to teach you how to work with other Apple platforms like macOS, tvOS and watchOS.
  • CircuitRacer: Fully updated for Swift 3 and iOS 10 and updated to reflect recent iAd changes.

Current 2D Apple Games by Tutorials PDF customers will receive this as a free update.

2D Apple Games by Tutorials will be released Monday, Oct 10 – get ready to game on! :]

[Order Now]

Sixth Course: 3D Apple Games by Tutorials, Second Edition

igt2-store

Creating amazing 3D games is easier than ever, thanks to the advent of SceneKit. The simplicity of SceneKit lets beginners create simple and stylish games in a short amount of time. Yet it’s also powerful enough to satisfy the needs of advanced developers, who want to create the next FPS killer.

We’ve updated the entire book to reflect the new features of SceneKit and brought it fully up to date with iOS 10 and Xcode 8. We’ve added a new chapter dedicated to creating 3D art:

MagicaVoxelExport00

  • 3D Art for Programmers: Learn how to create your own voxel art like Mr. Pig!

We’ve also broken out the following three chapters on converting your game from iOS to other platforms to give you more in-depth detail on the process:

  • macOS Games: Take your 3D games from the mobile screen to the desktop; learn how to port your games to macOS.
  • tvOS Games: See your 3D games on the big screen; learn how to port your games to tvOS.
  • watchOS Games: Your favorite games are as close as a flick of the wrist; learn how to port your games to watchOS.

3D Apple Games by Tutorials will be released Wednesday, Oct. 12.

[Order Now]

Seventh Course: iOS Animations by Tutorials, Third Edition

iAT-store

Next, we are happy to announce the third edition of Marin Todorov’s popular book: iOS Animations by Tutorials!

iOS Animations by Tutorials teaches you how to make delightful animations in your apps with Swift 3. You start with basic view and layer animations, move all the way through Auto Layout animations, view controller transitions, and even investigate 3D animations.

This is a free update for existing PDF customers — we’re amazed to see how popular this book has become!

iOS Animations by Tutorials Second Edition will be released Wednesday, Oct 19.

[Order Now]

Marin and I hope you enjoy the new chapters!

Eighth Course: iOS 10 by Tutorials

i10T-store

We’ve been releasing early access versions of iOS 10 by Tutorials this season, and we hope you’ve had a chance to check out the accompanying screencasts from our own Sam Davies.

We’re gearing up for the final release of iOS 10 by Tutorials, where you’ll learn about the following new features of iOS 10 and Swift 3:

  • Migrating to Swift 3: Learn how to migrate your code to Swift 3, including understanding the API naming conventions, updates to Foundation, switching toolchains, and more.
  • Message Apps: Learn how to integrate your own functionality into Apple’s messages apps, including sticker packs, collaborative messages, and more.
  • SiriKit: Learn how to integrate your apps with Siri, including making an extension, handling the conversation, and providing a custom UI.
  • UIView Property Animator: Learn about the new UIView Property Animator API, including using it for basic animations, interactive animations, and view controller transitions.
  • Speech Recognizer: Learn about the new speech recognizer that allows you to easily detect speech from within your apps.
  • Memory Debugger: Learn about Xcode 8’s powerful new memory debugger that allows you to easily detect and fix retain cycles and other memory problems.
  • Other Xcode 8 Improvements: Learn about other handy new features in Xcode 8, such as Auto Layout improvements, the new threading debugger, editor enhancements, and more.
  • User Notifications: Learn about the new user notifications API that consolidates remote and local notifications, and new features such as advanced appearance and in-app presentation.
  • Photography: Learn about some new features with photography in iOS 10, such as capturing and editing live photos, and advanced photo capture techniques.
  • And much, much more: App Transport security changes, UIControl updates, search and proactive improvements, and more.

iOS 10 by Tutorials will be released Wednesday, Oct. 26 — and don’t forget that you’ll get this book for free if you are a raywenderlich.com subscriber!

We’ve enjoyed working on the early releases, and we hope you enjoy the finished product!

[Order Now]

Ninth Course: Unity Games by Tutorials

UA-store

Hopefully you caught our surprise early access launch of Unity Games by Tutorials back at the end of August! If not, check out the early access trailer:

Here’s why Unity is a fantastic platform for game development:

  • It’s free to use. If you’re an indie game developer, you can download and start using Unity for free, which is great when you’re just learning.
  • It’s cross-platform. With Unity, you make your game once and you can build it for a variety of platforms, including Windows, macOS, Linux, iOS, and more.
  • It has a visual editor. Unlike other game platforms where you have to type tons of code before you see anything on the screen, with Unity you can simply import an asset and drag and drop. This visual style of development is great for beginners and professionals alike, and makes game development fast and fun.
  • Live debugging. With Unity you can click a button to preview your game instantly in the editor, and you can even modify game objects on the fly. For example, you can drag new enemies onto the level as you play it, tweak gameplay values and more, allowing for an iterative game design process.
  • Unity is fun! You can think of Unity like a box of LEGO: the only limits are those of your own imagination.

Here’s a brief overview of the book:

  • Section I, Hello, Unity!: This section covers everything you need to know to get started with Unity. You’ll learn your way around the UI, how to work with game assets and physics, and create a 3D twin-stick combat game: Bobblehead Wars.
  • Section II, First-Person Games: Move on to more complex game development topics, such as adding in-game UI elements, advanced camera techniques and using multiple weapons. In this section, you’ll build a fast-paced first-person shooter: Robot Rampage.
  • Section III: Unity 2D Games: Unity makes it easy to build your own classic 2D platformer. You’ll learn how to work with 2D physics, how to build levels with sprites and colliders, how to work with raycasting, animation controllers, and how to store and retrieve player data for your games.
  • Section IV, Tower Defense: In this section, you’ll create a 3D tower defense game — Runestrife — with such beautifully-rendered enemies it almost seems a shame to shoot them. Learn how to create a map using 3D tiles, how to craft utility scripts, create waves of enemies, prepare your game for publishing, and even how to play your game in Virtual Reality!

Unity Games by Tutorials will be the final book released as part of the iOS 10 Feast, on Wednesday, Nov. 2.

[Order Now]

Dessert: iOS 10 Feast Giveaway

iOS10_Feast_Giveaway

You’re probably so full of iOS 10 goodies by now, but we hope you’ve saved room for just one more thing.

I asked the Tutorial Team to tell us what their favorite development tools are; the ones they use every day and just couldn’t imagine life without. We then asked the teams behind these tools if they’d like to join the Feast on an invite-only basis.

We had a huge response — tons of folks were kind enough to donate copies of their tools and products to join in the celebration, making this the biggest giveaway of iOS development tools we’ve ever done!

The grand prize winner will receive a massive prize pack of 50 prizes split into 13 categories:

Version Control Tools

  1. GitHub 1-year subscription ($84 value): Build your own projects on GitHub.com and invite collaborators to join you in unlimited private repositories.
  2. GitKraken Pro 1-year subscription ($60 value): Fantastic Git client; edit your merge conflict output in-app, maintain multiple profiles and more!
  3. Tower 2 ($79 value): Version control with Git – made easy. In a beautiful, efficient, and powerful app.

Continuous Integration

  1. Bitrise Pro 1-year subscription with 2 Concurrencies ($1080 value): iOS Continuous Integration and Delivery for your whole team, with dozens of integrations for your favorite services.
  2. Buddybuild 1-year team subscription ($1908 value): Buddybuild ties together a continuous integration, continuous delivery and an iterative feedback solution into a single, seamless platform for all your mobile apps.

App Localization

  1. Applanga 1-year Pro Subscription ($328 value): Everything you need to make mobile app localization fast and easy.

iOS Controls

  1. shinobicontrols professional bundle 1-year subscription ($1495 value) includes the following:
  • ShinobiCharts: Bring your app’s data to life with these professional chart components.
  • ShinobiToolkit: An excellent collection of UI controls to enhance your apps with features like Grids, Calendars, Carousels and Gauges.
  • ShinobiForms: Tools for building user input forms quickly and easily.

Other Development Tools

  1. AppCode 1-year subscription ($199 value): A smart IDE for iOS and macOS development.
  2. Base 2 ($29 value): Create, design, edit and browse SQLite 3 database files. It’s quick to get in to and get the data you need.
  3. Charles ($50 value): A slick HTTP proxy, monitor and reverse proxy that lets you view all of the HTTP and SSL/HTTPS traffic between their machine and the Internet.
  4. Dash for macOS ($30 value): Instant offline access to documentation sets, and extremely handy snippet manager.
  5. Dash for iOS ($10 value): Mobile offline access to documentation sets.
  6. Paw 1-year team subscription (up to 5 users) ($500 value): Powerful HTTP API test client that syncs automatically between team members.
  7. Reveal ($45 value) – A powerful runtime view debugging tool with advanced visualisations, comprehensive inspectors and the ability to modify applications on the fly.

Design Tools

  1. Acorn 5 ($30 value): An easy to use Mac image editor, designed for humans. With a ton of new features and refinements, you will make beautiful images in Acorn!
  2. AppCooker ($30 value): Design apps like a chef – right on your iPad!
  3. Astropad for iPad ($30 value): Turn your iPad into a graphics tablet!
  4. Avocode 3-month subscription ($39 value): Got a design? Turn it into code with Avocode. Upload and share your designs; get CSS, export assets, measure and handpick colors.
  5. Briefs ($10 value): Share your ideas without code. Create prototypes with the animation performance and responsive interaction of a native application.
  6. ColorSnapper 2 ($9 value): The classic color picker app for designers & developers which makes it easy to collect, adjust, organize and export colors of any pixel on the screen.
  7. Core Animator ($100 value): Turn your own animations into native iOS code.
  8. PaintCode 2 ($100 value): PaintCode is a vector drawing app that generates Core Graphics code in real time.
  9. Principle for Mac ($100 value): Principle makes it easy to create animated and interactive user interface designs!
  10. Promotee ($10 value): Make your app’s promo art shine using this tool to create slick framed product shots.
  11. Sketch 3 ($99 value): Vector-based app for designers of all types to create create beautiful, high-quality artwork.
  12. Zeplin 6-month Growing Business Subscription ($150 value): A tool that makes handing off designs from designers to developers much easier. Generate styleguides, and resources, automatically!

Productivity Tools

  1. Duet ($20 value): Lets you use your iDevice as an extra display for your Mac using the Lightning or 30-pin cable. In Retina. At 60 frames per second. Seriously.
  2. Focused ($30 value): No clutter, no distractions. Just a perfectly crafted set of tools to help you write and stay focused on the task at hand.
  3. Monodraw ($20 value): A neat tool for creating diagrams, layouts, and flow charts – in ASCII art! :]
  4. Quiver ($10 value): A notebook built for programmers; mix text, code, Markdown and LaTeX within one note, with live preview Markdown and LaTeX.
  5. Soulver ($12 value): A handy app to help you play around with numbers, more conveniently than a traditional calculator.
  6. 1Password 1-year Individual Subscription ($36 value): All your passwords and credentials in one, secure location.

Blogging Tools

  1. WordPress Premium 1-year subscription ($99 value): Create a beautiful website to promote your app with the same awesome software we use for our blog.

Game Tools

  1. Glyph Designer 2 1-year Subscription ($53 value): Design beautiful fonts for your iOS games.
  2. Particle Designer 2 ($80 value): Create amazing particle systems for your games with a visual editor designed specifically for Mac OS X.
  3. Spine ($69 value): Build realistic 2D skeletal animations and easily integrate them into your games.

Conference tickets

  1. RWDevCon ($1499 value): A free ticket to our official raywenderlich.com conference which also gets you into the exclusive pre-conference workshop! Come meet the team for some great hands-on tutorials, inspiration, and fun!

Books

  1. Swift Apprentice PDF ($55 value): A book for complete beginners to Apple’s brand new programming language – Swift 3.
  2. iOS Apprentice PDF ($55 value): Learn how to make iPhone and iPad apps from the ground up, with a series of epic-length tutorials for beginners!
  3. tvOS Apprentice ($55 value): Learn how to build great-looking interfaces with UI elements designed for the Apple TV experience, monetize your app with in-app purchases, add animations, and more!
  4. iOS 10 by Tutorials PDF ($55 value): Learn about the new APIs in iOS 10 and what’s new in Swift 3!
  5. watchOS by Tutorials PDF ($55 value): Learn about UI controls and layout, the new dock, notifications, Snap, and more!
  6. Core Data by Tutorials PDF ($55 value): Take control of your data in iOS apps using Core Data, Apple’s powerful object graph and persistence framework.
  7. iOS Animations by Tutorials Second Edition PDF ($55 value): Learn how to make iOS Animations in Swift 3 through a series of hands-on tutorials and challenges.
  8. 2D Apple Games by Tutorials PDF ($55 value): Learn how to make your own iOS and tvOS games using Swift 3 and SpriteKit.
  9. 3D Apple Games by Tutorials PDF ($55 value): Make stunning 3D games in SpriteKit, and learn how to build your own 3D game art!
  10. Unity Games by Tutorials PDF ($55 value): Create 4 complete Unity games from scratch, including a 3D first-person shooter, a twin-stick shooter, a 2D platformer, and a virtual reality tower defense game!
  11. Plus two more surprise books that we haven’t yet announced on the site! ($110 value)

Video Tutorials

  1. 1-year raywenderlich.com Subscription ($180 value): Get full access to our complete catalog of video tutorials – plus get access to a new video tutorial each week!

Bonus Loot

  1. raywenderlich.com T-shirt ($25 value): Sport a stylish gray t-shirt with a “Eat, Drink, Swift” design!

In total, the grand prize winner will receive over $9,000 in value!

Aww Yeah!

Do you wanna be this guy? Make your first entry into the contest by clicking the button below!


A huge thanks goes out to our sponsors and friends who helped make this massive giveaway possible. Don’t thank us — thank them for putting the community first!

Please be sure to check out their sites — these are the tools and conferences we know and love, and they are must-haves for serious iOS developers.

Note: To be eligible to win the grand prize, you must be based in the US. If you’re outside the US, don’t worry – you are eligible for the other 150 prizes listed below! :]

Bonus Prizes

We want to give as many people as possible a chance to win. So in addition to the grand prize winner, we’ll be giving out a bunch of bonus prizes to 227 random retweeters!

  • 16 people will win a copy of Paw individual license. ($50 value)
  • 15 people will win a free raywenderlich.com PDF book of their choice, and “Eat, Drink, Swift” t-shirt. ($80 value)
  • 15 people will win a copy of AppCooker. ($30 value)
  • 15 people will win a 1-year GitHub subscription. ($84 value)
  • 15 people will win a 1-year GitKraken Pro subscription. ($60 value)
  • 15 people will win a copy of Monodraw. ($20 value)
  • 15 people will win a copy of Quiver. ($10 value)
  • 15 people will win a copy of Zeplin. ($150 value)
  • 9 people will win an Axosoft Professional Edition account for 5 users. ($480 value)
  • 7 people will win an AppCode 1-year subscription. ($199 value)
  • 7 people will win an Avocode 3-month subscription. ($39 value)
  • 7 people will win a copy of Briefs. ($10 value)
  • 7 people will win a copy of Charles. ($50 value)
  • 7 people will win a copy of Color Snapper 2. ($9 value)
  • 7 people will win a copy of Core Animator. ($100 value)
  • 7 people will win a copy of Focused. ($30 value)
  • 7 people will win a Glyph Designer 1-year subscription. ($53 value)
  • 7 people will win a copy of Particle Designer 2. ($80 value)
  • 7 people will win a copy of Promotee. ($10 value)
  • 4 people will win an Buddybuild 1-year team subscription. ($1908 value)
  • 3 people will win a Applanga 1-year Pro subscription. ($328 value)
  • 3 people will win a copy of Base 2. ($29 value)
  • 3 people will win a copy of PaintCode. ($100 value)
  • 3 people will win a copy of Reveal. ($45 value)
  • 3 people will win a copy of Sketch 3. ($99 value)
  • 3 people will win a copy of Soulver. ($12 value)
  • 3 people will win a copy of Tower 2. ($79 value)
  • 3 people will win a WordPress Premium 1-year subscription. ($99 value)
  • 3 people will win a 1Password 1-year Individual Subscription. ($36 value)
  • 1 person will win a copy of Acorn. ($30 value)
  • 1 person will win a Bitrise Pro 6-month subscription with 2 Concurrencies. ($600 value)
  • 1 person will win a Bitrise Pro 1-year subscription with 1 Concurrency. ($540 value)
  • 1 person will win a Bitrise Pro 6-month subscription with 1 Concurrency. ($300 value)
  • 1 person will win a copy of Dash for macOS. ($30 value)
  • 1 person will win a copy of Dash for iOS. ($10 value)
  • 1 person will win a copy of Duet. ($20 value)
  • 1 team will win a Paw 1-year team license for 5 users. ($500 value)
  • 1 person will win a copy of Principle for Mac. ($100 value)
  • 1 person will win a shinobicontrols professional bundle 1-year subscription (includes shinobicharts, shinobitoolkit, and shinobiforms). ($1495 value)
  • 1 person will win a copy of Spine. ($69 value)

That’s a ton of winners — 228 in total — so you have a lot of chances to win. This makes for over $39,000 in value of awesome loot to win!

To be eligible for this epic giveaway, all you have to do is send one or more tweets during the iOS 10 Feast with the #ios10feast hashtag.

You can retweet posts with this hashtag (including this one!) or just send general comments. It’s OK to submit as many tweets as you like (as long as you aren’t spamming of course!) We will choose random winners from all of the tweets marked with the #ios10feast hashtag, and they’ll get the epic loot!

Want to make your first entry? Just tweet this post with the #ios10feast hashtag by clicking the button below!


The random drawing will be in 8 weeks, when the iOS 10 Feast is complete. So make your entries early and often! :]

iOS 10 Feast Month

In addition to the book releases, over the next eight weeks we’ll be releasing a few sample chapters from the books so you can check them out if you haven’t already.

cal_monthoftuts

Here’s the full release schedule for your convenience:

  • Week of 9/12: Swift Apprentice Second Edition book launch; Beginning Swift 3 Video Tutorial Series
  • Week of 9/19: iOS Apprentice Fifth Edition book launch
  • Week of 9/26: tvOS Apprentice Second Edition book launch; tvOS 10 tutorial series
  • Week of 10/3: watchOS by Tutorials Second Edition book launch; Core Data by Tutorials Third Edition book launch; watchOS 3 tutorial series; Core Data tutorial series
  • Week of 10/10: 2D Apple Games by Tutorials Second Edition book launch; 3D Games by Tutorials book launch; 2D Apple Games tutorial; 3D Apple Games tutorial series
  • Week of 10/17: iOS Animations by Tutorials Third Edition book launch; iOS Animation tutorial series
  • Week of 10/24: iOS 10 by Tutorials book launch; iOS 10 tutorial series
  • Week of 10/31: Unity Games by Tutorials book launch

Be sure to check back throughout the feast for new books, video tutorial series or our popular free tutorials!

Where To Go From Here?

If WWDC this year was like Christmas for iOS devs, then this has gotta be our Thanksgiving! :]

To sum it all up, here’s what you need to know:

  • Want some Swift 3 screencasts right now? Retweet this post; if it reaches 199 tweets, we’ll release the screencasts for free right away!
  • Want tons of new tutorials? Come back here each week for the next two months for new tutorials, books, or video tutorial series on iOS 10, tvOS 10, watchOS 3, and Swift 3!
  • Want to meet the team and fellow readers in-person? Sign up for RWDevCon: The Tutorial Conference for some hands-on iOS 10, Swift 3, tvOS, and watchOS tutorials – before tickets sell out!
  • Want to win some mad loot? Send one or more tweets during the next two months with the #ios10feast hashtag!
  • Want to learn about iOS 10, watchOS, tvOS, Unity, and Swift 3? Check out our new official store – the new books and bundles are up!
  • Want a discount on anything in the store? Just use coupon code IOS10FEAST at checkout and save 10%! But hurry, this code is only good during the iOS 10 Feast!

The Tutorial Team and I hope you enjoy this feast, and that you’re fat and happy by the end!

Stay tuned for a bunch of great iOS 10 and Swift 3 tutorials, and if you have any questions or comments about the iOS 10 Feast, please join the forum discussion below!

The post Introducing the iOS 10 Feast! appeared first on Ray Wenderlich.


iOS 10 Screencast: Swift 3 in 3 Minutes

iOS 10 Screencast: Swift 3 API Design Guidelines

iOS 10 Screencast: Foundation in Swift 3

iOS 10 Screencast: GCD & Core Graphics in Swift 3

Swift Tutorial Part 1: Expressions, Variables & Constants

$
0
0
Learn how to code using Swift 3 and playgrounds!

Learn how to code using Swift 3 and playgrounds!

Welcome to our mini series on getting started with programming in Swift!

In this series, you’ll learn some of the basics of programming while working in a modern, friendly environment with the Swift programming language.

Instead of boring you with a lot of theory, we’re going to get you coding right away by using Xcode playgrounds, which are sandbox-type environments where you can run code directly without having to code an entire app.

For this tutorial, you’ll need Xcode 8, the standard development environment for macOS, available here. If you have Xcode version 7 or below, some of the code in this tutorial won’t work as expected.

Getting Started

The set of tools you use to write software is often referred to as the toolchain. The part of the toolchain into which you write your code is known as the Integrated Development Environment (IDE). Xcode is your IDE, which includes playgrounds.

You’ll use playgrounds throughout this series to practice coding, so it’s important to understand how they work. That’s what you’ll learn during the rest of this tutorial.

Creating a Playground

When you open Xcode, it will greet you with the following welcome screen:

welcome_to_xcode

If you don’t see this screen, it’s most likely because the “Show this window when Xcode launches” option was unchecked. You can also open the screen by pressing Command-Shift-1 or clicking Window\Welcome to Xcode from the menu bar.

From the welcome screen, you can jump quickly into a playground by clicking on Get started with a playground. Click on that now and Xcode will take you to a new screen:

new_playground

From here, you can name the playground and select your desired platform. The name is merely cosmetic and for your own use; when you create your playgrounds, feel free to choose names that will help you remember what they’re about.

The second option you can see here is the platform. Currently, this can be either iOS, macOS or tvOS.

The platform you choose simply defines which template Xcode will use to create the playground. Each platform comes with its own environment set up and ready for you to begin playing around with code. For the purposes of this seris, choose whichever you wish. You won’t be writing any platform-specific code; instead, you’ll be learning the core principles of the Swift language.

Once you’ve chosen a name and a platform, click on Next and then save the playground. Xcode then presents you with the playground, like so:

empty_playground

New playgrounds don’t start entirely empty but have some basic starter code to get you going. Don’t worry — you’ll soon learn what this code means.

Playgrounds Overview

At first glance, a playground may look like a rather fancy text editor. Well, here’s some news for you: It is essentially just that!

playground_features

The above screenshot highlights the first and most important things to know about:

  1. Source editor: This is the area in which you’ll write your Swift code. It’s much like a text editor such as Notepad or TextEdit. You’ll notice the use of what’s known as a monospace font, meaning all characters are the same width. This makes the code much easier to read and format.
  2. Results sidebar: This area shows the results of your code. You’ll learn more about how code is executed as you read through the series. The results sidebar will be the main place you’ll look to confirm your code is working as expected.
  3. Execution control: Playgrounds execute automatically by default, meaning you can write code and immediately see the output. This control allows you to execute the playground again. Holding down the button allows you to switch between automatic execution and manual execution modes.
  4. Activity viewer: This shows the status of the playground. In the screenshot, it shows that the playground has finished executing and is ready to handle more code in the source editor. When the playground is executing, this viewer will indicate this with a spinner.
  5. Panel controls: These toggle switches show and hide three panels, one that appears on the left, one on the bottom and one on the right. The panels each display extra information that you may need to access from time to time. You’ll usually keep them hidden, as they are in the screenshot. You’ll learn more about each of these panels as you move through the series.

Playgrounds execute the code in the source editor from top to bottom. Every time you change the code, the playground will re-execute everything. You can also force a re-execution by clicking Editor\Execute Playground. Alternatively, you can use the execution control.

You can turn on line numbers on the left side of the source editor by clicking Xcode\Preferences…\Text Editing\Line Numbers. Line numbers can be very useful when you want to refer to parts of your code.

Once the playground execution is finished, Xcode updates the results sidebar to show the results of the corresponding line in the source editor. You’ll see how to interpret the results of your code as you work through the examples in this series.

Code comments, arithmetic operations, constants and variables are some of the fundamental building blocks of any language, and Swift is no different.

Code Comments

The Swift compiler generates executable code from your source code. To accomplish this, it uses a detailed set of rules. Sometimes these details can obscure the big picture of why you wrote your code a certain way or even what problem you are solving. To prevent this, it’s good to document what you wrote so that the next human who passes by will be able to make sense of your work. That next human, after all, may be a future you! :]

Swift, like most other programming languages, allows you to document your code through the use of what are called comments. These allow you to write any text directly along side your code, which is ignored by the compiler.

The first way to write a comment is like so:

// This is a comment. It is not executed.

This is a single line comment. You could stack these up like so to allow you to write paragraphs:

// This is also a comment.
// Over multiple lines.

However, there is a better way to write comments which span multiple lines. Like so:

/* This is also a comment.
   Over many..
   many...
   many lines. */

This is a multi-line comment. The start is denoted by /* and the end is denoted by */. Simple!

You should use code comments where necessary to document your code, explain your reasoning, or simply to leave jokes for your colleagues. :]

Printing Out

It’s also useful to see the results of what your code is doing. In Swift, you can achieve this through the use of the print command.

print will output whatever you want to the debug area (sometimes referred to as the console).

For example, consider the following code:

print("Hello, Swift Apprentice reader!")

This will output a nice message to the debug area, like so:

printing

You can hide or show the debug area using the button highlighted with the red box in the picture above. You can also click View\Debug Area\Show Debug Area to do the same thing.

Arithmetic Operations

When you take one or more pieces of data and turn them into another piece of data, this is known as an operation.

The simplest way to understand operations is to think about arithmetic. The addition operation takes two numbers and converts them into the sum of the two numbers. The subtraction operation takes two numbers and converts them into the difference of the two numbers.

You’ll find simple arithmetic all over your apps; from tallying the number of “likes” on a post, to calculating the correct size and position of a button or a window, numbers are indeed everywhere!

In this section, you’ll learn about the various arithmetic operations that Swift has to offer by considering how they apply to numbers. In later parts, you see operations for types other than numbers.

Simple Operations

All operations in Swift use a symbol known as the operator to denote the type of operation they perform.

Consider the four arithmetic operations you learned in your early school days: addition, subtraction, multiplication and division. For these simple operations, Swift uses the following operators:

  • Add: +
  • Subtract: -
  • Multiply: *
  • Divide: /

These operators are used like so:

2 + 6
 
10 - 2
 
2 * 4
 
24 / 3

Each of these lines is what is known as an expression. An expression has a value. In these cases, all four expressions have the same value: 8. You write the code to perform these arithmetic operations much as you would write it if you were using pen and paper.

broken_paper

In your playground, you can see the values of these expressions in the right-hand bar, known as the results sidebar, like so:

arithmetic_operations

If you want, you can remove the white space surrounding the operator:

2+6

Removing the whitespace is an all or nothing thing: you can’t mix styles. For example:

2+6   // OK
2 + 6 // OK
2 +6  // ERROR
2+ 6  // ERROR

It’s often easier to read expressions if you have white space on either side of the operator.

Decimal Numbers

All of the operations above have used whole numbers, more formally known as integers. However, as you will know, not every number is whole.

As an example, consider the following:

22 / 7

This, you may be surprised to know, results in the number 3. This is because if you only use integers in your expression, Swift makes the result an integer also. In this case, the result is rounded down to the next integer.

You can tell Swift to use decimal numbers by changing it to the following:

22.0 / 7.0

This time, the result is 3.142857142857143 as expected.

The Remainder Operation

The four operations you’ve seen so far are easy to understand because you’ve been doing them for most of your life. Swift also has more complex operations you can use, all of them standard mathematical operations, just less common ones. Let’s turn to them now.

The first of these is the remainder operation, also called the modulo operation. In division, the denominator goes into the numerator a whole number of times, plus a remainder. This remainder is exactly what the remainder operation gives. For example, 10 modulo 3 equals 1, because 3 goes into 10 three times, with a remainder of 1.

In Swift, the remainder operator is the % symbol, and you use it like so:

28 % 10

In this case, the result equals 8, because 10 goes into 28 twice with a remainder of 8.

Shift Operations

The shift left and shift right operations take the binary form of a decimal number and shift the digits left or right, respectively. Then they return the decimal form of the new binary number.

For example, the decimal number 14 in binary, padded to 8 digits, is 00001110. Shifting this left by two places results in 00111000, which is 56 in decimal.

Here’s an illustration of what happens during this shift operation:

shift_left

The digits that come in to fill the empty spots on the right become 0. The digits that fall off the end on the left are lost.

Shifting right is the same, but the digits move to the right.

The operators for these two operations are as follows:

  • Shift left: <<
  • Shift right: >>

These are the first operators you’ve seen that contain more than one character. Operators can contain any number of characters, in fact.

Here’s an example that uses both of these operators:

1 << 3
 
32 >> 2

Both of these values equal the number 8.

One reason for using shifts is to make multiplying or dividing by powers of two easy. Notice that shifting left by one is the same as multiplying by two, shifting left by two is the same as multiplying by four, and so on. Likewise, shifting right by one is the same as dividing by two, shifting right by two is the same as dividing by four, and so on.

In the old days, code often made use of this trick because shifting bits is much simpler for a CPU to do than complex multiplication and division arithmetic. Therefore the code was quicker if it used shifting. However these days, CPUs are much faster and compilers can even convert multiplication and division by powers of two into shifts for you. So you’ll see shifting only for binary twiddling, which you probably won’t see unless you become an embedded systems programmer!

Order of Operations

Of course, it’s likely that when you calculate a value, you’ll want to use multiple operators. Here’s an example of how to do this in Swift:

((8000 / (5 * 10)) - 32) >> (29 % 5)

Notice the use of parentheses, which in Swift serve two purposes: to make it clear to anyone reading the code — including yourself — what you meant, and to disambiguate. For example, consider the following:

350 / 5 + 2

Does this equal 72 (350 divided by 5, plus 2) or 50 (350 divided by 7)? Those of you who paid attention in school will be screaming “72!” And you would be right!

Swift uses the same reasoning and achieves this through what’s known as operator precedence. The division operator (/) has a higher precedence than the addition operator (+), so in this example, the code executes the division operation first.

If you wanted Swift to do the addition first — that is, to return 50 — then you could use parentheses like so:

350 / (5 + 2)

The precedence rules follow the same that you learned in math at school. Multiply and divide have the same precedence, higher than add and subtract which also have the same precedence.

Math functions

Swift also has a vast range of math functions for you to use when necessary. You never know when you need to pull out some trigonometry, especially when you’re a pro Swift-er and writing those complex games!

Note: Not all of these functions are part of Swift. Some are provided by the operating system. Don’t remove the import statement that comes as part of the playground template or Xcode will tell you it can’t find these functions.

For example, consider the following:

sin(45 * Double.pi / 180)
// 0.7071067811865475
 
cos(135 * Double.pi / 180)
// -0.7071067811865475

These compute the sine and cosine respectively. Notice how both make use of Double.pi which is a constant Swift provides us, ready-made with pi to as much precision as is possible by the computer. Neat!

Then there’s this:

sqrt(2.0)
// 1.414213562373095

This computes the square root of 2. Did you know that sin(45°) equals 1 over the square root of 2?

Note: Notice how you used 2.0 instead of 2 in the example above? Functions that are provided by the operating systems (like sqrt, sin and cos) are picky and accept only numbers that contain a decimal point.

Not to mention these would be a shame:

max(5, 10)
// 10
 
min(-5, -10)
// -10

These compute the maximum and minimum of two numbers respectively.

If you’re particularly adventurous you can even combine these functions like so:

max(sqrt(2.0), Double.pi / 2)
// 1.570796326794897

Naming Data

At its simplest, computer programming is all about manipulating data. Remember, everything you see on your screen can be reduced to numbers that you send to the CPU. Sometimes you yourself represent and work with this data as various types of numbers, but other times the data comes in more complex forms such as text, images and collections.

In your Swift code, you can give each piece of data a name you can use to refer to it later. The name carries with it an associated type that denotes what sort of data the name refers to, such as text, numbers, or a date.

You’ll learn about some of the basic types in this part, and you’ll encounter many other types throughout the rest of this series.

Constants

Take a look at this:

let number: Int = 10

This declares a constant called number which is of type Int. Then it sets the value of the constant to the number 10.

Note: Thinking back to operators, here’s another one. The equals sign, =, is known as the assignment operator.

The type Int can store integers. The way you store decimal numbers is like so:

let pi: Double = 3.14159

This is similar to the Int constant, except the name and the type are different. This time, the constant is a Double, a type that can store decimals with high precision.

There’s also a type called Float, short for floating point, that stores decimals with lower precision than Double. In fact, Double has about double the precision of Float, which is why it’s called Double in the first place. A Float takes up less memory than a Double but generally, memory use for numbers isn’t a huge issue and you’ll see Double used in most places.

Once you’ve declared a constant, you can’t change its data. For example, consider the following code:

let number: Int = 10
number = 0

This code produces an error:

Cannot assign to value: 'number' is a 'let' constant

In Xcode, you would see the error represented this way:

constant_reassign_error

Constants are useful for values that aren’t going to change. For example, if you were modeling an airplane and needed to keep track of the total number of seats available, you could use a constant.

You might even use a constant for something like a person’s age. Even though their age will change as their birthday comes, you might only be concerned with their age at this particular instant.

Variables

Often you want to change the data behind a name. For example, if you were keeping track of your bank account balance with deposits and withdrawals, you might use a variable rather than a constant.

If your program’s data never changed, then it would be a rather boring program! But as you’ve seen, it’s not possible to change the data behind a constant.

When you know you’ll need to change some data, you should use a variable to represent that data instead of a constant. You declare a variable in a similar way, like so:

var variableNumber: Int = 42

Only the first part of the statement is different: You declare constants using let, whereas you declare variables using var.

Once you’ve declared a variable, you’re free to change it to whatever you wish, as long as the type remains the same. For example, to change the variable declared above, you could do this:

var variableNumber: Int = 42
variableNumber = 0
variableNumber = 1_000_000

To change a variable, you simply assign it a new value.

Note: In Swift, you can optionally use underscores to make larger numbers more human-readable. The quantity and placement of the underscores is up to you.

This is a good time to take a closer look at the results sidebar of the playground. When you type the code above into a playground, you’ll see that the results sidebar on the right shows the current value of variableNumber at each line:

variables_results_sidebar

The results sidebar will show a relevant result for each line if one exists. In the case of a variable or constant, the result will be the new value, whether you’ve just declared a constant, or declared or reassigned a variable.

Using Meaningful Names

Always try to choose meaningful names for your variables and constants. Good names can acts as documentation and make your code easy to read.

A good name specifically describes what the variable or constant represents. Here are some examples of good names:

  • personAge
  • numberOfPeople
  • gradePointAverage

Often a bad name is simply not descriptive enough. Here are some examples of bad names:

  • a
  • temp
  • average

The key is to ensure that you’ll understand what the variable or constant refers to when you read it again later. Don’t make the mistake of thinking you have an infallible memory! It’s common in computer programming to look back at your own code as early as a day or two later and have forgotten what it does. Make it easier for yourself by giving your variables and constants intuitive, precise names.

Also, note how the names above are written. In Swift, it is common to camel case names. For variables and constants, follow these rules to properly case your names:

  • Start with a lowercase letter.
  • If the name is made up of multiple words, join them together and start every other word with an uppercase letter.
  • If one of these words is an abbreviation, write the entire abbreviation in the same case (e.g.: sourceURL and urlDescription)

In Swift, you can even use the full range of Unicode characters. For example, you could declare a variable like so:

var : Int = -1

That might make you laugh, but use caution with special characters like these. They are harder to type and therefore may end up causing you more pain than amusement.

bad_choice

Special characters like these probably make more sense in data that you store rather than in Swift code.

Increment and Decrement

A common operation that you will need is to be able to increment or decrement a variable. In Swift, this is achieved like so:

var counter: Int = 0
 
counter += 1
// counter = 1
 
counter -= 1
// counter = 0

The counter variable begins as 0. The increment sets its value to 1, and then the decrement sets its value back to 0.

These operators are similar to the assignment operator (=), except they also perform an addition or subtraction. They take the current value of the variable, add or subtract the given value and assign the result to the variable.

In other words, the code above is shorthand for the following:

var counter: Int = 0
counter = counter + 1
counter = counter - 1

Similarly, the *= and /= operators do the equivalent for multiplication and division, respectively:

var counter: Int = 10
 
counter *= 3  // same as counter = counter * 3
// counter = 30
 
counter /= 2  // same as counter = counter / 2
// counter = 15

Mini-exercises

If you haven’t been following along with the code in Xcode, now’s the time to create a new playground and try some exercises to test yourself!

  1. Declare a constant of type Int called myAge and set it to your age.
  2. Declare a variable of type Double called averageAge. Initially, set it to your own age. Then, set it to the average of your age and my own age of 30.
  3. Create a constant called testNumber and initialize it with whatever integer you’d like. Next, create another constant called evenOdd and set it equal to testNumber modulo 2. Now change testNumber to various numbers. What do you notice about evenOdd?
  4. Create a variable called answer and initialize it with the value 0. Increment it by 1. Add 10 to it. Multiply it by 10. Then, shift it to the right by 3. After all of these operations, what’s the answer?

Key Points

  • Code comments are denoted by a line starting with // or multiple lines bookended with /* and */.
  • Code comments can be used to document your code.
  • You can use print to write things to the debug area.
  • The arithmetic operators are:
    Add: +
    Subtract: -
    Multiply: *
    Divide: /
    Remainder: %
  • Constants and variables give names to data.
  • Once you’ve declared a constant, you can’t change its data, but you can change a variable’s data at any time.
  • Always give variables and constants meaningful names to save yourself and your colleagues headaches later.
  • Operators to perform arithmetic and then assign back to the variable:
    Add and assign: +=
    Subtract and assign: -=
    Multiply and assign: *=
    Divide and assign: /=

Challenges

Before moving on, here are some challenges to test your knowledge of variables and constants. You can try the code in a playground to check your answers.

  1. Declare a constant exercises with value 11 and a variable exercisesSolved with value 0. Increment this variable every time you solve an exercise (including this one).
  2. Given the following code:
age = 16
print(age)
age = 30
print(age)

Declare age so that it compiles. Did you use var or let?

  1. Consider the following code:
let a: Int = 46
let b: Int = 10

Work out what answer equals when you replace the final line of code above with each of these options:

// 1
let answer1: Int = (a * 100) + b
// 2
let answer2: Int = (a * 100) + (b * 100)
// 3
let answer3: Int = (a * 100) + (b / 10)
  1. Add parentheses to the following calculation. The parentheses should show the order in which the operations are performed and should not alter the result of the calculation.
5 * 3 - 4 / 2 * 2
  1. Declare two constants a and b of type Double and assign both a value. Calculate the average of a and b and store the result in a constant named average.
  2. A temperature expressed in °C can be converted to °F by multiplying by 1.8 then incrementing by 32. In this challenge, do the reverse: convert a temperature from °F to °C. Declare a constant named fahrenheit of type Double and assign it a value. Calculate the corresponding temperature in °C and store the result in a constant named celcius.
  3. Suppose the squares on a chessboard are numbered left to right, top to bottom, with 0 being the top-left square and 63 being the bottom-right square. Rows are numbered top to bottom, 0 to 7. Columns are numbered left to right, 0 to 7. Declare a constant position and assign it a value between 0 and 63. Calculate the corresponding row and column numbers and store the results in constants named row and column.
  4. Declare constants named dividend and divisor of type Double and assign both a value. Calculate the quotient and remainder of an integer division of dividend by divisor and store the results in constants named quotient and remainder. Calculate the remainder without using the operator %.
  5. A circle is made up of 2 radians, corresponding with 360 degrees. Declare a constant degrees of type Double and assign it an initial value. Calculate the corresponding angle in radians and store the result in a constant named radians.
  6. Declare four constants named x1, y1, x2 and y2 of type Double. These constants represent the 2-dimensional coordinates of two points. Calculate the distance between these two points and store the result in a constant named distance.
  7. Increment variable exercisesSolved a final time. Use the print function to print the percentage of exercises you managed to solve. The printed result should be a number between 0 and 1.

Where to Go From Here?

You can download the final playground from this part — along with the solutions for the challenges above — from this link.

In this tutorial, you’ve only dealt with only numbers, both integers and decimals. Of course, there’s more to the world of code than that! In the next part, you’re going to learn about more types such as strings, which allow you to store text.

The next part in this tutorial series deals with types and operations; once it's posted we'll provide a link right here so you can continue with your Swift adventures!

We encourage you to try the challenges above; if you find a particularly interesting solution to one of the challenges, or have any questions or comments on this tutorial, please tell us about it in the discussion below!

swift-apprentice

This tutorial was taken from Chapters 1 and 2 of Swift Apprentice, Second Edition available from the raywenderlich.com store.

The book has been completely updated to work with Xcode 8 and Swift 3. Check it out and let us know what you think!

The post Swift Tutorial Part 1: Expressions, Variables & Constants appeared first on Ray Wenderlich.

Viewing all 4373 articles
Browse latest View live


<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>