Swift for rubyists cover?fm=jpg&fl=progressive&q=75&w=300

Swift for Rubyists

We welcomed back JP Simard to speak at one of our recent SLUG meetups, graciously hosted by Iron.io and Heavybit! This meetup was a free Swift class with tips tailored for developers coming from Ruby. JP’s talk aimed at helping Rubyists understand Swift’s philosophy and practical implications.

As usual, video & slides are synchronized. The video is also subtitled. You can find a blog version of the talk (with code samples) below.


Some Swift Context (0:00)

A lot of Ruby developers ended up learning Objective-C because there’s a lot in common between the two languages. Even today, you can’t really effectively run Objective-C in AWS, and so many Objective-C developers learned Ruby. Ruby has been kind of like the de facto language to build scripting tools or back end tools. There’s currently a rich history of interoperability between the two, for example with tools like Ruby Motion and Cocoa Pods entirely written in Ruby to help with iOS and Mac development.

Ruby + Objective-C (2:22)

In order to talk about Swift, we should first talk about where it came from - Objective-C. Ruby and Objective-C have lots in common, though it might not look like it from the syntax. They share the same common ancestor of smalltalk, and they are very strongly based in the concepts of dynamic message dispatching and dynamic typing. These are things that made it easier for Objective-C developers to go over to Ruby and vice versa. They both have reflection methods - kind_of? or the isKindOfClass for Objective-C, respond_to? for Ruby.

Ruby + Swift: Similarities… (5:35)

How does Ruby relate to Swift? There are actually very few similarities between the two languages. The syntax might look more palatable to Rubyists, and a few other things like REPL are closer in philosophy to Swift. There’s almost a scripting feel to Swift, so you can use #! user/bin/xcrun Swift to run any arbitrary Swift code from the command line as a script. There are finally functional programming concepts in the Swift standard library, something that Ruby has had for a long time. And on one minor point, Swift now has string interpolation, which is something that Rubyists do all the time.

… & Differences (7:40)

All in all, moving from Objective-C to Swift, we’re seeing more and more differences between Ruby and Swift. Swift is still a compiled language like Objective-C, but it moves a lot more to the compiler. Objective-C had a very rich dynamic runtime where you could do a lot of metaprogramming, introspection, reflection, and all sorts of runtime hackery, but with Swift, there’s more emphasis on doing things at compile time. One of the biggest stumbling blocks are differences in APIs, libraries, and frameworks, since those are tailored to specific platforms and applications. Another major difference is that Swift is extremely type safe, and so we also have type safety generics. There are also some pretty major challenges in getting Swift to work outside of iOS and OSX.

Obstacles to running Swift outside iOS/OSX (10:00)

Three main steps would need to happen to be able to use Swift outside of iOS/OSX. The Swift compiler, runtime, and standard library would all need to be open sourced. The Swift compiler lives on top of Clang, but you can’t compile Swift without Clang or just with Clang. To use Swift across platforms, Apple would have to open source the Swift runtime. It shares a lot in common with Objective-C, probably to keep backwards compatibility. The first two have happened for Objective-C, but the fact is that the Objective-C standard library is thirty years old and still not open source. Developers may have to wait a long time if the Swift standard library becomes open source at all, in order to use Swift across multiple platforms.

REPL (12:56)

Swift was probably very heavily inspired by Ruby to have a REPL. The equivalent to the Ruby REPL isn’t strictly just the Swift REPL, but also the real-time programming feedback from using Playgrounds. These are part of the XCode IDE that allow you to see the results of whatever operations you’re running in real-time.

Demo (13:46)

There are three ways to run Swift. You can do it in XCode, in a Playground with XCode, or just straight from the command line through the REPL. This demo was done in an XCode 6 Playground and gave a brief view of Swift syntax. The goal was not to go into detail, but instead just to give a sense of what the language looks and feels like. Create a class with the “class” keyword and add properties. You can also make use of functions, initializers, and enums. Swift is not so verbose - type inference helps with this!

Get more development news like this

class Vehicle {}

enum CarModel: String {
  case Honda = "Honda", Toyota = "Toyota", Batmobile = "Batmobile"
}

class Car: Vehicle {
  var model: CarModel

  init(model: CarModel) {
    self.model = model
  }

  func drive() {
    println("driving my " + model.toRaw())
  }
}

let car = Car(model: .Batmobile)
car.drive
}

Seven Concepts in Swift and Ruby (20:42)

1. Classes (21:01)

Classes are defined in very similar ways in Ruby and Swift. In Ruby, we define a parent class Vehicle that doesn’t inherit from any other base class. You can inherit from this class and define methods.

