Skip to content

Instantly share code, notes, and snippets.

@lamprosg
Last active September 9, 2022 12:12
Show Gist options
  • Save lamprosg/eeda157a077c75b2a50e to your computer and use it in GitHub Desktop.
Save lamprosg/eeda157a077c75b2a50e to your computer and use it in GitHub Desktop.
(iOS) Swift tutorial
https://github.com/apple/swift-evolution
let maximumNumberOfLoginAttempts = 10 //Constant
var currentLoginAttempt = 0 //Variable
weak let tentacle = Tentacle() //Weak reference. Can not be let, because it can turn to nil
//Singleton example
static let sharedInstance = SomeClass()
//Lazy loading a variable (Initiliazing it when needed)
//Just add the lazy keyword
lazy var players = [String]()
/*
Declare your lazy property using the var keyword, not the let keyword,
because constants must always have a value before initialization completes
*/
//If you wanted to add logic to your lazy initialization
//Swift makes this easy by letting you define a closure after your lazy property:
lazy var players: [String] = {
var temporaryPlayers = [String]()
temporaryPlayers.append("John Doe")
return temporaryPlayers
}()
//Another example
class Person {
var name: String
lazy var personalizedGreeting: String = {
[unowned self] /*(Or [weak self]*/ in
return "Hello, \(self.name)!"
}()
//(Note that we had to say [unowned self] in here to prevent a strong reference cycle)
init(name: String) {
self.name = name
}
}
//The difference between unowned and weak is that weak is declared as an Optional while unowned is not.
//If you try to access an unowned variable that happens to be nil, it will crash the whole program.
var x = 0.0, y = 0.0, z = 0.0
var welcomeMessage: String //Variable of type String
welcomeMessage = "Hello"
var red, green, blue: Double
typealias AudioSample = UInt16 //Same as typedef
/**************************************/
//Tuples
//Tuples group multiple values into a single compound value.
let http404Error = (404, "Not Found")
// http404Error is of type (Int, String), and equals (404, "Not Found")
//You can decompose a tuple’s contents into separate constants or variables
let (statusCode, statusMessage) = http404Error
println("The status code is \(statusCode)")
//If you're interested in one variable use _
let (justTheStatusCode, _) = http404Error
//OR
println("The status code is \(http404Error.0)") //http404Error.1 for the 2d
//You can name the individual elements in a tuple when the tuple is defined:
let http200Status = (statusCode: 200, description: "OK")
println("The status code is \(http200Status.statusCode)")
/**************************************/
/**************************************/
//Optionals
//You use optionals in situations where a value may be absent.
var convertedNumber: Int? = 404
var surveyAnswer: String?
//Once you’re sure that the optional does contain a value, you can access its underlying value by adding an exclamation mark (!) to the end of the optional’s name
if convertedNumber != nil {
println("convertedNumber has an integer value of \(convertedNumber!).")
}
//Optional Binding
//You use optional binding to find out whether an optional contains a value
//and if so, to make that value available as a temporary constant or variable
if let actualNumber = possibleNumber.toInt()
{
println("\'\(possibleNumber)\' has an integer value of \(actualNumber)")
}
else
{
println("\'\(possibleNumber)\' could not be converted to an integer")
}
//Implicitly Unwrapped Optionals
//Sometimes it is clear from a program’s structure that an optional will always have a value.
//In these cases, it is useful to remove the need to check and unwrap the optional’s value every time it is accessed
let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // requires an exclamation mark
let assumedString: String! = "An implicitly unwrapped optional string."
let implicitString: String = assumedString // no need for an exclamation mark
/**************************************/
/**************************************/
//Assertions
//Condition if it's not met, the app exits
let age = -3
assert(age >= 0, "A person's age cannot be less than zero") //App will terminate
/**************************************/
/**************************************/
//Strings
var emptyString = "" // empty string literal
var anotherEmptyString = String() // initializer syntax
// these two strings are both empty, and are equivalent to each other
let yenSign: Character = "¥" //standalone character
if emptyString.isEmpty {
println("Nothing to see here")
}
var variableString = "Horse"
variableString += " and carriage"
for character in "Dog!🐶" {
println(character)
}
let string1 = "hello"
let string2 = " there"
var welcome = string1 + string2
let exclamationMark: Character = "!"
string2.append(exclamationMark)
let multiplier = 3
let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)"
//Number of characters
countElements(message)
if quotation == sameQuotation {
println("These two strings are considered equal")
}
let romeoAndJuliet = [
"Act 1 Scene 1: Verona, A public place",
"Act 1 Scene 2: Capulet's mansion",
"Act 1 Scene 3: A room in Capulet's mansion",
"Act 1 Scene 4: A street outside Capulet's mansion",
"Act 1 Scene 5: The Great Hall in Capulet's mansion",
"Act 2 Scene 1: Outside Capulet's mansion",
"Act 2 Scene 2: Capulet's orchard",
"Act 2 Scene 3: Outside Friar Lawrence's cell",
"Act 2 Scene 4: A street in Verona",
"Act 2 Scene 5: Capulet's mansion",
"Act 2 Scene 6: Friar Lawrence's cell"
]
for scene in romeoAndJuliet {
if scene.hasPrefix("Act 1 ") {
...
}
}
for scene in romeoAndJuliet {
if scene.hasSuffix("Capulet's mansion") {
...
} else if scene.hasSuffix("Friar Lawrence's cell") {
...
}
}
//utf8
dogString.utf8
//Other example
let galaxy = "Milky Way 🐮"
galaxy.count // 11
galaxy.isEmpty // false
galaxy.dropFirst() // "ilky Way 🐮"
String(galaxy.reversed()) // "🐮 yaW ykliM"
// Filter out any none ASCII characters
galaxy.filter { char in
let isASCII = char.unicodeScalars.reduce(true, { $0 && $1.isASCII })
return isASCII
} // "Milky Way "
/ Grab a subsequence of String
let endIndex = galaxy.index(galaxy.startIndex, offsetBy: 3)
var milkSubstring = galaxy[galaxy.startIndex...endIndex] // "Milk"
type(of: milkSubstring) // Substring.Type
// Concatenate a String onto a Substring
milkSubstring += "🥛" // "Milk🥛"
// Create a String from a Substring
let milkString = String(milkSubstring) // "Milk🥛"
/**************************************/
/**************************************/
//Arrays
var someInts = [Int]() //Empty array of ints
//OR
someInts = []
var shoppingList: [String] = ["Eggs", "Milk"] //Array of String values
//OR
var shoppingList = ["Eggs", "Milk"]
var threeDoubles = [Double](count: 3, repeatedValue: 0.0)
// threeDoubles is of type [Double], and equals [0.0, 0.0, 0.0]
shoppingList.count //Number of items
shoppingList.isEmpty
shoppingList.isEmpty //add a new item to the end of an array
//Or
shoppingList += ["Baking Powder"]
shoppingList += ["Chocolate Spread", "Cheese", "Butter"]
var firstItem = shoppingList[0] //Get 1st item
shoppingList[0] = "Six eggs" //Set 1st item
//OR
shoppingList.insert("Maple Syrup", atIndex: 0)
shoppingList[4...6] = ["Bananas", "Apples"] //Change items 4,5,6 with new items
let mapleSyrup = shoppingList.removeAtIndex(0) //Remove 1st item
shoppingList.removeLast() //Remove last item
//Iteration
for item in shoppingList {
println(item)
}
//OR
//With integer index (enumerate returns a tuple)
for (index, value) in enumerate(shoppingList) {
println("Item \(index + 1): \(value)")
}
//Adding arrays
var sixDoubles = threeDoubles + anotherThreeDoubles
//One sided ranges
var planets = ["Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"]
let outsideAsteroidBelt = planets[4...] // Before: planets[4..<planets.endIndex]
let firstThree = planets[..<4] // Before: planets[planets.startIndex..<4]
//Infinite Sequence
//Infinite range: 1...infinity
var numberedPlanets = Array(zip(1..., planets))
print(numberedPlanets) // [(1, "Mercury"), (2, "Venus"), ..., (8, "Neptune")]
planets.append("Pluto")
numberedPlanets = Array(zip(1..., planets))
print(numberedPlanets) // [(1, "Mercury"), (2, "Venus"), ..., (9, "Pluto")]
//Another great use for one-sided ranges is pattern matching:
func temperature(planetNumber: Int) {
switch planetNumber {
case ...2: // anything less than or equal to 2
print("Too hot")
case 4...: // anything greater than or equal to 4
print("Too cold")
default:
print("Justtttt right")
}
}
temperature(planetNumber: 3) // Earth
/**************************************/
/**************************************/
//Dictionaries
//The airports dictionary is declared as having a type of [String: String]
//which means “a Dictionary whose keys are of type String, and whose values are also of type String”.
var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
//Or
var airports = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
//Empty dictionary of a certain type
var namesOfIntegers = [Int: String]()
//Or empty general
namesOfIntegers = [:]
//Number of items
airports.count
airports.isEmpty
airports["LHR"] = "London" //Add an item
airports["LHR"] = "London Heathrow" //Change value of an item
//OR
let oldValue = airports.updateValue("Dublin Airport", forKey: "DUB")
let airportName = airports["DUB"] //Get an item
airports["APL"] = nil //Remove an item
//Or
let removedValue = airports.removeValueForKey("DUB")
//Get array of keys
let airportCodes = [String](airports.keys)
//Get array of values
let airportNames = [String](airports.values)
//Iteration
//Each item in the dictionary is returned as a (key, value) tuple,
for (airportCode, airportName) in airports {
println("\(airportCode): \(airportName)")
}
//OR
for airportCode in airports.keys {
println("Airport code: \(airportCode)")
}
for airportName in airports.values {
println("Airport name: \(airportName)")
}
//Sequence Based Initialization (2 arrays to dictionary)
let nearestStarNames = ["Proxima Centauri", "Alpha Centauri A", "Alpha Centauri B", "Barnard's Star", "Wolf 359"]
let nearestStarDistances = [4.24, 4.37, 4.37, 5.96, 7.78]
let starDistanceDict = Dictionary(uniqueKeysWithValues: zip(nearestStarNames, nearestStarDistances))
// ["Wolf 359": 7.78, "Alpha Centauri B": 4.37, "Proxima Centauri": 4.24, "Alpha Centauri A": 4.37, "Barnard's Star": 5.96]
//Duplicate Key Resolution
//You can now handle initializing a dictionary with duplicate keys any way you’d like.
//This helps avoid overwriting key-value pairs without any say in the matter
// Random vote of people's favorite stars
let favoriteStarVotes = ["Alpha Centauri A", "Wolf 359", "Alpha Centauri A", "Barnard's Star"]
let mergedKeysAndValues = Dictionary(zip(favoriteStarVotes, repeatElement(1, count: favoriteStarVotes.count)), uniquingKeysWith: +)
// ["Barnard's Star": 1, "Alpha Centauri A": 2, "Wolf 359": 1]
//You can filter results into a new object of the original type
let closeStars = starDistanceDict.filter { $0.value < 5.0 }
// Dictionary: ["Proxima Centauri": 4.24, "Alpha Centauri A": 4.37, "Alpha Centauri B": 4.37]
//Dictionary gained a very useful method for directly mapping its values:
let mappedCloseStars = closeStars.mapValues { "\($0)" }
// ["Proxima Centauri": "4.24", "Alpha Centauri A": "4.37", "Alpha Centauri B": "4.37"]
/*
A common practice when accessing a value on Dictionary is to use the nil coalescing operator to give a default value
in case the value is nil. In Swift 4, this becomes much cleaner and allows you to do some awesome in line mutation
*/
let siriusDistance = mappedCloseStars["Wolf 359", default: "unknown"] // "unknown"
// Subscript with a default value used for mutating
var starWordsCount: [String: Int] = [:]
for starName in nearestStarNames {
let numWords = starName.split(separator: " ").count
starWordsCount[starName, default: 0] += numWords // Amazing
}
//starWordsCount -> ["Wolf 359": 2, "Alpha Centauri B": 3, "Proxima Centauri": 2, "Alpha Centauri A": 3, "Barnard's Star": 2]
//initialize a Dictionary from a Sequence and to group them into buckets
let starsByFirstLetter = Dictionary(grouping: nearestStarNames) { $0.first! }
// ["B": ["Barnard's Star"], "A": ["Alpha Centauri A", "Alpha Centauri B"], "W": ["Wolf 359"], "P": ["Proxima Centauri"]]
//You now have the ability to explicitly reserve capacity
starWordsCount.reserveCapacity(20)
/**************************************/
/**************************************/
//FOR
/*****************/
//General format
for initialization; condition; increment {
statements
}
//Or
initialization
while condition {
statements
increment
}
//Or
do {
statements
} while condition
/*****************/
//Ex.
for index = 0; index < 3; ++index {
println("index is \(index)")
}
//You use the for-in loop to iterate over collections of items, such as ranges of numbers, items in an array, or characters in a string.
for index in 1...5 {
println("\(index) times 5 is \(index * 5)")
}
//If you don’t need each value from the range, you can ignore the values by using an underscore in place of a variable name:
let base = 3
let power = 10
var answer = 1
for _ in 1...power {
answer *= base
}
println("\(base) to the power of \(power) is \(answer)")
// prints "3 to the power of 10 is 59049"
/**************************************/
/**************************************/
//Switch
//In contrast with switch statements in C and Objective-C, switch statements in Swift
//do not fall through the bottom of each case and into the next one by default.
//Instead, the entire switch statement finishes its execution as soon as the first matching switch case is completed
//without requiring an explicit break statement.
let count = 3_000_000_000_000
var naturalCount: String
switch count {
case 0:
naturalCount = "no"
case 1...3:
naturalCount = "a few"
case 4...9:
naturalCount = "several"
default:
naturalCount = "millions and millions of"
}
//You can use tuples to test multiple values in the same switch statement
let somePoint = (1, 1)
switch somePoint {
case (0, 0):
println("(0, 0) is at the origin")
case (_, 0):
println("(\(somePoint.0), 0) is on the x-axis")
case (0, _):
println("(0, \(somePoint.1)) is on the y-axis")
case (-2...2, -2...2):
println("(\(somePoint.0), \(somePoint.1)) is inside the box")
default:
println("(\(somePoint.0), \(somePoint.1)) is outside of the box")
}
//...
let a: Any = 5
switch a {
case is String:
print("a is string with value \(a)")
case let n as Int:
print (n + 1)
default: break
}
//Will loop only for all the Strings
for case is String in ["string", 123] as [Any] {
print("")
}
for case let s as String in ["string", 123] as [Any] {
print("\(s)")
}
//A switch case can bind the value or values it matches to temporary constants or variables
let anotherPoint = (2, 0)
switch anotherPoint {
case (let x, 0):
println("on the x-axis with an x value of \(x)")
case (0, let y):
println("on the y-axis with a y value of \(y)")
case let (x, y): //Serves as default cause it will certainly go in there if other fails
println("somewhere else at (\(x), \(y))")
}
//....
switch i {
case let j where j < 0:
print("i is negative")
case let j where j > 0:
print("i is positive")
case 0:
print("i is 0")
default:break
}
//Also for the for in loop
let array: [Any?] = [1,2,"", nil]
for case let n as Int in array where n>1 {
print("\(n)")
}
//A switch case can use a where clause to check for additional conditions.
let yetAnotherPoint = (1, -1)
switch yetAnotherPoint {
case let (x, y) where x == y:
println("(\(x), \(y)) is on the line x == y")
case let (x, y) where x == -y:
println("(\(x), \(y)) is on the line x == -y")
case let (x, y):
println("(\(x), \(y)) is just some arbitrary point")
}
// prints "(1, -1) is on the line x == -y"
//Optional pattern
//There’s a special syntax: appending ? to a case pattern safely unwraps an Optional tag.
switch i {
case 1?:
print("You have 1 thingy!")
case let n?:
print("You have \(n) thingies!")
case nil: break
}
//Note that the case let ? can be used also for the for in loop to iterate over the non-nil values
for case let n? in [1,2, nil] {
print("\(n)") //Will print 1 2
}
//You can match Optionals with any value using the _?
var i: Int?
switch i {
case 1:
print("You have 1 thingy!")
case _?:
print("You have many thingies!")
default:
break;
}
//Also in for statement, iterate over non nil values
for case _? in 1...3 {
// Do something three times.
}
//If you need to go through every case (like objective-c does without break) do it like this:
//Using the fallthrough keyword
let integerToDescribe = 5
var description = "The number \(integerToDescribe) is"
switch integerToDescribe {
case 2, 3, 5, 7, 11, 13, 17, 19:
description += " a prime number, and also"
fallthrough
default:
description += " an integer."
}
//you can mark a loop statement or switch statement with a statement label
//and use this label with the break statement or continue statement to end or continue the execution of the labeled statement.
//This is used when you have multiple nested statements (for, whule etc) and you want to break (or continue) one of them.
//General format of a while example
label name: while condition {
statements
}
//Ex.
gameLoop: while square != finalSquare {
switch square + diceRoll {
case finalSquare:
//You can break the statement like this:
break gameLoop //break and it's labeled name
default:
// this is a valid move, so find out its effect
square += diceRoll
square += board[square]
}
}
//The ?? operator
//Supplies a default value to an optional if it happens to be .None
//Ex.:
let something: String? = nil
let somethingElse = something ?? "hey here's a default value"
//Instead of
if let something = something {
somethingElse = something
}
//The Optionally Do— ?!
//Ex.:
//(left hand side) ?! (right hand side)
lhs ?! rhs
/*
The ? stipulates that the left-hand side (lhs) of the expression is some kind of Optional value.
The succeeding ! means that the right-hand side will only execute should the lhs be successfully unwrapped
*/
//Ex.:
//Instead of this
let mark: String? = “mark”
if mark != nil {
printHello()
}
//We do this
mark ?! printHello // prints HELLO
/**************************************/
//Enums
enum Movement {
case Left
case Right
case Top
case Bottom
}
if aMovement == .Left {
print("left")
}
//You can assgn different values unlike objective c where you can only assign intergers.
/*
You can assign:
Integer
Floating Point
String
Boolean
*/
enum House: String {
case Baratheon = "Ours is the Fury"
case Greyjoy = "We Do Not Sow"
case Martell = "Unbowed, Unbent, Unbroken"
case Stark = "Winter is Coming"
case Tully = "Family, Duty, Honor"
case Tyrell = "Growing Strong"
}
//If you want to access the values, you can do so with the rawValue property:
let bestHouse = House.Stark
print(bestHouse.rawValue)
// prints "Winter is coming"
//For String and Int types, you can even omit the values and the Swift compiler will do the right thing:
// Mercury = 1, Venus = 2, ... Neptune = 8
enum Planet: Int {
case Mercury = 1, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune
}
// North = "North", ... West = "West"
enum CompassPoint: String {
case North, South, East, West
}
//Ex.:
enum Movement: Int {
case Left = 0
case Right = 1
case Top = 2
case Bottom = 3
}
// creates a movement.Right case, as the raw value for that is 1
let rightMovement = Movement(rawValue: 1)
//If you use the rawValue initializer, keep in mind that it is a failable initializer
//i.e. you get back an Optional
//Associated Values
enum Student {
case Name(String)
case Mark(Int,Int,Int)
}
var studDetails = Student.Name("Swift")
var studMarks = Student.Mark(98,97,95)
switch studMarks {
case .Name(let studName):
println("Student name is: \(studName).")
case .Mark(let Mark1, let Mark2, let Mark3):
println("Student Marks are: \(Mark1),\(Mark2),\(Mark3).")
default:
println("Nothing")
}
//Another example
enum Trade {
case Buy(stock: String, amount: Int)
case Sell(stock: String, amount: Int)
}
func trade(type: Trade) {}
let trade = Trade.Buy(stock: "APPL", amount: 500)
if case let Trade.Buy(stock, amount) = trade {
print("buy \(amount) of \(stock)")
}
//Info for "if case let"
//http://fuckingifcaseletsyntax.com/
//KEYPATHS
//Reference key paths on types to get/set the underlying value of an instance
struct Lightsaber {
enum Color {
case blue, green, red
}
let color: Color
}
class ForceUser {
var name: String
var lightsaber: Lightsaber
var master: ForceUser?
init(name: String, lightsaber: Lightsaber, master: ForceUser? = nil) {
self.name = name
self.lightsaber = lightsaber
self.master = master
}
}
//Create some instances
let sidious = ForceUser(name: "Darth Sidious", lightsaber: Lightsaber(color: .red))
let obiwan = ForceUser(name: "Obi-Wan Kenobi", lightsaber: Lightsaber(color: .blue))
let anakin = ForceUser(name: "Anakin Skywalker", lightsaber: Lightsaber(color: .blue), master: obiwan)
// To create a key path, you simply use a back-slash followed by the property you're interested in:
// Create reference to the ForceUser.name key path
let nameKeyPath = \ForceUser.name
// Access the value from key path on instance
let obiwanName = obiwan[keyPath: nameKeyPath] // "Obi-Wan Kenobi"
// Use keypath directly inline and to drill down to sub objects
let anakinSaberColor = anakin[keyPath: \ForceUser.lightsaber.color] // blue
// Access a property on the object returned by key path
let masterKeyPath = \ForceUser.master
let anakinMasterName = anakin[keyPath: masterKeyPath]?.name // "Obi-Wan Kenobi"
// Change Anakin to the dark side using key path as a setter
anakin[keyPath: masterKeyPath] = sidious
anakin.master?.name // Darth Sidious
/*
Forget NSCoding in Swift 4
The only thing required to make a Swift type Encodable and Decodable is to implement the Codable protocol.
If all properties are Codable, the protocol implementation is automatically generated by the compiler.
*/
//Ex.
struct CuriosityLog: Codable {
enum Discovery: String, Codable {
case rock, water, martian
}
var sol: Int
var discoveries: [Discovery]
}
// Create a log entry for Mars sol 42
let logSol42 = CuriosityLog(sol: 42, discoveries: [.rock, .rock, .rock, .rock])
//To actually encode the object, you'll need to pass it to an encoder
let jsonEncoder = JSONEncoder() // One currently available encoder
// Encode the data
let jsonData = try jsonEncoder.encode(logSol42)
// Create a String from the data
let jsonString = String(data: jsonData, encoding: .utf8)
// "{"sol":42,"discoveries":["rock","rock","rock","rock"]}"
//Decode it
let jsonDecoder = JSONDecoder() // Pair decoder to JSONEncoder
// Attempt to decode the data to a CuriosityLog object
let decodedLog = try jsonDecoder.decode(CuriosityLog.self, from: jsonData)
decodedLog.sol // 42
decodedLog.discoveries // [rock, rock, rock, rock]
/*
In case you want to encode/decode keys included in the serialized data doesn’t match the property names of the Codable Type.
Codable types can declare a special nested enumeration named CodingKeys that conforms to the CodingKey protocol.
When this enumeration is present, its cases serve as the authoritative list of properties that must be included
when instances of a codable type are encoded or decoded.
*/
//The names of the enum cases should exactly match the property names of the Codable Type.
//Omit the properties from CodingKeys if you want to omit them from encoding/decoding process.
//A property omitted from CodingKeys needs a default value.
//Example:
struct Photo: Codable
{
//...Other properties...
//This property is not included in the CodingKeys enum and hence will not be encoded/decoded.
var format: String = "png"
enum CodingKeys: String, CodingKey
{
case title = "name"
case url = "link"
case isSample
case metaData
case type
case size
}
}
//To convert from snake case, i.e.from this "first_name" to this firstName, no need to impement CodingKeys
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
//DATES
//Codable is able to handle ISO-8601 with a built-in date converter. So, given this JSON:
//So to decode this:
"time_of_birth": "1999-04-03T17:30:31Z"
//You need to do this:
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
decoder.dateDecodingStrategy = .iso8601 //Others are .millisecondsSince1970 .secondsSince1970 etc
//Custom dates
let formatter = DateFormatter()
formatter.dateFormat = "dd-MM-yyyy"
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(formatter)
//Or more advanced example
decoder.dateDecodingStrategy = .custom { decoder in
// pull out the number of days from Codable
let container = try decoder.singleValueContainer()
let numberOfDays = try container.decode(Int.self)
// create a start date of Jan 1st 1970, then a DateComponents instance for our JSON days
let startDate = Date(timeIntervalSince1970: 0)
var components = DateComponents()
components.day = numberOfDays
// create a Calendar and use it to measure the difference between the two
let calendar = Calendar(identifier: .gregorian)
return calendar.date(byAdding: components, to: startDate) ?? Date()
}
//If you want to change the format of the encoding/decoding
//You need to implement encode(to:) and init(from:) methods of Encodable and Decodable protocols explicitly.
//Example
struct Photo
{
var title: String
var size: Size
enum CodingKeys: String, CodingKey
{
case title = "name"
case width
case height
}
}
extension Photo: Encodable
{
func encode(to encoder: Encoder) throws
{
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(title, forKey: .title)
try container.encode(size.width, forKey: .width)
try container.encode(size.height, forKey: .height)
}
}
extension Photo: Decodable
{
init(from decoder: Decoder) throws
{
let values = try decoder.container(keyedBy: CodingKeys.self)
title = try values.decode(String.self, forKey: .title)
let width = try values.decode(Double.self, forKey: .width)
let height = try values.decode(Double.self, forKey: .height)
size = Size(width: width, height: height)
}
}
//The above will put width and height at the same level with title, outside of the size
//Like so:
{
"title":"Apple",
"width":150,
"height":150
}
//----------------------------
// Using property wrappers
// https://nilcoalescing.com/blog/ReducingTheCodableBoilerplate/
@propertyWrapper
struct TimeIntervalSince1970Encoded {
let wrappedValue: Date
}
extension TimeIntervalSince1970Encoded: Codable {
init(from decoder: Decoder) throws {
var container = try decoder.singleValueContainer()
let timeInterval = try container.decode(TimeInterval.self)
self.wrappedValue = Date(timeIntervalSince1970: timeInterval)
}
func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
try container.encode(self.wrappedValue.timeIntervalSince1970)
}
}
struct UserRecord: Codable {
let name: String
let dateOfBirth: Date
@TimeIntervalSince1970Encoded
var created: Date
}
// The JSON
{
"name": "Bill",
"dateOfBirth": "2020-04-23T18:25:43.518Z",
"created": 1616363597
}
//----------------------------
// Bonus: Codable conformance with associated values
// https://nilcoalescing.com/blog/CodableConformanceForSwiftEnumsWithMultipleAssociatedValuesOfDifferentTypes/
// Encode and decode polymorphic types in Swift
// https://nilcoalescing.com/blog/BringingPolymorphismToCodable/
/*
The test works by allocating the instance on the main queue, while immediately deallocating it on the background queue.
This triggers the deinit call, and the test succeeds.
*/
/*
If the test fails due to the 5-second timeout, you have just found a retain cycle!
*/
func testCleanup() {
// Extend your class inline in order to add closure property `deinitCalled`,
// which indicates when/if your class's deinit() gets called
class ClassUnderTest: CLASS_YOU_WANT_TO_TEST {
var deinitCalled: (() -> Void)?
deinit { deinitCalled?() }
}
// Set up async expectation, which causes the test to wait for `deinitCalled`
// to be called
let exp = expectation(description: "exp")
// Initialize the class
var instance: ClassUnderTest? = ClassUnderTest()
// Set up up the `deinitCalled` closure, making the test succeed
instance?.deinitCalled = {
exp.fulfill()
}
// On a different queue, remove the instance from memory,
// which should call `deinit`, in order to clean up resources.
// If this doesn't cause `deinit` to be called, you probably have a
// retain cycle
DispatchQueue.global(qos: .background).async {
instance = nil
}
// Wait for max. five seconds for the test to succeed, if not,
// you may have a memory leak due to a retain cycle
waitForExpectations(timeout: 5)
}
func testCleanup() {
// Extend your class inline in order to add closure property `deinitCalled`,
// which indicates when/if your class's deinit() gets called
class ClassUnderTest: CLASS_YOU_WANT_TO_TEST {
var deinitCalled: (() -> Void)?
deinit { deinitCalled?() }
}
// Set up async expectation, which causes the test to wait for `deinitCalled`
// to be called
let exp = expectation(description: "exp")
// Initialize the class
var instance: ClassUnderTest? = ClassUnderTest()
// Set up up the `deinitCalled` closure, making the test succeed
instance?.deinitCalled = {
exp.fulfill()
}
// On a different queue, remove the instance from memory,
// which should call `deinit`, in order to clean up resources.
// If this doesn't cause `deinit` to be called, you probably have a
// retain cycle
DispatchQueue.global(qos: .background).async {
instance = nil
}
// Wait for max. five seconds for the test to succeed, if not,
// you may have a memory leak due to a retain cycle
waitForExpectations(timeout: 5)
}
//Function that takes a person’s name as input (type String) and returns a greeting for that person (returns Sting)
func sayHello(personName: String) -> String
{
let greeting = "Hello, " + personName + "!"
return greeting
}
//Call it
println(sayHello("Anna"))
//Multiple parameters
func halfOpenRangeLength(start: Int, end: Int) -> Int {
return end - start
}
println(halfOpenRangeLength(1, 10))
// prints "9"
//No parameters
func sayHelloWorld() -> String {
return "hello, world"
}
//No return value
func sayGoodbye(personName: String) {
println("Goodbye, \(personName)!")
}
sayGoodbye("Dave")
// prints "Goodbye, Dave!"
/******************************/
//Functions with Multiple Return Values
//The example below defines a function called minMax, which finds the smallest and largest numbers in an array of Int values:
//returns a tuple containing two Int values
func minMax(array: [Int]) -> (min: Int, max: Int) {
var currentMin = array[0]
var currentMax = array[0]
for value in array[1..<array.count]
{
if value < currentMin
{
currentMin = value
}
else if value > currentMax
{
currentMax = value
}
}
return (currentMin, currentMax)
}
//Call it
let bounds = minMax([8, -6, 2, 109, 3, 71])
println("min is \(bounds.min) and max is \(bounds.max)")
// prints "min is -6 and max is 109"
//Optional Tuple Return Types
func minMax(array: [Int]) -> (min: Int, max: Int)? {
if array.isEmpty
{
return nil
}
var currentMin = array[0]
var currentMax = array[0]
for value in array[1..<array.count]
{
if value < currentMin
{
currentMin = value
}
else if value > currentMax
{
currentMax = value
}
}
return (currentMin, currentMax)
}
//You can use optional binding to check whether this version of the minMax function returns an actual tuple value or nil
if let bounds = minMax([8, -6, 2, 109, 3, 71]) {
println("min is \(bounds.min) and max is \(bounds.max)")
}
// prints "min is -6 and max is 109"
//External Parameter Names
//If you want users of your function to provide parameter names when they call your function
//define an external parameter name for each parameter, in addition to the local parameter name.
//Format:
func someFunction(externalParameterName localParameterName: Int) {
// function body goes here, and can use localParameterName
// to refer to the argument value for that parameter
}
//Ex.
//withJoiner is the external parameter name that will be called when calling the function
//all other parameters are internal and can be used only locally inside the function
func join(string s1: String, toString s2: String, withJoiner joiner: String) -> String
{
return s1 + joiner + s2
}
//Call it
join(string: "hello", toString: "world", withJoiner: ", ")
// returns "hello, world"
//Or use the local parameter as an external parameter using the "#'
func containsCharacter(#string: String, #characterToFind: Character) -> Bool {
for character in string {
if character == characterToFind {
return true
}
}
return false
}
//Call it
let containsAVee = containsCharacter(string: "aardvark", characterToFind: "v")
// containsAVee equals true, because "aardvark" contains a "v"
//Default values
func containsCharacter(#string: String = "default", #characterToFind: Character) -> Bool {
for character in string {
if character == characterToFind {
return true
}
}
return false
}
//Swift provides an automatic external name for any parameter that has a default value.
//The automatic external name is the same as the local name, as if you had written a hash symbol before the local name
//Ex.
func join(s1: String, s2: String, joiner: String = " ") -> String {
return s1 + joiner + s2
}
//Therefore it must be providd when calling the function
join("hello", "world", joiner: "-")
// returns "hello-world"
/******************************/
/******************************/
//Variadic Parameters
//A variadic parameter accepts zero or more values of a specified type (Used like an array inside the function)
func arithmeticMean(numbers: Double...) -> Double {
var total: Double = 0
for number in numbers
{
total += number
}
return total / Double(numbers.count)
}
//Call it
arithmeticMean(3, 8.25, 18.75)
// returns 10.0, which is the arithmetic mean of these three numbers
//Constant and Variable Parameters
//Function parameters are constants by default.
//Trying to change the value of a function parameter from within the body of that function results in a compile-time error
//BUT if you want to, you can create a new one inside
//this is called Variable shadowing
//Ex.
func someFunction(withArgument number: Int) -> result {
//You hide (i.e.) shadow the original declaration
var number = number
number+= 2
number*=number
return number
}
//In-Out Parameters (Pass by reference really)
//Variable parameters, as described above, can only be changed within the function itself
// If you want a function to modify a parameter’s value, and you want those changes to persist after the function call has ended
//define that parameter as an in-out parameter instead.
//They can't have default values and variadic parameters cannot be marked as inout
//Ex.
func swapTwoInts(a:inout Int, b:inout Int) {
let temporaryA = a
a = b
b = temporaryA
}
var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
println("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
// prints "someInt is now 107, and anotherInt is now 3"
//Using Function Types
//You use function types just like any other types in Swift
//You can define a constant or variable to be of a function type
//and assign an appropriate function to that variable
//Ex.
var mathFunction: (Int, Int) -> Int = addTwoInts
//You can now call the assigned function with the name mathFunction:
println("Result: \(mathFunction(2, 3))")
// prints "Result: 5"
//As with any other type, you can leave it to Swift to infer the function type
//when you assign a function to a constant or variable:
//Ex.
let anotherMathFunction = addTwoInts
// anotherMathFunction is inferred to be of type (Int, Int) -> Int
//Function Types as Parameter Types
func printMathResult(mathFunction: (Int, Int) -> Int, a: Int, b: Int) {
println("Result: \(mathFunction(a, b))")
}
printMathResult(addTwoInts, 3, 5)
// prints "Result: 8"
//Function Types as Return Types
func stepForward(input: Int) -> Int {
return input + 1
}
func stepBackward(input: Int) -> Int {
return input - 1
}
func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
return backwards ? stepBackward : stepForward
}
var currentValue = 3
let moveNearerToZero = chooseStepFunction(currentValue > 0)
// moveNearerToZero now refers to the stepBackward() function
//Nested Functions
//You can also define functions inside the bodies of other functions, known as nested functions.
//Ex.
func chooseStepFunction(backwards: Bool) -> (Int) -> Int {
func stepForward(input: Int) -> Int { return input + 1 }
func stepBackward(input: Int) -> Int { return input - 1 }
return backwards ? stepBackward : stepForward
}
/******************************/
//Special literal expressions.
//http://static1.squarespace.com/static/5592eb03e4b051859f0b377f/t/574607294c2f852e1525ae83/1464207151023/?format=1500w
//They return usefull information and can be used as any other value (e.g. string value, number etc.)
//Ex.:
//Set it as the default value of the parameter
//This will print the name of the function that called through to the function it's a part of
func wreakHavocOnHumanity(krakenArmy: [Kraken], callingFunction: String = #function) {
//There's a bug! There's too much havoc being wreaked!
//Let's figure out who all is calling this function so we can
//fix the bug! We absolutely need to make sure that the
//appropriate amount of havoc be wreaked.
print("Name of Function who called \(#function): \(callingFunction).")
}
/*
Other Special literal expressions are:
#function The name of the file where the literal is located
#file See example above
#line The line number where the literal is located
#column The character location in the line of where the literal is located
//Ex.
struct Resolution {
var width = 0
var height = 0
}
class VideoMode {
var resolution = Resolution()
var interlaced = false
var frameRate = 0.0
var name: String?
}
//All structures have an automatically-generated memberwise initializer
//which you can use to initialize the member properties of new structure instances
let vga = Resolution(width: 640, height: 480)
//All structures and enumerations are value types in Swift.
//This means that any structure and enumeration instances you create
//and any value types they have as properties—are always copied when they are passed around in your code
//Classes Are Reference Types
/***********************/
//PROPERTY OBSERVERS
//Property observers observe and respond to changes in a property’s value
/*
You have the option to define either or both of these observers on a property:
- willSet is called just before the value is stored.
- didSet is called immediately after the new value is stored.
*/
//Ex.
class StepCounter {
var totalSteps: Int = 0 {
willSet(newTotalSteps) {
println("About to set totalSteps to \(newTotalSteps)")
}
didSet {
if totalSteps > oldValue {
println("Added \(totalSteps - oldValue) steps")
}
}
}
}
let stepCounter = StepCounter()
stepCounter.totalSteps = 200
// About to set totalSteps to 200
// Added 200 steps
stepCounter.totalSteps = 360
// About to set totalSteps to 360
// Added 160 steps
stepCounter.totalSteps = 896
// About to set totalSteps to 896
// Added 536 steps
/***********************/
/***********************/
//Static and class member vriables
/*
In C and Objective-C, you define static constants and variables associated with a type as global static variables.
In Swift, however, type properties are written as part of the type’s definition
within the type’s outer curly braces, and each type property is explicitly scoped to the type it supports.
*/
//You define type properties for value types with the static keyword
//and type properties (or type methods) for class types with the class keyword (type properties are static properties for classes)
//Ex.
struct SomeStructure {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
// return an Int value here
}
}
class SomeClass {
class var computedTypeProperty: Int {
// return an Int value here
}
class func someTypeMethod() {
// type method implementation goes here
}
}
//CALL IT
SomeClass.someTypeMethod()
/***********************/
/***********************/
//LOCAL - EXTERNAL VARIABLE NAMES
//Ex.
class Counter {
var count: Int = 0
func incrementBy(amount: Int, numberOfTimes: Int) {
count += amount * numberOfTimes
}
}
//CALL IT
let counter = Counter()
counter.incrementBy(amount:5, numberOfTimes: 3)
// counter value is now 15
/*
Swift 2
You don’t need to define an external parameter name for the first argument value
because its purpose is clear from the function name incrementBy
In Swift 3 you have to put the first parameter name as well
*/
/***********************/
/***********************/
//Mutating methods can assign an entirely new instance to the implicit self property.
//The Point example shown above could have been written in the following way instead:
struct Point {
var x = 0.0, y = 0.0
mutating func moveByX(deltaX: Double, y deltaY: Double) {
self = Point(x: x + deltaX, y: y + deltaY)
}
}
/***********************/
/***********************/
//Inheritance
class SomeSubclass: SomeSuperclass {
// subclass definition goes here
}
//Overriding methods
class Train: Vehicle {
override func makeNoise() {
println("Choo Choo")
}
}
//If you don't want to be overriden make it final
final func makeNoise() {
println("Choo Choo")
}
//Overriding properties
class Car: Vehicle {
var gear = 1
override var description: String {
return super.description + " in gear \(gear)"
}
}
//CALL IT
let car = Car()
car.currentSpeed = 25.0
car.gear = 3
println("Car: \(car.description)")
// Car: traveling at 25.0 miles per hour in gear 3
/* You can prevent a method, property, or subscript from being overridden by marking it as final */
//COMPUTED PROPERTIES
//They do not actually store a value.
//Instead, they provide a getter and an optional setter to retrieve and set other properties and values indirectly.
//Ex.
struct Rect {
var origin = Point()
var size = Size()
var center: Point {
get {
let centerX = origin.x + (size.width / 2)
let centerY = origin.y + (size.height / 2)
return Point(x: centerX, y: centerY)
}
set(newCenter) { //You can avoid the newCenter variable and use the keyword newValue at its place
origin.x = newCenter.x - (size.width / 2)
origin.y = newCenter.y - (size.height / 2)
}
}
}
var square = Rect(origin: Point(x: 0.0, y: 0.0), size: Size(width: 10.0, height: 10.0))
let initialSquareCenter = square.center
square.center = Point(x: 15.0, y: 15.0)
println("square.origin is now at (\(square.origin.x), \(square.origin.y))")
// prints "square.origin is now at (10.0, 10.0)"
//If you want a read only property
var center: Point {
get {
let centerX = origin.x + (size.width / 2)
let centerY = origin.y + (size.height / 2)
return Point(x: centerX, y: centerY)
}
}
//OR with no getter
var center: Point { get }
//Properties with private setter and public getter
//Making set private
private(set) weak var email:String?
private(set) lazy var emails = [String]()
//Get has default access control (internal)
//If we want to make it public for example
public private(set) weak var email:String?
//INITIALIZATON
//Swift provides an automatic external name for every parameter in an initializer
//if you don’t provide an external name yourself
//Ex.1
struct Fahrenheit {
var temperature: Double
init() {
temperature = 32.0
}
}
//-----------
//Ex.2
struct Celsius {
var temperatureInCelsius: Double
init(fromFahrenheit fahrenheit: Double) {
temperatureInCelsius = (fahrenheit - 32.0) / 1.8
}
init(fromKelvin kelvin: Double) {
temperatureInCelsius = kelvin - 273.15
}
}
//CALL IT
let boilingPointOfWater = Celsius(fromFahrenheit: 212.0)
// boilingPointOfWater.temperatureInCelsius is 100.0
let freezingPointOfWater = Celsius(fromKelvin: 273.15)
// freezingPointOfWater.temperatureInCelsius is 0.0
/* If you do not want to use an external name for an initializer parameter
write an underscore (_) instead of an explicit external name
for that parameter to override the default behavior.*/
//Ex.
init(_ celsius: Double) {
temperatureInCelsius = celsius
}
//Unlike subclasses in Objective-C, Swift subclasses do not inherit their superclass initializers by default
//If you want a custom subclass to present one or more of the same initializers as its superclass
//you can provide a custom implementation of those initializers within the subclass.
//And you have to override it
//Ex.
class Vehicle {
var numberOfWheels = 0
var description: String {
return "\(numberOfWheels) wheel(s)"
}
}
class Bicycle: Vehicle {
override init() {
super.init()
numberOfWheels = 2
}
}
/* The Bicycle subclass defines a custom designated initializer, init().
This designated initializer matches a designated initializer from the superclass of Bicycle
and so the Bicycle version of this initializer is marked with the override modifier
*/
/* f your subclass doesn’t define any designated initializers, it automatically inherits all of its superclass designated initializers. */
//CONVENIENCE INITIALIZERS
/*Convenience initializers are secondary, supporting initializers for a class.
You can define a convenience initializer to call a designated initializer from the same class as the convenience initializer
with some of the designated initializer’s parameters set to default values.
*/
//Ex.
class Food {
var name: String
init(name: String) {
self.name = name
}
convenience init() {
self.init(name: "[Unnamed]")
}
}
class RecipeIngredient: Food {
var quantity: Int
init(name: String, quantity: Int) {
self.quantity = quantity
super.init(name: name)
}
override convenience init(name: String) { //This overrides the designated initialiser and is a convenience initialiaser
self.init(name: name, quantity: 1)
}
}
//FAILABLE INITIALIZERS
struct Animal {
let species: String
init?(species: String) {
if species.isEmpty { return nil }
self.species = species
}
}
//CALL IT
let someCreature = Animal(species: "Giraffe")
// someCreature is of type Animal?, not Animal
if let giraffe = someCreature {
println("An animal was initialized with a species of \(giraffe.species)")
}
// prints "An animal was initialized with a species of Giraffe"
/*If you pass an empty string value to the failable initializer’s species parameter,
the initializer triggers an initialization failure: */
//REQUIRED INITIALIZERS
//every subclass of the class must implement that initializer:
class SomeSubclass: SomeClass {
required init() {
// subclass implementation of the required initializer goes here
}
}
/* You do not write the override modifier when overriding a required designated initializer: */
//OPTIONAL DEINITIALIZATION
deinit {
// perform the deinitialization
}
/***********************/
/***********************/
//Optional Chaining
//Optional chaining is a process for querying and calling properties, methods, and subscripts
//on an optional that might currently be nil
//Ex.
class Person {
var residence: Residence?
}
class Residence {
var numberOfRooms = 1
}
//CALL IT
let john = Person()
//Force uwrap it
let roomCount = john.residence!.numberOfRooms //this triggers a runtime error because residence default value is nil (upon initialization)
//USING OPTIONAL CHAINING
//Use a question mark in place of the exclamation mark:
if let roomCount = john.residence?.numberOfRooms {
println("John's residence has \(roomCount) room(s).")
} else {
println("Unable to retrieve the number of rooms.")
}
// prints "Unable to retrieve the number of rooms."
//This tells Swift to “chain” on the optional residence property and to retrieve the value of numberOfRooms if residence exists.
//You can also attempt to set a property’s value through optional chaining:
john.residence?.numberOfRooms = 5
//This will failt because residence is nil
/***********************/
/***********************/
//TYPE CASTING
//Type casting is a way to check the type of an instance
//Ex.
if item is Movie //Movie is a class
/***********************/
/***********************/
//BASIC INHERITANCE
//OPEN VS PUBLIC
//"open" is a new access level introduced in Swift 3
/*
An open class is accessible and subclassable outside of the defining module.
An open class member is accessible and overridable outside of the defining module.
A public class is accessible but not subclassable outside of the defining module.
A public class member is accessible but not overridable outside of the defining module.
*/
//Examples
/// ModuleA:
// This class is not subclassable outside of ModuleA.
public class NonSubclassableParentClass {
// This method is not overridable outside of ModuleA.
public func foo() {}
// This method is not overridable outside of ModuleA because
// its class restricts its access level.
// It is not invalid to declare it as `open`.
open func bar() {}
// The behavior of `final` methods remains unchanged.
public final func baz() {}
}
// This class is subclassable both inside and outside of ModuleA.
open class SubclassableParentClass {
// This property is not overridable outside of ModuleA.
public var size : Int
// This method is not overridable outside of ModuleA.
public func foo() {}
// This method is overridable both inside and outside of ModuleA.
open func bar() {}
/// The behavior of a `final` method remains unchanged.
public final func baz() {}
}
// The behavior of `final` classes remains unchanged.
public final class FinalClass { }
//An open class may not also be declared final.
//More info on open
//https://github.com/apple/swift-evolution/blob/master/proposals/0117-non-public-subclassable-by-default.md
//FILEPRIVATE
/*
Before the introduction of Swift 3, the private keyword limited the use of entities (classes, structures, enumerations, …)
to the source file in which they were defined.
Swift 3 introduces the fileprivate keyword that replaces the private keyword.
As the name implies, the fileprivate keyword limits access to the entity to the source file it is declared in.
*/
//Ex.
class NotesViewController: UIViewController {
fileprivate var dataSource = [String]()
}
extension NotesViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return dataSource.count
}
...
}
//You can use it in the extension as well.
//If it was private you could not, like in other languages.
/***********************/
//FORMAT
extension SomeType: SomeProtocol, AnotherProtocol {
// new functionality to add to SomeType goes here
}
//Ex.
//You can extend the Rect structure to provide an additional initializer that takes a specific center point and size:
extension Rect {
init(center: Point, size: Size) {
let originX = center.x - (size.width / 2)
let originY = center.y - (size.height / 2)
self.init(origin: Point(x: originX, y: originY), size: size)
}
}
//Adda function
extension Int {
func repetitions(task: () -> ()) {
for i in 0..<self {
task()
}
}
}
//The example below adds a new mutating method called square to Swift’s Int type, which squares the original value
//With a mutating function
extension Int {
mutating func square() {
self = self * self
}
}
var someInt = 3
someInt.square()
// someInt is now 9
//FORMAT
protocol SomeProtocol {
// protocol definition goes here
}
//You can limit protocol adoption to class types (and not structures or enumerations)
//by adding the class keyword to a protocol’s inheritance list.
protocol SomeClassOnlyProtocol: class, SomeInheritedProtocol {
// class-only protocol definition goes here
}
/*
Property requirements are always declared as variable properties, prefixed with the var keyword
Gettable and settable properties are indicated by writing { get set } after their type declaration,
and gettable properties are indicated by writing { get }.
*/
protocol SomeProtocol {
var mustBeSettable: Int { get set }
var doesNotNeedToBeSettable: Int { get }
optional func incrementForCount(count: Int) -> Int
optional var fixedIncrement: Int { get }
class func someTypeMethod()
}
//FULL EXAMPLE
protocol FullyNamed {
var fullName: String { get }
}
struct Person: FullyNamed {
var fullName: String
}
class Starship: FullyNamed {
var fullName: String {
return (prefix != nil ? prefix! + " " : "") + name
}
}
//DELEGATION
protocol DiceGame {
var dice: Dice { get }
func play()
}
protocol DiceGameDelegate {
func gameDidStart(game: DiceGame)
func game(game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int)
func gameDidEnd(game: DiceGame)
}
class SnakesAndLadders: DiceGame {
let finalSquare = 25
let dice = Dice(sides: 6, generator: LinearCongruentialGenerator())
var square = 0
var board: [Int]
init() {
board = [Int](count: finalSquare + 1, repeatedValue: 0)
board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02
board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08
}
var delegate: DiceGameDelegate?
func play() {
square = 0
delegate?.gameDidStart(self)
gameLoop: while square != finalSquare {
let diceRoll = dice.roll()
delegate?.game(self, didStartNewTurnWithDiceRoll: diceRoll)
switch square + diceRoll {
case finalSquare:
break gameLoop
case let newSquare where newSquare > finalSquare:
continue gameLoop
default:
square += diceRoll
square += board[square]
}
}
delegate?.gameDidEnd(self)
}
}
class DiceGameTracker: DiceGameDelegate {
var numberOfTurns = 0
func gameDidStart(game: DiceGame) {
numberOfTurns = 0
if game is SnakesAndLadders {
println("Started a new game of Snakes and Ladders")
}
println("The game is using a \(game.dice.sides)-sided dice")
}
func game(game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int) {
++numberOfTurns
println("Rolled a \(diceRoll)")
}
func gameDidEnd(game: DiceGame) {
println("The game lasted for \(numberOfTurns) turns")
}
}
//Imagine identical functions with parameters with different types
//Ex.
func swapTwoStrings(a:inout String, b:inout String) {
let temporaryA = a
a = b
b = temporaryA
}
func swapTwoInts(a:inout Int, b:inout Int) {
let temporaryA = a
a = b
b = temporaryA
}
//You would call it like this
var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
println("someInt is now \(someInt), and anotherInt is now \(anotherInt)")
// prints "someInt is now 107, and anotherInt is now 3"
//USING GENERICS
func swapTwoValues<T>(a:inout T, b:inout T) {
let temporaryA = a
a = b
b = temporaryA
}
//CALL IT
In the two examples below, T is inferred to be String
var someString = "hello"
var anotherString = "world"
swapTwoValues(&someString, &anotherString)
// someString is now "world", and anotherString is now "hello"
//Can be used in variables too
var items = [Int]()
//can be
var items = [T]()
//Or returning from a function
func pop() -> T
//EXTENDING A GENERIC TYPE
extension Stack {
var topItem: T? {
return items.isEmpty ? nil : items[items.count - 1]
}
}
/*
The most common way to define a closure function is with the {} syntax known as closure expressions,
which is lightweight syntax to introduce functions without a full declaration and name.
*/
//https://medium.com/@abhimuralidharan/functional-swift-all-about-closures-310bc8af31dd
//Closure expression syntax has the following general form:
//The start of the closure’s body is introduced by the in keyword.
{ (parameter list) -> return type in
statements
}
//Example:
//Normak function
func addTwoInts(a: Int,b: Int) -> Int
//It is similar to the function declaration but with no name and the parameters and return type are written inside the curly braces, not outside of them.
var addTwoInts = { (a: Int,b: Int) -> Int in
return a + b
}
//Calling the function
addTwoInts(1, 2)
var simpleFunction : () -> Void = { print("Hello world") }
//INLINE CLOSUES
//Instead of:
func addTwoInts(_ a: Int, _ b: Int) -> Int {
return a + b
}
printMathResult(addTwoInts, 3, 5)
//You can write:
printMathResult({ (a: Int,b: Int) -> Int in return a + b}, 3, 5)
//This leads to sorter code and it is preferable.
//Example: It’s just a function definition that takes a function as an argument
func hardProcessingWithString(input: String, completion: (result: String) -> Void) {
completion(“we finished!”)
}
//The completion closure here is just a function that takes a string and returns void
//Using it, using the somewhat strange {() in } syntax
hardProcessingWithString(“commands”) {
(result: String) in
println(“got back: (result)“)
}
//We magically have the results that we passed in the closure
/*********
closures take 3 forms:
Global functions are closures that have a name and do not capture any values.
Nested functions are closures that have a name and can capture values from their enclosing function.
Closure expressions are unnamed closures written in a lightweight syntax that can capture values
from their surrounding context.
**********/
//SORTED METHOD
//The sorted function for an array of Strings (by Apple)
func sorted(by areInIncreasingOrder: (String, String) -> Bool) -> [String]
let names = ["Animals", "Pulse", "The Wall", "Echoes", "Shine On"]
//sorting function by String length
func byStringLength(a: String, b: String) -> Bool {
return a.count < b.count
}
names.sorted(by: byStringLength)
//using sorting closure expression
names.sorted(by: { (a: String, b: String) -> Bool in return a.count < b.count })
//The parameters types can be inferred
names.sorted(by: { (a, b) in return a.count < b.count })
//Swift also does not require you to have parenthesis around the arguments in this form
names.sorted(by: { a, b in return a.count < b.count })
//It is always possible to infer the parameter types and return type when passing a closure to a function or method
//as an inline closure expression.
//As a result, you never need to write an inline closure in its fullest form when the closure is used as a function or
//method argument.
//Single-expression closures can implicitly return the result of their single expression
//by omitting the return keyword from their declaration
names.sorted(by: { a, b in a.count < b.count })
/*
Swift automatically provides shorthand argument names to inline closures,
which can be used to refer to the values of the closure’s arguments by the names $0, $1, $2, and so on.
If you use these shorthand argument names within your closure expression,
you can omit the closure’s argument list from its definition, and the number and type of the shorthand argument names will be inferred from the expected function type.
The in keyword can also be omitted, because the closure expression is made up entirely of its body:
*/
names.sorted(by: { $0.count < $1.count })
//You can ignore arguments in the argument list with the wildcard character _.
names.sorted(by: { _, _ in return arcrandom()%2==1 })
//TRAILING CLOSURES
//If you need to pass a closure expression to a function as the function’s final argument and the closure expression is long,
//it can be useful to write it as a trailing closure instead
func someFunctionThatTakesAClosure(closure: () -> Void) {
// function body goes here
}
// Here's how you call this function without using a trailing closure:
someFunctionThatTakesAClosure(closure: {
// closure's body goes here
})
// Here's how you call this function with a trailing closure instead:
someFunctionThatTakesAClosure() {
// trailing closure's body goes here
}
//So for the above example
reversedNames = names.sorted() { $0 > $1 }
//If a closure expression is provided as the function or method’s only argument
//and you provide that expression as a trailing closure, you do not need to write a pair of parentheses ()
reversedNames = names.sorted { $0 > $1 }
//CAPTURING VALUES
//In Swift, the simplest form of a closure that can capture values is a nested function
//A nested function can capture any of its outer function’s arguments
//and can also capture any constants and variables defined within the outer function.
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
return incrementer
}
var incrementByTen = makeIncrementer(forIncrement: 10)
//Closure attributes
//@nonescaping, @escaping
/*
The @noescaping Basically what it does is tell the compiler that the passed-in closure
can NOT be used outside the body of the function it's passed into.
Since Swift 3.0 all closures are nonescaping. So if you put nothing it's noescaping
So in nonescaping closures you can not store them inside of variables to be used anywhere/anytime that you want.
Or the compiler won't let you.
The resut of the noescaping as default is you are not required to put "self" inside the closure
unless you define it @escaping
*/
//Example:
//http://static1.squarespace.com/static/5592eb03e4b051859f0b377f/t/569c91ae40667a727eeb8ecf/1453101487649/?format=2500w
//Example:
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
print("before append")
completionHandlers.append(completionHandler)
print("after append")
}
someFunctionWithEscapingClosure {
print("Hello")
}
let hndl = completionHandlers[0]
hndl()
//If it was not tagged as @escaping you could not call hndl() because it is called outside of the block
/// ERROR HANDLING
enum VendingMachineError: Error {
case invalidSelection
case insuficientFunds(coinsNeeded:Int)
case outOfStock
case outOfOrder
}
//throw VendingMachineError.invalidSelection
//throw VendingMachineError.insuficientFunds(coinsNeeded: 5)
func buy(product:String,coins:Int) throws {
if coins<10 {
throw VendingMachineError.insuficientFunds(coinsNeeded: 6)
}
}
func buyWithChange(product:String,coins:Int) throws -> Int {
if coins<10 {
throw VendingMachineError.insuficientFunds(coinsNeeded: 6)
}
return 1
}
//This is an unhandled exception tha causes crash
//buy(product: "test", coins: 0)
func buyMilk() throws {
try buy(product: "Milk", coins: 2)
}
do {
//throw VendingMachineError.insuficientFunds(coinsNeeded: 1)
try buyMilk()
} catch VendingMachineError.invalidSelection {
print("invalidSelection")
} catch VendingMachineError.outOfStock {
print("outOfStock")
} catch VendingMachineError.outOfOrder {
print("outOfOrder")
} catch VendingMachineError.insuficientFunds(let coinsNeeded) where coinsNeeded>5 {
print("You dont have enought coins(5) \(coinsNeeded)")
} catch VendingMachineError.insuficientFunds(let coinsNeeded) {
print("You dont have enought coins \(coinsNeeded)")
}
//OR
let myChange = try? buyWithChange(product: "CocaCola", coins: 5)
//OR
var c = try! buyWithChange(product: "CocaCola", coins: 11)
//Example
extension Vector2D {
static func += (left: inout Vector2D, right: Vector2D) {
left = left + right
}
}
//Usage
var original = Vector2D(x: 1.0, y: 2.0)
let vectorToAdd = Vector2D(x: 3.0, y: 4.0)
original += vectorToAdd
//Example
extension Vector2D {
static func == (left: Vector2D, right: Vector2D) -> Bool {
return (left.x == right.x) && (left.y == right.y)
}
static func != (left: Vector2D, right: Vector2D) -> Bool {
return !(left == right)
}
}
let twoThree = Vector2D(x: 2.0, y: 3.0)
let anotherTwoThree = Vector2D(x: 2.00, y: 3.0)
if twoThree == anotherTwoThree {
print("These two vectors are equivalent.")
}
//If the operator does not exist you have to declare it
//Example
prefix operator +++
extension Vector2D {
static prefix func +++ (vector: inout Vector2D) -> Vector2D {
vector += vector
return vector
}
}
var toBeDoubled = Vector2D(x: 1.0, y: 4.0)
let afterDoubling = +++toBeDoubled
/*
Subscripts are shortcuts for accessing elements from a collection, sequence or list.
They are used to set and retrieve values by index without needing separate methods for setting and retrieval.
A type can have multiple subscripts and subscripts can have more than one dimension.
To access elements via subscripts write one or more values between square brackets after the instance name
*/
//Example subscript for String detection & replacement.
extension String {
subscript(pattern: String) -> String? {
get {
let range = self.rangeOfString(pattern)
if !range.isEmpty {
return pattern
} else {
return nil
}
}
set(replacement) {
let range = self.rangeOfString(pattern)
if !range.isEmpty {
self = self.stringByReplacingCharactersInRange(range, withString: replacement!)
}
}
}
}
var aString = "We ❤ Swift"
print(aString["❤"]) // ❤
print(aString["Objective-C"]) // nil
aString["We"] = "You"
print(aString) // You ❤ Swift
//You can make subscripts for your own classes
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment