-
Notifications
You must be signed in to change notification settings - Fork 4
Swift Xcode
Cmd 0: hide/show left panel
Cmd 1: show project navigator
Cmd Option 0: hide/show right panel
Option left-click a file: Open file side by side
Cmd Shift F: Search within file / file name
Cmd Shift O: Open file
Option left-click a variable: Check its type
let greeting = "Hello world!"
let firstChar = greeting[greeting.startIndex] // 'H'
let secondCharIndex = greeting.index(greeting.startIndex, offsetBy: 1)
let secondChar = greeting[secondCharIndex] //'e'
let lastCharIndex = greeting.index(before: greeting.endIndex)
let lastChar = greeting[lastCharIndex] //'!'
let secondLastCharIndex = greeting.index(greeting.endIndex, offsetBy: -2)
let secondLastChar = greeting[secondLastCharIndex] //'d'
var welcome = "hello"
welcome.insert("!", at: welcome.endIndex)
// welcome now equals "hello!"
welcome.insert(contentsOf: " there", at: welcome.index(before: welcome.endIndex))
// welcome now equals "hello there!"
welcome.remove(at: welcome.index(before: welcome.endIndex))
// welcome now equals "hello there"
let range = welcome.index(welcome.endIndex, offsetBy: -6)..<welcome.endIndex
welcome.removeSubrange(range)
// welcome now equals "hello"
let greeting2 = "Hello,world!"
let index = greeting2.firstIndex(of: ",") ?? greeting2.endIndex
let excludeIndexSubstring = greeting2[..<index] //"Hello" of type String.SubSequence
let includeIndexSubstring = greeting2[...index] //"Hello," of type String.SubSequence
let newString = String(excludeIndexSubstring) //"Hello" of type String
var someInts = [Int]()
someInts.append(3)
var threeDoubles = Array(repeating: 0.0, count: 3)
// threeDoubles is of type [Double], and equals [0.0, 0.0, 0.0]
var anotherThreeDoubles = Array(repeating: 2.5, count: 3)
// anotherThreeDoubles is of type [Double], and equals [2.5, 2.5, 2.5]
var sixDoubles = threeDoubles + anotherThreeDoubles
// sixDoubles is inferred as [Double], and equals [0.0, 0.0, 0.0, 2.5, 2.5, 2.5]
var shoppingList = ["Eggs", "Milk"]
shoppingList += ["Baking Powder"]
shoppingList += ["Chocolate Spread", "Cheese", "Butter"]
shoppingList.insert("Maple Syrup", at: 0)
let mapleSyrup = shoppingList.remove(at: 0)
let apples = shoppingList.removeLast()
for item in shoppingList {
print(item)
}
for (index, value) in shoppingList.enumerated() {
print("Item \(index + 1): \(value)")
}
A type must be hashable in order to be stored in a set
var letters = Set<Character>()
let count = letters.count
let isEmpty = letters.isEmpty
letters.insert("a")
letters = [] // letters is now an empty set, but is still of type Set<Character>
var favoriteGenres: Set<String> = ["Rock", "Classical", "Hip hop"]
var favoriteGenres2: Set = ["Rock", "Classical", "Hip hop"] //Simpler than above line
let removedGenre = favoriteGenres.remove("Rock")
let contains = favoriteGenres.contains("Funk")
for genre in favoriteGenres.sorted() {
print("\(genre)")
}
let oddDigits: Set = [1, 3, 5, 7, 9]
let evenDigits: Set = [0, 2, 4, 6, 8]
let singleDigitPrimeNumbers: Set = [2, 3, 5, 7]
oddDigits.union(evenDigits).sorted() // [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
oddDigits.intersection(evenDigits).sorted() // []
oddDigits.subtracting(singleDigitPrimeNumbers).sorted() // [1, 9]
oddDigits.symmetricDifference(singleDigitPrimeNumbers).sorted() // [1, 2, 9]
let houseAnimals: Set = ["🐶", "🐱"]
let farmAnimals: Set = ["🐮", "🐔", "🐑", "🐶", "🐱"]
let cityAnimals: Set = ["🐦", "🐭"]
houseAnimals.isSubset(of: farmAnimals) // true
farmAnimals.isSuperset(of: houseAnimals) // true
farmAnimals.isDisjoint(with: cityAnimals) // true. To determine whether two sets have no values in common.
A dictionary Key type must conform to the Hashable protocol, like a set’s value type.
var namesOfIntegers = [Int: String]()
namesOfIntegers[16] = "sixteen"
namesOfIntegers = [:] // namesOfIntegers is once again an empty dictionary of type [Int: String]
let count = namesOfIntegers.count
let isEmpty = namesOfIntegers.isEmpty
var airports: [String: String] = ["YYZ": "Toronto Pearson", "DUB": "Dublin"]
var airports2 = ["YYZ": "Toronto Pearson", "DUB": "Dublin"] //Simpler format than above line
airports["LHR"] = "London"
let oldValue = airports.updateValue("Dublin Airport", forKey: "DUB") //oldValue is of type string?
airports["APL"] = nil //Remove a key-value pair
let removedValue = airports.removeValue(forKey: "DUB") //Another way to remove a key-value pair
for (airportCode, airportName) in airports {
print("\(airportCode): \(airportName)")
}
for airportCode in airports.keys {
print("Airport code: \(airportCode)")
}
for airportName in airports.values {
print("Airport name: \(airportName)")
}
let airportCodes = [String](airports.keys) // airportCodes is ["LHR", "YYZ"]
let airportNames = [String](airports.values) // airportNames is ["London Heathrow", "Toronto Pearson"]
// Open range (i.e. not include 60)
let minuteInterval = 5
let minutes = 60
for tickMark in stride(from: 0, to: minutes, by: minuteInterval) {
print (tickMark)// render the tick mark every 5 minutes (0, 5, 10, 15 ... 45, 50, 55)
}
//Close range (i.e. include 12)
let hours = 12
let hourInterval = 3
for tickMark in stride(from: 3, through: hours, by: hourInterval) {
print (tickMark)
}
var a = 0
var b = 5
while a < b {
print (a)
if a == 3 {
break
}
a += 1
}
a = 0
repeat {
print (a)
a += 1
}while a < b
switch statements in Swift do not fall through the bottom of each case and into the next one by default.
let approximateCount = 62
let countedThings = "moons orbiting Saturn"
let naturalCount: String
switch approximateCount {
case -1: fallthrough
case 0:
naturalCount = "no"
case 1..<5:
naturalCount = "a few"
case 5..<12:
naturalCount = "several"
case 12..<100:
naturalCount = "dozens of"
case 100..<1000:
naturalCount = "hundreds of"
default:
naturalCount = "many"
}
print("There are \(naturalCount) \(countedThings).")
Tuple
let somePoint = (1, 1)
switch somePoint {
case (0, 0):
print("\(somePoint) is at the origin")
case (_, 0):
print("\(somePoint) is on the x-axis")
case (0, _):
print("\(somePoint) is on the y-axis")
case (-2...2, -2...2):
print("\(somePoint) is inside the box")
default:
print("\(somePoint) is outside of the box")
}
// Prints "(1, 1) is inside the box"
Tuple let
let anotherPoint = (2, 0)
switch anotherPoint {
case (let x, 0):
print("on the x-axis with an x value of \(x)")
case (0, let y):
print("on the y-axis with a y value of \(y)")
case (let x, let y):
print("somewhere else at (\(x), \(y))")
}
where
let yetAnotherPoint = (1, -1)
switch yetAnotherPoint {
case let (x, y) where x == y:
print("(\(x), \(y)) is on the line x == y")
case let (x, y) where x == -y:
print("(\(x), \(y)) is on the line x == -y")
case let (x, y):
print("(\(x), \(y)) is just some arbitrary point")
}
label name: while condition {
statements
}
guard always has an else clause
func greet(person: [String: String]) {
guard let name = person["name"] else {
return
}
print("Hello \(name)!")
guard let location = person["location"] else {
print("I hope the weather is nice near you.")
return
}
print("I hope the weather is nice in \(location).")
}
greet(person: ["name": "John"])
// Prints "Hello John!"
// Prints "I hope the weather is nice near you."
greet(person: ["name": "Jane", "location": "Cupertino"])
// Prints "Hello Jane!"
// Prints "I hope the weather is nice in Cupertino."
if #available(iOS 10, macOS 10.12, *) {
// Use iOS 10 APIs on iOS, and use macOS 10.12 APIs on macOS
} else {
// Fall back to earlier iOS and macOS APIs
}
func greet(p person: String) -> String {
return "Hello, " + person + "!"
}
print(greet(p : "Anna"))
func greeting(_ person: String) -> String {
return "Hello, " + person + "!"
}
print (greeting("Tom"))
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)
}
let bounds = minMax(array: [8, -6, 2, 109, 3, 71])
print("min is \(bounds.min) and max is \(bounds.max)")
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)
}
if let bounds = minMax(array : [Int]()) {
print("min is \(bounds.min) and max is \(bounds.max)")
}else{
print("Array is empty")
}
func someFunction(parameterWithoutDefault: Int, parameterWithDefault: Int = 12) {
print ("\(parameterWithoutDefault), \(parameterWithDefault)")
}
someFunction(parameterWithoutDefault: 3, parameterWithDefault: 6) // parameterWithDefault is 6
someFunction(parameterWithoutDefault: 4)
func arithmeticMean(_ numbers: Double...) -> Double {
var total: Double = 0
for number in numbers {
total += number
}
return total / Double(numbers.count)
}
arithmeticMean(1, 2, 3, 4, 5) // returns 3.0, which is the arithmetic mean of these five numbers
arithmeticMean(3, 8.25, 18.75) // returns 10.0, which is the arithmetic mean of these three numbers
func swapTwoInts(_ a: inout Int, _ b: inout Int) {
let temporaryA = a
a = b
b = temporaryA
}
var someInt = 3
var anotherInt = 107
swapTwoInts(&someInt, &anotherInt)
print("someInt is now \(someInt), and anotherInt is now \(anotherInt)") // Prints "someInt is now 107, and anotherInt is now 3"
func addTwoInts(_ a: Int, _ b: Int) -> Int {
return a + b
}
func multiplyTwoInts(_ a: Int, _ b: Int) -> Int {
return a * b
}
var mathFunction: (Int, Int) -> Int = addTwoInts
print("Result: \(mathFunction(2, 3))") // Prints "Result: 5"
mathFunction = multiplyTwoInts
print("Result: \(mathFunction(2, 3))") // Prints "Result: 6"
let anotherMathFunction = addTwoInts //Type can be inferred
Pass function as parameter types
func printMathResult(_ mathFunction: (Int, Int) -> Int, _ a: Int, _ b: Int) {
print("Result: \(mathFunction(a, b))")
}
printMathResult(addTwoInts, 3, 5) // Prints "Result: 8"
//Pass function as return types
func stepForward(_ input: Int) -> Int {
return input + 1
}
func stepBackward(_ input: Int) -> Int {
return input - 1
}
func chooseStepFunction(backward: Bool) -> (Int) -> Int {
return backward ? stepBackward : stepForward
}
var currentValue = 3
let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
print("Counting to zero:")
//Counting to zero:
while currentValue != 0 {
print("\(currentValue)... ")
currentValue = moveNearerToZero(currentValue)
}
print("zero!")
func chooseStepFunction(backward: Bool) -> (Int) -> Int {
func stepForward(input: Int) -> Int { return input + 1 }
func stepBackward(input: Int) -> Int { return input - 1 }
return backward ? stepBackward : stepForward
}
var currentValue = -4
let moveNearerToZero = chooseStepFunction(backward: currentValue > 0)
// moveNearerToZero now refers to the nested stepForward() function
while currentValue != 0 {
print("\(currentValue)... ")
currentValue = moveNearerToZero(currentValue)
}
print("zero!")
// -4...
// -3...
// -2...
// -1...
// zero!
let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
func backward(_ s1: String, _ s2: String) -> Bool {
return s1 > s2
}
var reversedNames = names.sorted(by: backward) // reversedNames is equal to ["Ewa", "Daniella", "Chris", "Barry", "Alex"]
reversedNames = names.sorted(by: {(s1: String, s2: String) -> Bool in
return s1 > s2
})
reversedNames = names.sorted(by: {s1, s2 in return s1 > s2})
reversedNames = names.sorted(by: {s1, s2 in s1 > s2})
reversedNames = names.sorted(by: {$0 > $1})
reversedNames = names.sorted(by: >)
If you need to pass a closure expression to a function as the function’s final argument, it can be useful to write it as a trailing closure instead. A trailing closure is written after the function call’s parentheses, even though it is still an argument to the function.
reversedNames = names.sorted() { $0 > $1 }
reversedNames = names.sorted { $0 > $1 } //If closure is the only argument
Here’s how you can use the map(_:) method with a trailing closure to convert an array of Int values into an array of String values. The array [16, 58, 510] is used to create the new array ["OneSix", "FiveEight", "FiveOneZero"]:
let digitNames = [
0: "Zero", 1: "One", 2: "Two", 3: "Three", 4: "Four",
5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"
]
let numbers = [16, 58, 510]
let closure = { (number : Int) -> String in
var number = number
var output = ""
repeat {
output = digitNames[number % 10]! + output
number /= 10
} while number > 0
return output
}
let strings = numbers.map(closure)
let strings2 = numbers.map{ (number : Int) -> String in
var number = number
var output = ""
repeat {
output = digitNames[number % 10]! + output
number /= 10
} while number > 0
return output
}
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 incrementBy5 = makeIncrementer(forIncrement: 5)
print(incrementBy5()) // 5
print(incrementBy5()) // 10
var anotherIncrementBy5 = incrementBy5
print(anotherIncrementBy5()) // 15
var incrementBy10 = makeIncrementer(forIncrement: 10)
print (incrementBy10()) // 10
print (incrementBy10()) // 20
print (incrementBy10()) // 30
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) { //completionHandler will be called later in the code after someFunctionWithEscapingClosure() is called
completionHandlers.append(completionHandler)
}
func someFunctionWithNonescapingClosure(closure: () -> Void) {
closure()
}
class SomeClass {
var x = 10
func doSomething() {
someFunctionWithEscapingClosure {
print ("set x to 100")
self.x = 100
}
someFunctionWithNonescapingClosure {
print ("set x to 200")
x = 200
}
}
}
let instance = SomeClass()
instance.doSomething()
print(instance.x) //200 , "set x to 100" is not printed
completionHandlers.first?()
print(instance.x) //100
var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
print(customersInLine.count) // Prints "5"
let customerProvider = { customersInLine.remove(at: 0) } //type is () -> String
print(customersInLine.count) // Prints "5"
print("Now serving \(customerProvider())!") // Prints "Now serving Chris!"
print(customersInLine.count) // Prints "4"
// customersInLine is ["Ewa", "Barry", "Daniella"]
func serve(customer customerProvider: () -> String) {
print("Now serving \(customerProvider())!")
}
serve(customer: {customersInLine.remove(at: 0)}) // Prints "Now serving Alex!"
// customersInLine is ["Ewa", "Barry", "Daniella"]
func serve(customer customerProvider: @autoclosure () -> String) {
print("Now serving \(customerProvider())!")
}
serve(customer: {customersInLine.remove(at: 0)}) // Prints "Now serving Ewa!"
serve(customer: customersInLine.remove(at: 0)) // {} can be omitted due to @autoclosure. Prints "Now serving Barry!"
enum Beverage: CaseIterable {
case coffee, tea, juice
}
let numberOfChoices = Beverage.allCases.count
print("\(numberOfChoices) beverages available")
for beverage in Beverage.allCases {
print(beverage)
}
enum Barcode {
case upc(Int, Int, Int, Int)
case qrCode(String)
}
var productBarcode = Barcode.upc(8, 85909, 51226, 3)
switch productBarcode {
case .upc(let numberSystem, let manufacturer, let product, let check):
print("UPC: \(numberSystem), \(manufacturer), \(product), \(check).")
case .qrCode(let productCode):
print("QR code: \(productCode).")
}
enum Animal: String {
case cat = "tom"
case mouse = "jerry"
case duck = "donald"
}
let aMouse = Animal.mouse
print(aMouse.rawValue)
enum Planet: Int, CaseIterable { //Implict raw values. the implicit value for each case is one more than the previous case.
case mercury = 100, venus, earth, mars, jupiter, saturn, uranus, neptune
}
for p in Planet.allCases{
print ("\(p) = \(p.rawValue)") // 100 - 108
}
let possiblePlanet = Planet(rawValue: 102) //earth
enum ArithmeticExpression {
case number(Int)
indirect case addition(ArithmeticExpression, ArithmeticExpression)
indirect case multiplication(ArithmeticExpression, ArithmeticExpression)
}
let five = ArithmeticExpression.number(5)
let four = ArithmeticExpression.number(4)
let sum = ArithmeticExpression.addition(five, four)
let product = ArithmeticExpression.multiplication(sum, ArithmeticExpression.number(2))
func evaluate(_ expression: ArithmeticExpression) -> Int {
switch expression {
case let .number(value):
return value
case let .addition(left, right):
return evaluate(left) + evaluate(right)
case let .multiplication(left, right):
return evaluate(left) * evaluate(right)
}
}
print(evaluate(product))
Structs are value types while Classes are reference types
var struct1 = MyStruct()
struct1.name = "old name"
var struct2 = struct1
struct2.name = "new name"
//Now struct1.name = "old name"
var class1 = MyClass()
class1.name = "old name"
var class2 = class1
class2.name = "new name"
//Now class1.name = "new name"
Note that identical to (represented by three equals signs, or ===) doesn’t mean the same thing as equal to (represented by two equals signs, or ==).
let class1 = MyClass(name: "name")
let class2 = class1
let class3 = MyClass(name: "name")
class1 === class2 //True
class1 === class3 //False
class1 == class3 //Maybe True TODO
class DataImporter {
var filename : String
init() {
print ("Init Data Importer")
filename = "data.txt"
}
// the DataImporter class would provide data importing functionality here
}
class DataManager {
lazy var importer = DataImporter()
//var importer = DataImporter()
func doStuff(){
print ("Do stuff")
}
func doImport(){
print (importer.filename)
}
}
let manager = DataManager()
manager.doStuff()
manager.doImport()
// Prints Do stuff, Init Data Importer, data.txt with lazy keyword
// Prints Init Data Importer, Do stuff, data.txt without lazy keyword
struct AlternativeRect {
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 {
origin.x = newValue.x - (size.width / 2)
origin.y = newValue.y - (size.height / 2)
}
}
}
class StepCounter {
var totalSteps: Int = 0 {
willSet {
print("About to set totalSteps to \(newValue), add to \(totalSteps)")
}
didSet {
if totalSteps > oldValue {
print("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
struct SomeStructure {
static var storedTypeProperty = "Some value."
static var computedTypeProperty: Int {
return 1
}
}
print(SomeStructure.storedTypeProperty) // Prints "Some value."
SomeStructure.storedTypeProperty = "Another value."
print(SomeStructure.storedTypeProperty) // Prints "Another value."
print(SomeStructure.computedTypeProperty) // Prints "1"
SomeStructure.computedTypeProperty = 6 //Compilation error as computedTypeProperty is a read only property