Swift for Ruby Devs: The Basics pt. 1

In a previous post, Swift for Ruby Devs: Type Systems, we looked at the major differences and similarities between Swift and Ruby. Swift, being a statically-typed language as opposed to a dynamically-typed language, requires us to write our code and think about how we design our interfaces a little bit differently. With an understanding of the differences in type systems between Swift and Ruby, we can now start digging into Swift's syntax and how it compares to Ruby.

Variables and Constants



Ruby

Let's first look at how we define variables and constants in Ruby. Variables are defined by simply writing the name of the variable and assigning an initial value to it. Constants are defined in the same way except we must write the name of the constant in all uppercase letters.

my_variable = 1

MY_CONSTANT = 1  



Since Ruby is dynamically-typed, we can later change the value of our variable to a String, Class, or any other type we'd like. Even constants can be later changed (though you'll get a warning) and are technically only immutable by convention.

my_variable = 1  
  => 1
my_variable = "One"  
  => "One"

MY_CONSTANT = 1  
MY_CONSTANT = "One"  
  warning: already initialized constant MY_CONSTANT
  warning: previous definition of MY_CONSTANT was here
  => "One"

MY_CONSTANT  
  => "One"



Swift

When defining variables in Swift, we prefix variable names with the var keyword and constant names with the let keyword.

var myVariable = 1

let myConstant = 1  



The first difference you may notice in Swift is the convention in naming variables and constants. In Swift, we use CamelCase to separate each word. In Ruby, we use underscores _.

Another difference you'll notice is that constants in Swift are not defined in all uppercase letters. The let keyword denotes a constant, while in Ruby, all uppercase letters denote a constant.

In Swift, the compiler encourages you to default to using constants unless you're absolutely certain that the value will later change. In that case, you're encouraged to use a variable. The complier will give you a warning if a variable is defined that is never changed.

One last major difference to note is that variables and constants in Swift are strictly-typed. This means that once the type of a variable is declared, it cannot be later changed to a different type. Also, the immutability of constants is strictly enforced. You cannot change a constant after defining it, even if it's the same type. Attempting to do so will result in an error. This strictly-typed nature of Swift means that, when defining a variable or constant, we must either supply an initial value or be explicit about the type of object it will contain.

var myVariable = "Here's my string"

myVariable = 123  
  => error: cannot assign value of type 'Int' to type 'String'

let myConstant = 2

myConstant = 3  
  error: cannot assign to value: 'myConstant' is a 'let' constant
  note: change 'let' to 'var' to make it mutable

myConstant  
  => 2



Fun Fact: Constants and variables in Swift can contain Unicode characters, including emojis. 😀

let 🐶🐮 = "dogcow"

🐶🐮
  => "dogcow"


Basic types



Strings

When creating strings in Swift, it's worth mentioning that we cannot use single quotes ' ' like we do in Ruby. Instead we must always use double quotes " ". In Ruby, one convention is to always use single quotes unless string interpolation is involved or if we'll be escaping characters. If you're used to this convention, the Swift compiler will throw an error if you mistakenly create a single-quoted string.

let myString = 'A beautiful day'  
  => Single-quoted string literal found, use ""



Another difference you'll find in Swift when dealing with Strings is the syntax for string interpolation. Whereas in Ruby we use #{} for string interpolation, we use \() in Swift.

weekday = 'Monday'

my_string = "Today is #{weekday}!"  
  => "Today is Monday!"
let weekday = "Monday"

let myString = "Today is \(weekday)!"  
  => "Today is Monday!"



Integers and Floating Point Numbers

In Swift, we write integers the same way we do in Ruby; by supplying a whole number. The only difference worth mentioning is that in Swift, an integer's type is Int. In Ruby, this object's type is an Integer.

In Swift, there are two types of floating point numbers; Float and Double. We write these the same way we write floats in Ruby, with decimal numbers. When writing a floating point number in Swift, it'll default to a Double unless you explicitly define it's type as a Float.

let version = 3.0

version.dynamicType  
  => Double.Type

let version: Float = 3.0

version.dynamicType  
  => Float.Type



The difference between a Float and a Double is that a Float represents a 32-bit floating-point number whereas a Double represents a 64-bit floating-point number. What this means is beyond the scope of this post, but here's a snippet from Apple's Swift language guide that attempts to explain the difference.

Double has a precision of at least 15 decimal digits, whereas the precision of Float can be as little as 6 decimal digits. The appropriate floating-point type to use depends on the nature and range of values you need to work with in your code. In situations where either type would be appropriate, Double is preferred.

As you can see from the last sentence in the snippet, a Double is preferred over a Float in situations where using either is appropriate. This is why Swift will default to a Double when using type inference to set a floating point number to a variable.

Booleans

Booleans in Swift are written in the same way they're written in Ruby, with the true or false boolean literals. The difference being is that in Swift, this will create an object of type Bool, whereas in Ruby it'll create an object of type TrueClass or FalseClass. Also worth noting is that a Bool is not a Class but rather a Struct in Swift. In practice, however, these differences are not important. We will still interact with Booleans in the same manner we do in Ruby.

