FREE: Follow my new 100 Days of Swift challenge! >>

How to check whether a value is inside a range

Paul Hudson       @twostraws

Most folks realize Swift’s ranges are powerful, but not enough people realize just how powerful they are - and how completely natural they can be to use.

Let’s start off with the basics. You can create a closed range like this:

``````let startNumber = 10
let endNumber = 100
let numberRange = startNumber...endNumber``````

That range is a data type just like any other, so it has methods you can use. For example, you can query whether a number lies inside the range like this:

``````if numberRange.contains(50) {
print("Number is inside the range")
} else {
print("Number is outside the range")
}``````

Querying range bounds like this is so common that Swift even has its own dedicated operator, `~=`. It means “contains”, and it lets you use a shorter syntax if you want:

``````if numberRange ~= 50 {
print("Number is inside the range")
} else {
print("Number is outside the range")
}``````

Personally I prefer using `contains()` because it reads much more naturally, but the `~=` operator has the advantage that it can be used without parentheses, like this:

``````if 10...100 ~= 50 {
print("Number is inside the range")
}``````

To do that using `contains()` isn’t particularly beautiful:

``````if (10...100).contains(50) {
print("Number is inside the range")
}``````

So far these are just regular ranges that most folks use all the time. To take things up a notch, let’s look at date ranges: you can specify a start and end date, then check whether other dates lie inside that range.

This works the same way as using integers. Start by creating a range:

``````let startDate = Date().addingTimeInterval(-1000)
let endDate = Date().addingTimeInterval(1000)
let dateRange = startDate...endDate``````

Next, pick out a test date:

``let testDate = Date()``

Now use `contains()` to figure out whether the test date lies inside the range:

``````if dateRange.contains(testDate) {
print("Inside the range")
}``````

You can use the `~=` operator here too:

``````if dateRange ~= testDate {
print("Inside the range")
}``````

Even strings get in on this range action. So, we could define a range of strings to contain all animals that start with A through M like this:

``````let startAnimal = "aardvark"
let endAnimal = "mule"
let animalsAThroughM = startAnimal...endAnimal``````

You can then check if other animals lie inside that range like this:

``````if animalsAThroughM.contains("giraffe") {
print("Giraffes are in the range!")
}``````

This behavior isn’t magic – it’s just a side effect of these types conforming to the `Comparable` protocol. This protocol is what allows you to compare integers, strings, and dates like this:

``````1 < 5
"abc" < "def"
firstDate < lastDate``````

This means if your own types also conform to `Comparable` they can be used as ranges too. For example, here’s a simple `User` struct that conforms to `Comparable` by comparing its lone property:

``````struct User: Comparable {
var name: String

static func < (lhs: User, rhs: User) -> Bool {
return lhs.name < rhs.name
}
}``````

We can use that to create ranges by creating two instances and combining them into a range:

``````let adele = User(name: "Adele")
let taylor = User(name: "Taylor")
let userRange = adele...taylor``````

Now we can create a test object and check whether it lies inside the range:

``````let paul = User(name: "Paul")

if userRange.contains(paul) {
print("Paul is in!")
}``````

Because the `paul` instance compares as greater than `adele` but less than `taylor`, Swift considers it part of the range.

As you’ve seen, ranges are powerful in Swift partly because they can be used with several important built-in types, but also because you can use them with your own types just by conforming to `Comparable`!