If you want to learn to develop apps for iOS and OS X there are a number of bottlenecks that you need to overcome.
- Learn how to program.
- Learn a language for developing on the Apple ecosystem, generally Swift or Objective-C.
- Learn the development tools: Xcode etc.
- Learn the Cocoa APIs.
If you already know how to program in Ruby, then you’ve already overcome the first bottleneck.
But if Ruby is your main programming language, then bottleneck number 2 has up to now been a significant hurdle.
Thus in recent years there have been several tools that allow you to develop iOS applications without having to learn Objective-C (e.g. Titanium, PhoneGap, rubymotion, Xamarin)
For Rubyists who are accustomed to the benefits and comforts that come with programming in Ruby, Objective-C is not a particularly pleasant language to program in and the learning-curve can be quite steep. But that has changed significantly with the recent release of Swift.
The jump from Ruby to Swift is much smaller than the jump from ruby to Objective-C. If you like Ruby, you’ll probably find that a lot to like about Swift.
In this article I’ll outline a number of interesting features from Swift and compare them with the equivalent features in Ruby.
Some basics
Constants and variables
Everything in mungable in Ruby. Even constants are constant only by convention - you’ll get a warning if you try to change them but you can still go ahead and change them.
Swift is much stricter (in general, this is a theme. Swift is generally more strict about things than Ruby.)
In Ruby constants are conventionally defined using capital letters. If we really want to change a constant, we can. Ruby will generate a warning if we do this, but not an error.
We can create a new variable at any time simply by assigning it. And we can change the type of that variable at any time by assigning a new value. In this example, x
is consecutively an Integer
, a custom Object
and a String
all in the space of a few lines. This dynamism is a core part of Ruby’s philosophy - in Ruby anything can change at any time. This is what gives rise to a lot of Ruby’s meta-programming power. But it can also lead to some frustrating bugs and bad habits when comes to programming in the large.
Swift takes a much stricter approach to constants and variables. In Swift all values are strictly typed. This has two implications that are a significant change to what you are accustomed to with Ruby.
- You must declare the type of all of your variables and constants, or else assign them an initial value which Swift will use to infer their type.
- You can’t change the type of a variable once you’ve declared it. It’s type is fixed and cannot be changed. For constants, their value is also fixed and can’t be changed once they’ve been initialised.
In Swift you declare a variable using the var
keyword and constants using the let
keyword.
Constants are immutable. Once you’ve declared them, they cannot be changed again. Attempting to modify a constant in any way will result in an error.
Arrays
In Ruby, you can declare an array and add multiple different types to it.
Arrays is Swift are similar to arrays in Ruby with one notable difference. In Swift, arrays are typed. You declare the type of the array (or Swift infers it) and you can only add items that are of the same type to the array. You can’t generally store multiple different types in an array. There are ways to include objects of different types in an array (using protocols or inheritance), but for the most part you’ll stick to only having one type of thing in your array.
Dictionaries
Similar to Arrays, Dictionaries in Swift are typed. You need to specify the type of both the keys and the values when you declare the dictionary. Once initialised, your dictionary can only contain key-value pairs that match that type signature.
Also, similar to arrays, you can declare the type signature for the dictionary explicitly or Swift can infer it from the initial values.
Apart from being typed, dictionaries in Swift are similar to dictionaries in Ruby. Here’s an example of declaring a dictionary and accessing it’s elements.
Tuples
A tuple is an ordered list of elements. Tuples provide a way to group multiple values together. Many languages provide tuples as a native data type. Commonly there are two ways of accessing the data in a tuple: by position or by name. Swift provides tuples as a native data type along with the ability to access the tuples data both by name and by position.
Ruby doesn’t formally have tuples, although you can approximate their behaviour using a couple of techniques.
For example, you can use a convention where you treat a short list as a tuple. This is a common pattern in Ruby, where you return a short list of 2 or 3 elements from a method and these are decomposed into individual variables using the assignment operator.
Or, if you want to be able to use dot notation to access elements in a tuple, you could use an object or struct.
So while Ruby doesn’t provide tuples as a native type, we can approximate much of the behaviour of tuples using Ruby arrays or structs.
In Swift tuples are one of the native types. A tuple can consist of multiple different types. Tuples are ordered and elements of tuples can also be named. So we can access elements of a tuple both by position and name, and we can deconstruct data into a tuple using the assignment operator. Lets go through some examples of how we use tuples in swift.
Tuples are defined using ()
. We can decompose a tuple into it’s parts using the assignment operator.
Here we create a tuple consisting of 2 floats. We can assign the contents to lat
and lon
using assignment. If we don’t care about all of the contents of the tuple we can use _
to indicate fields that we don’t care about. In line 5 we just extract the value of the first element of the tuple into a constant - the _
indicates that we don’t care about the second.
We can also access the elements of a tuple by position or by name. Here we define a tuple with named fields. We do this by providing the names as keys when we create the tuple (line 1). In this example we’ve named the first field code
, and the second field message
.
Naming things
One immediately noticeable difference is code style, especially with regard to how we name things. Ruby generally uses snake-case for methods and variables and camel-case for classes and capitalised names for constants.
Swift programs use camel case for all these. Essentially:
- CamelCase for everything
- class names and top-level constants should be capitalized
- method names, function names and variable names should start with a lower-case letter
Truthiness
In Ruby nil
and false
are falsey and everything else is truthy. Thus you can use the presence of something as a logical condition. In Ruby we can use any type or expression in a boolean operator or an if condition.
Swift is much stricter in it’s treatment of boolean values. Booleans in Swift are of type Bool
and can be either true
or false
. Anything in Swift that takes a boolean, must use a real boolean type. Any operator or function that expects a boolean must be given a boolean or the type system will complain.
So we can’t do things like what’s shown below in Swift. In both cases we’ll get an error along the lines of “Type ‘String’ does not conform to protocol ‘BooleanType’”.
Booleans in Swift are declared using the keyword Bool
or by initializing them with true
or false
.
Running Code
One of the major differences between writing Ruby code and writing Swift code is the tooling involved.
Although some Rubyists use an IDE such as RubyMine, most generally use a text editor such as Sublime, Emacs or Vi in combination with the command line and interactive console.
For Swift development you’ll generally need to get to know Apple’s development tools. While it’s possible to setup your development workflow using other tools, I wouldn’t recommend it, especially if you are new to mac development.
As a Ruby developer you’ll probably use interactive console a lot when doing development. The repl (e.g. irb, pry) provides a quick way of testing out code and inspecting how a piece of code behaves.
Previously with Objective-C you’d have to compile and run a program to test out new code. The compile-run cycle was a pain for developers used to developing with an interactive repl.
Luckily, the tools for interactive code development have improved significantly with the release of Swift. Xcode now has an interactive repl for running Swift code. It also has a new feature called playgrounds that allow you to try out and run code interactively.
Static typing and type inference
Swift is statically, strongly typed and uses type inference to determine the initial type of all your variables and constants.
We’ve already touched on swift’s type system a couple of times - it’s one of the most immediately noticeable differences between ruby and swift. It such an important part of the language that it’s time to dive in deeper before we go any further.
Typing in Ruby
First lets review typing in Ruby. Ruby is strongly, dynamically typed.
Let’s break down those two dimensions. Being strongly typed means that an operation that expects a certain type is checked for that type before the operation is performed. With a weakly typed language, no such checking is done. Lets take a short detour to look at a weakly typed example to help us better understand strong typing.
JavaScript is a weakly typed language. If we fire up our JavaScript console and try the following:
we can see that JavaScript will happily take a string and an integer and use them as arguments to the +
function. In this example it coerces the integer to a string and appends it to the string.
If we try the same in ruby we get an error since ruby is strongly typed.
The +
method in ruby expects two arguments to be of the same type. The +
method called on a string expects to be passed a string, and the +
method passed to a number expects to be passed a number. So in Ruby we need to be more specific about the behaviour we want here and do the correct type coercion ourselves.
Ruby is also dynamically typed. Dynamic typing means that the type of something can change through the course of a program, and the type is determined at the time at which it is used.
In this example, x starts out as a string, and we can pass it to +
to concatenate it to another string. Then we can change it’s type to an Float, just by assigning a Float value to it. Now when we call +
with x as an argument we get different behaviour because it’s now a number.
In fact Ruby doesn’t really have a strong notion of types at all. Everything is an object and the “type” of an object is determined by it’s behaviour rather than the class of the object. This approach is often referred to as “duck typing”. If it looks like a duck and it quacks like a duck, then for all we care it’s a duck. The type of something doesn’t matter - what matters is how it behaves. So you can usually pass any object as an argument to a method if it implements the behaviour that the method expects. In the following example we see an array of pets, where each pet is a different class, and each pet makes some kind of noise. We iterate through each object calling it’s make_noise
method.
The output from running this program is
``` ruby code/duck_typing.rb Quack!
Woof! ```
In this example, Ruby doesn’t care about the type of each object, (i.e. which class it is), only that it responds to the make_noise
method. If it looks like something that can make_noise
, then that’s good enough. Ruby doesn’t do any type-checking other than checking that the object responds to the method that we’re trying to call on it.
That’s a quick tour of typing in Ruby. If you’ve been mainly programming in Ruby for a while, then you’re probably not used to thinking about the types of your objects in any great detail, especially during the design phase of your program, since it’s easy to change them as you go along. Swift’s type system will be one of the most immediately noticeable and profound differences in philosophy between the two languages. Lets look at Swift’s type system.
Typing and type inference in Swift
Swift is strongly, statically typed.
Statically typed
Being statically typed means that all your variables, constants, functions must have their types declared in advance. Then the compiler uses these type declarations to check that there are no type errors when it compiles the program. If there is a type error, your program won’t compile.
Unlike Ruby, all your variables must have a declared type, and once declared (or inferred) that type cannot change. Trying to change the type of something will result in an error.
In this example, we declare an integer variable (Swift infers that it’s an integer from our initial assignment). Trying to assign a string value to our variable will cause swifts to raise a type error.
Type inference
As we’ve mentioned already Swift uses type inference to infer what types your various variables have. Alternatively you can explicitly declare the type of your variables but in practice you often don’t need to. Swift will infer the type of a var if you assign it an initial value. So when you declare a variable, you need to either give it an initial type, or explicitly declare it’s type. Here’s an example of how we can declare a type explicitly.
In line 1 we declare a string variable and give it in initial value. Because we gave it an initial value, we don’t need to explicitly declare the type of the variable - Swift infers that this is a string.
In line 2 we see how to initialize a variable without giving it an initial value. In this case, because we didn’t give the variable an initial value, swift can’t infer it’s type, so we need to explicitly define it’s type as a String
. At line 3 then we can assign a string value to that variable. If we tried to assign a value of a different type we would get an error.
Strongly typed
Like Ruby, Swift is strongly typed. Whenever you use a variable or pass something as a function argument, Swift checks that it is of the correct type. You can’t pass a String to a function that expects an Integer etc. Unlike ruby, swift does this check at compile time (since it’s statically typed) whereas ruby does this check at runtime (and then only at the point where the method being checked is called).
Here’s an example where we declare a function. A function has a type signature and anytime you call the function, your function call must correspond to that type signature.
The type signature for this function indicates that it takes 2 arguments, both of which are integers and it returns an integer ( -> Int
indicates the return type of the function)
If we try to call this function with 2 integers as arguments (line 5) it returns an integer. But if we try to call it with arguments that aren’t 2 integers we’ll get an error (line 6).
Type Safety
Ruby is not type-safe. In Ruby you can pass an object of any type to a function - Ruby will happily use that object if it implements the expected methods (duck-typing). If it doesn’t, then you’ll end up with a runtime error.
Swift is a type-safe language. All variables have a declared type. All functions/methods have a type signature which declares the types of it’s arguments and the type of what it returns. The Swift compiler checks at compile time that all your types match up and your program won’t compile if they don’t.
Swift uses the type signature of your functions and methods to make sure they are being called correctly (i.e. with the correct types). It also uses type signature for function dispatch. So you can define multiple versions of a function with different types signatures, and the one that gets called is the one where the arguments type signatures match. Here’s an example where we defining two functions with the same name, but different type signatures. If we call call the function with floats as arguments, the first one gets called, and if we call it with integer arguments, the second one gets called.
The benefits of static typing
One of the things I liked when I started programming in Python and Ruby was that I didn’t need to declare the type of everything. Just being able to assign and use a variable when I needed it felt freeing, and the lack of a compilation step really seemed to speed up development. Later I learned that you don’t really gain much in the long term. That initial boost in development speed gets eaten up later in the project, when you’re debugging runtime errors, writing extensive test suites etc.
Having to declare your types and satisfy the compiler’s type system is not as onerous as it may appear. Since I started using languages with good, strong type systems, it’s become something that I miss when I’m programming in dynamic languages such as Ruby. A few points on the benefits/costs of static typing:
- In Ruby, you still need to be aware of the types of the objects (along with how they behave) that your are passing around your program. Often passing the wrong type of object to a method that expects something else will result in an error (which you won’t notice until your program is running). In fact by not explicitly declaring and designing your types, you increase the cognitive workload associated with keeping track of all the objects you’re passing around your program.
- You don’t actually save that much. In the grander scheme of things, declaring the types of things in your program is a tiny portion of the overall amount of work involved in building a working program. And whatever time and typing you end up saving by not having to declare your types, you’ll end up using it to write unit tests and writing documentation for your function arguments anyway.
- With static typing you end up with less meta-programming power. For some problems this means that you’ll end up with a solution that is more explicit and less “magicy”. Ruby has powerful meta-programming facilities which can facilitate elegant solutions for come kinds of problems (e.g implemening a DSL). For larger systems and long-term maintenance and readability the absense of magic can actually a benefit.
- You may have been put off static-typing from experiences in other languages such as java or c++. They’re type systems suck, and get in your way as much as they help you. However the type system in Swift is better. If you embrace it you’ll find it extremely useful in writing more correct programs.
- You can often deduce what a function does from it’s type signature. This is very useful for making code more readable and understandable
- Refactoring becomes easier, and you have a bunch of compiler errors to tell you where you broke things.
- In Swift you don’t give up on the advantages of being able to develop without a compile step. The fact that you have a repl and playgrounds give you many of the benefits that come with dynamic languages by removing the historically slow and annoying step of having to constantly compile your code to see if works.
Duck Typing, Generics and Protocols
We already discussed how Ruby implements duck typing, and how you can pass any type of objects to a function so long as they implement the expected behaviour.
There are 2 concepts in Swift that support similar behaviour.
Generics allow you to define code that works with multiple types. Protocols allow you to declare that something must implement particular functionality.
Let’s consider a simple function that does nothing except return it’s argument.
In Ruby we could write:
Ruby will happily return the argument here without worrying about it’s type.
Now in Swift, we have to define the type signature of this function when we define it. And based on what we know so far, we would need to define a function that takes and integer as an argument, and another function that takes a string as an argument, and another function for whatever other types we want to support.
That’s where generics come in. We can define this as a generic function, which indicates it can work with multiple types. Here’s how we define our generic doNothing function in swift.
Putting <T>
here after the function name indicates that we are defining a generic function, with T
as the placeholder for the type of whatever get’s passed as an argument. Specifically, the type signature for this function says that it’s a generic function that takes a single argument of type T
and returns something of type T
.
We previously had an example of duck typing in ruby.
But in Swift an array must be typed - we have to specify the type of an array and then we can only put objects of that type into it.
Protocols allow us to specify that something must implement specific behaviour. But Protocols are also first class types in Swift. So we can use a protocol as the type for an array, and then we can put any object that adopts that protocol into that array.
Here we declare an array for holding our animals and give it a type of Noisy
. That array can store any object that implements the Noisy
protocol. This lets us implement duck typing in Swift, while still getting the benefits of Swift’s type system.
In Summary
Swift is a very nice language and for anyone fluent in Ruby is should be relatively easy to learn. Like Ruby, functions and blocks are first-class objects and many of the patterns that are familiar to Ruby programmers translate directly to Swift. Classes in Swift are more or less the same as in Ruby. Optionals in Swift are an interesting analog to how we often use nil
in Ruby.
The major difference in Swift that is noticeable when coming from Ruby is the type system. Once you get used to that, the rest is plain sailing. Swift is easy enough to learn that you don’t lose anything by choosing to develop in Swift over choosing to use something like RubyMotion. The learning curve in learning Swift will be relatively gentle compared to the learning curve of the various Cocoa APIs.