Collection Data Structures in Swift

Niv Yahel
What kind of performance do you want from your data structure?

What kind of performance do you want from your data structure?

Update note: This tutorial was updated for Swift 2.2 and Xcode 7.3 by Niv Yahel. Original post by Tutorial Team member Ellen Shapiro.

Imagine you have an application that needs to work with a lot of data. Where do you put that data? How do you keep it organized and handle it efficiently?

If your program only manages one number, you store it in one variable. If it has two numbers then you’d use two variables.

What if it has 1000 numbers, 10,000 strings or the ultimate library of memes? And wouldn’t it be nice to be able to find a perfect meme in an instant?

In that case, you’ll need one of the fundamental collection data structures, such as Arrays, Dictionarys, and Sets. As you’ll learn, these collection data structures allow you and the user to manipulate huge databases with a swipe across a screen. Thanks to Swift and its ever-evolving nature, you’ll be able to use native data structures that are blazing fast!

Here’s how the tutorial will flow:

  1. First, you’ll review what a data structure is, and then you’ll learn about Big-O notation. It’s the standard tool for describing the performance of different data structures.
  2. Next you’ll observe these data structures by measuring the performance of arrays, dictionaries, and sets — the most basic data structures available in Cocoa development. Incidentally, it’ll also double as a rudimentary introduction to performance testing.
  3. As you proceed, you’ll compare the performance of mature Cocoa structures with newer, Swift-only counterparts.
  4. Finally, you’ll briefly review some related types offered by Cocoa. These are data structures that you might be surprised to learn are already at your fingertips!

Getting Started

Before you dive in and explore the data structures used in iOS, you should review these basic, key concepts about what they are and how to measure their performance.

There are many types of collection data structures, and each represents a specific way to organize and access a collection of items. A specific collection type might make some activities especially efficient such as adding a new item, finding the smallest item, or ensuring you’re not adding duplicates.

Without collection data structures, you’d be stuck trying to manage items one by one. A collection allows you to:

  1. Handle all those items as one entity
  2. Imposes some structure
  3. Efficiently insert, remove, and retrieve items

What is “Big-O” Notation?

Big-O notation — that’s the letter O, not the number zero — is a way of describing the efficiency of an operation on a data structure. There are various kinds of efficiency: you could measure how much memory the data structure consumes, how much time an operation takes under the worst case scenario, or how much time an operation takes on average. It’s not the raw memory or time that we care about here though. It is the way the memory or time scales, as the size of the data structure scales.

In this tutorial, you’ll measure how much time an operation takes on average.

Common sense will tell you that an operation takes longer to perform when there are larger quantities of data. But sometimes there is little or no slowdown, depending on the data structure.

Big-O notation is a precise way of describing this.

You write an exact functional form that roughly describes how the running time changes based on the number of elements in the structure.

When you see Big-O notation written as O(something-with-n), n is the number of items in the data structure, and something-with-n is roughly how long the operation will take.

“Roughly”, ironically enough, has a specific meaning: the behavior of the function at the asymptotic limit of very large n. Imagine n is a really, really large number — you’re thinking about how the performance of some operation will change as you go from n to n+1.

The most commonly seen Big-O performance measures are as follows, in order from best to worst performance:

O(1) — (constant time)
No matter how many items are in a data structure, this function calls the same number of operations. This is considered ideal performance.

O(log n) — (logarithmic)
The number of operations this function calls grows at the rate of the logarithm of the number of items in the data structure. This is good performance, since it grows considerably slower than the number of items in the data structure.

O(n) — (linear)
The number of operations this function calls will grow linearly with the size of the structure. This is considered decent performance, but it can grind along with larger data collections.

O(n (log n)) — (“linearithmic”)
The number of operations called by this function grows by the logarithm of the number of items in the structure multiplied by the number of items in the structure. Predictably, this is about the lowest level of real-world tolerance for performance. While larger data structures perform more operations, the increase is somewhat reasonable for data structures with small numbers of items.

