Swift has become a standard language for iOS development and is becoming a popular language for server side programming as well. My previous programming experience is mostly with PHP and JavaScript, so Swift was a whole new world when I started. I’m writing this post to give web developers coming from PHP, Python or Ruby a head start on how to use Swift.
Installing Swift on a Mac is super easy. Just install Xcode! It has a great playgrounds feature for trying out the language.
To use Swift on Linux, just follow the instructions on Swift.org to download and run the compiler. Alternatively, a Docker image is available for running Swift.
If you are on Windows 10, you could install Swift in the Linux Subsystem, but the easiest method would be to install the Docker image mentioned above.
print("Hello, world!")
The first thing you will notice is that Swift doesn’t require semi-colons at the end of each line, similar to Ruby or Python. Swift was built with both scripting languages and compiled languages in mind, so you will see different features of both types in the language.
Also, similar to Ruby and Python, you don’t have to use parentheses in conditionals, but you do use brackets to surround the contents of the block like Java and PHP.
if true {
print("It is true")
}
Swift uses the same structure for loops.
let fruits = ["Apple", "Pear", "Orange"]
for fruit in fruits {
print("My favorite fruit is: \(fruit)")
}
In Swift, data names can be declared with two different words: let
and var
. Unlike ES6 JavaScript, let
is for constants and var
is for regular variables. The value of a let
constant cannot be changed after it is set, but a var
can be changed as needed.
If you write the following code, you will get an error:
let name = "Andrew"
name = "not Andrew"
But, the following is acceptable:
var count = 1
count = 2
The Swift compiler will give you a warning if you create a variable and then never change it, so it is best practice to start with a constant and change it to a variable only when it needs to become something else.
Everything in Swift is statically typed, but the types are inferred like Go or Rust. Type inference basically means that the compiler figures out the type of a variable based on its initial value. So, if you try to create a constant or variable without a type defined, you must set it’s value immediately so the compiler know what type it is.
For example, if you run this code, you will get an error.
let favoriteMovie
favoriteMovie = "Return of the Jedi"
You have to either declare the constant type or set the constant immediately.
let favoriteMovie: String
favoriteMovie = "Return of the Jedi"
let secondFavoriteMovie = "The Empire Strikes Back"
Swift has types for all standard pieces of data: Int
String
Float
Double
Bool
and more.
One of my favorite features of Swift is parameter naming. When you create a function and name the parameters, the code calling the function has to name the parameters as well.
func sayHello(greeting: String) {
print(greeting)
}
sayHello(greeting: "Hola!")
Once you get used to it, it makes your functions much more expressive. If you don’t want to have named parameters, you can put an _
in front of the name to omit it.
func sayGoodbye(_ goodbye: String) {
print(goodbye)
}
sayGoodbye("Adios!")
In the Swift standard library, you will see functions written both ways depending on the situtation so that is a good reference to use to write your own functions.
If you have read any Swift code before, you probably have noticed several instances where a question mark is used. A question mark is used to create a special type and it basically means the value can be either a type or nil. For example, if a variable is declared like var optional: String?
, it means optional
can be either a String
or nil
.
Now, optionals require a little more work to get the value. To use a variable with an optional type, you have to unwrap the value.
let optional: String? = "I am an optional"
if let value = optional {
print(value)
}
By using an if statement, you are verifying that the value in the optional is not nil
and if so, it is set to value
to be used within the if
block.
You can also unwrap an optional by using a guard statement.
enum OptionalError: Error {
case optionalIsNil
}
let value: String? = nil
guard let name = value else {
throw OptionalError.optionalIsNil
}
A guard statement attempts to unwrap the value into the name
constant, but if it returns nil
, it runs the content of the block. In this case, because value
is nil
, the guard block throws a customer error indicating something went wrong.
Optionals can be confusing the first time you use them and you may be asking, why use them then? The goal of optionals, is to make sure your code handles every situation where your constants and variables could be nil
. Let’s say that you have a function which gets the current user from a session, you will want to handle the situation where the session is empty.
func getCurrentUser(session: Session) -> User? {
if session.has("currentUser") {
return session.get("currentUser")
}
return nil
}
let session = Session()
if let user = getCurrentUser(session: session) {
print(user.name)
}
In this case, getCurrentUser
could return nil
so we are only printing the user’s name if the value returned from the function actually is a User object. If we didn’t check for nil
, we would have a runtime error because no name would exist. In the end, the goal of optionals is to make your code more correct.
As you start coding in Swift, you will notice that there are two constructs that look almost the same. Here is an example:
struct CarSize {
var height: Double
var width: Double
var length: Double
init(height: Double, width: Double, length: Double) {
self.height = height
self.width = width
self.length = length
}
func getArea() -> Double {
return width * length
}
}
class Car {
var make: String
var model: String
var color: String
init(make: String, model: String, color: String) {
self.make = make
self.model = model
self.color = color
}
func getType() -> String {
return "\(make): \(model)"
}
}
let carSize = CarSize(height: 10, width: 10, length: 20)
let car = Car(make: "Chevrolet", model: "Corvette", color: "Red")
Both structs and classes are used to create custom types that can contain properties and methods. So what is the difference?
First, only a class can inherit from another class. A struct cannot inherit from a struct. If you require any type of inheritance, then you must use a class.
Second, instances of a class are passed by reference and instances of a struct are passed by value. Basically, that means they are stored in memory differently. When you create an instance of a class, the variable holding the instance does not actually contain the instance, it contains a memory id which is a reference to where the instance exists in memory. A variable holding an instance of a struct, actually holds the struct, not just a reference. Therefore, when you pass a class instance into a function, the function receives a reference to an object, but when you pass a struct instance into a function, it receives a copy of the struct. There are advantages and disadvantages to either scenario, so choosing a struct or class depends on what you are trying to accomplish.
In the end, structs are typically used for wrapping simple data values, like the CarSize
in the example and classes are used for anything else which may include types that change often or have mutating methods. Apple recommends using classes in most cases.
I hope this quick rundown helps you understand some of the Swift concepts more quickly. I recommend reading Apple’s The Swift Programming Language book to learn more. Please post a comment if you any questions.
Happy Swifting!