Swift Tutorial: Introducing Structures

Learn about structures, a fundamental compound type in Swift that let you define your own types – complete with data and actions! By Erik Kerber.

Leave a rating/review
Save for later
Share
Note from Ray: This is an abridged version of a chapter from the Swift Apprentice, to give you a sneak peek of what’s inside the book, released as part of the iOS 9 Feast. We hope you enjoy!

Using the fundamental building blocks such as variables and constants (some of which you learned about in our previous free excerpt from the Swift Apprentice), you might think you’re ready to conquer the world! Almost ;]

Most programs that perform complex tasks would probably benefit from higher levels of abstraction. In other words, in addition to an Int, String or Array, they’ll need new types that are specific to the domain of the task at hand.

This tutorial will introduce structures, which are a “named type”. Like a String, Int or Array, you can define your own structures to create named types to later use in your code. By the end of this tutorial, you’ll know how to define and use your own structures.

Introducing Structures

Imagine you’re writing a program that calculates if a potential customer is within range of a pizza delivery restaurant. You might write code like this:

let latitude: Double = 44.9871
let longitude: Double = -93.2758
let range: Double = 200.0

func isInRange(lat: Double, long: Double) -> Bool {
  // And you thought in Math class
  // you would never use the Pythagorean theorem!
  let difference = sqrt(pow((latitude - lat), 2) +
    pow((longitude - long), 2))
  let distance = difference * 0.002
  return distance < range
}

Simple enough, right? A successful pizza delivery business may eventually expand to include multiple locations, which adds a minor twist to the deliverable calculator:

let latitude_1: Double = 44.9871
let longitude_1: Double = -93.2758

let latitude_2: Double = 44.9513
let longitude_2: Double = -93.0942

Now what? Do you update your function to check against both sets of coordinates? Eventually, the rising number of customers will force the business to expand, and soon it might grow to a total of 10 stores! Not only that, but some new stores might have different delivery ranges, depending on whether they're in a big city or a sprawling suburb.

You might briefly consider creating an array of latitudes and longitudes, but it would be difficult to both read and maintain. Fortunately, Swift has additional tools to help you simplify the problem.

Your First Structure

Structures, or structs, are one of the named types in Swift that allow you to encapsulate related properties and behaviors. You can define it, give it a name and then use it in your code.

In the example of the pizza business, it's clear that latitude and longitude are closely related—close enough that you could think of them as a single value:

struct Location {
  let latitude: Double
  let longitude: Double
}

This block of code demonstrates the basic syntax for defining a struct. In this case, it's a type named Location that combines both latitude and longitude.

The basic syntax begins with the struct keyword followed by the name and a pair of curly braces. Everything between the curly braces is a “member” of the struct.

Now that you've defined your first struct, you can instantiate one and store it in a constant or variable just like any other type you've worked with:

let pizzaLocation = Location(latitude: 44.9871, longitude: -93.2758)

Instead of storing the coordinates in two separate variables, you store them together!

To create the Location value, you use the name of the type along with parentheses containing both the latitude and the longitude parameters. Swift defines this initializer automatically, and it’s a subject this tutorial will cover in more detail later.

You may remember that there's also a range involved, and now that the pizza business is expanding, there may be different ranges associated with different restaurants. You can create another struct that represents the location and the range, like so:

struct DeliveryRange {
  var range: Double
  let center: Location
}

let storeLocation = Location(latitude: 44.9871, longitude: -93.2758)
var pizzaRange = DeliveryRange(range: 200, center: storeLocation)

Now there's a new struct named DeliveryRange that contains a variable range of the pizza restaurant along with the Location center.

Accessing Members

With your DeliveryRange defined and an instantiated value in hand, you may be wondering how you can use these values. In Swift, structures and the other types you'll learn about use a simple “dot syntax” to access their members:

print(pizzaRange.range) // 200

You can even access members of members using dot syntax:

print(pizzaRange.center.latitude) // 44.9871

Similar to how you can read values with dot syntax, you can also assign them. If the delivery range of one pizza location becomes larger, you could assign the new value to the existing variable:

pizzaRange.range = 250

image001

In addition to properties, you must declare the struct itself as a variable to be able to modify it:

let constPizzaRange = DeliveryRange(range: 200, center: storeLocation)

// Error: change ‘let’ to ‘var’ above to make it mutable
constPizzaRange.range = 250

Now that you have the range and location together in one struct, how would you rewrite isInRange(_:long:) to take a struct parameter instead of two Double values?

[spoiler title="Rewrite isInRange to use Location and DeliveryRange"]

let latitude: Double = 44.9871
let longitude: Double = -93.2758

func isInRange(deliveryRange: DeliveryRange, customer: Location) -> Bool {
  let pizzaLocation = deliveryRange.center

  let difference = sqrt(pow((customer.latitude - pizzaLocation.latitude), 2) +
    pow((customer.longitude - pizzaLocation.longitude), 2))
  let distance = difference * 0.002
  return distance < deliveryRange.range
}

[/spoiler]

Initializing a Struct

When you defined the Location and DeliveryRange structs, Swift automatically generated a way to create these values:

Location(latitude: 44.9871, longitude: -93.2758)

When you create an instance of a type such as Location, you need to use a special kind of function called an initializer. Swift will automatically create a default initializer, such as the one shown above, that takes each member as a named parameter.

You can also create your own initializer, which allows you to customize how a structure's members are assigned:

struct Location {
  let latitude: Double
  let longitude: Double

  // String in GPS format "44.9871,-93.2758"
  init(coordinateString: String) {
    let crdSplit = coordinateString.characters.split(",")
    latitude = atof(String(crdSplit.first!))
    longitude = atof(String(crdSplit.last!))
  }
}

Here, the init keyword marks this function as an initializer for the Location struct.

In this case, the initializer takes a latitude and longitude together as a comma-separated string. This could be useful if you're reading locations from a text file.

let coords = Location(coordinateString: "44.9871,-93.2758")
print(coords.latitude) // 44.9871
print(coords.longitude) // -93.2758

Keep in mind that as soon as you define a custom initializer, Swift won't add the automatically-generated one. If you still need it, you'll have to define it yourself!

Erik Kerber

Contributors

Erik Kerber

Author

Over 300 content creators. Join our team.