O(n²) — (quadratic)
The number of operations called by this function grows at a rate that equals the size of the data structure, squared — poor performance at best. It grows quickly enough to become unusably slow even if you’re working with small data structures.

O(2^n) — (exponential)
The number of operations called by this function grows by two to the power of the size of the data structure. The resulting very poor performance becomes intolerably slow almost immediately.

O(n!) (factorial)
The number of operations called by this function grows by the factorial of the size of the data structure. Essentially, you have the worst case scenario for performance. For example, in a structure with just 100 items, the multiplier of the number of operations is 158 digits long. Witness it for yourself on wolframalpha.com.

Here’s a more visual representation of performance and how it degrades when there are more items in a collection, going from one to 25 items:

Big-O Notation

Did you notice that you can’t even see the green O(log n) line because it is so close to the ideal O(1) at this scale? That’s pretty good! On the other hand, operations that have Big-O notations of O(n!) and O(2^n) degrade so quickly that by the time you have more than 10 items in a collection, the number of operations spikes completely off the chart.

Yikes! As the chart clearly demonstrates, the more data you handle, the more important it is to choose the right structure for the job.

Now that you’ve seen how to compare the performance of operations on data structures, it’s time to review the three most common types used in iOS and explore how they perform in theory and in practice.

Common iOS Data Structures

The three most common data structures in iOS are arrays, dictionaries and sets, each of which deserves your attention. In this section, you’ll:

  1. Consider how they differ in ideal terms as fundamental abstractions
  2. Examine the performance of the actual concrete classes that iOS offers for representing those abstractions.

For the three types, iOS offers multiple concrete classes that work for the same abstraction. In addition to the old Foundation data structures available in Swift and Objective-C, there are new Swift-only versions of data structures that integrate tightly with the language.

Since its introduction, Swift has brought many performance improvements to Swift data structures and now outperforms Foundation data structures in the majority of cases. However, the “best” one to use still depends on the operation you want to perform.

Arrays

Arrays

An array is a group of items placed in a specific order, and you can access each item via an index — a number that indicates its position in the order. When you write the index in brackets after the name of the array variable, this is subscripting.

Swift arrays are immutable if you define them as constants with let, and mutable if you define them as variables with var.

In contrast, a Foundation NSArray is immutable by default. If you want to add, remove or modify items after creating the array, you must use the mutable variant class NSMutableArray.

An NSArray is heterogeneous, meaning it can contain Cocoa objects of different types. Swift arrays are homogeneous, meaning that each Array is guaranteed to contain only one type of object.

However, you can still define a single Swift Array so it stores various types of Cocoa objects by specifying that the one type is AnyObject, since every Cocoa type is also a subtype of this.

Expected Performance and When to Use Arrays

The primary reason to use an array is when the order of variables matters. Think about those times when you sort contacts by first or last name, a to-do list by date, or any other situation when it’s critical to find or display data in a specific order.

Apple’s documentation includes three key expectations for Array performance in the CFArray header:

  1. Accessing any value at a particular index in an array is at worst O(log n), but should usually be O(1).
  2. Searching for an object at an unknown index is at worst O(n (log n)), but will generally be O(n).
  3. Inserting or deleting an object is at worst O(n (log n)) but will often be O(1).

These guarantees subtly deviate from the simple “ideal” array that you might expect from a computer science textbook or the C language, where an array is always a sequence of items laid out contiguously in memory.

Consider it a useful reminder to check the documentation!

In practice, these expectations make sense when you think about them:

  1. If you already know where an item is, then looking it up in the array should be fast.
  2. If you don’t know where a particular item is, you’ll need to look through the array from beginning to end. Your search will be slower.
  3. If you know where you’re adding or removing an object it’s not too difficult. Although, you may need to adjust the rest of the array afterwards, and that’s more time-consuming.

How well do these expectations align with reality? Keep reading to find out!

Note: Swift officially became open source in December of 2015. You can look through the Swift source code yourself to see how these data structures are implemented under the hood!

Sample App Testing Results

Download the sample project and open it in Xcode. It’s time to play around with testing methods that will create and/or test an array and show how long it took to perform each task.

Note: In the app, the Debug configuration automatically sets optimization to a level equal to the release configuration — this is so that when you test the application you get the same level of optimization you’d see in the real world.

You need a minimum of 1000 items to run tests with the sample app, so that results are large enough to detect. When you build and run, the slider will be set to 1000. Press the Create Array and Test button, and you’ll be testing in no time:

Swift 2.1.1 Create 1000 items

Drag the slider over to the right side until it hits 10,000,000, and press Create Array and Test again to see the difference in creation time with a significantly larger array:

Swift 1.2 Create 10,000,000

These tests were run against an iPhone 6 running iOS 9.2 from Xcode 7.2, which includes Swift 2.1.1. With 10,000 times as many items, creating the array only takes about 1,537 times as much time.

Note: A while ago, when Swift was a little baby, this used to take…quite a bit longer. On an iPhone 5 running the iOS 8 Gold Master with the Swift 1.0/Xcode 6.0 Gold Master:

Swift 1000 Items vs 100,000 itesms

In this case, for around 106.5 times the number of items, it took roughly 1,417 times as much time to create the array. Any more than a few dozen items, and this was unusable.

Since the dark days of old, Swift has made tremendous performance improvements and you can now easily make huge arrays with few problems!

What about NSMutableArray? You can still call Foundation classes from Swift without having to drop back down to Objective-C.

Take a look inside the DataManipulators folder in Xode. Here you’ll find the various objects that handle the work of setting up the array and performing the various tasks that are then timed. You’ll notice there is a class called SwiftArrayManipulator. This conforms to ArrayManipulator which is where the methods are defined that the timing code uses.

There is also a class called NSArrayManipulator that also conforms to ArrayManipulator. Because of this, you can easily swap in an NSMutableArray for a Swift Array. The project code is simple enough that you can try an NSMutableArray with a single line change to compare the performance.

Open ArrayViewController.swift and change line 27 from:

let arrayManipulator: ArrayManipulator = SwiftArrayManipulator()

To:

let arrayManipulator: ArrayManipulator = NSArrayManipulator()

Build and run again, and press Create Array and Test to test the creation of an NSMutableArray with 1000 items:

NSArray Create 1000 items Xcode 7.2

…and then again with 10,000,000 items:

NSArray Create 10,000,000 items Xcode 7.2

Raw performance for creation is close to Swift, but not as good. Some of that, however, may come from the need to jockey between objects that can be stored in an NSArray and its Swift counterparts.

However, you only create an array once, and you perform other operations on it far more often, such as finding, adding, or removing objects.

In more exhaustive testing, such as when you’re using some of Xcode 7’s performance testing methods to call each of these methods 50 times, patterns begin to emerge:

  • Creating a Swift Array and an NSArray degrade at roughly the same rate between O(log n) and O(n). Swift is faster than Foundation, but both are in the region of 0.01 seconds. This is a significant improvement over Swift 1.2, where it took 3x as long as Foundation!
  • Adding items to the beginning of an array is considerably slower in a Swift Array than in an NSArray, which is around O(1). Adding items to the middle of an array is a bit slower in a Swift Array than in an NSArray. Adding items to the end of a Swift Array, which is less than O(1), is actually faster than adding items to the end of an NSArray, which comes in just over O(1).
  • Removing objects demonstrates similar in performance degradation across NSArray and Swift Array. From the beginning, middle or end, removing an object degrades between O(log n) and O(n). Raw time is much worse in Swift when you remove from the beginning of an Array, but the distinction is a matter of milliseconds.
  • Looking up items within an array is where Swift earns its name. Look ups by index and object grow at very close rates for both Swift arrays and NSArray. When you’re querying by index, large Swift arrays perform nearly 10 times faster in raw time than using comparable NSMutableArray objects.

