Ranges in Swift Explained
You use ranges in Swift to define values between a lower and upper limit. Ranges are useful for creating slices of arrays, checking if a value is contained in a range, and much more.
In this tutorial, we’ll discuss how you can work with ranges in Swift. We’ll discuss ranges vs. arrays, types of ranges, working with ranges and strings, and tips and tricks for practical iOS development.
Table of Contents
Types of Ranges in Swift
First things first. What’s a range? A range in Swift defines stuff – usually intgers – between a lower and upper limit. It’s the same as that we’re saying: “A zoo has animals ranging from aardvarks to zebras.” Tigers and dolphins fit in that range.
Here’s an example of a range in Swift:
let allAges = 0…99
print(allAges.contains(42))
In the above code, we’ve used the closed range operator … to define a range from 0 to 99, including 99. The range contains the number 42, for example.
Swift has a few range operators:
- Closed ranges with a…b
- Half-open ranges with a..
- One-sided ranges with a…, …b and ..
Closed Ranges
The closed range operator a…b is the simplest. It contains every item between the lower bound, often called a, and the upper bound, often called b, including b. It’s what we mean when we say: “0 to 99, including 99.”
Here’s an example:
let numbers = 0…10
for number in numbers {
print(number)
}
The above code uses the … operator to print every number from 0 to 10 in a for loop. The type of numbers in the above example is ClosedRange
Behind the scenes, the above for loop uses a different type called CountableClosedRange. This is an alias for ClosedRange
Half-Open Ranges
The half-open range operator a..b is the other range operator you’ll often work with. Instead of …, this operator does not include its upper bound in the range. So a range of 0..<99 does not include 99 in the range.
Here’s an example:
let numbers = 0..<5
print(Array(numbers))
The above code prints [0, 1, 2, 3, 4], so you’ll see that the 0..<5 range does not include the number 5. That’s because of the half-open range operator ..<.
Perhaps you’re wondering why we have 2 different operators for essentially the same thing. If you don’t want the last item of a range, why don’t you stop the range one item earlier? A range 0...99 is the same as 0..<100, right?
As it turns out, much of the work with ranges happens around the edges of things. For example, an array’s last index number is the same as its length minus one. We can either limit the range with a calculation, i.e. 0...array.count - 1, or we can use the half-open range operator: 0.. Let’s put those ranges to use! The first use case we’ll discuss is the most common one: ranges and arrays. In practical app development, you often use ranges in conjunction with arrays. let names = [“Bernard”, “Dolores”, “Maeve”, “Lee”] for i in 0.. That’s not all ranges are good for. We can use range operators to extract sections of an array, called a slice. Check this out: let names = [“Ford”, “Arthur”, “Zaphod”, “Marvin”, “Eddie”] In the above code, we’ve defined an array of strings called names. With the closed range operator … we’re “selecting” a subsection of that array, namely the items 0, 1 and 2. The resulting slice is printed out: [“Ford”, “Arthur”, “Zaphod”]. Neat! It’s worth noting here that the type of the slice constant is ArraySlice You could see a slice as an index-only copy of the array, which references the old names array. Swift automatically transforms the slice to an actual Array type if needed, or you can use the Array() initializer yourself. One-Sided Ranges Let’s take that one step further with the third type of range: a one-sided range. They work the same as the closed and half-open ranges, except that they either don’t have a lower bound, or don’t have an upper bound. This results in the following one-sided range operators: See how that works? You only define one limit — and everything up to or from that limit. Here’s an example: let names = [“Ford”, “Arthur”, “Zaphod”, “Marvin”, “Eddie”] Strings in Swift are funky. In many programming languages, you can deal with strings as if they are arrays of characters. That doesn’t work in Swift. Check this out: let name = “Reinder” In short, a character in Swift doesn’t have a fixed width so you can’t calculate the index of an arbitrary character in a string. When you say “Get me the 7th character!” you might end up between characters 5 and 9, depending on the length of the individual characters in the string. Swift solves this problem by providing APIs for strings that work with indices. You can “walk” or “stride” these indices, increasing them in steps. Swift figures out whether a next index involves stepping over one or multiple bytes. Once you’ve created indices, you can use regular range operators. Take a look at this example: let text = “The lazy dog jumped over the quick brown fox” let start = text.index(text.startIndex, offsetBy: 4) print(text[start.. Let’s discuss a few things to keep in mind when working with ranges. First, it’s important to be mindful of out-of-bounds errors that might occur. Then, we’ll take a look at the ~= pattern matching operator. Off-by-One Errors let names = [“Ford”, “Arthur”, “Zaphod”, “Marvin”, “Eddie”] for i in 0…names.count { Looks like OK Swift code, right? It contains a sneaky error though, called off-by-one (OBOE). When you run it, you get this output: Fatal error: Index out of range See that 0…names.count code? This will iterate from 0 to the end of the array, except… the index of the last item of the array is equal to names.count – 1. The above range will loop from 0 to 5, including 5, and 5 is not a valid index of the array. The last index is 4, of course! It’s easy to make off-by-one errors when working with ranges. You can deal with the errors as they occur, or be mindful of the edges of your ranges and collections as you work with them. Here’s one last cool thing you can do with ranges: let statusCode = 403 if 400..<500 ~= statusCode { print("Oops!")
}
In the above code, we’ve defined a statusCode constant with a value 403. Then, we’re checking if this value is contained within the range 400..<500 by using the ~= pattern matching operator in the conditional. It’s essentially the same as (400..<500).contains(statusCode), but it’s more concise.
You can use the pattern matching operator in switch statements too, to select a range of values instead of a single value. Neat! We’ve looked at how you can use ranges in Swift in this tutorial. Here’s what we discussed:Working with Ranges and Arrays
let slice = names[0…2]
print(slice)
print(names[3…]) // [“Marvin”, “Eddie”]
print(names[…2]) // [“Ford”, “Arthur”, “Zaphod”]
print(names[..<2]) // ["Ford", "Arthur"]
Keep in mind that slices and one-sided ranges are just constructs. They define a view into a dataset that does not include a hardcopy of that dataset. For example, if you were to a define a freeform range like 1..., it doesn’t include every hard-coded integer number from 1 to infinity. Instead, you apply it onto your dataset, and it gives you every item from 1 to the end of your dataset.Using Strings with Ranges
print(name[2])
You’d expect the above code to output “i”, which is the 3rd character in the string. It doesn’t work that way, for a few reasons, the most important being that strings in Swift are encoded as UTF-8 and that’s a variable width encoding.
let end = text.index(text.startIndex, offsetBy: 8)Pro Tips and Tricks
Check this out:
print(names[i])
}Pattern Matching Operator with Ranges
Further Reading
Related Articles
Most Popular Posts
Top 7 Integrations to Automate Your Business Workflows
By Abhinav Girdhar | July 16, 2020
Web Research for Beginners: How to find the details of a business and its owner?
By Abhinav Girdhar | January 20, 2020
10 Tips to create a Perfect Organizational Chart
By Abhinav Girdhar | October 19, 2022
The Ultimate Guide to Setting up Facebook Business Manager Account
By Abhinav Girdhar | March 24, 2020
A Beginner’s Guide to Chatbot Marketing [Benefits and Tips]
By Abhinav Girdhar | June 18, 2020