class Vehicle
end

class Car < Vehicle
  def initialize(model)
    @model = model
  end
  
  def drive
    "driving my " + @model
  end
end

car = Car.new('Batmobile')
car.drive # => Driving my Batmobile

In Swift, you can do the same thing, but there are a few differences. You can initialize model to an empty string instead of model. In addition, the drive function is returning a string, which is due to the type safety of Swift. In Ruby, we wouldn’t have to explicitly say what type it was returning.

class Car {
    var model = ""
    func drive() -> String {
        return "Driving my " + model
    }
}

let car = Car()
car.model = "Batmobile"
car.drive()

2. Closures (23:23)

Closures are a pretty fundamental building block of these two languages, so it’s definitely used a lot in Ruby. If we wanted to pass a block to a method, we would do it like this:

def say_hello(&block)
  block.call
end

say_hello { puts "Hello there" } # => "Hello there"

Swift uses the same amount of lines, but with a few other differences. Swift’s strict type safety comes back into play, where we have to specify what input arguments the block should take, as well as what output the block should return. This can reduce a lot of programming bugs by requiring explicit definitions.

func sayHello(block: () -> ()) {
  block()
}

sayHello { println("Hello there") } // => "Hello there"

3. Type Safety & Inference (25:03)

Type inference is something that Ruby and Objective-C have. When you couple it with type safety, you get some pretty powerful stuff. Ruby has a dynamic type system, where you can set all sorts of different types to the same variable. This is very powerful but could also lead to bugs, like when you put the wrong kind of value into a variable.

name = "John"
name = Time.now()
name = 123.45

In Swift, it’s a lot harder to pass in the wrong thing. This is basically what is meant by type safety, where we encourage both the user and users of the code to know what the intention is. In the following example, if you don’t explicitly state the types, the Swift compiler will infer anInt to be an Int, and aDouble to be a Double. When you run the code, the compiler will throw a warning and it won’t even compile.

let anInt = 3
let aDouble = 0.1416
var pi = anInt + aDouble // Compile warning

pi = 3 + 0.1416
// Compiles: number literals are untyped

4. Mutability (29:42)

Mutability is not exactly common in Ruby, but it is supported. The freeze method allows you to make a mutable object into an immutable object. However, Ruby doesn’t do a deep freeze - you can still modify things later on.

str = "abc".freeze
# => "abc"
hash = { str => { str => "value" } }.freeze
# => {"abc"=>{"abc"=>"value"}}
hash[str] = "foo"
# => RuntimeError: can't modify frozen Hash
hash[str][str] = "bar"
# => "bar"
hash
# => {"abc"=>{"abc"=>"bar"}}

let str = "abc"
// => "abc"
let hash = [str: [str: "value"]]
// => ["abc": ["abc": "value"]]
hash[str] = [str: "foo"]
// => compile error
hash[str]![str] = "bar"
// => compile error

In Objective-C, we had entirely new classes to define mutable and immutable types. Instead of having different classes, Swift deals with mutability by changing how you first declare your variable. The keyword var is mutable, while let is immutable. If you tried to reassign a value to a let variable, that would produce a compile-time error. It’s also a mutating function - if we had an array and wanted to append to it, that would also be a compile time error. By moving these problems from runtime exceptions into compile-time errors, Swift has shortened the development cycle (for more experienced developers) because you can see things as you type them, rather than after you’ve tried to run your app. Swift actually doesn’t support exceptions at all, which shows how strong the emphasis is on compile-time warnings.

var letter = "a"
letter = b // works

let a = "a"
a = "b" // compilation error

5. Functional Programming (33:12)

Even very object oriented nonfunctional languages tend to have some sort of functional programming library, so for this talk I’ll limit myself to the standard library. With Ruby, you’ve always been able to do things like map the values in an array or filter an array.

numbers = [1, 2, 3, 4]
numbers.map { |n|
  3 * n
} # => [3, 6, 9, 12]
numbers.select { |n| n % 2 == 0 } # => [2, 4]

With Swift, you can do the same thing without having to resort to any sort of external library. The following code example shows, in addition to functional concepts, closures and how to specify closure inputs. There’s also mutability at play, where map won’t actually modify the contents of numbers but instead return a completely new array.

let numbers = [1, 2, 3, 4]
numbers.map {
  (n: Int) -> Int in
  return 3 * n
} // => [3, 6, 9, 12]
numbers.filter {$0 % 2 == 0} // => [2, 4]

6. Optionals (35:15)