Dictionaries

Dictionaries

Dictionaries are a way of storing values that don’t need to be in any particular order and are uniquely associated with keys. You use the key to store or look up a value.

Dictionaries also use subscripting syntax, so when you write dictionary["hello"], you’ll get the value associated with the key hello.

Like arrays, Swift dictionaries are immutable if you declare them with let and mutable if you declare them with var. Similarly on the Foundation side, there are both NSDictionary and NSMutableDictionary classes for you to use.

Another characteristic that is similar to Swift arrays is that dictionaries are strongly typed, and you must have known key and value types. NSDictionary objects are able to take any NSObject as a key and store any object as a value.

You’ll see this in action when you call a Cocoa API that takes or returns an NSDictionary. From Swift, this type appears as [NSObject: AnyObject]. This indicates that the key must be an NSObject subclass, and the value can be any Swift-compatible object.

When to use Dictionaries

Chaplin

Chaplin still knows when to use dictionaries. Do you?

Dictionaries are best used when there isn’t a particular order to what you need to store, but the data has meaningful association.

To help you examine how dictionaries and other data structures in the rest of this tutorial work, create a Playground by going to File…\New\Playground, and name it DataStructures.

For example, pretend you need to store a data structure of all your friends and the names of their cats, so you can look up the cat’s name using your friend’s name. This way, you don’t have to remember the cat’s name to stay in that friend’s good graces.

First, you’d want to store the dictionary of people and cats. Add the following to the playground:

let cats = [ "Ellen" : "Chaplin", "Lilia" : "George Michael", "Rose" : "Friend", "Bettina" : "Pai Mei"]

Thanks to Swift type inference, this will be defined as [String: String] — a dictionary with string keys and string values.

Now try to access items within it. Add the following:

cats["Ellen"] //returns Chaplin as an optional
cats["Steve"] //Returns nil

Note that subscripting syntax on dictionaries returns an optional. If the dictionary doesn’t contain a value for a particular key, the optional is nil; if it does contain a value for that key you get the wrapped value.

Because of that, it’s a good idea to use the if let optional-unwrapping syntax to access values in a dictionary. Add the following:

if let ellensCat = cats["Ellen"] {
    print("Ellen's cat is named \(ellensCat).")
} else {
    print("Ellen's cat's name not found!")
}

Since there is a value for the key “Ellen”, this will print out “Ellen’s cat is named Chaplin” in your Playground.

Expected Performance

Once again, Apple outlines the expected performance of dictionaries in Cocoa in the CFDictionary.h header file:

  1. The performance degradation of getting a single value is guaranteed to be at worst O(log n), but will often be O(1).
  2. Insertion and deletion can be as bad as O(n (log n)), but typically be closer to O(1) because of under-the-hood optimizations.

These aren’t quite as obvious as the array degradations. Due to the more complex nature of storing keys and values versus a lovely ordered array, the performance characteristics are harder to explain.

Sample App Testing Results

DictionaryManipulator is a similar protocol to ArrayManipulator, and it tests dictionaries. With it, you can easily test the same operation using a Swift Dictionary or an NSMutableDictionary.

To compare the Swift and Cocoa dictionaries, use a similar procedure as you used for the arrays. Build and run the app and select the Dictionary tab at the bottom.

Run a few tests – you’ll notice that dictionaries take significantly longer to create than arrays. If you push the item slider up to 10,000,000 items, you might even be able to get a memory warning or even an out-of-memory crash!

Back in Xcode, open DictionaryViewController.swift and find the dictionaryManipulator property:

let dictionaryManipulator: DictionaryManipulator = SwiftDictionaryManipulator()

Replace it with the following:

let dictionaryManipulator: DictionaryManipulator = NSDictionaryManipulator()

