Let’s dive into Swift! For the next several articles, I’m going to share my observations as I noodle around with Swift on the Xcode 6 beta and try to get a feel for the language.
Keep in mind that Swift is in beta, and given the loose way “beta” is defined in these agile, pivot-happy days, it could be subject to all sorts of changes that may invalidate some of what I cover here.
Example 1: No semicolons, implicitly-typed variables, basic variable types, output, and string interpolation
We’ll start off with a couple of variable declarations, and then we’ll print them out on the console. Here’s the code:
var name = "Dëathtöngüë" var radius = 4.0 println("Hey, \(name), we're working with a radius of \(radius).\n")
And here’s its output:
Hey, Dëathtöngüë, we're working with a radius of 4.0.
No semicolons
Note that unlike Objective-C (or C, or most other languages that borrow its syntax), the lines of code above don’t end in semicolons. Swift is like JavaScript in that a line break is enough to mark the end of a statement, and ending them with semicolons is optional (much to the chagrin of the pro-mandatory-semicolon people in the Great JavaScript Semicolon Debate). You do need to use a semicolon to separate statements if you want to put them on the same line. For example, the following code compiles:
var name = "Dëathtöngüë"; var radius = 4.0; println("Hey, \(name), we're working with a radius of \(radius).\n");
My recommendation: follow the style in Apple’s example code, be like our friends in the Lua, Python, and Ruby worlds, and don’t use semicolons to separate statements.
Implicitly-typed variables
Note that we didn’t have to specify the types of the name
and radius
variables. All we did was declare them with the var
keyword, and then we assigned values to them:
var name = "Dëathtöngüë" var radius = 4.0
This code is legal JavaScript, but it’s rather different underneath. JavaScript associates types with values, while Swift associates types with variables. The compiler infers the variables’ types from the values assigned to them:
name
was assigned a string literal, which means that it should be of typeString
, andradius
was assigned a numeric literal with a decimal point, so the compiler assigned it the typeDouble
.
This feature goes by names like type inference and implicit typing. We’ve had this in C# since late 2007, and it’s nice to see it in Swift.
Swift’s basic types
The basic types that I’ve encountered in the documentation so far are:
Int
Float
Double
Bool
(whose values are true and false as opposed to Objective-C’s YES and NO)String
Output
The println()
function sends output to the console. It’s capable of outputting all of Swift’s basic types without your having to convert them to String
first. All of the following lines work in Swift:
println(5) println(5.5) println(5 * 5) println(true) println("Five by five, chief.")
String interpolation
\()
is used for string interpolation, in a manner similar to Ruby’s and CoffeeScript’s #{}
.
Example 2: Defining functions, defining constants, and Unicode names
Let’s take our initial code and add a function that calculates the area of a circle, given its radius:
var name = "Dëathtöngüë" var radius = 4.0 println("Hey, \(name), we're working with a radius of \(radius).\n") func circleArea(circleRadius: Double) -> Double { let π = 3.14 return π * circleRadius * circleRadius } println("The circle's area is \(circleArea(radius)).")
Here’s its output:
Hey, Dëathtöngüë, we're working with a radius of 4.0. The circle's area is 50.24.
Defining functions
Function headers in Swift have the following format… …and they’re bounded by braces — {
and }
— just like in Objective-C and every other C-like language. The return
keyword works as you’d expect it to: it specifies the function’s return value and returns program control to whatever called the function in the first place.
There’s a lot more to function definitions, and I’ll cover than in a later article.
Defining constants
Take a closer look at the definition inside the circleArea
function:
let π = 3.14
Just as var
is used to declare variables, let
is used to declare constants. As with variables, if there’s enough information in the value you’re assigning to the constant, the compiler will infer its type.
As you might expect, any attempt to redefine a constant results in an error. After all, a redefinable constant is a variable.
Unicode names
In our constant declaration, we used the greek letter π
(which you get by typing option-p) instead of pi.
Swift’s source files are Unicode, which means that you’re not limited to the Roman character set for naming things. This feature is meant to let people whose native languages with non-Roman alphabets give more meaningful names to their variables, constants, functions, classes, and so on.
As I wrote in an earlier post, it also lets you use emoji, which may be useful for amusing demos or in the event someone declares an Obfuscated Swift programming contest.
Explicitly declaring variable types
There are times when the compiler just doesn’t have enough information to infer the type of a variable. For instance, if you make the following declaration…
var someValue = 3.0
…the compiler will assume that someValue
‘s type is Double
. What if you need someValue
to be a Float
instead? You’ll need to specify its type, as shown below:
var someValue:Float = 3.0
If you need to declare a variable but don’t want to assign a value to it immediately, you’ll also have to specify its type, as the compiler won’t have enough information to infer it:
var finalScore:Int var playerToBeNamedLater:String var iDontFeelLikeTypingADecimalPoint:Double = 3
Example 3: Remember, Swift is a strongly-typed language
Try the following code, but take note: it won’t work!
// This code won't work! func circleArea(circleRadius: Double) -> Double { let π = 3.14 return π * circleRadius * circleRadius } var floatRadius:Float = 3.0 // Here's where the error occurs: println("The other circle's area is \(circleArea(floatRadius)).")
The problem is that you’re passing a Float
to a function that expects a Double
parameter. You need to convert floatRadius
into a Double
first. Luckily that’s pretty simple — all the basic types have conversion functions:
Int()
takes a numeric orBool
argument and returns itsInt
equivalent, with the fraction removed (not rounded). TheBool
valuetrue
is converted to1
,false
is converted to0
.Float()
takes a numeric argument and returns itsFloat
equivalent. You lose decimal precision if you useFloat()
on aDouble
, and gain some if you use it on anInt
. TheBool
valuetrue
is converted to1.0
,false
is converted to0.0
.Double()
takes a numeric argument and returns itsDouble
equivalent. You gain decimal precision if you useDouble()
on anInt
orFloat
. TheBool
valuetrue
is converted to1.0
,false
is converted to0.0
.Bool()
takes a numeric orString
argument and returns itsBool
equivalent. As far as I can tell, only0
equates tofalse
; everything else — even the empty string""
— equates totrue
. I triedBool(nil)
and got an error.String()
takes a numeric argument and returns itsString
equivalent.
This code works, because we use Float()
to convert floatRadius
into a Double
before passing it to circleArea()
:
func circleArea(circleRadius: Double) -> Double { let π = 3.14 return π * circleRadius * circleRadius } var floatRadius:Float = 3.0 // We have to convert floatRadius to a Double before // passing it to circleArea(). println("The other circle's area is \(circleArea(Double(floatRadius))).")
Why all the type fussiness?
The payoff for all this fussiness about type is speed. As Matt Galloway writes in his Swift Language Highlights article on RayWenderlich.com:
In Swift, the compiler knows much more [than Objective-C] about the types in play in any method call. It knows exactly where [any given function or method] is defined. Because of this, it can optimise certain call sites by jumping directly to the implementation rather than having to go through dynamic dispatch. In other cases, it can use vtable style dispatch, which is far less overhead than dynamic dispatch in Objective-C. This is the kind of dispatch that C++ uses for virtual functions.