One aspect of Swift that doesn’t really have an equivalent in Ruby or even Objective-C is Optionals. You have to explicitly specify that a variable is an Optional, otherwise the compiler will expect it to have a value at all times. To create an optional, you use the question mark operator to say that a variable may be nil, and if not, will definitely be of the type it was declared.

var string = ""
if string == nil {} // => compilation error: can never be nil

var optString: String?
if optString == nil {
  optString = "foobar"
}

if let forSureAString = optString {
  println("forSureAString: " + forSureAString)
}

The exclamation mark operator unwraps the value that an optional holds.

7. Generics (38:18)

Generics is something that a lot of languages have, though Objective-C and Ruby are two prominent languages that don’t really have support for that. In this example, we’re defining an enum, which can only have a finite number of possible states. The cases are either None or Some(T), which is where the generics come into play. When we define the enum as OptionalValue, we're saying it's generic and later can be specialized. Enums, classes, functions, class functions, and even functions outside of classes can all be generic. If we wanted to specialize an enum, we could do like below, where we set it to hold on optional Ints. In fact, arrays are generic - in Swift, they can only hold values of a specific type.

// Re-implement the Swift standard 
// library's optional type
enum OptionalValue<T> {
  case None
  case Some(T)
}
var maybeInt: OptionalValue<Int> = .None
maybeInt = .Some(100)

// Specialized Array
var letters: [String]
letters = ["a"]

Lots more! (41:43)

There’s a lot more to Swift language that couldn’t be covered: protocols, super-enums, structs, pattern matching, Objective-C interoperability, and runtime. All of these concepts are powerful and very useful when needed. Swift is actually pretty far from Ruby, but they also share a few things in common. Swift has a lot more of a scripting feel, it has a REPL, and it has a lot of shorthand concepts that really make it more legible than Objective-C has even been. As far as the future of Swift goes, I think it might have a role in replacing or at least complementing Ruby and a lot of Mac only scripting.

Ultimately, differences between Swift and Ruby boil down to API differences, along with the fact that Swift is compiled & has type safety. Swift does take similar concepts and features from JavaScript, such as its readability, syntax, and tooling.

Resources (Apple & elsewhere) (43:31)

Q&A (44:10)

Q: Have you built anything significant with Swift yet?
JP: The remote I was using to trigger these slides on my phone was written entirely in Swift, and it hasn’t crashed yet so it’s still running. Other than that, Realm is a database for iOS that has some pretty strong Swift support give that the language is still not production ready. There’s also a lot of great opensource work coming out like Alamofire, which is a networking client for use with Swift. People are definitely starting to build things of real value, but maybe not exactly of production value at this point, given that Swift is still very much in beta.

Q: After using Swift and Ruby, how do you feel about the difference in type safety?
JP: I’m very much in favour of very strong type safety and moving as much as possible to compile time, as long as we don’t lose anything at runtime. Unfortunately, I think that there are quite a few things we’ve lost going from Objective-C to Swift. Most of that, in my opinion, tends to be in the metaprogramming and reflection side of things that leverage a strong dynamic system. For example, being able to dynamically intercept messages, pass objects, and do cool stuff with that. But there are still ways to regain some of the dynamic dispatching and ways to go around the type safety. For example, the concept of AnyObject is discouraged, but it is basically an anonymous object.

Q: In Ruby, you can call methods on a class and see everything it conforms to at runtime. Is there a way to do that in Swift?
JP: The truth is that at runtime there’s no way for you to do that in Swift. There are plenty of ways to do that in Objective-C. Technically, you could tell your Swift classes to use the Objective-C style dynamic dispatching and then you’d be able to do that in Swift. At that point you’re not in pure Swift though, and you’re relying on the fact that Swift still leverages the Objective-C runtime.

Q: I’m new to iOS development in general, so I was wondering what kind of testing framework you use? Maybe test driven development?
JP: There are lots of testing frameworks available if you’re getting started on iOS. I didn’t write this remote app with TDD or BDD, but there are a lot who do use that. There are also some great frameworks like Kiwi and Specta. Apple also comes with built-in XCTest, which is good enough for most cases as well. For Swift specifically, there’s also Quick and Nimble.


Next Up: Build a Realtime Swift App with Realm #1: Building Reactive Apps with Realm

General link arrow white

About the content

This content has been published here with the express permission of the author.

JP Simard

JP works at Realm on the Objective-C & Swift bindings, creator of jazzy (the documentation tool Apple forgot to release) and enjoys hacking on Swift tooling.

4 design patterns for a RESTless mobile integration »

close