Now the app will use NSDictionary under the hood. Build and run the app again, and run a few more tests. If you’re running Swift 2.1.1, your findings should be similar to the results of more extensive testing:

  • In raw time, creating Swift dictionaries is faster than creating NSMutableDictionaries but both degrade at roughly the same O(n) rate.
  • Adding items to Swift dictionaries is roughly 10 times faster than adding them to NSMutableDictionaries in raw time, and both degrade close to the best-case-scenario O(1) rate promised by Apple’s documentation.
  • Removing items from Swift dictionaries is roughly as fast as removing items from NSMutableDictionaries, but the degradation of performance is again close to O(1) for both types.
  • Both Swift and Foundation dictionaries are able to lookup at roughly an O(1) rate. Like Swift arrays, dictionaries are faster in raw time than Foundation dictionaries, though the performance is very, very close.

These are amazing improvements over Swift 1.2. where creating dictionaries was significantly slower than creating NSMutableDictionaries. Back then, adding items to Swift dictionaries was also only three times faster. Removing Swift dictionaries was one – three times slower.

Swift 2.0 is what made native Swift dictionaries the better option.

And now, on to the final major data structure used in iOS: Sets!

Sets

Sets

A set is a data structure that stores unordered, unique values. Unique is the key word; you won’t won’t be able to add a duplicate.

Swift sets are type-specific, so all the items in a Swift Set must be of the same type.

Swift added support for a native Set structure in version 1.2 – for earlier versions of Swift, you could only access Foundation’s NSSet.

Note that like arrays and dictionaries, a native Swift Set is immutable if you declare it with let and mutable if you declare it with var. Once again on the Foundation side, there are both NSSet and NSMutableSet classes for you to use.

When to use Sets

Sets are most useful when uniqueness matters, but order does not. For example, what if you wanted to select four random names out of an array of eight names, with no duplicates?

Enter the following into your Playground:

let names = ["John", "Paul", "George", "Ringo", "Mick", "Keith", "Charlie", "Ronnie"]
var stringSet = Set<String>() // 1
var loopsCount = 0
while stringSet.count < 4 {
    let randomNumber = arc4random_uniform(UInt32(names.count)) // 2
    let randomName = names[Int(randomNumber)] // 3
    print(randomName) // 4
    stringSet.insert(randomName) // 5
    loopsCount += 1 // 6
}
 
// 7
print("Loops: " + loopsCount.description + ", Set contents: " + stringSet.description)

In this little code snippet, you do the following:

  1. Initialize the set so you can add objects to it. It is a set containing String objects.
  2. Pick a random number between 0 and the count of names.
  3. Grab the name at the selected index.
  4. Log the selected name to the console.
  5. Add the selected name to the mutable set. Remember, if the name is already in the set, then the set won’t change since it doesn’t store duplicates.
  6. Increment the loop counter so you can see how many times the loop ran.
  7. Once the loop finishes, print out the loop counter and the contents of the mutable set.

Since this example uses a random number generator, you’ll get a different result every time. Here’s an example of the log produced while writing this tutorial:

John
Ringo
John
Ronnie
Ronnie
George
Loops: 6, Set contents: ["Ronnie", "John", "Ringo", "George"]

Here, the loop ran six times in order to get four unique names. It selected Ronnie and John twice, but they only wound up in the set once.

As you’re writing the loop in the Playground, you’ll notice that it runs on a, well, loop, and you’ll get a different number of loops each time. In this case, you’ll need at least four loops, since there must always be four items in the set to break out of the loop.

Now that you’ve seen Set at work on a small scale, it’s time to examine performance with a larger batch.

Sample App Testing Results

Apple didn’t outline overall expectations for set performance as they did for dictionaries and arrays, so in this case you’ll just look at real-world performance.

The Swift Set documentation outlines performance characteristics for a couple of methods, but NSMutableSet does not.

The sample project has NSSetManipulator and SwiftSetManipulator objects in the SetViewController class similar to the setup in the array and dictionary view controllers, and can be swapped out the same way.

