Swift¶
As explained by Hacking With Swift - Swift In Sixty Seconds and the official Swift Book
Complex Types¶
Set
Set(["red", "green", "blue", "red", "blue"])
.Array
let beatles = ["john", "paul", "george", "ringo"]
.Tuple
var name = (first: "Taylor", last: "Swift")
access vianame.0
orname.first
.Enum
enum Result {
case success
case failure
}
Associated values
enum Activity {
case bored
case running(destination: String)
case talking(topic: String)
case singing(volume: Int)
}
let talking = Activity.talking(topic: "football")
CustomStringConvertible
Codable
Mappable
Operators¶
Usual operators
+
-
*
/
%
-=
+=
==
!=
<
<=
&&
||
print(firstCard == secondCard ? "Cards are the same" : "Cards are different")
And range operator
0...50
0..<50
Loops¶
As usual but do while
is called repeat while
and exiting outer loops is possible by using labels:
outerLoop: for i in 1...10 {
for j in 1...10 {
let product = i * j
print ("\(i) * \(j) is \(product)")
if product == 50 {
print("It's a bullseye!")
break outerLoop
}
}
}
Functions¶
Default value
func greet(_ person: String, nicely: Bool = true) {
if nicely == true {
print("Hello, \(person)!")
} else {
print("Oh no, it's \(person) again...")
}
}
Variadic functions Mark unknown amount of parameters with ...
.
func square(numbers: Int...) {
for number in numbers {
print("\(number) squared is \(number * number)")
}
}
square(numbers: 1, 2, 3, 4, 5)
Throwing errors
enum PasswordError: Error {
case obvious
}
func checkPassword(_ password: String) throws -> Bool {
if password == "password" {
throw PasswordError.obvious
}
return true
}
do {
try checkPassword("password")
print("That password is good!")
} catch {
print("You can't use that password.")
}
Inout parameters can be changed inside your function.
func doubleInPlace(number: inout Int) {
number *= 2
}
var myNum = 10
doubleInPlace(number: &myNum)
Closures¶
https://www.hackingwithswift.com/sixty
let driving = {
print("I'm driving in my car")
}
driving()
With parameters
let driving = { (place: String) in
print("I'm going to \(place) in my car")
}
driving("London")
With return values
let drivingWithReturn = { (place: String) -> String in
return "I'm going to \(place) in my car"
}
let message = drivingWithReturn("London")
print(message)
Closures as parameter
let driving = {
print("I'm driving in my car")
}
func travel(action: () -> Void) {
print("I'm getting ready to go.")
action()
print("I arrived!")
}
travel(action: driving)
Trailing closures syntax
func travel(action: () -> Void) {
print("I'm getting ready to go.")
action()
print("I arrived!")
}
travel() {
print("I'm driving in my car")
}
// because of missing params
travel {
print("I'm driving in my car")
}
Closures with parameters
func travel(action: (String) -> Void) {
print("I'm getting ready to go.")
action("London")
print("I arrived!")
}
travel { (place: String) in
print("I'm going to \(place) in my car")
}
Closures with return values
func travel(action: (String) -> String) {
print("I'm getting ready to go.")
let description = action("London")
print(description)
print("I arrived!")
}
travel { (place: String) -> String in
return "I'm going to \(place) in my car"
}
Shorthand parameter names
func travel(action: (String) -> String) {
print("I'm getting ready to go.")
let description = action("London")
print(description)
print("I arrived!")
}
travel { (place: String) -> String in
return "I'm going to \(place) in my car"
}
travel { place -> String in
return "I'm going to \(place) in my car"
}
Closures with multiple parameters
func travel(action: (String, Int) -> String) {
print("I'm getting ready to go.")
let description = action("London", 60)
print(description)
print("I arrived!")
}
travel {
"I'm going to \($0) at \($1) miles per hour."
}
Return closures from functions
func travel() -> (String) -> Void {
return {
print("I'm going to \($0)")
}
}
let result = travel()
result("London")
Capturing values
Generics¶
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) {
// function body goes here
}
func mid<T>(array: [T]) -> T? {
guard !array.isEmpty else { return nil }
return array.sorted()[(array.count - 1) / 2]
}
func mid<T: Comparable>(array: [T]) -> T? {
guard !array.isEmpty else { return nil }
return array.sorted()[(array.count - 1) / 2]
}
mid(array: [3, 5, 1, 2, 4]) // 3
Structs¶
Comparing Structures and Classes
Structures and classes in Swift have many things in common. Both can:
Define properties to store values
Define methods to provide functionality
Define subscripts to provide access to their values using subscript syntax
Define initializers to set up their initial state
Be extended to expand their functionality beyond a default implementation
Conform to protocols to provide standard functionality of a certain kind
struct Sport {
var name: String
}
var tennis = Sport(name: "Tennis")
print(tennis.name)
Computed properties
struct Sport {
var name: String
var isOlympicSport: Bool
var olympicStatus: String {
if isOlympicSport {
return "\(name) is an Olympic sport"
} else {
return "\(name) is not an Olympic sport"
}
}
}
let chessBoxing = Sport(name: "Chessboxing", isOlympicSport: false)
print(chessBoxing.olympicStatus)
Property observers via didSet
and willSet
.
struct Progress {
var task: String
var amount: Int {
didSet {
print("\(task) is now \(amount)% complete")
}
}
}
Methods
struct City {
var population: Int
func collectTaxes() -> Int {
return population * 1000
}
}
Mutating methods When you want to change a property inside a method, you need to mark it using the mutating keyword,
struct Person {
var name: String
mutating func makeAnonymous() {
name = "Anonymous"
}
}
var person = Person(name: "Ed")
person.makeAnonymous()
Initializers
struct User {
var username: String
init() {
username = "Anonymous"
print("Creating a new user!")
}
}
Lazy
lazy var familyTree = FamilyTree()
Static properties and methods
struct Student {
static var classSize = 0
var name: String
init(name: String) {
self.name = name
Student.classSize += 1
}
}
Access control
struct Person {
private var id: String
init(id: String) {
self.id = id
}
}
Classes¶
Comparing Structures and Classes
Classes have additional capabilities that structures don’t have:
Inheritance enables one class to inherit the characteristics of another.
Type casting enables you to check and interpret the type of a class instance at runtime.
Deinitializers enable an instance of a class to free up any resources it has assigned.
Reference counting allows more than one reference to a class instance.
class Dog {
var name: String
var breed: String
init(name: String, breed: String) {
self.name = name
self.breed = breed
}
}
Inheritance
class Poodle: Dog {
init(name: String) {
super.init(name: name, breed: "Poodle")
}
}
Overriding methods
class Dog {
func makeNoise() {
print("Woof!")
}
}
[...]
class Poodle: Dog {
override func makeNoise() {
print("Yip!")
}
}
Final class no other class can inherit from it.
final class Dog {
var name: String
var breed: String
init(name: String, breed: String) {
self.name = name
self.breed = breed
}
}
Deinitializers
class Person {
var name = "John Doe"
init() {
print("\(name) is alive!")
}
func printGreeting() {
print("Hello, I'm \(name)")
}
deinit {
print("\(name) is no more!")
}
}
Protocols¶
Protocols are a way of describing what properties and methods something must have.
protocol Identifiable {
var id: String { get set }
}
struct User: Identifiable {
var id: String
}
func displayID(thing: Identifiable) {
print("My ID is \(thing.id)")
}
Inheritance
protocol Payable {
func calculateWages() -> Int
}
protocol NeedsTraining {
func study()
}
protocol HasVacation {
func takeVacation(days: Int)
}
protocol Employee: Payable, NeedsTraining, HasVacation { }
Extensions
extension Int {
func squared() -> Int {
return self * self
}
}
Protocol extensions
let pythons = ["Eric", "Graham", "John", "Michael", "Terry", "Terry"]
let beatles = Set(["John", "Paul", "George", "Ringo"])
extension Collection {
func summarize() {
print("There are \(count) of us:")
for name in self {
print(name)
}
}
}
pythons.summarize()
beatles.summarize()
Protocol oriented programming
Protocol extensions can provide default implementations for our own protocol methods. This makes it easy for types to conform to a protocol, and allows a technique called “protocol-oriented programming” – crafting your code around protocols and protocol extensions.
protocol Identifiable {
var id: String { get set }
func identify()
}
extension Identifiable {
func identify() {
print("My ID is \(id).")
}
}
struct User: Identifiable {
var id: String
}
let twostraws = User(id: "twostraws")
twostraws.identify()
See this playground and this playground for protocol oriented programming.
Optionals¶
Unwrapping optionals
if let unwrapped = name {
print("\(unwrapped.count) letters")
} else {
print("Missing name.")
}
Unwrapping with guards
func greet(_ name: String?) {
guard let unwrapped = name else {
print("You didn't provide a name!")
return
}
print("Hello, \(unwrapped)!")
}
Force unwrap
let str = "5"
let num = Int(str)
let num = Int(str)!
let age: Int! = nil
Nil coalescing
func username(for id: Int) -> String? {
if id == 1 {
return "Taylor Swift"
} else {
return nil
}
}
let user = username(for: 15) ?? "Anonymous"