Last active
September 9, 2022 12:12
-
-
Save lamprosg/eeda157a077c75b2a50e to your computer and use it in GitHub Desktop.
(iOS) Swift tutorial
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
https://github.com/apple/swift-evolution |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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) | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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. | |
/***********************/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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") | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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] | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/// 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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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