In both cases, if you’re looking for pure speed, using a Set probably won’t make you happy. Compare the numbers on Set and NSMutableSet to the numbers for Array and NSMutableArray, and you’ll see set creation is considerably slower — that’s the price you pay for checking if every single item in a data structure is unique.

Detailed testing revealed that since Swift’s Set shows that performance degradation and raw time for most operations is extremely similar to that of NSSet: Creation, removal, and lookup operations are all nearly identical between Foundation and Swift in raw time. Perhaps the extra time in the oven paid off for sets.

Creation degrades for both Swift and Foundation set types at a rate of around O(n). This is expected because every single item in the set must be checked for equality before a new item may be added.

Removing and looking up actions are both around O(1) performance degradations across Swift and Foundation, making set lookup considerably faster than array lookup. This is largely because set structures use hashes to check for equality, and the hashes can be calculated and stored in sorted order.

Overall, it appears that adding an object to an NSSet stays near O(1), whereas it can degrade at a rate higher than O(n) with Swift’s Set structure.

Swift has seen very significant performance improvements in collection data structure performance in its short public life, and will hopefully continue to see them as Swift evolves.

Lesser-known Foundation Data Structures

Arrays, dictionaries and sets are the workhorses of data-handling. However, Cocoa offers a number of lesser-known and perhaps under-appreciated collection types. If a dictionary, array or set won’t do the job, it’s worth checking if one of these will work before you create something from scratch.

NSCache

Using NSCache is very similar to using NSMutableDictionary – you just add and retrieve objects by key. The difference is that NSCache is designed for temporary storage for things that you can always recalculate or regenerate. If available memory gets low, NSCache might remove some objects. They are thread-safe, but Apple’s documentation warns:

…The cache may decide to automatically mutate itself asynchronously behind the scenes if it is called to free up memory.

This means that an NSCache is like an NSMutableDictionary, except that Foundation may automatically remove an object at any time to relieve memory pressure. This is good for managing how much memory the cache uses, but can cause issues if you rely on an object that may potentially be removed.

NSCache also stores weak references to keys rather than strong references.

NSCountedSet

NSCountedSet tracks how many times you’ve added an object to a mutable set. It inherits from NSMutableSet, so if you try to add the same object again it will only be reflected once in the set.

However, an NSCountedSet tracks how many times an object has been added. You can see how many times an object was added with countForObject().

Note that when you call count on an NSCountedSet, it only returns the count of unique objects, not the number of times all objects were added to the set.

To illustrate, at the end of your Playground take the array of names you created in your earlier NSMutableSet testing and add each one to an NSCountedSet twice:

let countedMutable = NSCountedSet()
for name in names {
    countedMutable.addObject(name)
    countedMutable.addObject(name)
}

Then, log out of the set itself and find out how many times “Ringo” was added:

let ringos = countedMutable.countForObject("Ringo")
print("Counted Mutable set: \(countedMutable)) with count for Ringo: \(ringos)")

Your log should read:

Counted Mutable set: {(
    George,
    John,
    Ronnie,
    Mick,
    Keith,
    Charlie,
    Paul,
    Ringo
)}) with count for Ringo: 2

Note that while you may see a different order for the set, you should only see “Ringo” appear in the list of names once, even though you can see that it was added twice.

NSOrderedSet

An NSOrderedSet along with its mutable counterpart, NSMutableOrderedSet, is a data structure that allows you to store a group of distinct objects in a specific order.

“Specific order” — gee, that sounds an awful lot like an array, doesn’t it? Apple succinctly sums up why you’d want to use an NSOrderedSet instead of an array (emphasis mine):

You can use ordered sets as an alternative to arrays when element order matters and performance while testing whether an object is contained in the set is a consideration — testing for membership of an array is slower than testing for membership of a set.

Because of this, the ideal time to use an NSOrderedSet is when you need to store an ordered collection of objects that cannot contain duplicates.