var on = true  
var off = false  


Operators



In Swift, the syntax we use for interacting with operators is the same as in Ruby.

3 * 3  
  => 9

"dog" == "dawg"
  => false

3 >= 2  
  => true

var number = 3  
number += 1  
  => 4

!false
  => true


Ranges


Ranges in Swift are written almost in the same way as they are written in Ruby with a few differences. In Ruby we use the .. and ... syntax.

1..2  
  => 1..2

1...2  
  => 1...2

(1..2).to_a
  => [1, 2]

(1...2).to_a
  => [1]



The .. syntax creates a range that includes the number on the right-end of the operator. The ... syntax creates a range that includes all numbers up to the number on the right-end of the operator.

In Swift, we also use the ... syntax but it has a different meaning. The ... syntax creates a range that includes the number on the right-end of the operator. To create a range that includes all numbers up to the number on the right-end of the operator, we instead use the ..< syntax.

1...2  
  => 1..<3

1..<2  
  => 1..<2

Array(1...2)  
  => [1, 2]

Array(1..<2)  
  => [1]


Collections



Arrays and Hashes (known as "Dictionaries" in Swift) are some of the most heavily used basic data structures in most programming languages. We use them to group related pieces of data together in meaningful ways. Swift and Ruby share many things in common when defining and interacting with these data types, but there are a few differences that are important to understand.

Arrays

In Swift, we create arrays in the same manner we do in Ruby; with the [] syntax. Because Swift is a type-safe language, one important difference is that arrays in Swift cannot have elements of different types.

scores = ["3", "4", 5, 6, 7]  
  => ["3", "4", 5, 6, 7]
var scores = ["3", "4", 5, 6, 7]  
  => error: type of expression is ambiguous without more context



This error the Swift compiler returns may not be the most helpful error message, but what it's basically telling us is that it doesn't know whether this should be an array with elements of type String or type Int. It doesn't know which type to infer.

We interact with arrays in Swift similarly to how we interact with arrays in Ruby.

var todo = ["Finish something", "Do something else", "Do another thing"]

todo.append("yet another thing")  
todo += ["all the things"]

todo.count  
  => 5

todo[4]  
  => "all the things"

todo[4] = "Don't do anything"

todo.removeAtIndex(3)

todo.count  
  => 4



In Ruby, the two most common ways to append an element to an array is to use the push method or the << shorthand. These methods don't exist for arrays in Swift but to achieve the same goal, Swift implements the append method as shown in the example above.

Hashes (Dictionaries)

In Ruby, hashes are denoted by curly braces {} and key-value pairs are added using the hashrocket syntax => or by using symbols.

{ key_one: "value one", "key_two" => "value two" }



In Swift, we use brackets [] instead of curly braces {} to create dictionaries. Also worth noting is that Symbol's and hashrockets don't exist in Swift. We create a key-value pair by using the : syntax on the key, which can be a string, an integer, etc.

[ "keyOne": "value one", "keyTwo": "value two" ]



We can interact with dictionaries in Swift the same way we interact with hashes in Ruby.

var myHash = [ "keyOne": "value one", "keyTwo": "value two" ]

myHash["keyOne"]  
  => "value one"

myHash["keyTwo"] = "new value"

myHash["keyTwo"]  
  => "new value"



An important difference to note is that due to Swift's strictly-typed nature, all keys and values in a dictionary must be of a single type. The keys and values don't have to match in type, however.

var myHash = [ "keyOne": "value one", 2: "value two" ]  
  => error: type of expression is ambiguous without more context



This is the same error we saw above when trying to create an array with elements of different types. The compiler can't decide which type to infer from the keys.



Enumerable vs. Sequence

In Ruby, we're able to iterate over strings, arrays, and hashes, and perform tasks on them thanks to the Enumerable module that's mixed into these classes. The Enumerable module implements many useful methods such as each, map, reduce, and select, to iterate over a collection and perform some work. When learning what methods are available to us when interacting with collections, the Enumerable module as well as the individual collection classes, provide us with all the information we need.

The equivalent of the Enumerable module in Swift is the Sequence protocol. We'll discuss protocols in more detail in a later post but they define a set of behaviors expected of objects. Classes or other types that adopt a protocol must conform to that protocol by adhering to those expectations. Here's a definition of protocols from the official Swift docs:

A protocol defines a blueprint of methods, properties, and other requirements that suit a particular task or piece of functionality. The protocol can then be adopted by a class, structure, or enumeration to provide an actual implementation of those requirements.

Arrays and dictionaries in Swift conform to the Sequence protocol which provides methods like map, reduce, and filter, to iterate over a collection and perform some work. To learn what methods are available to us in Swift for iterating over a collection, the Sequence protocol linked above as well as the individual collection classes themselves, are a good place to start.


Summary


Examining the differences as well as the similarities in syntax between Swift and Ruby of the most basic data types, will help us get up and running quickly when programming in Swift and will prepare us to understand more complex concepts that are unique to the Swift programming language. We've covered the very basics such as variables, strings, and hashes. In a later post, we'll dig into control structures, methods, and classes, to name a few, so we can start building real world applications.