This post is the first in a series I am doing on Swift for Rubyists.
All of the examples from this post are available as a playground to download at this repo.
Update: I have corrected a mistake in this post which stated that mixed Swift arrays of Ints and Strings worked because they were inferred to be the AnyObject type. This was incorrect. This only works in cases where you have imported the “Foundation” library or if you use the type Any. My apologies. See this article for further explanation.
Arrays
Swift arrays are like arrays in most modern languages including Ruby. In some cases, they share some similar characteristics. In others, they are very different. We’ll compare how Swift handles arrays vs Ruby.
Swift Array Declaration and Instantiation
Due to type-safe nature of Swift, the declaration syntax of Swift arrays to Ruby arrays is similar, but the differences are very important.
Declarations
In Ruby, it couldn’t be simpler:
[]
In Swift, there are three main ways to declare an array. In the first two ways, you must declare the types that the array will hold. Here are the first two using an example of declaring an array to hold String types.
var swift_core: [String] var swift_core_again: Array<String>
Literals
Luckily, Swift has a third option. They are literals and they are very similar to Ruby literals. The examples I use throughout this post will be mostly be literals. The syntax is almost identical.
ruby_array = ["Matz", "Patterson"] var swift_array = ["Lattner", "Apple"]
In the case of literals, we do not have to declare a type for the array because Swift uses type inference to infer the type of objects that it can hold.
There is one important exception for using literals that I must point-out. It’s the shorthand syntax for how Ruby declares and instantiates an array. In Ruby, the shorthand way of doing this is:
arr = []
However, if you try to do this in Swift, and you have imported Objective-C’s Foundation library, it will compile, but it actually creates an NSArray, not a Swift Array. Take a look.
import Foundation var arr = [] print("\(_stdlib_getDemangledTypeName(arr))") //"NSArray"
So, if you try to use the Ruby shorthand for declaring and instantiating an array, you will get very different results in Swift.
If you wanted to do the above to create a Swift Array, you would use:
var arr = [String]()
Subtle, but important. There are actually ways to convert an NSArray to a Swift Array, but we won’t cover those in this post.
Let’s step back and talk about declaration vs. instantiation in Swift. Unless you are using a literal, Swift requires you to declare a type before instantiating an array. So, you actually have two steps if you decide not to use a literal. Declaring an array in Swift does not actually instantiate it. In this example, we declare a type in Swift, but do not instantiate it, so when we go to inspect the “count” property, we get an error.
var not_instantiated_array = [String] not_instantiated_array.count //error
We haven’t actually instantiated our array so no “count” property exists. The proper syntax to do both is:
[<SomeType>]()
There are many reasons why you might want to declare an array, but not instantiate it, however, we won’t cover those here. Just know that Swift treats them separately when declaring an array.
Containing Items
Maybe the biggest and most important difference between Swift arrays and Ruby arrays is what kind of items that they hold.
In Ruby, arrays can hold multiple kinds of items in one array. For example, this is a valid array in Ruby:
mixed_array = ["apple", 1, OpenStruct]
On the other hand, Swift emphasizes type-safety. Swift arrays can only hold instances of one type.
Here are some examples of valid arrays in Swift:
var fun = ["Some Nights", "We Are Young", "Carry On"] var lions = [9, 81, 90, 21] var name = UILabel() var email = UITextField() var views: [UIView] = [name, email]
Mixed arrays, such as those of Ints and Strings, can be achieved only when you have imported the “Foundation” library. For example, here’s a valid array that holds both Int and String types.
import Foundation var inebriated_array = [99, "bottles", "of beer"]
This can be achieved because Swift is automatically converting the instances to NSStrings and NSNumber classes. Check out this article here for more information.
Type-safety also applies if you try to add an element which does not match the type of the existing elements of an already instantiated array. For example, if we try to add an element using Swift’s “#append” method, this will not compile:
var broken_up = ["Blink"] broken_up.append(182) //compile error
Appending
Ruby and Swift are almost identical with regards to the syntax of how to add an element on to the end of an array. Here’s an example in Ruby of adding two arrays together.
a = ["Simon"] a += ["Garfunkel"]
In Swift, it’s the same.
var band = ["Simon"] band += ["Garfunkel"]
In Ruby, you can use “#push”, which returns the modified array so you can assign it to something else.
chocolate_shake = ["Milk", "Ice cream", "Chocolate"] mud_slide = chocolate_shake.push("Rum")
In Swift, you can use “#append”, but the method does not return the modified array.
var chocolate_shake = ["Milk", "Ice cream", "Chocolate"] chocolate_shake.append("Rum") print(chocolate_shake)
There is no equivalent to Ruby’s popular “<<” syntax out of the box. Although, we may cover this under Custom Operators later.
isEmpty vs. Empty?
Ruby provides many useful convenience methods for finding out more information about the state of an array. For example, Ruby provides an #empty? method to check whether an array has any elements.
vending_machine= [] vending_machine.empty? => true
Swift provides a similar feature via a property called isEmpty.
var vending_machine = Int vending_machine.isEmpty //returns true
Iteration
Iterating over the elements in an array in Ruby or Swift are very similar. Like many languages, Ruby and Swift both offer a “for…in” loop. Here’s an example in Ruby.
beatles = ["John", "Paul", "George", "Ringo"] for member in beatles puts "Here's #{member}" end
In Swift, the syntax is almost exactly the same.
var beatles = ["John", "Paul", "George", "Ringo"] for member in beatles { println("Here's (member)") }
If you need the current index of the element that you are iterating over, similar to Ruby’s “#each_with_index” method. You would use the “#emunerate” function in the for..in loop. Each iteration of enumerate returns a tuple containing the index and the element. We’ll cover tuples another time.
var beatles = ["John", "Paul", "George", "Ringo"] for (index, member) in emunerate(beatles) { println("Playing #(index)") println("Here's (member)") }
More to Learn
This is just a brief introduction to Swift arrays for Rubyists. If you have found this information useful and would like to learn more, please sign up for the Swift newsletter below and I will send you updates when new posts and videos become available. You’ll also receive access to my video tutorial “Advanced Swift Arrays”.
Learn more about other areas of Swift from a Rubyist’s perspective.
I also highly urge you to read Apple’s official documentation on Swift.
Example extension to get Ruby-like .each iteration http://benscheirman.com/2014/06/implementing-ruby-style-iteration-in-swift/
That’s a great article on Swift extensions and Ruby. I’ve come across it before as well. Thanks for sharing.