Note: While NSCountedSet inherits from NSMutableSet, NSOrderedSet inherits from NSObject. This is a great example of how Apple names classes based on what they do, but not necessarily how they work under the hood.

NSHashTable and NSMapTable

Map table?

Not this kind of Map Table. (Courtesy the Tennessee Valley Authority(!) via Flickr Creative Commons)

NSHashTable is another data structure that is similar to Set, but with a few key differences from NSMutableSet.

You can set up an NSHashTable using any arbitrary pointers and not just objects, so you can add structures and other non-object items to an NSHashTable. You can also set memory management and equality comparison terms explicitly using NSHashTableOptions enum.

NSMapTable is a dictionary-like data structure, but with similar behaviors to NSHashTable when it comes to memory management.

Like an NSCache, an NSMapTable can hold weak references to keys. However, it can also remove the object related to that key automatically whenever the key is deallocated. These options can be set from the NSMapTableOptions enum.

NSIndexSet

An NSIndexSet is an immutable collection of unique unsigned integers intended to represent indexes of an array.

If you have an NSArray of ten items where you regularly need to access items’ specific positions, you can store an NSIndexSet and use NSArray’s objectsAtIndexes: to pull those objects directly:

let items : NSArray = ["one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten"]
 
let indexSet = NSMutableIndexSet()
indexSet.addIndex(3)
indexSet.addIndex(8)
indexSet.addIndex(9)
 
items.objectsAtIndexes(indexSet) // returns ["four", "nine", "ten"]

You specify that “items” is an NSArray since right now, Swift arrays don’t have an equivalent way to access multiple items using an NSIndexSet or a Swift equivalent.

An NSIndexSet retains the behavior of NSSet that only allows a value to appear once. Hence, you shouldn’t use it to store an arbitrary list of integers unless only a single appearance is a requirement.

With an NSIndexSet, you’re storing indexes as sorted ranges, so it is more efficient than storing an array of integers.

Pop Quiz!

Now that you’ve made it this far, you can test your memory with a quick quiz about what sort of structure you might want to use to store various types of data.

For the purposes of this quiz, assume you have an application where you display information in a library.

Q: What would you use to create a list of every author in the library?

Solution Inside: Unique List of Author Names SelectShow

Q: How would you store the alphabetically sorted titles of a prolific author’s entire body of work?

Solution Inside: Alphabetically-Sorted Titles SelectShow

Q: How would you store the most popular book by each author?

Solution Inside: Most Popular Book by each author SelectShow

Where to Go From Here?

I’d like to give special thanks to my fellow tutorial team member, Ellen Shapiro, who initially created this tutorial and updated it until Swift 1.2. I would also like to thank Chris Wagner, who started on an Objective-C version of this article before the SwiftHammer came down upon us all, for passing along his notes and sample project for me to use while pulling this tutorial together.

I’ll also say thanks to the Swift team at Apple — constant improvements to Swift have made the native data structures stay true to the Swift name. In the foreseeable future, it seems that we won’t have a need for the Foundation data structures anymore. :]

If you want to learn more about data structures for iOS, here are a few excellent resources:

Got more questions? Go nuts in the comments below!

Niv Yahel

My name is Niv Yahel and I'm a co-founder and CTO of Vanhawks. I'm extremely passionate about coding and even more so about helping others improve. When I'm not working, I can be found following more tutorials, reading books, playing video games, or playing guitar.

Other Items of Interest

raywenderlich.com Weekly

Sign up to receive the latest tutorials from raywenderlich.com each week, and receive a free epic-length tutorial as a bonus!

Advertise with Us!

PragmaConf 2016 Come check out Alt U

Our Books

Our Team

Video Team

... 8 total!

Swift Team

... 15 total!

iOS Team

... 34 total!

Android Team

... 16 total!

macOS Team

... 13 total!

Apple Game Frameworks Team

... 13 total!

Unity Team

... 10 total!

Articles Team

... 10 total!

Resident Authors Team

... 6 total!