Swift is a compiled, strongly-typed language. The compiler uses type inference to identify the type of an object, or you can explicitly use type annotation.
var favoritePeanut = "Charlie Brown" // String
var theAnswer = 42 // Int
var singleFare = 2.75 // Double (Double for "double precision" Float)
var unicodePersonInChinese = "\u{4EBA}" //String
var myIntArray: [Int] = [1,2,3,4]
var myHashmapMappingStringToInt: [String: Int] = ["c": 3, "b": 2]
Swift can use let
to declare immutable constants. Attempting to mutate a let
constant will give a compile-time error
let pi = Double.pi
pi = 3 // Compile-time error
Swift conditionals do not require ()
syntax. If an if
condition is not met, the next else if
block will run. If no if
or else if
blocks run, an else
block will run. Conditionals are not required to have else if
or else
blocks.
let temperatureInFahrenheit = 90
if temperatureInFahrenheit <= 32 {
print("It's very cold. Consider wearing a scarf.")
} else if temperatureInFahrenheit >= 86 {
print("It's really warm. Don't forget to wear sunscreen.")
} else {
print("It's not that cold. Wear a t-shirt.")
}
// Prints "It's really warm. Don't forget to wear sunscreen."
The Ternary Operator gives us a shorthand way of writing an if/else construct. Its syntax is a ? b : c
and is read, "if a then b, otherwise c". In this case a
would be a boolean expression, and b
and c
can be of any type but must be the same type.
let temperatureInFahrenheit = 60
let message = temperatureInFahrenheit <= 32 ? "cold" : "warm"
print(message) // prints "warm"
Swift no longer supports C-style (for i = 0; i < 10; i++)
loops. Instead, the main method of iteration is using a for in
loop.
for in
loops can operate over a range that's either open or closed.
for i in 0..<10 {
print(i)
}
// Prints 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
for i in 0...10 {
print(i)
}
// Prints 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
Swift can also iterate over any struct or class that implements the Collection protocol.
for str in ["hi", "there", "this", "is", "an", "array"] {
print(str)
}
for (key, value) in ["a": 1, "b": 2, "c":3] {
print("The key is \(key) and the value is \(value)")
}
Strings are Collections of Characters.
for char in "this string can be iterated over" {
print("\(char) is the character being used in the current iteration")
}
struct User {
let name: String
var isLoggedIn: Bool
}
var userOne = User(name: "Adam", isLoggedIn: false)
userOne.isLoggedIn = true // Changes isLoggedIn to true
userOne.name = "Beth" //Compile error because name is a let constant
class Counter {
var count = 0
func increment() {
count += 1
}
func increment(by amount: Int) {
count += amount
}
func reset() {
count = 0
}
}
let myCounter = Counter()
// the initial counter value is 0
myCounter.increment()
// the counter's value is now 1
myCounter.increment(by: 5)
// the counter's value is now 6
myCounter.reset()
// the counter's value is now 0
let radius = 5.0
let pi = Double.pi
let circleArea = pi * radius * radius
class SomeClass {
class func someTypeMethod() {
// type method implementation goes here
}
}
SomeClass.someTypeMethod()
class TwoDimensionalPoint {
var x: Double
var y: Double
init(x: Double, y: Double) {
self.x = x
self.y = y
}
}
class ThreeDimensionalPoint: TwoDimensionalPoint {
var z: Double
init(x: Double, y: Double, z: Double) {
self.z = z
super.init(x: x, y: y)
}
}
An enumeration defines a common type for a group of related values and enables you to work with those values in a type-safe way within your code.
https://docs.swift.org/swift-book/LanguageGuide/Enumerations.html
enum CompassPoint: CaseIterable {
case north
case south
case east
case west
}
let move = CompassPoint.east
switch move {
case .north:
print("moving north")
case .south:
print("moving south")
case .east:
print("moving east")
case .west:
print("moving west")
}
print("there are \(CompassPoint.allCases.count) compass points")
// iterating through an enum
for point in CompassPoint.allCases {
print(point)
}
Swift have robust handling to work with data that may be nil. ?
after a type indicates that it is an Optional. !
Force unwraps an optional.
var temperature: Double? = nil
if temperature != nil {
print(temperature!)
} else {
print("Thermometer has no current valid reading.")
}
// Prints "Thermometer has no current valid reading."
Only optional types can be assigned to nil
.
var myInt = 4
myInt = nil // Compile-time error
var temperature: Double? = nil
if let unwrappedTemperature = temperature {
print(unwrappedTemperature)
} else {
print("Thermometer has no current valid reading.")
}
// Prints "Thermometer has no current valid reading
func divide(_ numerator: Int, by denominator: Int) -> Int {
guard denominator != 0 else { return nil }
return numerator / denominator
}
let dividend = divide(5, by: 2) // 2
let otherDividend = divide(4, by: 0) // nil
The function divide
takes in two arguments: numerator
and denominator
. numerator
has an external parameter name of _
, and denominator
has an external parameter name of by
. When invoking a function, we use the external parameter name. If no external parameter name is specified, it is the same as the internal argument name:
func double(value: x) -> Int {
return value * 2
}
let doubledFive = double(value: 5) //doubledFive is 10
Closures are self-contained blocks of functionality that can be passed around and used in your code. Closures in Swift are similar to blocks in C and Objective-C and to lambdas in other programming languages.
https://docs.swift.org/swift-book/LanguageGuide/Closures.html
Closures in Swift are first-order types. The syntax for a closure type is as follows:
let myDoubler: (Int) -> Int = { val: Int in return val * 2 }
Swift also offers a shorthand syntax using numbers prefixed by $
.
let myDoubler: (Int) -> Int = { $0 * 2 }
All of the following are equally valid in Swift:
// function on one line
reversedNames = names.sorted(by: { (s1: String, s2: String) -> Bool in return s1 > s2 } )
// The by parameter of sorted(by:) knows its type (String, String) -> Bool
// A call to the function that defines a closure can infer the type of its arguments.
reversedNames = names.sorted(by: { s1, s2 in return s1 > s2 } )
// If one line, "return" is inferred
reversedNames = names.sorted(by: { s1, s2 in s1 > s2 } )
// We can refer to arguments by numbered shorthand names so they don't need to be declared.
// This function takes two parameters. They can be found in $0 and $1
reversedNames = names.sorted(by: { $0 > $1 } )
// String overloads the > operator which works just like any function,
// Swift sees that its type, `>(_:String, _:String) -> Bool` matches what sort expects.
reversedNames = names.sorted(by: >)
// Trailing closure syntax
// When the last argument in a function is a closure, you have the option to define it outside the parentheses
reversedNames = names.sorted() { $0 > $1 }
// Trailing closure syntax omitting the parentheses
// When a closure is the only argument to a function, the parentheses can be omitted as well
reversedNames = names.sorted { $0 > $1 }