├── 10. Properties.playground ├── contents.xcplayground ├── section-1.swift └── timeline.xctimeline ├── 11. Methods.playground ├── contents.xcplayground ├── section-1.swift └── timeline.xctimeline ├── 12. Subscripts.playground ├── contents.xcplayground ├── section-1.swift └── timeline.xctimeline ├── 13. Inheritance.playground ├── contents.xcplayground ├── section-1.swift └── timeline.xctimeline ├── 14a. Initialization.playground ├── contents.xcplayground ├── section-1.swift └── timeline.xctimeline ├── 14b. Initializer Chaining.playground ├── contents.xcplayground ├── section-1.swift └── timeline.xctimeline ├── 15. Deinitialization.playground ├── contents.xcplayground ├── section-1.swift └── timeline.xctimeline ├── 16. ARC.playground ├── contents.xcplayground ├── section-1.swift └── timeline.xctimeline ├── 17. Optional Chaining.playground ├── contents.xcplayground ├── section-1.swift └── timeline.xctimeline ├── 18. Type Casting.playground ├── contents.xcplayground ├── section-1.swift └── timeline.xctimeline ├── 19. Nested Types.playground ├── contents.xcplayground ├── section-1.swift └── timeline.xctimeline ├── 1a. The Basics.playground ├── contents.xcplayground ├── section-1.swift └── timeline.xctimeline ├── 1b. Type alliases.playground ├── contents.xcplayground ├── section-1.swift └── timeline.xctimeline ├── 1c. Tuples.playground ├── contents.xcplayground ├── section-1.swift └── timeline.xctimeline ├── 1d. Optionals.playground ├── contents.xcplayground ├── section-1.swift └── timeline.xctimeline ├── 1e. Assertions.playground ├── contents.xcplayground ├── section-1.swift └── timeline.xctimeline ├── 2. Basic operations.playground ├── contents.xcplayground ├── section-1.swift └── timeline.xctimeline ├── 20. Extensions.playground ├── contents.xcplayground ├── section-1.swift └── timeline.xctimeline ├── 21. Protocols.playground ├── contents.xcplayground ├── section-1.swift └── timeline.xctimeline ├── 22. Generics.playground ├── contents.xcplayground ├── section-1.swift └── timeline.xctimeline ├── 23. Advanced Operators.playground ├── contents.xcplayground ├── section-1.swift └── timeline.xctimeline ├── 3. Strings and Characters.playground ├── contents.xcplayground ├── section-1.swift └── timeline.xctimeline ├── 4a. Arrays.playground ├── contents.xcplayground ├── section-1.swift └── timeline.xctimeline ├── 4b. Dictionaries.playground ├── contents.xcplayground ├── section-1.swift └── timeline.xctimeline ├── 5. Control Flow.playground ├── contents.xcplayground ├── section-1.swift └── timeline.xctimeline ├── 6. Functions.playground ├── contents.xcplayground ├── section-1.swift └── timeline.xctimeline ├── 7. Closures.playground ├── contents.xcplayground ├── section-1.swift └── timeline.xctimeline ├── 8. Enumerations.playground ├── contents.xcplayground ├── section-1.swift └── timeline.xctimeline ├── 9. Classes and Structures.playground ├── contents.xcplayground ├── section-1.swift └── timeline.xctimeline ├── 99. Not The End.playground ├── contents.xcplayground ├── section-1.swift └── timeline.xctimeline └── README.md /10. Properties.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /10. Properties.playground/section-1.swift: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------------------------ 2 | // Things to know: 3 | // 4 | // * Properties store values in classes, structures and enumerations. 5 | // ------------------------------------------------------------------------------------------------ 6 | 7 | // Here's a structure with a couple simple stored properties: 8 | struct FixedLengthRange 9 | { 10 | var firstValue: Int 11 | let length: Int 12 | } 13 | 14 | // Structures must have all of their properties initialized upon instantiation. 15 | // 16 | // This won't compile since the struct includes properties that havn't been initialized with 17 | // default values: 18 | // 19 | // var anotherRangeOfThreeItems = FixedLengthRange() 20 | // 21 | // In order to instantiate a FixedLengthRange struct, we must use the memberwise initializer. Note 22 | // that this will initialize a constant property and a variable property inside the struct: 23 | var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3) 24 | rangeOfThreeItems.firstValue = 6 25 | 26 | // ------------------------------------------------------------------------------------------------ 27 | // Lazy Stored Properties 28 | // 29 | // A Lazy Stored Property is a value that is not calculated until its first use. 30 | // 31 | // They are declared using the "lazy" attribute and may not be constant. 32 | // 33 | // Global and local variables are all lazy, except that they don't need the lazy attribute. 34 | // 35 | // Here, we'll define a couple classes to showcase Lazy Stored Properties. In this example, let's 36 | // assume that DataImporter is a time-consuming process, and as such, we will want to use a lazy 37 | // stored property whenever we use it. This way, if we end up never using it, we never pay the 38 | // penalty of instantiating it. 39 | class DataImporter 40 | { 41 | var filename = "data.txt" 42 | } 43 | 44 | class DataManager 45 | { 46 | lazy var importer = DataImporter() 47 | var data = [String]() 48 | } 49 | 50 | // Now let's instantiate the data manager and add some simple data to the class: 51 | let manager = DataManager() 52 | manager.data.append("Some data") 53 | manager.data.append("Some more data") 54 | 55 | // Notice how we haven't used the importer yet, so it is nil: 56 | manager 57 | 58 | // So now let's access it: 59 | manager.importer.filename 60 | 61 | // And now we see the importer was created: 62 | manager 63 | 64 | // ------------------------------------------------------------------------------------------------ 65 | // Computed Properties 66 | // 67 | // Computed properties don't store data, but rather use getters and setters for accessing values 68 | // that are computed up on request. 69 | // 70 | // Computed Properties are available for global as well as local variables. 71 | // 72 | // We'll start with a few structures that we'll use to show how computed properties work. 73 | struct Point 74 | { 75 | var x = 0.0, y = 0.0 76 | } 77 | struct Size 78 | { 79 | var width = 0.0, height = 0.0 80 | } 81 | 82 | // The following structure includes a computed property with a Point type named 'center'. Notice 83 | // that 'center' is variable (not constant) which is a requirement of computed properties. 84 | // 85 | // Every computed property must have a getter, but does not need a setter. If we provide a setter, 86 | // we need to know the new value being assigned to the computed property. We've called this 87 | // value 'newCenter'. Note that this value does not have a type annotation because the computed 88 | // property already knows that it is a Point type. Providing a type annotation would be an error. 89 | struct Rect 90 | { 91 | var origin = Point() 92 | var size = Size() 93 | var center: Point 94 | { 95 | get 96 | { 97 | let centerX = origin.x + (size.width / 2) 98 | let centerY = origin.y + (size.height / 2) 99 | return Point(x: centerX, y: centerY) 100 | } 101 | set(newCenter) 102 | { 103 | origin.x = newCenter.x - (size.width / 2) 104 | origin.y = newCenter.y - (size.height / 2) 105 | } 106 | } 107 | } 108 | 109 | // Here, we'll create a square from our Rect structure 110 | var square = Rect(origin: Point(x: 0.0, y: 0.0), size: Size(width: 10.0, height: 10.0)) 111 | 112 | // We can now get the center point, computed from the Rect's origin and size. Being a computed 113 | // property, we can treat it just like any other peroperty. 114 | let initialSquareCenter = square.center 115 | 116 | // Since we provided a setter, we can also set the center point as if it is a stored property. 117 | // This will effectively update the Rect's origin and size based on the specified center point. 118 | square.center = Point(x: 15, y: 15) 119 | 120 | // We can see that the origin has been updated from (0, 0) to (10, 10): 121 | square.origin 122 | 123 | // Shorthand Setter Declaration 124 | // 125 | // The computed property's setter from the Rect structure provided a parameter on the setter named 126 | // 'newCenter'. If we don't specify this parameter, Swift will automatically generate an input 127 | // value named 'newValue' for us. 128 | // 129 | // Here, AlternativeRect is the same declaration as Rect above, except that the setter uses 130 | // Swift's default setter value, 'newValue': 131 | struct AlternativeRect 132 | { 133 | var origin = Point() 134 | var size = Size() 135 | var center: Point 136 | { 137 | get 138 | { 139 | let centerX = origin.x + (size.width / 2) 140 | let centerY = origin.y + (size.height / 2) 141 | return Point(x: centerX, y: centerY) 142 | } 143 | set 144 | { 145 | origin.x = newValue.x - (size.width / 2) 146 | origin.y = newValue.y - (size.height / 2) 147 | } 148 | } 149 | } 150 | 151 | // We can also have a read-only computed property by simply omitting the setter: 152 | struct Cube 153 | { 154 | var width = 0.0, height = 0.0, depth = 0.0 155 | var volume: Double 156 | { 157 | get 158 | { 159 | return width * height * depth 160 | } 161 | } 162 | } 163 | 164 | // Alternatively, Swift allows us to shorten the syntax of a read-only computed property by 165 | // omitting the get{} construct and inserting the code for the getter directly into the property 166 | // declaration: 167 | struct AnotherCube 168 | { 169 | var width = 0.0, height = 0.0, depth = 0.0 170 | var volume: Double 171 | { 172 | return width * height * depth 173 | } 174 | } 175 | 176 | // Let's declare our structure and read the 'volume' property 177 | var cube = AnotherCube(width: 4, height: 5, depth: 2) 178 | cube.volume 179 | 180 | // Since the 'volume' property is read-only, if we tried to assign a value to it, it would 181 | // would generate a compiler error. 182 | // 183 | // The following line of code will not compile: 184 | // 185 | // cube.volume = 8.0 186 | 187 | // ------------------------------------------------------------------------------------------------ 188 | // Property Observers 189 | // 190 | // Property observers allow us to respond to changes in a property's value. We can declare an 191 | // observer that contains our code that is run just before a property is set (optionally allowing 192 | // us to alter the value being set) and an observer that is run just after a property has been 193 | // modified. 194 | // 195 | // Property observers are available for global as well as local variables. 196 | // 197 | // These observers are not called when a property is first initialized, but only when it changes. 198 | // 199 | // Similar to setters, each 'willSet' and 'didSet' will receive a parameter that represents (in 200 | // the case of 'willSet') the value about to be assigned to the property and (in the case of 201 | // 'didSet') the value that was previously stored in the property prior to modification. 202 | class StepCounter 203 | { 204 | var totalSteps: Int = 0 205 | { 206 | willSet(newTotalSteps) 207 | { 208 | "About to step to \(newTotalSteps)" 209 | } 210 | didSet(oldTotalSteps) 211 | { 212 | "Just stepped from \(oldTotalSteps)" 213 | } 214 | } 215 | } 216 | 217 | // Let's create an instance of StepCounter so we can try out our observer 218 | let stepCounter = StepCounter() 219 | 220 | // The following will first call 'willSet' on the 'totalSteps' property, followed by a change to 221 | // the value itself, followed by a call to 'didSet' 222 | stepCounter.totalSteps = 200; 223 | 224 | // Similar to setters, we can shorten our observers by omitting the parameter list for each. When 225 | // we co this, Swift automatically provides parameters named 'newValue' and 'oldValue' 226 | class StepCounterShorter 227 | { 228 | var totalSteps: Int = 0 229 | { 230 | willSet{ "About to step to \(newValue)" } 231 | didSet { "Just stepped from \(oldValue)" } 232 | } 233 | } 234 | 235 | // We can also override the value being set by modifying the property itself within the 'didSet' 236 | // observer. This only works in the 'didSet'. If you attempt to modify the property in 'willSet' 237 | // you will receive a compiler warning. 238 | // 239 | // Let's try wrapping our value to the range of 0...255 240 | class StepCounterShorterWithModify 241 | { 242 | var totalSteps: Int = 0 243 | { 244 | willSet{ "About to step to \(newValue)" } 245 | didSet { totalSteps = totalSteps & 0xff } 246 | } 247 | } 248 | var stepper = StepCounterShorterWithModify() 249 | stepper.totalSteps = 345 250 | stepper.totalSteps // This reports totalSteps is now set to 89 251 | 252 | // ------------------------------------------------------------------------------------------------ 253 | // Type Properties 254 | // 255 | // Until now, we've been working with Instance Properties and Instance Methods, which are 256 | // associated to an instance of a class, structure or enumeration. Each instance gets its own copy 257 | // of the property or method. 258 | // 259 | // Type properties are properties that are attached to the class, structure or enumeration's type 260 | // itself and not any specific instance. All instances of a type will share the same Type Property. 261 | // 262 | // These are similar to 'static' properties in C-like languages. 263 | // 264 | // For Value types (structs, enums) we use the 'static' keyword. For Class types with the 'class' 265 | // keyword. 266 | // 267 | // Type properties can be in the form of stored properties or computed properties. As with normal 268 | // stored or computed properties, all the same rules apply except that stored type properties must 269 | // always have a default value. This exception is necessary because the initializer (which we'll 270 | // learn about later) is associated to a specific instance and as such, cannot be reliable for 271 | // initializing type properties. 272 | // 273 | // Here is a class with a couple of type properties 274 | struct SomeStructure 275 | { 276 | static var storedTypeProperty = "some value" 277 | 278 | // Here's a read-only computed type property using the short-hand read-only syntax 279 | static var computedTypeProperty: Int { return 4 } 280 | } 281 | 282 | // Similarly, here's an enumeration with a couple of type properties 283 | enum SomeEnum 284 | { 285 | static let storedTypeProperty = "some value" 286 | 287 | static var computedTypeProperty: Int { return 4 } 288 | } 289 | 290 | // Classes are a little different in that they cannot contain stored type properties, but may 291 | // only contain computed type properties 292 | class SomeClass 293 | { 294 | // The following line won't compile because classes aren't allowed stored type properties 295 | // 296 | // class var storedTypeProperty = "some value" 297 | 298 | // This is read-only, but you can also do read/write 299 | class var computedTypeProperty: Int { return 4 } 300 | } 301 | 302 | -------------------------------------------------------------------------------- /10. Properties.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /11. Methods.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /11. Methods.playground/section-1.swift: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------------------------ 2 | // Things to know: 3 | // 4 | // * Methods can be in the form of Instance Methods, which apply to a given instance of a class 5 | // struct or enumeration and Type Methods, which apply to the type itself, like static methods 6 | // in C-like languages. 7 | // ------------------------------------------------------------------------------------------------ 8 | 9 | // Instance Methods 10 | // 11 | // Instance methods work on instances of a class, structure or enumeration. In order to call an 12 | // instance method, you must first instantiate the class, structure or enumeration and then place 13 | // the call on that instance. 14 | // 15 | // Here, we'll create a class with a single instance method: 16 | class SomeClass 17 | { 18 | func doSomething() 19 | { 20 | // ... 21 | } 22 | } 23 | 24 | // Since this should be pretty clear, let's jump into parameters for methods with internal and 25 | // external names. 26 | // 27 | // The defaults for external names are different for methods than they are for global functions. 28 | // 29 | // For methods, the default behavior is that the caller must always specify all but the first 30 | // external parameter name when calling the method. Member authors need not specify the external 31 | // names for those parameters as the default is to treat all parameters as if they had the "#" 32 | // specifier, which creates an external parameter name that mirrors the local parameter name. 33 | // 34 | // To override this default-external-names-for-second-and-beyond-parameters, specify an "_" as the 35 | // external parameter name for all but the first parameter. 36 | // 37 | // If you want the caller to also use external name for the first parameter, be sure to add your 38 | // own '#' symbol to the local name or specify the external name explicitly. 39 | // 40 | // Here's a class that exercises the various combinations of internal and external name usages: 41 | class Counter 42 | { 43 | var count = 0; 44 | 45 | // No parameters 46 | func increment() 47 | { 48 | count++ 49 | } 50 | 51 | // One parameter, no external parameter name needed by caller 52 | func incrementBy(amount: Int) 53 | { 54 | count += amount 55 | } 56 | 57 | // One parameter, overriding default behavior to requre caller to use external parameter name 58 | // on first (and only) parameter 59 | func addValueTo(value amount: Int) 60 | { 61 | count += amount 62 | } 63 | 64 | // Two parameters. Since no external names are specified, default behavior is implied: Caller 65 | // need not specify the first parameter's external name, but must specify all others: 66 | func addTwiceWithExternalImplied(first: Int, second: Int) 67 | { 68 | count += first 69 | count += second 70 | } 71 | 72 | // Two parameters. Using explicit external parameter names on all parameters to force caller 73 | // to use them for all parameters, including the first. 74 | func addTwiceWithExternalSpecified(a first: Int, b second: Int) 75 | { 76 | count += first 77 | count += second 78 | } 79 | 80 | // Two parameters. Using the external parameter shorthand ("#") to force caller to use 81 | // external parameter name on first parameter and defaulting to shared local/external names 82 | // for the rest. 83 | func addTwiceWithExternalSpecified2(#first: Int, second: Int) 84 | { 85 | count += first 86 | count += second 87 | } 88 | 89 | // Two parameters. Disabling all external names 90 | func addTwiceWithExternalSpecified3(first: Int, _ second: Int) 91 | { 92 | count += first 93 | count += second 94 | } 95 | } 96 | 97 | // Now let's see how we call each of those functions 98 | var counter = Counter() 99 | counter.increment() 100 | counter.incrementBy(4) 101 | counter.addValueTo(value: 4) 102 | counter.addTwiceWithExternalImplied(50, second: 4) 103 | counter.addTwiceWithExternalSpecified(a: 50, b: 4) 104 | counter.addTwiceWithExternalSpecified2(first: 10, second: 10) 105 | counter.addTwiceWithExternalSpecified3(10, 10) 106 | counter.count 107 | 108 | // The 'self' property refers to the current instance of a class, structure or enumeration. For 109 | // C++ developers, think of 'self' as 'this'. 110 | class Point 111 | { 112 | var x: Int = 10 113 | 114 | func setX(x: Int) 115 | { 116 | // Using self to disambiguate from the local parameter 117 | self.x = x 118 | } 119 | } 120 | 121 | // ------------------------------------------------------------------------------------------------ 122 | // Mutation 123 | // 124 | // Instance methods cannot by default modify properties of structures or enumerations. To enable 125 | // this, mark them as 'mutating': 126 | struct Point2 127 | { 128 | var x: Int = 10 129 | 130 | // Note the need for the keyword 'mutating' 131 | mutating func setX(x: Int) 132 | { 133 | self.x = x 134 | } 135 | } 136 | 137 | // We'll create a constant Point2... 138 | let fixedPoint = Point2(x: 3) 139 | 140 | // Because 'fixedPoint' is constant, we are not allowed to call mutating memthods: 141 | // 142 | // The following line won't compile: 143 | // 144 | // fixedPoint.setX(4) 145 | 146 | // If you're working with a structure or enumeration (not a class), uou can assign to 'self' 147 | // directly 148 | struct Point3 149 | { 150 | var x = 0 151 | 152 | // This does not work with classes 153 | mutating func replaceMe(newX: Int) 154 | { 155 | self = Point3(x: 3) 156 | } 157 | } 158 | 159 | // Assigning to 'self' in an enumeration is used to change to a different member of the same 160 | // enumeration: 161 | enum TriStateSwitch 162 | { 163 | case Off, Low, High 164 | mutating func next() 165 | { 166 | switch self 167 | { 168 | case Off: 169 | self = Low 170 | case Low: 171 | self = High 172 | case High: 173 | self = Off 174 | } 175 | } 176 | } 177 | 178 | // ------------------------------------------------------------------------------------------------ 179 | // Type Methods 180 | // 181 | // Type methods are like C++'s static methods. 182 | // 183 | // They can only access Type members. 184 | struct LevelTracker 185 | { 186 | var currentLevel = 1 187 | static var highestUnlockedLevel = 1 188 | 189 | static func unlockedLevel(level: Int) 190 | { 191 | if level > highestUnlockedLevel 192 | { 193 | highestUnlockedLevel = level 194 | } 195 | } 196 | static func levelIsUnlocked(level: Int) -> Bool 197 | { 198 | return level <= highestUnlockedLevel 199 | } 200 | mutating func advanceToLevel(level: Int) -> Bool 201 | { 202 | if LevelTracker.levelIsUnlocked(level) 203 | { 204 | currentLevel = level 205 | return true 206 | } 207 | else 208 | { 209 | return false 210 | } 211 | } 212 | } 213 | 214 | // To call a type method, use the type name, not the instance name: 215 | LevelTracker.levelIsUnlocked(3) 216 | 217 | // If we attempt to use an instance to call a type method, we'll get an error 218 | var levelTracker = LevelTracker() 219 | 220 | // The following line will not compile: 221 | // 222 | // levelTracker.levelIsUnlocked(3) 223 | 224 | // For classes, type methods use the 'class' keyword rather than the 'static' keyword: 225 | class SomeOtherClass 226 | { 227 | class func isGreaterThan100(value: Int) -> Bool 228 | { 229 | return value > 100 230 | } 231 | } 232 | 233 | // We call class type methods with the type name just as we do for structures and enumerations: 234 | SomeOtherClass.isGreaterThan100(105) 235 | -------------------------------------------------------------------------------- /11. Methods.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /12. Subscripts.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /12. Subscripts.playground/section-1.swift: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------------------------ 2 | // Things to know: 3 | // 4 | // * Subscripts allow you to declare functionality for instances to make use of the subscript 5 | // operator ( [] ). 6 | // 7 | // * Subscripts are available for classes, structures and enumerations. 8 | // 9 | // * Subscripts are declared much like getters and setters for properties. 10 | // ------------------------------------------------------------------------------------------------ 11 | 12 | // Subscripts are declared like getters and setters, and follow the same syntax rules for 13 | // read-only and read-write variants. They also can employ the same syntactic simplifications 14 | // available for getters and setters. 15 | // 16 | // Here's a structure that utilizes a subscript so that we can see the syntax of the declaration. 17 | struct TimesTable 18 | { 19 | let multiplier: Int 20 | 21 | // Read-only subscript using simplified getter syntax 22 | subscript(index: Int) -> Int 23 | { 24 | return multiplier * index 25 | } 26 | 27 | // Overloaded subscript for type Double, also read-only using the simplified getter syntax 28 | subscript(index: Double) -> Int 29 | { 30 | return multiplier * Int(index) 31 | } 32 | } 33 | 34 | // We can now make use of our newly created subscripts 35 | let threeTimesTable = TimesTable(multiplier: 3) 36 | threeTimesTable[3] 37 | threeTimesTable[4.0] 38 | 39 | // Subscripts can take any parameter type and variadic parameters, but cannot use inout or default 40 | // parameters. 41 | // 42 | // Here's a more complex example: 43 | struct Matrix 44 | { 45 | let rows: Int 46 | let columns: Int 47 | var grid: [Double] 48 | 49 | init (rows: Int, columns: Int) 50 | { 51 | self.rows = rows 52 | self.columns = columns 53 | grid = Array(count: rows * columns, repeatedValue: 0.0) 54 | } 55 | 56 | func indexIsValidForRow(row: Int, column: Int) -> Bool 57 | { 58 | return row >= 0 && row < rows && column >= 0 && column < columns 59 | } 60 | 61 | // Subscript with getter & setter as well as dual-parameter subscript 62 | subscript(row: Int, column: Int) -> Double 63 | { 64 | get 65 | { 66 | assert(indexIsValidForRow(row, column: column), "Index out of range") 67 | return grid[row * columns + column] 68 | } 69 | set 70 | { 71 | assert(indexIsValidForRow(row, column: column), "Index out of range") 72 | grid[row * columns + column] = newValue 73 | } 74 | } 75 | } 76 | 77 | // We'll create a standard 4x4 identity matrix 78 | var matrix = Matrix(rows: 4, columns: 4) 79 | matrix[0,0] = 1 80 | matrix[1,1] = 1 81 | matrix[2,2] = 1 82 | matrix[3,3] = 1 83 | -------------------------------------------------------------------------------- /12. Subscripts.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /13. Inheritance.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /13. Inheritance.playground/section-1.swift: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------------------------ 2 | // Things to know: 3 | // 4 | // * There is no default base class for Swift objects. Any class that doesn't derive from 5 | // another class is a base class. 6 | // ------------------------------------------------------------------------------------------------ 7 | 8 | // Let's start with a simple base class: 9 | class Vehicle 10 | { 11 | var numberOfWheels: Int 12 | var maxPassengers: Int 13 | 14 | func description() -> String 15 | { 16 | return "\(numberOfWheels) wheels; up to \(maxPassengers) passengers" 17 | } 18 | 19 | // Must initialize any properties that do not have a default value 20 | init() 21 | { 22 | numberOfWheels = 0 23 | maxPassengers = 1 24 | } 25 | } 26 | 27 | // ------------------------------------------------------------------------------------------------ 28 | // Subclasses 29 | // 30 | // Now let's subclass the Vehicle to create a two-wheeled vehicle called a Bicycle 31 | class Bicycle: Vehicle 32 | { 33 | // We'll make this a 2-wheeled vehicle 34 | override init() 35 | { 36 | super.init() 37 | numberOfWheels = 2 38 | } 39 | } 40 | 41 | // We can call a member from the superclass 42 | let bicycle = Bicycle() 43 | bicycle.description() 44 | 45 | // Subclasses can also be subclassed 46 | class Tandem: Bicycle 47 | { 48 | // This bicycle has 2 passengers 49 | override init() 50 | { 51 | super.init() 52 | maxPassengers = 2 53 | } 54 | } 55 | 56 | // Here, we'll create a car that includes a new description by overriding the superclass' instance 57 | // method 58 | class Car: Vehicle 59 | { 60 | // Adding a new property 61 | var speed: Double = 0.0 62 | 63 | // New initialization, starting with super.init() 64 | override init() 65 | { 66 | super.init() 67 | maxPassengers = 5 68 | numberOfWheels = 4 69 | } 70 | 71 | // Using the override keyword to specify that we're overriding a function up the inheritance 72 | // chain. It is a compilation error to leave out 'override' if a method exists up the chain. 73 | override func description() -> String 74 | { 75 | // We start with the default implementation of description then tack our stuff onto it 76 | return super.description() + "; " + "traveling at \(speed) mph" 77 | } 78 | } 79 | 80 | // Here, we'll check our description to see that it does indeed give us something different from 81 | // the superclass' default description: 82 | let car = Car() 83 | car.speed = 55 84 | car.description() 85 | 86 | // ------------------------------------------------------------------------------------------------ 87 | // Overriding Properties 88 | // 89 | // We can override property getters and setters. This applies to any property, including stored and 90 | // computed properties 91 | // 92 | // When we do this, our overridden property must include the name of the property as well as the 93 | // property's type. 94 | class SpeedLimitedCar: Car 95 | { 96 | // Make sure to specify the name and type 97 | override var speed: Double 98 | { 99 | get 100 | { 101 | return super.speed 102 | } 103 | // We need a setter since the super's property is read/write 104 | // 105 | // However, if the super was read-only, we wouldn't need this setter unless we wanted to 106 | // add write capability. 107 | set 108 | { 109 | super.speed = min(newValue, 40.0) 110 | } 111 | } 112 | } 113 | 114 | // We can see our override in action 115 | var speedLimitedCar = SpeedLimitedCar() 116 | speedLimitedCar.speed = 60 117 | speedLimitedCar.speed 118 | 119 | // We can also override property observers 120 | class AutomaticCar: Car 121 | { 122 | var gear = 1 123 | override var speed: Double 124 | { 125 | // Set the gear based on changes to speed 126 | didSet { gear = Int(speed / 10.0) + 1 } 127 | 128 | // Since we're overriding the property observer, we're not allowed to override the 129 | // getter/setter. 130 | // 131 | // The following lines will not compile: 132 | // 133 | // get { return super.speed } 134 | // set { super.speed = newValue } 135 | } 136 | } 137 | 138 | // Here is our overridden observers in action 139 | var automaticCar = AutomaticCar() 140 | automaticCar.speed = 35.0 141 | automaticCar.gear 142 | 143 | // ------------------------------------------------------------------------------------------------ 144 | // Preenting Overrides 145 | // 146 | // We can prevent a subclass from overriding a particular method or property using the 'final' 147 | // keyword. 148 | // 149 | // final can be applied to: class, var, func, class methods and subscripts 150 | // 151 | // Here, we'll prevent an entire class from being subclassed by applying the . Because of this, 152 | // the finals inside the class are not needed, but are present for descriptive purposes. These 153 | // additional finals may not compile in the future, but they do today: 154 | final class AnotherAutomaticCar: Car 155 | { 156 | // This variable cannot be overridden 157 | final var gear = 1 158 | 159 | // We can even prevent overridden functions from being further refined 160 | final override var speed: Double 161 | { 162 | didSet { gear = Int(speed / 10.0) + 1 } 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /13. Inheritance.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /14a. Initialization.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /14a. Initialization.playground/section-1.swift: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------------------------ 2 | // Things to know: 3 | // 4 | // * Swift provides an initializer (which partially resembles a function) to ensure that every 5 | // property in a class, structure or enumeration is fully initialized. 6 | // 7 | // * Optionals do not need to be initialized as they are automatically initialized to nil if no 8 | // default value is provided. 9 | // ------------------------------------------------------------------------------------------------ 10 | 11 | // Here's a basic use of an initializer. 12 | // 13 | // The Fahrenheit class will create a 'temperature' property that does not have a default value. 14 | // Because there is no default value, the property must be initialized in the initializer. 15 | // 16 | // Without the initializer, we would get an error whenever we tried to instantiate the Fahrenheit 17 | // class. 18 | struct Fahrenheit 19 | { 20 | var temperature: Double 21 | 22 | init() 23 | { 24 | temperature = 32.0 25 | } 26 | } 27 | 28 | // Since the class can fully initialize itself, we can safely instantiate it with no error: 29 | var f = Fahrenheit() 30 | 31 | // Since the temperature is always defined as "32.0", it is cleaner and preferred to use a default 32 | // value instead of setting it inside the initializer: 33 | struct AnotherFahrenheit 34 | { 35 | var temperature: Double = 32.0 36 | } 37 | 38 | // Initializers can also include parameters. Here's an example of using a parameter to initialize 39 | // the class's temperature to a given value. 40 | // 41 | // The following class contains two initializers: 42 | struct Celsius 43 | { 44 | var temperatureInCelsius: Double = 0.0 45 | 46 | // Initialize our temperature from Fahrenheit 47 | init(fromFahrenheit fahrenheit: Double) 48 | { 49 | temperatureInCelsius = (fahrenheit - 32.0) / 1.8 50 | } 51 | 52 | // Initialize our temperature from Kelvin 53 | init(kelvin: Double) 54 | { 55 | temperatureInCelsius = kelvin - 273.15 56 | } 57 | } 58 | 59 | // Now let's try our new initializers 60 | let boilingPotOfWater = Celsius(fromFahrenheit: 212.0) 61 | let freezingPointOfWater = Celsius(kelvin: 273.15) 62 | 63 | // External parameter names are automatically generated for the initializer. In order to opt-out 64 | // of this automated convenience, you can specify "_" for the extnenal name explicitly. 65 | // 66 | // Here's a class that defines two initializers - one that makes use of the automatic external 67 | // name generation and one that opts out: 68 | struct Color 69 | { 70 | let red = 0.0, green = 0.0, blue = 0.0 71 | 72 | // This initializer will make use of automatically generated exernal names 73 | init(red: Double, green: Double, blue: Double) 74 | { 75 | self.red = red 76 | self.green = green 77 | self.blue = blue 78 | } 79 | 80 | // This initializer opts out by explicitly declaring external names with "_" 81 | init(_ red: Double, _ blue: Double) 82 | { 83 | self.red = red 84 | self.green = 0 85 | self.blue = blue 86 | } 87 | } 88 | 89 | // Here, we can see our efforts pay off: 90 | let magenta = Color(red: 1.0, green: 0.0, blue: 1.0) 91 | let purple = Color(1.0, 0.5) 92 | 93 | // Optional properties do not need to be initialized: 94 | class SurveyQuestion 95 | { 96 | var text: String 97 | 98 | // Response is optional, and is automatically initialized to nil 99 | var response: String? 100 | 101 | init(text: String) 102 | { 103 | // We only need to initialize 'text' 104 | self.text = text 105 | } 106 | 107 | func ask() -> String 108 | { 109 | return text 110 | } 111 | } 112 | 113 | // Constants need initialization as well. In the following example, our constant has a default 114 | // value. However, if we initialize the class with the init(text: String) initializer, we can 115 | // modify the default value to use the one passed in: 116 | class SurveyQuestion2 117 | { 118 | // Default value of "No question" 119 | let text: String = "No question" 120 | 121 | var response: String? 122 | 123 | init(text: String) 124 | { 125 | // Initializing the constant, 'text', even though it has a default value, we can modify 126 | // that default value here 127 | self.text = text 128 | } 129 | 130 | init() 131 | { 132 | // We do nothing here and let the default value for 'text' define its value 133 | } 134 | 135 | func ask() -> String 136 | { 137 | return text 138 | } 139 | } 140 | 141 | // Here, we initialize the class with a blank initializer (calling init()) to let text's default 142 | // value initialize the stored value 143 | let noQuestion = SurveyQuestion2() 144 | 145 | // Here, we'll us an aalternat initializer to specify a different value for 'text' 146 | let beetsQuestion = SurveyQuestion2(text: "Do you like beets?") 147 | 148 | // ------------------------------------------------------------------------------------------------ 149 | // Default Initializer 150 | // 151 | // If all properties have default values (including optionals defaulting to nil) AND you do not 152 | // create your own initlializer AND there is no superclass, Swift will create a default 153 | // initializer (with no parameters) for you. This initializer sets all properties to their 154 | // default values. 155 | // 156 | // If you create your own initializer, Swift will not create the default initializer. If you want 157 | // your custom initializers as well as the default initializer, then put your initializers in an 158 | // extension. 159 | class ShoppingListItem 160 | { 161 | var name: String? 162 | var quantity = 1 163 | var purchased = false 164 | 165 | // No init(...) initializer 166 | } 167 | 168 | // ------------------------------------------------------------------------------------------------ 169 | // Memberwise Initializers for Structure Types 170 | // 171 | // Similar to the default initializer for classes, structures that do not implement an initializer 172 | // but have default values for their stored properties will get a default memberwise initializer. 173 | // 174 | // As with classes, if you want your custom initializers as well as the default memberwise 175 | // initializer, then put your initializers in an extension. 176 | struct Size 177 | { 178 | var width = 0.0 179 | var height = 0.0 180 | } 181 | 182 | // Here, we call the default memberwise initializer that Swift created for us 183 | let twoByTwo = Size(width: 2.0, height: 2.0) 184 | 185 | // ------------------------------------------------------------------------------------------------ 186 | // Initializer Delegatgion for Value Types 187 | // 188 | // Sometimes, it's convenient to have multiple initializers that call other initializers to do 189 | // some of the heavy lifting. 190 | // 191 | // In the following example, we have a class 'Rect' that has multiple initializers. One that 192 | // initializes our rect given an origin and size, while another that calls upon the origin/size 193 | // initializer from calculations based on the a center point. 194 | // 195 | // This concept is further extended in "Initializer Chaining", covered later. 196 | struct Point 197 | { 198 | var x = 0.0 199 | var y = 0.0 200 | } 201 | 202 | struct Rect 203 | { 204 | var origin = Point() 205 | var size = Size() 206 | 207 | // We create a basic initializer to use the default values Since we define other initializers, 208 | // the system won't create this for us, so we need to define it ourselves. Since we're using 209 | // the defaults, it is an empty closure. 210 | init() {} 211 | 212 | // Init from origin/size 213 | init(origin: Point, size: Size) 214 | { 215 | self.origin = origin 216 | self.size = size 217 | } 218 | 219 | // Init from center/size - note how we use the init(origin:size) to perform actual 220 | // initialization 221 | init(center: Point, size: Size) 222 | { 223 | let originX = center.x - size.width / 2 224 | let originY = center.y - size.height / 2 225 | self.init(origin: Point(x: originX, y: originY), size: size) 226 | } 227 | } 228 | 229 | // Here, we call the three initializers: 230 | let basicRect = Rect() 231 | let originRect = Rect(origin: Point(x: 2.0, y: 2.0), size: Size(width: 5.0, height: 5.0)) 232 | let centerRect = Rect(center: Point(x: 4.0, y: 4.0), size: Size(width: 3.0, height: 3.0)) 233 | -------------------------------------------------------------------------------- /14a. Initialization.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /14b. Initializer Chaining.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /14b. Initializer Chaining.playground/section-1.swift: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------------------------ 2 | // Things to know: 3 | // 4 | // * Initializer Chaining refers to the way in which initialization takes place along the class 5 | // hierarchy. 6 | // 7 | // * Designated Initializers are those that are responsible for ensuring that the instance of a 8 | // class, structure or enumeration is properly initialized. These are the initializers you've 9 | // seen so far. Designated initializers are also resonsible for calling the superclass' 10 | // initializer. 11 | // 12 | // * Convenience Initializers are initializers that are provided for specialized initialization 13 | // and must call one of the designated initializers within a class in order to fulfill the 14 | // requirement of properly initializing a class. 15 | // ------------------------------------------------------------------------------------------------ 16 | 17 | // Designated Initializers are the normal initializers ("init(...)") functions that you've seen 18 | // so far. If the system creates an initializer for you, it will be a designated initializer. 19 | // 20 | // Convenience Initializers allow you to offer different code-paths through initialization, which 21 | // call upon the designated initializers to do the heavy lifting. They use the keyword 22 | // "convenience". They are actually different than designated initializers because Swift uses the 23 | // difference (designated vs. convenience) to perform two-phase initialization, which we'll cover 24 | // shortly. 25 | // 26 | // Here's what a simple case looks like with a designated and convenience initializer: 27 | class Food 28 | { 29 | var name: String 30 | 31 | // Designated initializer - required as the class does not have a default value for 'name'. 32 | // There can be more than one of these, but the fewer the better, usually, design-wise. 33 | init(name: String) 34 | { 35 | self.name = name 36 | } 37 | 38 | // Here, we'll use a convenience initializer to initialize 'name' to an unnamed Food 39 | convenience init() 40 | { 41 | // Must call the designated in same class 42 | self.init(name: "[unnamed]") 43 | } 44 | } 45 | 46 | // Here we make use of our two initializers 47 | let namedMeat = Food(name: "Bacon") 48 | let mysteryMeat = Food() 49 | 50 | // ------------------------------------------------------------------------------------------------ 51 | // Two-Phase Initialization 52 | // 53 | // Two-phase initialization is a new concept enforced by Swift. Think of it like this: 54 | // 55 | // Phase 1: Subclasses MUST FIRST initialize any stored properties that are part of their subclass. 56 | // They must do this before they are allowed to cusomize a super's stored property. 57 | // 58 | // Subclasses MUST THEN call the super's initializer, which will repeat this process 59 | // until the top-most superclass (the base class) is reached. 60 | // 61 | // At this point we can assume that the class is fully initialized (but not necessarily 62 | // customized by the subclasses that may need to alter the initialization results. 63 | // 64 | // Phase 2: In the base class' initializer, we can customize its properties before it returns to 65 | // the caller (a subclass.) That subclass is then allowed to write to any/all stored 66 | // properties defined by itself or the superclass chain. This continues until you get the 67 | // the bottom-most subclass. 68 | // 69 | // A note about convenience initializers: 70 | // 71 | // Convenience initializers must always call into a designated initializer from the current class. 72 | // A designated initializer for a subclass must then into a super's iniitializer, which may also 73 | // be a convenience initializer, because that convenience initializer will be required to 74 | // eventually call a designated initializer before going higher up the chain. 75 | // 76 | // Let's derive a class from Food so we can see this in action: 77 | class RecipeIngredient: Food 78 | { 79 | var quantity: Int 80 | 81 | // This is a designated initializer (because it has no 'convenience' keyword) 82 | init(name: String, quantity: Int) 83 | { 84 | // We must initialize our new stored properties first (this is Phase 1) 85 | self.quantity = quantity 86 | 87 | // Next, we must call super's initializer (still, Phase 1) 88 | super.init(name: name) 89 | 90 | // Only after the super's initializer is called, can we customize any properties that 91 | // originated from someplace up the class hierarchy chain. 92 | self.name = "Ingredient: " + name 93 | } 94 | 95 | // Here, we'll create a convenience initializer that simply provides a default quantity 96 | // value of 1. Note that in order for this to compile, we are required to call a designated 97 | // initializer. 98 | convenience override init(name: String) 99 | { 100 | self.init(name: name, quantity: 1) 101 | } 102 | } 103 | 104 | // Now we can call our various initializers to see them in action: 105 | let oneMysteryItem = RecipeIngredient() 106 | let oneBacon = RecipeIngredient(name: "Bacon") 107 | let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6) 108 | 109 | // ------------------------------------------------------------------------------------------------ 110 | // Inheriting a full set of the super's initializers 111 | // 112 | // In the following class, we don't specify any initializer, but that's OK because we have default 113 | // values for the stored property (purchased). 114 | // 115 | // Not providing any initializer at all allows us to gain a full set of the super's initializers. 116 | class ShoppingListItem: RecipeIngredient 117 | { 118 | var purchased = false 119 | var description: String 120 | { 121 | var output = "\(quantity) x \(name)" 122 | output += purchased ? " ✔" : " ✘" 123 | return output 124 | } 125 | } 126 | 127 | // Let's initialize our new ShoppingListItem using the super's initializer 128 | let lotsOfCheese = ShoppingListItem(name: "cheese", quantity: 99) 129 | 130 | // Here, we can create an array of ShippingListItems 131 | var breakfastList = [ 132 | ShoppingListItem(), 133 | ShoppingListItem(name: "Bacon"), 134 | ShoppingListItem(name: "Eggs", quantity: 6), 135 | ] 136 | 137 | // ------------------------------------------------------------------------------------------------ 138 | // For individual properties, we can set their default value with a closure or function instead 139 | // of just a literal value. 140 | // 141 | // In the example below, we use a closure to calculate the default value for 'estimatedPi'. Note 142 | // the parenthesis at the end of the closure. Without them, we would be asking to set 'estimatedPI' 143 | // to a closure type rather than to the result of the closure. Also, the parenthesis tell it to 144 | // execute the closure immediately. 145 | // 146 | // Also note that these closures used for default values for properties are not allowed to access 147 | // any of the other properties of the class because it's assumed that they have not yet been 148 | // initialized. 149 | class ClassWithPI 150 | { 151 | let estimatedPI: Double = 152 | { 153 | let constant1 = 22.0 154 | let constant2 = 7.0 155 | 156 | // Must return the type specified by the property 157 | return constant1 / constant2 158 | }() 159 | } 160 | 161 | // Here's a more pracitcal example. Note that since the closure is required to return the type 162 | // of the property it is initializing, it must create a temporary of the same type which is 163 | // initialized and then returned. 164 | struct CheckerBoard 165 | { 166 | let boardColors: [Bool] = 167 | { 168 | var temporaryBoard = [Bool]() 169 | var isBlack = false 170 | for i in 1...10 171 | { 172 | for j in 1...10 173 | { 174 | temporaryBoard.append(isBlack) 175 | isBlack = !isBlack 176 | } 177 | 178 | isBlack = !isBlack 179 | } 180 | 181 | // Return the temporary in order to set 'boardColors' 182 | return temporaryBoard 183 | }() 184 | 185 | func squareIsBlackAtRow(row: Int, column: Int) -> Bool 186 | { 187 | return boardColors[(row * 10) + column] 188 | } 189 | } 190 | 191 | // We can now check our work 192 | var board = CheckerBoard() 193 | board.squareIsBlackAtRow(1, column: 1) // Should be false 194 | board.squareIsBlackAtRow(1, column: 2) // Should be true 195 | -------------------------------------------------------------------------------- /14b. Initializer Chaining.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /15. Deinitialization.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /15. Deinitialization.playground/section-1.swift: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------------------------ 2 | // Things to know: 3 | // 4 | // * Deinitializers are called automatically before a class instance is 5 | // deallocated,so you can access all properties in the deinitializer. 6 | // 7 | // * You cannot call them directly. 8 | // 9 | // * The superclass' deinitializer is called before the subclass'. 10 | // 11 | // * Swift uses ARC to manage memory, so deinitializers shouldn't always 12 | // be necessary. However, you might open a file and need to close it 13 | // when a class is deallocated. 14 | // ------------------------------------------------------------------------------------------------ 15 | 16 | // Let's create a couple classes to work with... 17 | struct Bank 18 | { 19 | static var coinsInBank = 10_000 20 | static func vendCoins(var numberOfCoinsToVend: Int) -> Int 21 | { 22 | numberOfCoinsToVend = min(numberOfCoinsToVend, coinsInBank) 23 | coinsInBank -= numberOfCoinsToVend 24 | return numberOfCoinsToVend 25 | } 26 | 27 | static func receiveCoins(coins: Int) 28 | { 29 | coinsInBank += coins 30 | } 31 | } 32 | 33 | class Player 34 | { 35 | var coinsInPurse: Int 36 | 37 | init(coins: Int) 38 | { 39 | coinsInPurse = Bank.vendCoins(coins) 40 | } 41 | 42 | func winCoins(coins: Int) 43 | { 44 | coinsInPurse += coins 45 | } 46 | 47 | deinit 48 | { 49 | Bank.receiveCoins(coinsInPurse) 50 | } 51 | } 52 | 53 | // Let's exercise the Player class a bit and create a new player with 100 54 | // coins in his purse (pulled from the Bank.) 55 | var playerOne: Player? = Player(coins: 100) 56 | playerOne!.coinsInPurse 57 | Bank.coinsInBank 58 | 59 | // The Player now wins 2000 coins! 60 | playerOne!.winCoins(2_000) 61 | playerOne!.coinsInPurse 62 | Bank.coinsInBank 63 | 64 | // When we cause playerOne to be deallocated, the deinitializer is called 65 | playerOne = nil 66 | 67 | // This should print 12000 coins, but the playgrounds don't appear to do 68 | // this correctly. If you put this code into a project and compile/run 69 | // it (with minor changes to print variables using println) then you 70 | // will see that the bank does indeed have 12000 coins. 71 | Bank.coinsInBank 72 | -------------------------------------------------------------------------------- /15. Deinitialization.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /16. ARC.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /16. ARC.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /17. Optional Chaining.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /17. Optional Chaining.playground/section-1.swift: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------------------------ 2 | // Things to know: 3 | // 4 | // * Optional Chaining is the process of safely referencing a series of optionals (which contain 5 | // optionals, which contain optionals, etc.) without having to perform the optional unwrapping 6 | // checks at each step along the way. 7 | // ------------------------------------------------------------------------------------------------ 8 | 9 | // Consider a case of forced unwrapping like "someOptional!.someProperty". We already know that 10 | // this is only safe if we know that the optional will never be nil. For times that we don't know 11 | // this, we must check the optional before we can reference it. This can become cumbersome for 12 | // situations where many optionals are chained together as properties of properties. Let's create 13 | // a series of optionals to show this: 14 | class Artist 15 | { 16 | let name: String 17 | init(name: String) { self.name = name } 18 | } 19 | 20 | class Song 21 | { 22 | let name: String 23 | var artist: Artist? 24 | init(name: String, artist: Artist?) 25 | { 26 | self.name = name 27 | self.artist = artist 28 | } 29 | } 30 | 31 | class MusicPreferences 32 | { 33 | var favoriteSong: Song? 34 | init(favoriteSong: Song?) { self.favoriteSong = favoriteSong } 35 | } 36 | 37 | class Person 38 | { 39 | let name: String 40 | var musicPreferences: MusicPreferences? 41 | init (name: String, musicPreferences: MusicPreferences?) 42 | { 43 | self.name = name 44 | self.musicPreferences = musicPreferences 45 | } 46 | } 47 | 48 | // Here, we'll create a working chain: 49 | var someArtist: Artist? = Artist(name: "Somebody with talent") 50 | var favSong: Song? = Song(name: "Something with a beat", artist: someArtist) 51 | var musicPrefs: MusicPreferences? = MusicPreferences(favoriteSong: favSong) 52 | var person: Person? = Person(name: "Bill", musicPreferences: musicPrefs) 53 | 54 | // We can access this chain with forced unwrapping. In this controlled environment, this will work 55 | // but it's probably not advisable in a real world situation unless you're sure that each member 56 | // of the chain is sure to never be nil. 57 | person!.musicPreferences!.favoriteSong!.artist! 58 | 59 | // Let's break the chain, removing the user's music preferences: 60 | if var p = person 61 | { 62 | p.musicPreferences = nil 63 | } 64 | 65 | // With a broken chain, if we try to reference the arist like before, we will get a runtime error. 66 | // 67 | // The following line will compile, but will crash: 68 | // 69 | // person!.musicPreferences!.favoriteSong!.artist! 70 | // 71 | // The solusion here is to use optional chaining, which replaces the forced unwrapping "!" with 72 | // a "?" character. If at any point along this chain, any optional is nil, evaluation is aborted 73 | // and the entire result becomes nil. 74 | // 75 | // Let's see this entire chain, one step at a time working backwards. This let's us see exactly 76 | // where the optional chain becomes a valid value: 77 | person?.musicPreferences?.favoriteSong?.artist 78 | person?.musicPreferences?.favoriteSong 79 | person?.musicPreferences 80 | person 81 | 82 | // Optional chaining can be mixed with non-optionals as well as forced unwrapping and other 83 | // contexts (subcripts, function calls, return values, etc.) We won't bother to create the 84 | // series of classes and instances for the following example, but it should serve as a valid 85 | // example of how to use optional chaining syntax in various situations. Given the proper context 86 | // this line of code would compile and run as expected. 87 | // 88 | // person?.musicPreferences?[2].getFavoriteSong()?.artist?.name 89 | // 90 | // This line could be read as: optional person's second optional music preference's favorite song's 91 | // optional artist's name. 92 | -------------------------------------------------------------------------------- /17. Optional Chaining.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /18. Type Casting.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /18. Type Casting.playground/section-1.swift: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------------------------ 2 | // Things to know: 3 | // 4 | // * Type casting allows us to check the type of an instance and/or treat that instance as a 5 | // different type from somewhere else in its own hieararchy. 6 | // 7 | // * Type casting also allows us to determine if a type conforms to a protocol. 8 | // 9 | // * Additionally we can create 10 | // ------------------------------------------------------------------------------------------------ 11 | 12 | // Let's start by creating a few types to work with: 13 | class MediaItem 14 | { 15 | var name: String 16 | init(name: String) { self.name = name } 17 | } 18 | 19 | class Movie: MediaItem 20 | { 21 | var director: String 22 | init(name: String, director: String) 23 | { 24 | self.director = director 25 | super.init(name: name) 26 | } 27 | } 28 | 29 | class Song: MediaItem 30 | { 31 | var artist: String 32 | init(name: String, artist: String) 33 | { 34 | self.artist = artist 35 | super.init(name: name) 36 | } 37 | } 38 | 39 | // We'll create a library of Movies and Songs. Note that Swift will infer the type of the array to 40 | // be MediaItem[]. 41 | let library = 42 | [ 43 | Movie(name: "Casablanca", director: "Michael Curtiz"), 44 | Song(name: "Blue Suede Shoes", artist: "Elvis Presley"), 45 | Movie(name: "Citizen Kane", director: "Orson Welles"), 46 | Song(name: "The One And Only", artist: "Chesney Hawkes"), 47 | Song(name: "Never Gunna Give You Up", artist: "Rick Astley") 48 | ] 49 | 50 | // ------------------------------------------------------------------------------------------------ 51 | // Checking type 52 | // 53 | // We can check the type of an object very simply with the 'is' operator, also known as the Type 54 | // Check Operator. 55 | library[0] is Movie // true 56 | library[0] is Song // false 57 | 58 | // Let's see this in action. Lets loop through the library and count up the movies and songs: 59 | var movieCount = 0 60 | var songCount = 0 61 | for item in library 62 | { 63 | if item is Movie { ++movieCount } 64 | else if item is Song { ++songCount } 65 | } 66 | 67 | // Our final Movie and Song counts: 68 | movieCount 69 | songCount 70 | 71 | // ------------------------------------------------------------------------------------------------ 72 | // Downcasting 73 | // 74 | // As with other languages, downcasting refers to casting an object to a subclass within its 75 | // hierarchy. This is done with the Type Cast Operator, "as". 76 | // 77 | // Because not all objects can be downcasted, the result of a downcast may be nil. Because of this, 78 | // we have two options for downcasting. We can downcast to an optional using the "as?" operator 79 | // or downcast with forced unwrapping of the optional with the standard "as" operator. As with 80 | // forced unwrapping, only do this if you know for certain that the downcast will succeed. 81 | // 82 | // Let's see this in action using optional binding and the "as?" operator. Remember that our 83 | // library has type MediaItem[], so if we cast an element of that array to either a Movie or Song, 84 | // will will be downcasting the instance. 85 | for item in library 86 | { 87 | if let movie = item as? Movie 88 | { 89 | "Movie: '\(movie.name)' was directed by \(movie.director)" 90 | } 91 | else if let song = item as? Song 92 | { 93 | "Song: '\(song.name)' was performed by \(song.artist)" 94 | } 95 | } 96 | 97 | // ------------------------------------------------------------------------------------------------ 98 | // Type Casting for Any and AnyObject 99 | // 100 | // * AnyObject allows us to store an instance to any class type. 101 | // 102 | // * Any allows us to store a reference to any type at all, excluding functino types. 103 | // 104 | // We should strive to use Any and AnyObject only when we must (like when an API function returns 105 | // arrays of values of any object type.) Otherwise, it's important that we use explicit types to 106 | // conform to Swift's type safety. 107 | // 108 | // Let's see AnyObject in action. We'll define an array of type AnyObject[] and populate it with 109 | // some movies: 110 | let someObjects: [AnyObject] = 111 | [ 112 | Movie(name: "2001: A Space Odyssey", director: "Stanley Kubrick"), 113 | Movie(name: "Moon", director: "Duncan Jones"), 114 | Movie(name: "Alien", director: "Ridley Scott"), 115 | ] 116 | 117 | // Here, we know that someObjects[] only contains Movie instances, so we'll use our forced version 118 | // of the typecast operator, "as". Note, however, that if somebody modifies the code later and adds 119 | // an instance of a non-Movie type (which they can do), we'll crash. This is why it's important 120 | // to limit our use of AnyObject and Any to only those cases where we absolutely need it. 121 | // 122 | // Let's see how we would use the someObjects array: 123 | for object: AnyObject in someObjects 124 | { 125 | let movie = object as Movie 126 | "Movie: '\(movie.name)' was directed by \(movie.director)" 127 | } 128 | 129 | // Alternatively, we can downcast the array itself rather than each item: 130 | var someMovies = someObjects as [Movie] 131 | for movie in someMovies 132 | { 133 | "Movie: '\(movie.name)' was directed by \(movie.director)" 134 | } 135 | 136 | // Finally, we can avoid the additional local variable and performt he downcast right inside 137 | // the loop structure: 138 | for movie in someObjects as [Movie] 139 | { 140 | "Movie: '\(movie.name)' was directed by \(movie.director)" 141 | } 142 | 143 | // Any allows us store references to any type at all (not including functions), which can include 144 | // integers, floating points, strings or objects. 145 | // 146 | // Let's see this in action. We'll create an array of type Any[] and fill it with random bits and 147 | // pieces of stuff: 148 | var things = [Any]() 149 | 150 | things.append(0) 151 | things.append(0.0) 152 | things.append(42) 153 | things.append(3.14159) 154 | things.append("Hello") 155 | things.append((3.0, 5.0)) 156 | things.append(Movie(name: "Ghostbusters", director: "Ivan Reitman")) 157 | 158 | // We can now use a switch statement with a mix of "is" and "as" operators to parse through the 159 | // things array and get information from it. 160 | // 161 | // We'll use a switch statement to check each type (and even include a few where clauses to get a 162 | // bit more specific about the value inside the type.) Note that when we do this, we use the forced 163 | // version of the 'as' operator, which is safe in the context of a 'case' because a matched case 164 | // means that it's guaranteed to be a valid value of the given type. 165 | for thing in things 166 | { 167 | switch thing 168 | { 169 | case 0 as Int: 170 | "zero as an Int" 171 | case 0 as Double: 172 | "zero as a Double" 173 | case let someInt as Int: 174 | "an integer value of \(someInt)" 175 | case let someDouble as Double where someDouble > 0: 176 | "a positive Double value of \(someDouble)" 177 | case is Double: 178 | "some other double that I don't want to print" 179 | case let someString as String: 180 | "a string value of \(someString)" 181 | case let (x, y) as (Double, Double): 182 | "a Tuple used to store an X/Y floating point coordinate: \(x), \(y)" 183 | case let movie as Movie: 184 | "A movie called '\(movie.name)'" 185 | default: 186 | "Something else" 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /18. Type Casting.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /19. Nested Types.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /19. Nested Types.playground/section-1.swift: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------------------------ 2 | // Things to know: 3 | // 4 | // * Nested types are utility classes and structures that are declared within other classes, 5 | // structures and enumerations. 6 | // ------------------------------------------------------------------------------------------------ 7 | 8 | // Let's take a look at how we might define a Blackjack Card using nested types. 9 | // 10 | // Each card has a suit (Spades, Hearts, Diamonds, Clubs) and a rank (Ace, King, Queen, etc.) This 11 | // is a natural use of nested types because the suit and rank enumerations only apply to a 12 | // Blackjack card and so they are scoped to within that that type. 13 | // 14 | // Additionally, since the rank of an Ace can be valued at either an 11 or a 1, we can create 15 | // a nested structure within the Rank enumeration that allows us to multiple values for a given 16 | // rank. 17 | // 18 | // Let's see how this might be implemented: 19 | struct BlackjackCard 20 | { 21 | // Nested Suit enumeration 22 | enum Suit: Character 23 | { 24 | case Spades = "♠", Hearts = "♡", Diamonds = "♢", Clubs = "♣" 25 | } 26 | 27 | // Nested Rank enumeration 28 | enum Rank: Int 29 | { 30 | case Two = 2, Three, Four, Five, Six, Seven, Eight, Nine, Ten 31 | case Jack, Queen, King, Ace 32 | 33 | // A rank can possibly have two values (for the Ace), so we'll use this structure 34 | // to contain those two values. We could just as well use a Tuple, but we're showcasing 35 | // nested types. 36 | // 37 | // Note that since all Ranks have a value but only some ranks have a second value, we'll 38 | // define the first value as an Int and the second value as an Int? 39 | struct Values 40 | { 41 | let first: Int 42 | let second: Int? 43 | } 44 | 45 | // Here we have a computed property to return the values of a rank. Note that the only 46 | // Rank to return multiple values is the Ace. However, we also use this to provide the 47 | // proper value for Jack/King/Queen, which are all equivalent to 10. 48 | var values: Values 49 | { 50 | switch self 51 | { 52 | case .Ace: 53 | return Values(first: 1, second: 11) 54 | case .Jack, .Queen, .King: 55 | return Values(first: 10, second: nil) 56 | default: 57 | return Values(first: self.rawValue, second: nil) 58 | } 59 | } 60 | } 61 | 62 | // BlackjackCard properties and methods 63 | let rank: Rank 64 | let suit: Suit 65 | 66 | var description: String 67 | { 68 | var output = "A \(suit.rawValue) with a value of \(rank.values.first)" 69 | if let second = rank.values.second 70 | { 71 | output += " or \(second)" 72 | } 73 | return output 74 | } 75 | } 76 | 77 | // Let's initialize a BlackJack card for the Ace of Spades. Note that BlackjackCard doesn't define 78 | // any initializers, so we're able to use the default memberwise initializer. 79 | // 80 | // Also note that since the initializer knows thet type of each member being initialized (both of 81 | // which are enumerations) we can use the shorthand method (.Something) for each member's initial 82 | // value. 83 | let theAceOfSpades = BlackjackCard(rank: .Ace, suit: .Spades) 84 | theAceOfSpades.description 85 | 86 | // To access the nested type, we can drill down into the type using type names: 87 | let heartsSymbol = String( BlackjackCard.Suit.Hearts.rawValue ) 88 | -------------------------------------------------------------------------------- /19. Nested Types.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /1a. The Basics.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /1a. The Basics.playground/section-1.swift: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------------------------ 2 | // Things to know: 3 | // 4 | // * Swift is Apple's new programming language for iOS and OSX. If you know C or Objective-C, then 5 | // these playgrounds should serve as a solid primer for making the switch to Swift. 6 | // 7 | // * Some experience programming in a C-like langauge is expected. If not, then I'm sorry but 8 | // you're just not the target audience. 9 | // ------------------------------------------------------------------------------------------------ 10 | 11 | // ------------------------------------------------------------------------------------------------ 12 | // Constants & Variables - These are known as "Stored Values" in Swift 13 | 14 | // Use the 'let' keyword to define a constant 15 | let maximumNumberOfLoginAttempts = 10 16 | 17 | // Use the 'var' keyword to define a variable 18 | // 19 | // Tip: only use variables when you need a stored value that changes. Otherwise, prefer constants. 20 | var currentLoginAttempt = 0 21 | 22 | // Constants cannot change. This line wouldn't compile: 23 | // maximumNumberOfLoginAttempts = 9 24 | 25 | // Variables can change: 26 | currentLoginAttempt += 1 27 | 28 | // You also can't redeclare a variable or constant once it has been declared. These lines 29 | // won't compile: 30 | // let maximumNumberOfLoginAttempts = 10 31 | // var currentLoginAttempt = "Some string which is not an Int" 32 | 33 | // You can combine them on a single line with a comma 34 | let a = 10, b = 20, c = 30 35 | var x = 0.0, y = 0.0, z = 0.0 36 | 37 | // Specifying the type with type annotations 38 | // 39 | // The built-in types in Swift are: Int, Double, Float, Bool, String, Array, Dictionary 40 | // There are variations of these (like UInt16), but those are the basic types. Note that all of 41 | // these types are capitalized. 42 | // 43 | // Because of inference (assuming 42 is an Int an "some text" is a String), type annotations are 44 | // usually pretty rare in Swift 45 | // 46 | // Here's how you specify the type. If we didn't specify the type as Double, tye type would have 47 | // been inferred to be Int. 48 | var SomeDouble: Double = 4 49 | 50 | // Constant & Variable names cannot contain any mathematical symbols, arrows private-use (or 51 | // invalid) Unicode code points or line-and-box drawing characters. Nor can they begin with a 52 | // number. Otherwise, it's open season for naming your variables! (yes, really!) 53 | // 54 | // Here are some oddly named, but perfectly valid, constants: 55 | let π = 3.14159 56 | let 你好 = "你好世界" 57 | let 🐶🐮 = "dogcow" 58 | 59 | // You can print a value using println 60 | let fiveHundred = 500 61 | println("The current value of fiveHundred is: \(fiveHundred)") 62 | 63 | // Since we're using Playgrounds, we'll just put the raw string on the line which is an expression 64 | // that evaluates to itself, printing the result in the right-hand pane in the playground, like so: 65 | "The current value of fiveHundred is: \(fiveHundred)" 66 | 67 | // ------------------------------------------------------------------------------------------------ 68 | // A note about variable names 69 | // 70 | // As with most languages, you cannot give an identifier (such as a variable or constant name, 71 | // class name, etc.) the same name as a keyword. For example, you can't define a constant named 72 | // "let": 73 | // 74 | // The following line of code will not compile: 75 | // 76 | // let let = 0 77 | // 78 | // However, sometimes it would be convenient to do so and Swift provides a means to enable this 79 | // by surrounding the identifier with backticks (`). Here's an example: 80 | let `let` = 42.0 81 | 82 | // We can now use `let` like any normal variable: 83 | x = `let` 84 | 85 | // This works for any keyword: 86 | let `class` = "class" 87 | let `do` = "do" 88 | let `for` = "for" 89 | 90 | // Additionally, it's important to know that this works on non-colliding identifier names: 91 | let `myConstant` = 123.456 92 | 93 | // Also note that `myConstant` and myConstant refer to the same constant: 94 | myConstant 95 | 96 | // ------------------------------------------------------------------------------------------------ 97 | // Comments 98 | // 99 | // You've probably already figured this out, but anything after the "//" is a comment. There's more 100 | // to comments, though: 101 | 102 | /* This is a comment 103 | that spans multiple lines */ 104 | 105 | // The multi-line comments are handy because they can nest, which allows you to safely comment out 106 | // blocks of code, even if they have multi-line comments in them: 107 | 108 | /* 109 | // Some variable 110 | var someVar = 10 111 | 112 | /* A function 113 | * 114 | * This is a common way to comment functions, but it makes it difficult to comment out these 115 | * blocks. 116 | */ 117 | func doSomething() 118 | { 119 | return 120 | } 121 | */ 122 | 123 | // ------------------------------------------------------------------------------------------------ 124 | // Semicolons 125 | // 126 | // Semicolons on the end of a line are optional, but the preferred style for Swift is to not use 127 | // them to terminate lines. 128 | var foo1 = 0 129 | var foo2 = 0; // optional semicolon 130 | 131 | // However, if you want to put two lines of code on one line, you'll need the semicolon to separate 132 | // them. 133 | foo1 = 1; foo2 = 2 134 | 135 | // ------------------------------------------------------------------------------------------------ 136 | // Integers 137 | // 138 | // There are multiple types of integers. Signed and unsigned with sizes of 8, 16, 32 and 64 bits. 139 | // Here are a couple samples: 140 | let meaningOfLife: UInt8 = 42 // Unsigned 8-bit integer 141 | let randomNumber: Int32 = -34 // Signed 32-bit integer 142 | 143 | // There is also Int and UInt, the defaults. These will default to the size of the current 144 | // platform's native word size. On 32-bit platforms, Int will be equivalent to Int32/UInt32. On 145 | // 64-bit platforms it is equivalent to Int64/UInt64. 146 | // 147 | // Similarly, there is 148 | // 149 | // Tip: For code interoperability, prefer Int over its counterparts. 150 | let tirePressurePSI = 52 151 | 152 | // To find the bounds of any integer, try ".min" or ".max" 153 | UInt8.min 154 | UInt8.max 155 | Int32.min 156 | Int32.max 157 | 158 | // ------------------------------------------------------------------------------------------------ 159 | // Floating point numbers 160 | // 161 | // Double is a 64-bit floating point numbers and Float is a 32-bit floating point number 162 | let pi: Double = 3.14159 163 | let pie: Float = 100 // ... becase it's 100% delicious! 164 | 165 | // ------------------------------------------------------------------------------------------------ 166 | // Type Safety and Type Inference 167 | // 168 | // Swift is a strongly typed language, and as such, every stored value MUST have a type and can 169 | // only be used where that specific type is expected. 170 | // 171 | // Integer literals are inferred to be Int 172 | let someInt = 1234 173 | 174 | // Floating point literals are always inferred to be Double 175 | let someDouble = 1234.56 176 | 177 | // If you want a Float instead, you must use type annotation 178 | let someFloat: Float = 1234.56 179 | 180 | // String literals are inferred to be String type 181 | let someString = "This will be a String" 182 | 183 | // Here's a bool 184 | let someBool = true 185 | 186 | // These lines won't compile because we are specifying a type that doesn't match the given value 187 | // let someBool: Bool = 19 188 | // let someInteger: Int = "45" 189 | // let someOtherInt: Int = 45.6 190 | 191 | // ------------------------------------------------------------------------------------------------ 192 | // Numeric literals 193 | // 194 | // You can specify numbers in a few interesting ways 195 | let decimalInteger = 17 196 | let binaryInteger = 0b10001 // 17 in binary notation 197 | let octalInteger = 0o21 // ...also 17 (Octal, baby!) 198 | let hexInteger = 0x11 // ...and 17 in Hexidecimal 199 | 200 | // Floating point numbers can be specified in a few different ways as well. Here are a few raw 201 | // examples (not assigned to variables): 202 | 1.25e2 // Scientific notation 203 | 1.25e-2 204 | 0xFp2 // Break this down into "0xF", "p", "2". Read as 15 (0xF) to the power of (p) 2, which is 60 205 | 0xFp-2 206 | 0xC.3p0 207 | 208 | // We can pad our literals as well: 209 | 000123.456 // Zero padding 210 | 0__123.456 // Underscores are just ignored 211 | 212 | // Numeric type conversion 213 | 214 | // A number that won't fit in the given type will not compile 215 | // let cannotBeNegative: UInt8 = -1 216 | // let tooBig: Int8 = Int8.max + 1 217 | 218 | // Since the default type for numeric values is Int, you need to specify a different type 219 | let simpleInt = 2_000 // Int 220 | let twoThousand: UInt16 = 2_000 // Specified as UInt16 221 | let one: UInt8 = 1 // Specified as UInt8 222 | 223 | // This will infer a UInt16 based on the types of both operands 224 | let twoThousandAndOne = twoThousand + UInt16(one) 225 | 226 | // Conversions between integer and floating point types must be made explicit 227 | let three = 3 // Inferred to be Int 228 | let pointOneFourOneFiveNine = 0.14159 // Inferred to be Double 229 | let doublePi = Double(three) + pointOneFourOneFiveNine // Explicit conversion of Int to Double 230 | 231 | // The inverse is also true - conversion from floating point to integer must be explicit 232 | // 233 | // Conversions to integer types from floating point simply truncate the fractional part. So 234 | // doublePi becomes 3 and -doublePi becomes -3 235 | let integerPi = Int(doublePi) 236 | let negativePi = Int(-doublePi) 237 | 238 | // Literal numerics work a little differently since the literal values don't have an explicit 239 | // type assigned to them. Their type is only inferred at the point they are evaluated. 240 | let someValue = 3 + 0.14159 241 | -------------------------------------------------------------------------------- /1a. The Basics.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /1b. Type alliases.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /1b. Type alliases.playground/section-1.swift: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------------------------ 2 | // Things to know: 3 | // 4 | // * Type Aliases allow you to provide a different name for types, 5 | // similar to 'typedef' in C. 6 | // ------------------------------------------------------------------------------------------------ 7 | 8 | // Create an alias for UInt16 called "AudioSample" 9 | typealias AudioSample = UInt16 10 | 11 | // This actually calls UInt16.min 12 | var maxAmplituedFound = AudioSample.min 13 | 14 | // We can also typealias custom types 15 | struct MySimpleStruct 16 | { 17 | static let a = 99 18 | } 19 | 20 | typealias MyAliasedName = MySimpleStruct 21 | MyAliasedName.a 22 | 23 | -------------------------------------------------------------------------------- /1b. Type alliases.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /1c. Tuples.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /1c. Tuples.playground/section-1.swift: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------------------------ 2 | // Things to know: 3 | // 4 | // * Tuples are groups of values combined into a single, compound value 5 | // ------------------------------------------------------------------------------------------------ 6 | 7 | // Defining a Tuple - use parenthesis around the comma-delimited list of values 8 | // 9 | // This Tuple doesn't specify types, so it relies on inference 10 | let httpError404 = (404, "Not found") 11 | 12 | // We can also specify the type in order to avoid inferrence 13 | let someOtherTuple = (Double(100), Bool(false)) 14 | 15 | // Decomposing typles looks like this 16 | let (statusCode, statusMessage) = httpError404 17 | statusCode 18 | statusMessage 19 | 20 | // We can also decompose into variables instead of constants, but you probably figured that out 21 | var (varStatusCode, varStatusMessage) = httpError404 22 | varStatusCode 23 | varStatusMessage 24 | 25 | // You can also access them with the dot operator followed by their index: 26 | httpError404.0 27 | httpError404.1 28 | 29 | // Alternatively, you can name the elements of a Tuple 30 | let namedTuple = (statusCode: 404, message: "Not found") 31 | 32 | // When you name the elements you effectively assign names to their indices, so the dot operator 33 | // works with names or integers: 34 | namedTuple.statusCode == namedTuple.0 35 | namedTuple.message == namedTuple.1 36 | 37 | -------------------------------------------------------------------------------- /1c. Tuples.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /1d. Optionals.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /1d. Optionals.playground/section-1.swift: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------------------------ 2 | // Things to know: 3 | // 4 | // * An optional value is a stored value that can either hold a value or "no value at all" 5 | // 6 | // * This is similar to assigning a stored value to "nil" but Optionals work with any type of 7 | // stored value, including Int, Bool, etc. 8 | // ------------------------------------------------------------------------------------------------ 9 | 10 | // An optional declaration adds a "?" immediately after the explicit type. The following line 11 | // defines a value 'someOptional' that can either hold an Int or no value at all. In this case 12 | // we set an optional Int value to .None (similar to nil) 13 | let someOptional: Int? = .None 14 | 15 | // Let's try to convert a String to an Int 16 | // 17 | // Using the String's toInt() method, we'll try to convert a string to a numeric value. Since not 18 | // all strings can be converted to an Integer, the toInt() returns an optional, "Int?". This way 19 | // we can recognize failed conversions without having to trap exceptions or use other arcane 20 | // methods to recognize the failure. 21 | // 22 | // Here's an optional in action 23 | let notNumber = "abc" 24 | let failedConversion = notNumber.toInt() 25 | 26 | // Notice how failedConversion is 'nil', even though it's an Int 27 | failedConversion 28 | 29 | // Let's carry on with a successful conversion 30 | let possibleNumber = "123" 31 | var optionalConvertedNumber = possibleNumber.toInt() 32 | 33 | // This one worked 34 | optionalConvertedNumber 35 | 36 | // If we assign it to a constant, the type of that constant will be an Optional Int (Int?) 37 | let unwrapped = optionalConvertedNumber // 'unwrapped' is another optional 38 | 39 | // ------------------------------------------------------------------------------------------------ 40 | // Alternate syntax for Optionals 41 | // 42 | // The use of a "?" for the syntax of an optional is syntactic sugar for the use of the Generic 43 | // Optional type defined in Swift's standard library. We haven't gotten into Generics yet, but 44 | // let's not let that stop us from learning this little detail. 45 | // 46 | // These two lines are of equivalent types: 47 | let optionalA: String? = .None 48 | let optionalB: Optional = .None 49 | 50 | // ------------------------------------------------------------------------------------------------ 51 | // Unwrapping 52 | // 53 | // The difference between Int and Int? is important. Optionals essentially "wrap" their contents 54 | // which means that Int and Int? are two different types (with the latter being wrapped in an 55 | // optional. 56 | // 57 | // We can't explicity convert to an Int because that's not the same as an Int?. The following 58 | // line won't compile: 59 | // 60 | // let unwrappedInt: Int = optionalConvertedNumber 61 | 62 | // One way to do this is to "force unwrap" the value using the "!" symbol, like this: 63 | let unwrappedInt = optionalConvertedNumber! 64 | 65 | // Implicit unwrapping isn't very safe because if the optional doesn't hold a value, it will 66 | // generate a runtime error. To verify that is's safe, you can check the optional with an if 67 | // statement. 68 | if optionalConvertedNumber != .None 69 | { 70 | // It's now safe to force-unwrap because we KNOW it has a value 71 | let anotherUnwrappedInt = optionalConvertedNumber! 72 | } 73 | else 74 | { 75 | // The optional holds "no value" 76 | "Nothing to see here, go away" 77 | } 78 | 79 | // ------------------------------------------------------------------------------------------------ 80 | // Optional Binding 81 | // 82 | // We can conditionally store the unwrapped value to a stored value if the optional holds a value. 83 | // 84 | // In the following block, we'll optionally bind the Int value to a constant named 'intValue' 85 | if let intValue = optionalConvertedNumber 86 | { 87 | // No need to use the "!" suffix as intValue is not optional 88 | intValue 89 | 90 | // In fact, since 'intValue' is an Int (not an Int?) we can't use the force-unwrap. This line 91 | // of code won't compile: 92 | // intValue! 93 | } 94 | else 95 | { 96 | // Note that 'intValue' doesn't exist in this "else" scope 97 | "No value" 98 | } 99 | 100 | // We can still use optional binding to bind to another optional value, if we do so explicitly 101 | // by specifying the type of the stored value that we're binding to. 102 | if let optionalIntValue:Int? = optionalConvertedNumber 103 | { 104 | // 'optionalIntValue' is still an optional, but it's known to be safe. We can still check 105 | // it here, though, because it's still an optional. If it weren't optional, this if statement 106 | // wouldn't compile: 107 | if optionalIntValue != .None 108 | { 109 | // 'optionalIntValue' is optional, so we still use the force-unwrap here: 110 | "intValue is optional, but has the value \(optionalIntValue!)" 111 | } 112 | } 113 | 114 | // Setting an optional to 'nil' sets it to be contain "no value" 115 | optionalConvertedNumber = nil 116 | 117 | // Now if we check it, we see that it holds no value: 118 | if optionalConvertedNumber != .None 119 | { 120 | "optionalConvertedNumber holds a value (\(optionalConvertedNumber))! (this should not happen)" 121 | } 122 | else 123 | { 124 | "optionalConvertedNumber holds no value" 125 | } 126 | 127 | // We can also try optional binding on an empty optional 128 | if let optionalIntValue = optionalConvertedNumber 129 | { 130 | "optionalIntValue holds a value (\(optionalIntValue))! (this should not happen)" 131 | } 132 | else 133 | { 134 | "optionalIntValue holds no value" 135 | } 136 | 137 | // Because of the introduction of optionals, you can no longer use nil for non-optional 138 | // variables or constants. 139 | // 140 | // The following line will not compile 141 | // 142 | // var failure: String = nil // Won't compile 143 | 144 | // The following line will compile, because the String is optional 145 | var noString: String? = nil 146 | 147 | // If we create an optional without an initializer, it is automatically initialized to nil. In 148 | // future sections, we'll learn that all values must be initialized before they are used. Because 149 | // of this behavior, this variable is considered initialized, even though it holds no value: 150 | var emptyOptional: String? 151 | 152 | // Another way to implicitly unwrap an optional is during declaration of a new stored value 153 | // 154 | // Here, we create an optional that we are pretty sure will always hold a value, so we give Swift 155 | // permission to automatically unwrap it whenever it needs to. 156 | // 157 | // Note the type, "String!" 158 | var assumedString: String! = "An implicitly unwrapped optional string" 159 | 160 | // Although assumedString is still an optional (and can be treated as one), it will also 161 | // automatically unwrap if we try to use it like a normal String. 162 | // 163 | // Note that we perform no unwrapping in order to assign the value to a normal String 164 | let copyOfAssumedString: String = assumedString 165 | 166 | // Since assumedString is still an optional, we can still set it to nil. This is dangerous to do 167 | // because we assumed it is always going to hold a value (and we gave permission to automatically 168 | // unwrap it.) So by doing this, we are taking a risk: 169 | assumedString = nil 170 | 171 | // BE SURE that your implicitly unwrapped optionals actually hold values! 172 | // 173 | // The following line will compile, but will generate a runtime error because of the automatic 174 | // unwrapping. 175 | // 176 | // let errorString: String = assumedString 177 | 178 | // Like any other optional, we can still check if it holds a value: 179 | if assumedString != nil 180 | { 181 | "We have a value" 182 | } 183 | else 184 | { 185 | "No value" 186 | } 187 | -------------------------------------------------------------------------------- /1d. Optionals.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /1e. Assertions.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /1e. Assertions.playground/section-1.swift: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------------------------ 2 | // Things to know: 3 | // 4 | // * Assertions only trigger in debug mode and not in published builds 5 | // 6 | // * Assertions cause your app to terminate on the line and the debugger jumps to the line 7 | // ------------------------------------------------------------------------------------------------ 8 | 9 | // Let's start with a value... 10 | let age = 3 11 | 12 | // You can assert with a message 13 | assert(age >= 0, "A person's age cannot be negative") 14 | 15 | // You can assert without the message 16 | assert(age >= 0) 17 | 18 | -------------------------------------------------------------------------------- /1e. Assertions.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /2. Basic operations.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /2. Basic operations.playground/section-1.swift: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------------------------ 2 | // Things to know: 3 | // 4 | // * Assuming knowledge of C here, so a lot will be left out that is the the same, such as 5 | // "let i = 1 + 2" 6 | // 7 | // * Unary operators work on a single target. Of them, you have prefix operators such as !b or 8 | // postfix operators such as i++. 9 | // 10 | // * Binary operators work on two targets and are infix operators because they go between two 11 | // values, such as a + b 12 | // 13 | // * Ternary operators work on three targets. There is only one ternary: a ? b : c. 14 | // ------------------------------------------------------------------------------------------------ 15 | 16 | // We have our standard assignment operator (=). Note that the assignment operator in Swift does 17 | // not actually return a value, so the statment "if (x = y) {}" will not work. This helps prevent 18 | // accidental assignments during conditionals. 19 | var a = 10.0 20 | var b = 3.0 21 | 22 | // Assignment can also take multiple values (for Tuples): 23 | let (x, y) = (5, 6) 24 | x 25 | y 26 | 27 | // Aside from the standard mathematics operators (+, -, /, *), there is also the remainder operator 28 | // (%) which is not to be confused with modulo. They work differently because of the way remainders 29 | // are calculated for negative numbers and can be used with floating point values. 30 | var c = a / b // Floatng point result 31 | var d = a % b // Floating point remainder 32 | 33 | // ------------------------------------------------------------------------------------------------ 34 | // Range operators 35 | // 36 | // The range operator with two dots means up to but NOT including the final value. 37 | // 38 | // This is called the "Half-Closed Range Operator" 39 | for i in 1..<10 40 | { 41 | i // prints 1 through 9 42 | } 43 | 44 | // The range operator with three dots is inclusive with last value like 45 | // 46 | // This is called the "Closed Range Operator" 47 | for i in 1...10 48 | { 49 | i // prints 1 through 10 50 | } 51 | 52 | // ------------------------------------------------------------------------------------------------ 53 | // Unary, Binary and Ternary operators 54 | // 55 | // Unary prefix operators appear before their taget. Here we increment a then negate it: 56 | ++a 57 | a = -a 58 | 59 | // You can also use the uniary + operator, though it doesn't do anything 60 | a = +a 61 | 62 | // We have the compound assigment operator 63 | a += 10 64 | 65 | // The logical NOT 66 | var truefalse = true 67 | truefalse = !truefalse 68 | 69 | // Unary postfix operators appear after their target: i++ 70 | a-- 71 | a 72 | 73 | // Binary operators are infix because they appear between to targets 74 | a + b 75 | 76 | // String concatenation uses the + operator: 77 | "hello, " + "world" 78 | 79 | // To add characters, convert them to a string 80 | let dog: Character = "🐶" 81 | let cow: Character = "🐮" 82 | let dogCow = String(dog) + String(cow) 83 | 84 | // Ternary operators work on three targets: 85 | truefalse ? a : b 86 | 87 | // ------------------------------------------------------------------------------------------------ 88 | // Identity operators 89 | // 90 | // We can test if the object reference refers to the same instance of an object (as opposed to two 91 | // objects that are "equivalent" based on some compare logic.) We do this with the === operator: 92 | class myclass {} 93 | var c1 = myclass() 94 | var c2 = myclass() 95 | c1 === c2 96 | c1 === c1 97 | 98 | // String comparisons are case sensitive 99 | "abc" == "abc" 100 | "abc" == "ABC" 101 | 102 | // ------------------------------------------------------------------------------------------------ 103 | // Logical operators 104 | // 105 | // Comparisons use the logical operators with AND, OR and NOT 106 | if (true && false) || !(false && true) 107 | { 108 | "true" 109 | } 110 | -------------------------------------------------------------------------------- /2. Basic operations.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /20. Extensions.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /20. Extensions.playground/section-1.swift: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------------------------ 2 | // Things to know: 3 | // 4 | // * Similar to Objective-C categories, extensions allow you to add functionality to an existing 5 | // type (class, struct, enumeration.) 6 | // 7 | // * You do not need access to the original source code to extend a type. 8 | // 9 | // * Extensions can be applied to built-in types as well, including String, Int, Double, etc. 10 | // 11 | // * With extensions, you can: 12 | // 13 | // o Add computed properties (including static) 14 | // o Define instance methods and type methods 15 | // o Provide new convenience initializers 16 | // o Define subscripts 17 | // o Define and use new nested types 18 | // o Make an existing type conform to a protocol 19 | // 20 | // * Extensions do not support adding stored properties or property observers to a type. 21 | // 22 | // * Extensions apply all instances of a type, even if they were created before the extension was 23 | // defined. 24 | // ------------------------------------------------------------------------------------------------ 25 | 26 | // Let's take a look at how extensions are declared. Note that, unlike Objective-C categories, 27 | // extensions are not named: 28 | extension Int 29 | { 30 | // ... code here 31 | } 32 | 33 | // ------------------------------------------------------------------------------------------------ 34 | // Computed properties 35 | // 36 | // Computed properties are a poweful use of extensions. Below, we'll add native conversion from 37 | // various measurements (Km, mm, feet, etc.) to the Double class. 38 | extension Double 39 | { 40 | var kmToMeters: Double { return self * 1_000.0 } 41 | var cmToMeters: Double { return self / 100.0 } 42 | var mmToMeters: Double { return self / 1_000.0 } 43 | var inchToMeters: Double { return self / 39.3701 } 44 | var ftToMeters: Double { return self / 3.28084 } 45 | } 46 | 47 | // We can call upon Double's new computed property to convert inches to meters 48 | let oneInchInMeters = 1.inchToMeters 49 | 50 | // Similarly, we'll convert three feet to meters 51 | let threeFeetInMeters = 3.ftToMeters 52 | 53 | // ------------------------------------------------------------------------------------------------ 54 | // Initializers 55 | // 56 | // Extensions can be used to add new convenience initializers to a class, but they cannot add 57 | // new designated initializers. 58 | // 59 | // Let's see this in action: 60 | struct Size 61 | { 62 | var width = 0.0 63 | var height = 0.0 64 | } 65 | struct Point 66 | { 67 | var x = 0.0 68 | var y = 0.0 69 | } 70 | struct Rect 71 | { 72 | var origin = Point() 73 | var size = Size() 74 | } 75 | 76 | // Since we didn't provide any initializers, we can use Swift's default memberwise initializer for 77 | // the Rect. 78 | var memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0), size: Size(width: 5.0, height: 5.0)) 79 | 80 | // Let's extend Rect to add a new convenience initializer. Note that we're still responsible for 81 | // ensuring that the instance is fully initialized. 82 | extension Rect 83 | { 84 | init (center: Point, size: Size) 85 | { 86 | let originX = center.x - (size.width / 2) 87 | let originY = center.y - (size.height / 2) 88 | self.init(origin: Point(x: originX, y: originY), size: size) 89 | } 90 | } 91 | 92 | // Let's try out our new initializer: 93 | let centerRect = Rect(center: Point(x: 4.0, y: 4.0), size: Size(width: 3.0, height: 3.0)) 94 | 95 | // Remember that if a class has an initializer, Swift will not provide the default memberwise 96 | // initializer. However, since we added an initializer via an Extension, we still have access 97 | // to Swift's memberwise initializer: 98 | var anotherRect = Rect(origin: Point(x: 1.0, y: 1.0), size: Size(width: 3.0, height: 2.0)) 99 | 100 | // ------------------------------------------------------------------------------------------------ 101 | // Methods 102 | // 103 | // As you might expect, we can add methods to an existing type as well. Here's a clever little 104 | // extention to perform a task (a closure) multiple times, equal to the value stored in the Int. 105 | // 106 | // Note that the integer value is stored in 'self'. 107 | extension Int 108 | { 109 | func repititions(task: () -> ()) 110 | { 111 | for i in 0.. Int 142 | { 143 | var decimalBase = 1 144 | for _ in 0 ..< digitIndex 145 | { 146 | decimalBase *= 10 147 | } 148 | return self / decimalBase % 10 149 | } 150 | } 151 | 152 | // And we can call our subscript directly on an Int, including a literal Int type: 153 | 123456789[0] 154 | 123456789[1] 155 | 123456789[2] 156 | 123456789[3] 157 | 123456789[4] 158 | 123456789[5] 159 | 123456789[6] 160 | 161 | // ------------------------------------------------------------------------------------------------ 162 | // Nested types 163 | // 164 | // We can also add nested types to an existing type: 165 | extension Character 166 | { 167 | enum Kind 168 | { 169 | case Vowel, Consonant, Other 170 | } 171 | var kind: Kind 172 | { 173 | switch String(self) 174 | { 175 | case "a", "e", "i", "o", "u": 176 | return .Vowel 177 | case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m", 178 | "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z": 179 | return .Consonant 180 | default: 181 | return .Other 182 | } 183 | } 184 | } 185 | 186 | // Let's test out our new extension with nested types: 187 | Character("a").kind == .Vowel 188 | Character("h").kind == .Consonant 189 | Character("+").kind == .Other 190 | -------------------------------------------------------------------------------- /20. Extensions.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /21. Protocols.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /21. Protocols.playground/section-1.swift: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------------------------ 2 | // Things to know: 3 | // 4 | // * Protocols define a required set of functionality (including methods and properties) for a 5 | // class, structure or enumeration. 6 | // 7 | // * Protocols are "adopted" by a class, structure or enumeration to provide the implementation. 8 | // This is called "conforming" to a protocol. 9 | // 10 | // * As you should already be aware, protocols are often used for delegation. 11 | // 12 | // * Protcol Syntax looks like this: 13 | // 14 | // protocol SomeProtocol 15 | // { 16 | // // protocol definition goes here 17 | // } 18 | // 19 | // * Protocol adoption looks like this: 20 | // 21 | // struct SomeStructure: FirstProtocol, AnotherProtocol 22 | // { 23 | // // structure definition goes here 24 | // } 25 | // 26 | // class SomeClass: SomeSuperclass, FirstProtocol, AnotherProtocol 27 | // { 28 | // // class definition goes here 29 | // } 30 | // ------------------------------------------------------------------------------------------------ 31 | 32 | // ------------------------------------------------------------------------------------------------ 33 | // Property requirements 34 | // 35 | // * A protocol can require the conforming type to provide an instance of a property or type 36 | // property. 37 | // 38 | // * The protocol can also specify if the property must be gettable or gettable and 39 | // settable. If a protocol only requires a gettable property, the conforming class can use 40 | // a stored property or a computed property. Also, the conforming class is allowed to add 41 | // a setter if it needs. 42 | // 43 | // * Property requirements are always declared as variable types with the 'var' introducer. 44 | // 45 | // Let's take a look at a simple protocol. As you'll see, we only need to define the properties 46 | // in terms of 'get' and 'set' and do not provide the actual functionality. 47 | protocol someProtocolForProperties 48 | { 49 | // A read/write property 50 | var mustBeSettable: Int { get set } 51 | 52 | // A read-only property 53 | var doesNotNeedToBeSettable: Int { get } 54 | 55 | // A type property always uses 'class'. This is the case even if adopted by a structure or 56 | // enumeration which will use 'static' when conforming to the protocol's property. 57 | class var someTypeProperty: Int { get set } 58 | } 59 | 60 | // Let's create a more practical protocol that we can actually conform to: 61 | protocol FullyNamed 62 | { 63 | var fullName: String { get } 64 | } 65 | 66 | // A structure that conforms to FullyNamed. We won't bother creating an explicit getter or setter 67 | // for the 'fullName' property because Swift's defaults will suffice. 68 | struct Person: FullyNamed 69 | { 70 | var fullName: String 71 | } 72 | 73 | let john = Person(fullName: "John Smith") 74 | 75 | // Let's try a more complex class 76 | class Starship: FullyNamed 77 | { 78 | var prefix: String? 79 | var name: String 80 | init(name: String, prefix: String? = nil) 81 | { 82 | self.name = name 83 | self.prefix = prefix 84 | } 85 | 86 | var fullName: String 87 | { 88 | return (prefix != .None ? prefix! + " " : "") + name 89 | } 90 | } 91 | 92 | // In the class above, we use a 'name' and an optional 'prefix' to represent the full name, then 93 | // provide a computed value to fulfill our 'fullName' requirement. 94 | // 95 | // Here it is in action: 96 | var ncc1701 = Starship(name: "Enterprise", prefix: "USS") 97 | ncc1701.fullName 98 | 99 | // ------------------------------------------------------------------------------------------------ 100 | // Method Requirements 101 | // 102 | // Similar to property requirements, the protocol only defines the method type, not the 103 | // implementation. 104 | // 105 | // Variadic parameters are allowed, but default values are not. 106 | // 107 | // Method requirements can be mutating - simply use the 'mutating' keyword before the function 108 | // definition. Note that when conforming to the protocol, only classes are required to use the 109 | // 'mutating' keyword when conforming to the protocol, following the normal procedure. 110 | // 111 | // As with property requirements, type methods are always prefixed with the 'class' keyword. 112 | // 113 | // Here's a simple example that defines the requirements for a 'random' function which takes 114 | // no parameters and returns a Double: 115 | protocol RandomNumberGenerator 116 | { 117 | func random() -> Double 118 | } 119 | 120 | // Let's adopt this protocol: 121 | class LinearCongruentialGenerator: RandomNumberGenerator 122 | { 123 | var lastRandom = 42.0 124 | var m = 139956.0 125 | var a = 3877.0 126 | var c = 29573.0 127 | func random() -> Double 128 | { 129 | lastRandom = ((lastRandom * a + c) % m) 130 | return lastRandom / m 131 | } 132 | } 133 | let generator = LinearCongruentialGenerator() 134 | generator.random() 135 | generator.random() 136 | generator.random() 137 | 138 | // ------------------------------------------------------------------------------------------------ 139 | // Protocols as Types 140 | // 141 | // Protocols are types, which means they can be used where many other types are allowed, 142 | // including: 143 | // 144 | // * As a parameter to a function, method or initializer 145 | // * As the type of a constant, variable or property 146 | // * As the type of items in an Array, Dictionary or other container 147 | // 148 | // Here's a protocol used as a type. We'll create a Dice class that stores a RandomNumberGenerator 149 | // as a constant so that it can be used to customize the randomization of the dice roll: 150 | class Dice 151 | { 152 | let sides: Int 153 | let generator: RandomNumberGenerator 154 | init(sides: Int, generator: RandomNumberGenerator) 155 | { 156 | self.sides = sides 157 | self.generator = generator 158 | } 159 | 160 | func roll() -> Int 161 | { 162 | return Int(generator.random() * Double(sides)) + 1 163 | } 164 | } 165 | 166 | // Just for fun, let's roll a few: 167 | let d6 = Dice(sides: 6, generator: generator) 168 | d6.roll() 169 | d6.roll() 170 | d6.roll() 171 | d6.roll() 172 | d6.roll() 173 | d6.roll() 174 | 175 | // ------------------------------------------------------------------------------------------------ 176 | // Adding Protocol Conformance with an Extension 177 | // 178 | // Existing classes, structures and enumerations can be extended to conform to a given protocol. 179 | // 180 | // Let's start with a simple protocol we'll use for expirimentation: 181 | protocol TextRepresentable 182 | { 183 | func asText() -> String 184 | } 185 | 186 | // We'll extend our Dice class: 187 | extension Dice: TextRepresentable 188 | { 189 | func asText() -> String 190 | { 191 | return "A \(sides)-sided dice" 192 | } 193 | } 194 | 195 | // Existing instances (such as 'd6' will automatically adopt and conform to the new protocol, even 196 | // though it was declared prior to the extension. 197 | d6.asText() 198 | 199 | // ------------------------------------------------------------------------------------------------ 200 | // Declaring Protocol Adoption with an Extension 201 | // 202 | // Some types may already adopt a protocol, but do not state so in their definition. Types do not 203 | // automatically adopt to protocols whose requirements they satisfy - protocol adoption must 204 | // always be stated explicitly. 205 | // 206 | // This can be resolved with by using an empty extension. 207 | // 208 | // To showcase this, let's create a structure that conforms to the TextRepresentable protocol, but 209 | // doesn't include this as part of the definition: 210 | struct Hamster 211 | { 212 | var name: String 213 | func asText() -> String 214 | { 215 | return "A hamster named \(name)" 216 | } 217 | } 218 | 219 | // Let's verify our work: 220 | let tedTheHamster = Hamster(name: "Ted") 221 | tedTheHamster.asText() 222 | 223 | // We can add TextRepresentable conformance to Hamster by using an empty extension. 224 | // 225 | // This works because the requirements are already met, so we don't need to include any additional 226 | // functionality within the extension definition. 227 | extension Hamster: TextRepresentable 228 | { 229 | 230 | } 231 | 232 | // ------------------------------------------------------------------------------------------------ 233 | // Collections of Protocol Types 234 | // 235 | // Hamsters and Dice don't have much in common, but in our sample code above, they both conform 236 | // to the TextRepresentable protocol. Because of this, we can create an array of things that are 237 | // TextRepresentable which includes each: 238 | let textRepresentableThigns: [TextRepresentable] = [d6, tedTheHamster] 239 | 240 | // We can now loop through each and produce its text representation: 241 | for thing in textRepresentableThigns 242 | { 243 | thing.asText() 244 | } 245 | 246 | // ------------------------------------------------------------------------------------------------ 247 | // Protocol Inheritance 248 | // 249 | // Protocols can inherit from other protocols in order to add further requirements. The syntax 250 | // for this is similar to a class ineriting from its superclass. 251 | // 252 | // Let's create a new text representable type, inherited from TextRepresentable: 253 | protocol PrettyTextRepresentable: TextRepresentable 254 | { 255 | func asPrettyText() -> String 256 | } 257 | 258 | // Let's make our Dice a little prettier. 259 | // 260 | // Note that the Dice already knows that it conforms to the TextRepresentable, which means we can 261 | // call asText() inside our asPrettyText() method. 262 | extension Dice: PrettyTextRepresentable 263 | { 264 | func asPrettyText() -> String 265 | { 266 | return "The pretty version of " + asText() 267 | } 268 | } 269 | 270 | // We can test our work: 271 | d6.asPrettyText() 272 | 273 | // ------------------------------------------------------------------------------------------------ 274 | // Protocol Composition 275 | // 276 | // Protocols can be combined such that a type conforms to each of them. For example, a person can 277 | // be an aged person as well as a named person. 278 | // 279 | // Protocol Composition is a syntactic method of declaring that an instance conforms to a set 280 | // of protocols. It takes the form "protocol". There can be as 281 | // many protocols between the angle brackets as you need. 282 | // 283 | // Let's start by creating a couple of protocols for expirimentation: 284 | protocol Named 285 | { 286 | var name: String { get } 287 | } 288 | protocol Aged 289 | { 290 | var age: Int { get } 291 | } 292 | 293 | // Here, we declare an Individual that conforms to both Name and Age protocols: 294 | struct Individual: Named, Aged 295 | { 296 | var name: String 297 | var age: Int 298 | } 299 | 300 | // Here, we can see the protocol composition at work as the parameter into the wishHappyBirthday() 301 | // function: 302 | func wishHappyBirthday(celebrator: protocol) -> String 303 | { 304 | return "Happy Birthday \(celebrator.name) - you're \(celebrator.age)!" 305 | } 306 | 307 | // If we call the member, we can see the celebratory wish for this individual: 308 | wishHappyBirthday(Individual(name: "Bill", age: 31)) 309 | 310 | // ------------------------------------------------------------------------------------------------ 311 | // Checking for Protocol Conformance 312 | // 313 | // We can use 'is' and 'as' for testing for protocol conformance, just as we've seen in the 314 | // section on Type Casting. 315 | // 316 | // In order for this to work with protocols, they must be marked with an "@objc" attribute. See 317 | // further down in this playground for a special note about the @objc attribute. 318 | // 319 | // Let's create a new protocol with the proper prefix so that we can investigate: 320 | @objc protocol HasArea 321 | { 322 | var area: Double { get } 323 | } 324 | 325 | class Circle: HasArea 326 | { 327 | let pi = 3.14159 328 | var radius: Double 329 | var area: Double { return pi * radius * radius } 330 | init(radius: Double) { self.radius = radius } 331 | } 332 | class Country: HasArea 333 | { 334 | var area: Double 335 | init(area: Double) { self.area = area } 336 | } 337 | class Animal 338 | { 339 | var legs: Int 340 | init(legs: Int) { self.legs = legs } 341 | } 342 | 343 | // We can store our objects into an array of type AnyObject[] 344 | let objects: [AnyObject] = 345 | [ 346 | Circle(radius: 3.0), 347 | Country(area: 4356947.0), 348 | Animal(legs: 4) 349 | ] 350 | 351 | // Then we can test each for conformance to HasArea: 352 | objects[0] is HasArea 353 | objects[1] is HasArea 354 | objects[2] is HasArea 355 | 356 | // ------------------------------------------------------------------------------------------------ 357 | // Optional Protocol Requirements 358 | // 359 | // Sometimes it's convenient to declare protocols that have one or more requirements that are 360 | // optional. This is done by prefixing those requirements with the 'optional' keyword. 361 | // 362 | // The term "optional protocol" refers to protocols that are optional in a very similar since to 363 | // optionals we've seen in the past. However, rather than stored values that can be nil, they 364 | // can still use optional chaining and optional binding for determining if an optional requirement 365 | // has been satisfied and if so, using that requirement. 366 | // 367 | // As with Protocol Conformance, a protocol that uses optional requirements must also be prefixed 368 | // with the '@objc' attribute. 369 | // 370 | // A special note about @objc attribute: 371 | // 372 | // * In order to check if an instance of a class conforms to a given protocol, that protocol must 373 | // be declared with the @objc attribute. 374 | // 375 | // * This is also the case with optional requirements in protocols. In order to use the optional 376 | // declaration modifier, the protocol must be declared with the @objc attribute. 377 | // 378 | // * Additionally, any class, structure or enumeration that owns an instance that conforms to a 379 | // protocol declared with the @objc attribute must also be declared with the @objc attribute. 380 | // 381 | // Here's another simple protocol that uses optional requrements: 382 | @objc protocol CounterDataSource 383 | { 384 | optional func incrementForCount(count: Int) -> Int 385 | optional var fixedIncrement: Int { get } 386 | } 387 | 388 | // In the class below, we'll see that checking to see if an instance conforms to a specific 389 | // requirement is similar to checking for (and accessing) optionals. We'll use optional chaining 390 | // for these optional reqirements: 391 | @objc class Counter 392 | { 393 | var count = 0 394 | var dataSource: CounterDataSource? 395 | func increment() 396 | { 397 | // Does the dataSource conform to the incrementForCount method? 398 | if let amount = dataSource?.incrementForCount?(count) 399 | { 400 | count += amount 401 | } 402 | // If not, does it conform to the fixedIncrement variable requirement? 403 | else if let amount = dataSource?.fixedIncrement? 404 | { 405 | count += amount 406 | } 407 | } 408 | } 409 | -------------------------------------------------------------------------------- /21. Protocols.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /22. Generics.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /22. Generics.playground/section-1.swift: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------------------------ 2 | // Things to know: 3 | // 4 | // * Generics allow flexible, reusable functions and types that can work with any type, subject 5 | // to restrictions that you define. 6 | // 7 | // * Swift's Array and Dictionary are both Generics. 8 | // 9 | // * Generics can be applied to Functions, Structures, Classes and Enumerations. 10 | // ------------------------------------------------------------------------------------------------ 11 | 12 | // The problem that Generics solve 13 | // 14 | // Consider the following function which can swap two Ints. 15 | func swapTwoInts(inout a: Int, inout b: Int) 16 | { 17 | let tmp = a 18 | a = b 19 | b = tmp 20 | } 21 | 22 | // What if we wanted to swap Strings? Or any other type? We would need to write a lot of different 23 | // swap functions. Instead, let's use Generics. Consider the following generic function: 24 | func swapTwoValues(inout a: T, inout b: T) 25 | { 26 | let tmp = a 27 | a = b 28 | b = tmp 29 | } 30 | 31 | // The 'swapTwoValues()' function is a generic function in a sense that some or all of the types 32 | // that it works on are generic (i.e., specific only to the calls placed on that function.) 33 | // 34 | // Study the first line of the function and notice the use of and the type for 'a' and 'b' as 35 | // type T. In this case, T is just a placeholder for a type and by studying this function, we can 36 | // see that both 'a' and 'b' are the same type. 37 | // 38 | // If we call this function with two Integers, it will treat the function as one declared to accept 39 | // two Ints, but if we pass in two Strings, it will work on Strings. 40 | // 41 | // If we study the body of the function, we'll see that it is coded in such a way as to work with 42 | // any type passed in: The 'tmp' parameter's type is inferred by the value 'a' and 'a' and 'b' must 43 | // be assignable. If any of this criteria are not met, a compilation error will appear for the 44 | // function call the tries to call swapTwoValues() with a type that doesn't meet this criteria. 45 | // 46 | // Although we're using T as the name for our type placeholder, we can use any name we wish, though 47 | // T is a common placeholder for single type lists. If we were to create a new implementation of 48 | // the Dictionary class, we would want to use two type parameters and name them effectively, such 49 | // as . 50 | // 51 | // A type placholder can also be used to define the return type. 52 | // 53 | // Let's call it a few times to see it in action: 54 | var aInt = 3 55 | var bInt = 4 56 | swapTwoValues(&aInt, &bInt) 57 | aInt 58 | bInt 59 | 60 | var aDouble = 3.3 61 | var bDouble = 4.4 62 | swapTwoValues(&aDouble, &bDouble) 63 | aDouble 64 | bDouble 65 | 66 | var aString = "three" 67 | var bString = "four" 68 | swapTwoValues(&aString, &bString) 69 | aString 70 | bString 71 | 72 | // ------------------------------------------------------------------------------------------------ 73 | // Generic Types 74 | // 75 | // So far we've seen how to apply Generics to a function, let's see how they can be applied to 76 | // a struct. We'll define a standard 'stack' implementation which works like an array that can 77 | // "push" an element to the end of the array, or "pop" an element off of the end of the array. 78 | // 79 | // As you can see, the type placeholder, once defined for a struct, can be used anywhere in that 80 | // struct to represent the given type. In the code below, the the type placeholder is used as the 81 | // type for a property, the input parameter for a method and the return value for a method. 82 | struct Stack 83 | { 84 | var items = [T]() 85 | mutating func push(item: T) 86 | { 87 | items.append(item) 88 | } 89 | mutating func pop() -> T 90 | { 91 | return items.removeLast() 92 | } 93 | } 94 | 95 | // Let's use our new Stack: 96 | var stackOfStrings = Stack() 97 | 98 | stackOfStrings.push("uno") 99 | stackOfStrings.push("dos") 100 | stackOfStrings.push("tres") 101 | stackOfStrings.push("cuatro") 102 | 103 | stackOfStrings.pop() 104 | stackOfStrings.pop() 105 | stackOfStrings.pop() 106 | stackOfStrings.pop() 107 | 108 | // ------------------------------------------------------------------------------------------------ 109 | // Type constraints 110 | // 111 | // So far, our type parameters are completely Generic - they can represent any given type. 112 | // Sometimes we may want to apply constraints to those types. For example, the Swift Dictionary 113 | // type uses generics and places a constraint on the key's type that it must be hashable (i.e., it 114 | // must conform to the Hashable protocol, defined in the Swift standard library.) 115 | // 116 | // Constraints are defined with the following syntax: 117 | func doSomethingWithKeyValue(someKey: KeyType, someValue: ValueType) 118 | { 119 | // Our keyType is known to be a Hashable, so we can use the hashValue defined by that protocol 120 | // shown here: 121 | someKey.hashValue 122 | 123 | // 'someValue' is an unknown type to us, we'll just drop it here in case it's ever used so we 124 | // can see the value 125 | someValue 126 | } 127 | 128 | // Let's see type constraints in action. We'll create a function that finds a given value within 129 | // an array and returns an optional index into the array where the first element was found. 130 | // 131 | // Take notice the constraint "Equatable" on the type T, which is key to making this function 132 | // compile. Without it, we would get an error on the conditional statement used to compare each 133 | // element from the array with the value being searched for. By including the Equatable, we tell 134 | // the generic function that it is guaranteed to receive only values that meet that specific 135 | // criteria. 136 | func findIndex(array: [T], valueToFind: T) -> Int? 137 | { 138 | for (index, value) in enumerate(array) 139 | { 140 | if value == valueToFind 141 | { 142 | return index 143 | } 144 | } 145 | return nil 146 | } 147 | 148 | // Let's try a few different inputs 149 | let doubleIndex = findIndex([3.14159, 0.1, 0.25], 9.3) 150 | let stringIndex = findIndex(["Mike", "Malcolm", "Andrea"], "Andrea") 151 | 152 | // ------------------------------------------------------------------------------------------------ 153 | // Associated types 154 | // 155 | // Protocols use a different method of defining generic types, called Associated Types, which use 156 | // type inference combined with Type Aliases. 157 | // 158 | // Let's jump right into some code: 159 | protocol Container 160 | { 161 | typealias ItemType 162 | mutating func append(item: ItemType) 163 | var count: Int { get } 164 | subscript(i: Int) -> ItemType { get } 165 | } 166 | 167 | // In the example above, we declare a Type Alias called ItemType, but because we're declaring 168 | // a protocol, we're only declaring the requirement for the conforming target to provide the 169 | // actual type alias. 170 | // 171 | // With Generics, the type of ItemType can actually be inferred, such that it provides the correct 172 | // types for the append() and the subscript implementations. 173 | // 174 | // Let's see this in action as we turn our Stack into a container: 175 | 176 | struct StackContainer : Container 177 | { 178 | // Here we find our original stack implementation, unmodified 179 | 180 | var items = [T]() 181 | mutating func push(item: T) 182 | { 183 | items.append(item) 184 | } 185 | mutating func pop() -> T 186 | { 187 | return items.removeLast() 188 | } 189 | 190 | // Below, we conform to the protocol 191 | 192 | mutating func append(item: T) 193 | { 194 | self.push(item) 195 | } 196 | var count: Int 197 | { 198 | return items.count 199 | } 200 | subscript(i: Int) -> T 201 | { 202 | return items[i] 203 | } 204 | } 205 | 206 | // The new StackContainer is now ready to go. You may notice that it does not include the 207 | // typealias that was required as part of the Container protocol. This is because the all of the 208 | // places where an ItemType would be used are using T. This allows Swift to perform a backwards 209 | // inferrence that ItemType must be a type T, and it allows this requirement to be met. 210 | // 211 | // Let's verify our work: 212 | var stringStack = StackContainer() 213 | stringStack.push("Albert") 214 | stringStack.push("Andrew") 215 | stringStack.push("Betty") 216 | stringStack.push("Jacob") 217 | stringStack.pop() 218 | stringStack.count 219 | 220 | var doubleStack = StackContainer() 221 | doubleStack.push(3.14159) 222 | doubleStack.push(42.0) 223 | doubleStack.push(1_000_000) 224 | doubleStack.pop() 225 | doubleStack.count 226 | 227 | // We can also extend an existing types to conform to our new generic protocol. As it turns out 228 | // Swift's built-in Array class already supports the requirements of our Container. 229 | // 230 | // Also, since the protocol's type inferrence method of implementing Generics, we can extend 231 | // String without the need to modify String other than to extend it to conform to the protocol: 232 | extension Array: Container {} 233 | 234 | // ------------------------------------------------------------------------------------------------ 235 | // Where Clauses 236 | // 237 | // We can further extend our constraints on a type by including where clauses as part of a type 238 | // parameter list. Where clauses provide a means for more constraints on associated types and/or 239 | // one or more equality relationships between types and associated types. 240 | // 241 | // Let's take a look at a where clause in action. We'll define a function that works on two 242 | // different containers that that must contain the same type of item. 243 | func allItemsMatch 244 | 245 | (someContainer: C1, anotherContainer: C2) -> Bool 246 | { 247 | // Check that both containers contain the same number of items 248 | if someContainer.count != anotherContainer.count 249 | { 250 | return false 251 | } 252 | 253 | // Check each pair of items to see if they are equivalent 254 | for i in 0.. 2 | 4 | 5 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /23. Advanced Operators.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /23. Advanced Operators.playground/section-1.swift: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------------------------ 2 | // Things to know: 3 | // 4 | // * Arithmetic operators in Swift do not automatically overflow. Adding two values that overflow 5 | // their type (for example, storing 300 in a UInt8) will cause an error. There are special 6 | // operators that allow overflow, including dividing by zero. 7 | // 8 | // * Swift allows developers to define their own operators, including those that Swift doesn't 9 | // currently define. You can even specify the associativity and precedence for operators. 10 | // ------------------------------------------------------------------------------------------------ 11 | 12 | // ------------------------------------------------------------------------------------------------ 13 | // Bitwise Operators 14 | // 15 | // The Bitwise operators (AND, OR, XOR, etc.) in Swift effeectively mirror the functionality that 16 | // you're used to with C++ and Objective-C. 17 | // 18 | // We'll cover them briefly. The odd formatting is intended to help show the results: 19 | var andResult: UInt8 = 20 | 0b00101111 & 21 | 0b11110100 22 | // 0b00100100 <- result 23 | 24 | var notResult: UInt8 = 25 | ~0b11110000 26 | // 0b00001111 <- result 27 | 28 | var orResult: UInt8 = 29 | 0b01010101 | 30 | 0b11110000 31 | // 0b11110101 <- result 32 | 33 | var xorResult: UInt8 = 34 | 0b01010101 ^ 35 | 0b11110000 36 | // 0b10100101 <- result 37 | 38 | // Shifting in Swift is slightly different than in C++. 39 | // 40 | // A lesser-known fact about C++ is that the signed right-shift of a signed value is 41 | // implementation specific. Most compilers do what most programmers expect, which is an arithmetic 42 | // shift (which maintains the sign bit and shifts 1's into the word from the right.) 43 | // 44 | // Swift defines this behavior to be what you expect, shifting signed values to the right will 45 | // perform an arithmetic shift using two's compilment. 46 | var leftShiftUnsignedResult: UInt8 = 32 << 1 47 | var leftShiftSignedResult: Int8 = 32 << 1 48 | var leftShiftSignedNegativeResult: Int8 = -32 << 1 49 | 50 | var rightShiftUnsignedResult: UInt8 = 32 >> 1 51 | var rightShiftSignedResult: Int8 = 32 >> 1 52 | var rightShiftSignedNegativeResult: Int8 = -32 >> 1 53 | 54 | // ------------------------------------------------------------------------------------------------ 55 | // Overflow operators 56 | // 57 | // If an arithmetic operation (specifically addition (+), subtraction (-) and multiplication (*)) 58 | // results in a value too large or too small for the constant or variable that the result is 59 | // intended for, Swift will produce an overflow/underflow error. 60 | // 61 | // The last two lines of this code block will trigger an overflow/underflow: 62 | // 63 | // var positive: Int8 = 120 64 | // var negative: Int8 = -120 65 | // var overflow: Int8 = positive + positive 66 | // var underflow: Int8 = negative + negative 67 | // 68 | // This is also true for division by zero, which can be caused with the division (/) or remainder 69 | // (%) operators. 70 | // 71 | // Sometimes, however, overflow and underflow behavior is exactly what the programmer may intend, 72 | // so Swift provides specific overflow/underflow operators which will not trigger an error and 73 | // allow the overflow/underflow to perform as we see in C++/Objective-C. 74 | // 75 | // Special operators for division by zero are also provided, which return 0 in the case of a 76 | // division by zero. 77 | // 78 | // Here they are, in all their glory: 79 | var someValue: Int8 = 120 80 | var aZero: Int8 = someValue - someValue 81 | var overflowAdd: Int8 = someValue &+ someValue 82 | var underflowSub: Int8 = -someValue &- someValue 83 | var overflowMul: Int8 = someValue &* someValue 84 | var divByZero: Int8 = 100 &/ aZero 85 | var remainderDivByZero: Int8 = 100 &% aZero 86 | 87 | // ------------------------------------------------------------------------------------------------ 88 | // Operator Functions (a.k.a., Operator Overloading) 89 | // 90 | // Most C++ programmers should be familiar with the concept of operator overloading. Swift offers 91 | // the same kind of functionality as well as additional functionality of specifying the operator 92 | // precednce and associativity. 93 | // 94 | // The most common operators will usually take one of the following forms: 95 | // 96 | // * prefix: the operator appears before a single identifier as in "-a" or "++i" 97 | // * postfix: the operator appears after a single identifier as in "i++" 98 | // * infix: the operator appears between two identifiers as in "a + b" or "c / d" 99 | // 100 | // These are specified with the three attributes, @prefix, @postfix and @infix. 101 | // 102 | // There are more types of operators (which use different attributes, which we'll cover shortly. 103 | // 104 | // Let's define a Vector2D class to work with: 105 | struct Vector2D 106 | { 107 | var x = 0.0 108 | var y = 0.0 109 | } 110 | 111 | // Next, we'll define a simple vector addition (adding the individual x & y components to create 112 | // a new vector.) 113 | // 114 | // Since we're adding two Vector2D instances, we'll use operator "+". We want our addition to take 115 | // the form "vectorA + vectorB", which means we'll be defining an infix operator. 116 | // 117 | // Here's what that looks like: 118 | func + (left: Vector2D, right: Vector2D) -> Vector2D 119 | { 120 | return Vector2D(x: left.x + right.x, y: left.y + right.y) 121 | } 122 | 123 | // Let's verify our work: 124 | var a = Vector2D(x: 1.0, y: 2.0) 125 | var b = Vector2D(x: 3.0, y: 4.0) 126 | var c = a + b 127 | 128 | // We've seen the infix operator at work so let's move on to the prefix and postfix operators. 129 | // We'll define a prefix operator that negates the vector taking the form (result = -value): 130 | prefix func - (vector: Vector2D) -> Vector2D 131 | { 132 | return Vector2D(x: -vector.x, y: -vector.y) 133 | } 134 | 135 | // Check our work: 136 | c = -a 137 | 138 | // Next, let's consider the common prefix increment operator (++a) and postfix increment (a++) 139 | // operations. Each of these performs the operation on a single value whle also returning the 140 | // appropriate result (either the original value before the increment or the value after the 141 | // increment.) 142 | // 143 | // Each will either use the @prefix or @postfix attribute, but since they also modify the value 144 | // they are also @assigmnent operators (and make use of inout for the parameter.) 145 | // 146 | // Let's take a look: 147 | prefix func ++ (inout vector: Vector2D) -> Vector2D 148 | { 149 | vector = vector + Vector2D(x: 1.0, y: 1.0) 150 | return vector 151 | } 152 | 153 | postfix func ++ (inout vector: Vector2D) -> Vector2D 154 | { 155 | var previous = vector; 156 | vector = vector + Vector2D(x: 1.0, y: 1.0) 157 | return previous 158 | } 159 | 160 | // And we can check our work: 161 | ++c 162 | c++ 163 | c 164 | 165 | // Equivalence Operators allow us to define a means for checking if two values are the same 166 | // or equivalent. They can be "equal to" (==) or "not equal to" (!=). These are simple infix 167 | // opertors that return a Bool result. 168 | // 169 | // Let's also take a moment to make sure we do this right. When comparing floating point values 170 | // you can either check for exact bit-wise equality (a == b) or you can compare values that are 171 | // "very close" by using an epsilon. It's important to recognize the difference, as there are 172 | // cases when IEEE floating point values should be equal, but are actually represented differently 173 | // in their bit-wise format because they were calculated differently. In these cases, a simple 174 | // equality comparison will not suffice. 175 | // 176 | // So here are our more robust equivalence operators: 177 | let Epsilon = 0.1e-7 178 | 179 | func == (left: Vector2D, right: Vector2D) -> Bool 180 | { 181 | if abs(left.x - right.x) > Epsilon { return false } 182 | if abs(left.y - right.y) > Epsilon { return false } 183 | return true 184 | } 185 | func != (left: Vector2D, right: Vector2D) -> Bool 186 | { 187 | // Here, we'll use the inverted result of the "==" operator: 188 | return !(left == right) 189 | } 190 | 191 | // ------------------------------------------------------------------------------------------------ 192 | // Custom Operators 193 | // 194 | // So far, we've been defining operator functions for operators that Swift understands and 195 | // for which Swift provides defined behaviors. We can also define our own custom operators for 196 | // doing other interestig things. 197 | // 198 | // For example, Swift doesn't support the concept of a "vector normalization" or "cross product" 199 | // because this functionality doesn't apply to any of the types Swift offers. 200 | // 201 | // Let's keep it simple, though. How about an operator that adds a vector to itself. Let's make 202 | // this a prefix operator that takes the form "+++value" 203 | // 204 | // First, we we must introduce Swift to our new operator. The syntax takes the form of the 205 | // 'operator' keyword, folowed by either 'prefix', 'postfix' or 'infix': 206 | // 207 | // Swift meet operator, operator meet swift: 208 | prefix operator +++ {} 209 | 210 | // Now we can declare our new operator: 211 | prefix func +++ (inout vector: Vector2D) -> Vector2D 212 | { 213 | vector = vector + vector 214 | return vector 215 | } 216 | 217 | // Let's check our work: 218 | var someVector = Vector2D(x: 5.0, y: 9.0) 219 | +++someVector 220 | 221 | // ------------------------------------------------------------------------------------------------ 222 | // Precedence and Associativity for Custom Infix Operators 223 | // 224 | // Custom infix operators can define their own associativity (left-to-right or right-to-left or 225 | // none) as well as a precedence for determining the order of operations. 226 | // 227 | // Associativity values are 'left' or 'right' or 'none'. 228 | // 229 | // Precedence values are ranked with a numeric value. If not specified, the default value is 100. 230 | // Operations are performed in order of precedence, with higher values being performed first. The 231 | // precedence values are relative to all other precedence values for other operators. The following 232 | // are the default values for operator precedence in the Swift standard library: 233 | // 234 | // 160 (none): Operators << >> 235 | // 150 (left): Operators * / % &* &/ &% & 236 | // 140 (left): Operators + - &+ &- | ^ 237 | // 135 (none): Operators .. ... 238 | // 132 (none): Operators is as 239 | // 130 (none): Operators < <= > >= == != === !== ~= 240 | // 120 (left): Operators && 241 | // 110 (left): Operators || 242 | // 100 (right): Operators ?: 243 | // 90 (right): Operators = *= /= %= += -= <<= >>= &= ^= |= &&= ||= 244 | // 245 | // Let's take a look at how we define a new custom infix operator with left associativity and a 246 | // precedence of 140. 247 | // 248 | // We'll define a function that adds the 'x' components of two vectors, but subtracts the 'y' 249 | // components. We'll call this the "+-" operator: 250 | infix operator +- { associativity left precedence 140 } 251 | func +- (left: Vector2D, right: Vector2D) -> Vector2D 252 | { 253 | return Vector2D(x: left.x + right.x, y: left.y - right.y) 254 | } 255 | 256 | // Check our work. Let's setup a couple vectors that result in a value of (0, 0): 257 | var first = Vector2D(x: 5.0, y: 5.0) 258 | var second = Vector2D(x: -5.0, y: 5.0) 259 | first +- second 260 | -------------------------------------------------------------------------------- /23. Advanced Operators.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /3. Strings and Characters.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /3. Strings and Characters.playground/section-1.swift: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------------------------ 2 | // Things to know: 3 | // 4 | // * Strings are bridged perfectly with NSString class 5 | // 6 | // * All Strings are Unicode compliant 7 | // ------------------------------------------------------------------------------------------------ 8 | 9 | // Here's a string 10 | var str: String = "Albatross! Get your albatross here!" 11 | 12 | // Strings have some special character constants. They are: 13 | "\0" // Null character 14 | "\\" // Backslash 15 | "\t" // Tab 16 | "\n" // Newline 17 | "\r" // Carriage return 18 | "\"" // Double quote 19 | "\'" // Single quote 20 | "\u{24}" // Single-byte Unicode 21 | "\u{2665}" // Double-byte unicode 22 | "\u{0001F49c}" // Four-byte unicode 23 | 24 | // Initializing an empty string - these are equivalent to each other 25 | var emptyString = "" 26 | var anotherEmptyString = String() 27 | 28 | // Use 'isEmpty' to check for empty String 29 | if emptyString.isEmpty 30 | { 31 | "Yep, it's empty" 32 | } 33 | 34 | // Strings are VALUE TYPES, but they're referenced for performance so they are only copied on 35 | // modification. 36 | func somefunc(a: String) 37 | { 38 | var b = a 39 | b = "Changed!" 40 | } 41 | 42 | var originalString = "Original" 43 | somefunc(originalString) 44 | originalString // not modified 45 | 46 | // You can iterate over a string like this: 47 | for character in originalString 48 | { 49 | character 50 | } 51 | 52 | // Characters use double-quotes to specify them, so you must be explicit if you want a Character 53 | // instead of a String: 54 | var notAString: Character = "t" 55 | 56 | // There is no length or count member of string, you have to use the global function, 57 | // countElements() 58 | // 59 | // This is much like calling strlen in which it iterates over the Unicode string and counts 60 | // characters. Note that Unicode chars are different lenghts, so this is a non-trivial process. 61 | // 62 | // “Note also that the character count returned by countElements is not always the same as the 63 | // length property of an NSString that contains the same characters. The length of an NSString is 64 | // based on the number of 16-bit code units within the string’s UTF-16 representation and not the 65 | // number of Unicode characters within the string. To reflect this fact, the length property from 66 | // NSString is called utf16count when it is accessed on a Swift String value.” 67 | countElements(originalString) 68 | 69 | // Strings can be concatenated with strings and characters 70 | var helloworld = "hello, " + "world" 71 | 72 | // ------------------------------------------------------------------------------------------------ 73 | // String interpolation 74 | // 75 | // String interpolation refers to referencing values inside of a String. This works different from 76 | // printf-style functions, in that String interpolation allows you to insert the values directly 77 | // in-line with the string, rather than as additional parameters to a formatting function. 78 | let multiplier = 3 79 | let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)" 80 | 81 | // ------------------------------------------------------------------------------------------------ 82 | // String comparison 83 | // 84 | // String comparison is case-sensitive and can be compared for equality 85 | var str1 = "We're a lot alike, you and I." 86 | var str2 = "We're a lot alike, you and I." 87 | str1 == str2 88 | 89 | // You can also compare prefix and suffix equality: 90 | str1.hasPrefix("We're") 91 | str2.hasSuffix("I.") 92 | str1.hasPrefix("I.") 93 | 94 | -------------------------------------------------------------------------------- /3. Strings and Characters.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /4a. Arrays.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /4a. Arrays.playground/section-1.swift: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------------------------ 2 | // Things to know: 3 | // 4 | // * Arrays are ordered lists of elements 5 | // 6 | // * The types of values that can be stored in an array must always be made clear either through 7 | // explicit type annotation or through type inference and does not have to be a base class type. 8 | // 9 | // * Arrays are type-safe and always clear about what they contain. 10 | // 11 | // * Arrays are value types, but Swift is smart about only copying when necessary to improve 12 | // performance. 13 | // 14 | // * Immutable arrays are immutable in terms of the array itself and the contents of the array. 15 | // This means you can't add/remove an element nor can you modify an element of an immutable 16 | // array. 17 | // ------------------------------------------------------------------------------------------------ 18 | 19 | // Create an array of Strings 20 | var someArray = Array() 21 | 22 | // Shorter, more common way to define an array of Strings 23 | var shorter: [String] 24 | 25 | // This is an array literal. Since all members are of type String, this will create a String array. 26 | // 27 | // If all members are not the same type (or cannot be inferred to a homogenized type) then you 28 | // would get a compiler error. 29 | ["Eggs", "Milk"] 30 | 31 | // Let's create an array with some stuff in it. We'll use an explicit String type: 32 | var commonPets: [String] = ["Cats", "Dogs"] 33 | 34 | // We can also let Swift infer the type of the Array based on the type of the initializer members. 35 | // 36 | // The folowing is an array of Strings 37 | var shoppingList = ["Eggs", "Milk"] 38 | 39 | // ------------------------------------------------------------------------------------------------ 40 | // Accessing and modifying an Array 41 | // 42 | // We can get the number of elements 43 | shoppingList.count 44 | 45 | // We can check to see if it's empty 46 | if !shoppingList.isEmpty { "it's not empty" } 47 | 48 | // We can append to the end 49 | shoppingList.append("Flour") 50 | shoppingList.append("Baking Powder") 51 | shoppingList.count 52 | 53 | // We can append another array of same type 54 | shoppingList += ["Chocolate Spread", "Cheese", "Butter"] 55 | shoppingList.count 56 | 57 | // We can get elements from the array by indexing them 58 | shoppingList[0] 59 | shoppingList[1] 60 | 61 | // We can modify an existing item 62 | shoppingList[0] = "Six Eggs" 63 | 64 | // We can use a range operator to modify existing items. This operation modifies a range with 65 | // a target range. If the target range has more or fewer elements in it, the size of the array 66 | // will be adjusted. 67 | // 68 | // Here, we replace 3 items with only two, removing an item: 69 | shoppingList[4...6] = ["Banannas", "Apples"] 70 | 71 | // Or we can replace two items with three, inserting a new item: 72 | shoppingList[4..<6] = ["Limes", "Mint leaves", "Sugar"] 73 | 74 | // We can insert an item at a given index 75 | shoppingList.insert("Maple Syrup", atIndex: 3) 76 | 77 | // We can remove the last element. During this, we can preserve the value of what was removed 78 | // into a stored value 79 | let apples = shoppingList.removeLast() 80 | 81 | // ------------------------------------------------------------------------------------------------ 82 | // Enumeration 83 | // 84 | // We can iterate over the the array using a for-in loop 85 | for item in shoppingList 86 | { 87 | item 88 | } 89 | 90 | // We can also use the the enumerate() method to return a tuple containing the index and value 91 | // for each element: 92 | for (index, value) in enumerate(shoppingList) 93 | { 94 | index 95 | value 96 | } 97 | 98 | // ------------------------------------------------------------------------------------------------ 99 | // Creating and initializing an array 100 | // 101 | // Earlier, we saw how to declare an array of a given type. Here, we see how to declare an array 102 | // type and then assign it to a stored value, which gets its type by inference: 103 | var someInts = [Int]() 104 | 105 | // Add the number '3' to the array 106 | someInts.append(3) 107 | someInts 108 | 109 | // We can assign it to an empty array, but we don't modify the type, since someInts is already 110 | // an Int[] type. 111 | someInts = [] 112 | 113 | // We can initialize an array and and fill it with default values 114 | var threeDoubles = [Double](count: 3, repeatedValue: 3.3) 115 | 116 | // We can also use the Array initializer to fill it with default values. Note that we don't need to 117 | // specify type since it is inferred: 118 | var anotherThreeDoubles = Array(count: 3, repeatedValue: 2.5) 119 | 120 | // If you store an array in a constant, it is considered "Immutable" 121 | let immutableArray = ["a", "b"] 122 | 123 | // In terms of immutability, it's important to consider that the array and its contents are treated 124 | // separately. Therefore, you can change the contents of an immutable array, but you can't change 125 | // the array itself. 126 | // 127 | // We can't change the contents of an immutable array: 128 | // 129 | // immutableArray[0] = "b" 130 | // 131 | // Nor can we change the size or add an element, you will get a compiler error: 132 | // 133 | // immutableArray += "c" 134 | 135 | // ------------------------------------------------------------------------------------------------ 136 | // Arrays are Value Types 137 | // 138 | // Arrays are value types that only copy when necessary, which is only when the array itself 139 | // changes (not the contents.) 140 | // 141 | // Here are three copies of an array: 142 | var a = [1, 2, 3] 143 | var b = a 144 | var c = a 145 | 146 | // However, if we change the contents of one array (mutating it), then it is copied and becomes its 147 | // own unique entity: 148 | a[0] = 42 149 | b[0] 150 | c[0] 151 | 152 | // Now that we've changed a, it should have been copied to its own instance. Let's double-check 153 | // that only b & c are the same: 154 | a 155 | b 156 | c 157 | 158 | // The same is true if we mutate the array in other ways (mofify the array's size)... 159 | b.append(4) 160 | 161 | // Now, we have three different arrays... 162 | a 163 | b 164 | c 165 | 166 | -------------------------------------------------------------------------------- /4a. Arrays.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /4b. Dictionaries.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /4b. Dictionaries.playground/section-1.swift: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------------------------ 2 | // Things to know: 3 | // 4 | // * Dictionaries store multiple values of the same type, each associated with a key which acts as 5 | // an identifier for that value within the dictionary. 6 | // 7 | // * Dictionaries are type-safe and always clear about what they contain. 8 | // 9 | // * The types of values that can be stored in a dictionary must always be made clear either 10 | // through explicit type annotation or through type inference. 11 | // ------------------------------------------------------------------------------------------------ 12 | 13 | // ------------------------------------------------------------------------------------------------ 14 | // Creating a dictionary 15 | // 16 | // This is a Dictionary literal. They contain a comma-separated list of key:value pairs: 17 | ["TYO": "Tokyo", "DUB": "Dublin"] 18 | 19 | // Let's use that literal to define and initialize a Dictionary. 20 | // 21 | // In this case, we use type annotation to explicitly declare a Dictionary containing String keys 22 | // and String values. This uses the syntactic sugar "[ KeyType: ValueType ]" to declare the 23 | // dictionary. 24 | var airports: [String : String] = ["TYO": "Tokyo", "DUB": "Dublin", "APL": "Apple Intl"] 25 | 26 | // The declaration for airports above could also have been declared in this way: 27 | var players: Dictionary = ["Who" : "First", "What" : "Second"] 28 | 29 | // In the case below, the literal contains only Strings for all keys and only Strings for all 30 | // values, so type inference works in our favor allowing us to avoid the type annotation: 31 | let inferredDictionary = ["TYO": "Tokyo", "DUB": "Dublin"] 32 | 33 | // ------------------------------------------------------------------------------------------------ 34 | // Accessing and modifying a Dictionary 35 | // 36 | // Let's get a value from the dictionary for the TYO airport: 37 | airports["TYO"] 38 | 39 | // What happens if we try to get a value that doesn't exist? 40 | // 41 | // Since this can happen, the Dictionary always retuns an optional, so in the next line of code, 42 | // notFound will be nil. It will also be of type String? 43 | var notFound = airports["FOO"] 44 | 45 | // We can get the number of elements in the dictionary: 46 | airports.count 47 | 48 | // We can add an element by accessing a key that doesn't exist: 49 | airports["LHR"] = "London" 50 | 51 | // Here's another way to set/update a value. This lets us get the previous value before we set 52 | // the new one. The returned value is optional, in case the new value doesn't replace an existing 53 | // one: 54 | var previousValue = airports.updateValue("Dublin International", forKey: "DUB") 55 | 56 | // We can remove an entry by setting the value for a key to nil: 57 | airports["APL"] = nil 58 | 59 | // Here's another way to remove a value. The returned value is set to the value that was removed. 60 | // Again, this is optional in case there was no value to remove. In this case, the APL airport 61 | // was already removed, so the return value will be a nil optional: 62 | var removedValue = airports.removeValueForKey("APL") 63 | 64 | // ------------------------------------------------------------------------------------------------ 65 | // Iterating over a Dictionary 66 | // 67 | // We can iterating over key/value pairs with a for-in loop, which uses a Tuple to hold the 68 | // key/value pair for each entry in the Dictionary: 69 | for (airportCode, airportName) in airports 70 | { 71 | airportCode 72 | airportName 73 | } 74 | 75 | // We can iterate over just the keys 76 | for airportCode in airports.keys 77 | { 78 | airportCode 79 | } 80 | 81 | // We can iterate over jsut the values 82 | for airportName in airports.values 83 | { 84 | airportName 85 | } 86 | 87 | // We can create an array from the keys or values 88 | // 89 | // Note that when doing this, the use of Array() is needed to convert the keys or values into 90 | // an array. 91 | var airportCodes = Array(airports.keys) 92 | var airportNames = Array(airports.values) 93 | 94 | // ------------------------------------------------------------------------------------------------ 95 | // Creating an empty Dictionary 96 | // 97 | // Here, we create an empty Dictionary of Int keys and String values: 98 | var namesOfIntegers = Dictionary() 99 | 100 | // Let's set one of the values 101 | namesOfIntegers[16] = "Sixteen" 102 | 103 | // We can empty a dictionary using an empty dictionary literal: 104 | namesOfIntegers = [:] 105 | 106 | // An immutable dictionary is a constant. 107 | let immutableDict = ["a": "one", "b": "two"] 108 | 109 | // Similar to arrays, we cannot modify the contents of an immutable dictionary. The following lines 110 | // will not compile: 111 | // 112 | // immutableDict["a"] = "b" // You cannot modify an element 113 | // immutableDict["c"] = "three" // You cannot add a new entry or change the size 114 | 115 | // Dictionaries are value types, which means they are copied on assignment. 116 | // 117 | // Let's create a Dictionary and copy it: 118 | var ages = ["Peter": 23, "Wei": 35, "Anish": 65, "Katya": 19] 119 | var copiedAges = ages 120 | 121 | // Next, we'll modify the copy: 122 | copiedAges["Peter"] = 24 123 | 124 | // And we can see that the original is not changed: 125 | ages["Peter"] 126 | 127 | -------------------------------------------------------------------------------- /4b. Dictionaries.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /5. Control Flow.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /5. Control Flow.playground/section-1.swift: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------------------------ 2 | // Things to know: 3 | // 4 | // * Much of the control flow in Swift is similar to C-like languages, but there are some key 5 | // differences. For example, switch-case constructs are much more flexible and powerful as well 6 | // as extensions to break and continue statements. 7 | // ------------------------------------------------------------------------------------------------ 8 | 9 | // ------------------------------------------------------------------------------------------------ 10 | // For loops 11 | // 12 | // We can loop through ranges using the closed-range operator ("..."). 13 | // 14 | // In the loop below, 'index' is a constant that is automatically declared. 15 | for index in 1...5 16 | { 17 | "This will print 5 times" 18 | 19 | // Being a constant, the following line won't compile: 20 | // 21 | // index = 99 22 | } 23 | 24 | // The constant 'index' from the previous loop is scoped only to the loop. As a result, you cannot 25 | // access it beyond the loop. The following line will not compile: 26 | // 27 | // index = 0 28 | 29 | // We can loop through ranges using the half-closed range operator ("..<") 30 | // 31 | // We can also reuse the name 'index' because of the scoping noted previously. 32 | for index in 1 ..< 5 33 | { 34 | "This will print 4 times" 35 | } 36 | 37 | // Apple's "Swift Programming Language" book states the following, which I find in practice to be 38 | // incorrect: 39 | // 40 | // “The index constant exists only within the scope of the loop. If you want to check the value of 41 | // index after the loop completes, or if you want to work with its value as a variable rather than 42 | // a constant, you must declare it yourself before its use in the loop.” 43 | // 44 | // In practice, I find that the loop constant overrides any local variable/constant and maintains 45 | // its scope to the loop and does not alter the locally defined value: 46 | var indx = 3999 47 | for indx in 1...5 48 | { 49 | indx // This ranges from 1 to 5, inclusive 50 | 51 | // 'indx' is still acting like a constant, so this line won't compile: 52 | // 53 | // indx++ 54 | } 55 | 56 | // After the loop, we find that 'indx' still contains the original value of 3999 57 | indx 58 | 59 | // We can use an underscore if you don't need access to the loop constant: 60 | for _ in 1...10 61 | { 62 | println("do something") 63 | } 64 | 65 | // We can iterate over arrays 66 | let names = ["Anna", "Alex", "Brian", "Jack"] 67 | for name in names 68 | { 69 | name 70 | } 71 | 72 | // We can iterate over a Dictionary's key/value pairs 73 | let numberOfLegs = ["Spider":8, "Ant":6, "Cat":4] 74 | for (animalName, legs) in numberOfLegs 75 | { 76 | animalName 77 | legs 78 | } 79 | 80 | // We can iterate over characters in a String 81 | for character in "Hello" 82 | { 83 | character 84 | } 85 | 86 | // We can use the For-Condition-Increment loop construct, which resembles the C-like variant 87 | // 88 | // Note that the loop value is a variable, not a constant. In fact, they cannot be constant 89 | // because of the increment statement (++index) 90 | for (var index = 0; index < 3; ++index) 91 | { 92 | index 93 | } 94 | 95 | // The parenthesis are optional for the For-Condition-Increment loop: 96 | for var index = 0; index < 3; ++index 97 | { 98 | index 99 | } 100 | 101 | // Variables are scoped to the For-Condition-Increment construct. To alter this, pre-declare index 102 | var index = 3000 103 | for index = 0; index < 3; ++index 104 | { 105 | index 106 | } 107 | index // Index holds 3 after running through the loop 108 | 109 | // ------------------------------------------------------------------------------------------------ 110 | // While loops 111 | // 112 | // While loops resemble other C-like languages. They perform the condition before each iteration 113 | // through the loop: 114 | while index > 0 115 | { 116 | --index 117 | } 118 | 119 | // Do-While loops also resemble their C-like language counterparts. They perform the condition 120 | // after each iteration through the loop. As a result, they always execute the code inside the 121 | // loop at least once: 122 | do 123 | { 124 | ++index 125 | } while (index < 3) 126 | 127 | // ------------------------------------------------------------------------------------------------ 128 | // Conditional Statements 129 | // 130 | // The if statement is very similar to C-like languages, except that the parenthesis are optional. 131 | // You can also chain multiple conditions with 'else' and 'else if' statements: 132 | if (index > 0) 133 | { 134 | "Index is positive" 135 | } 136 | else if index == 0 137 | { 138 | "index is zero" 139 | } 140 | else 141 | { 142 | "index is negative" 143 | } 144 | 145 | // Switch statements are more powerful than their C-like counterparts. Here are a few of those 146 | // differences to get us started: 147 | // 148 | // Unlike C-like languages, switch statements do not require a "break" statement to prevent falling 149 | // through to the next case. 150 | // 151 | // Additionally, multiple conditions can be separated by a comma for a single case to match 152 | // multiple conditions. 153 | // 154 | // Switch statements must also be exhaustive and include all possible values, or the compiler will 155 | // generate an error. 156 | // 157 | // There are many more differences, but let's start with a simple switch statement to get our feet 158 | // wet: 159 | let someCharacter: Character = "e" 160 | switch someCharacter 161 | { 162 | case "a", "e", "i", "o", "u": 163 | "a vowel" 164 | 165 | case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t", "v", "w", "x", "u", "z": 166 | "a consonant" 167 | 168 | // Necessary because switch statements must be exhaustive in order to capture all Characters 169 | default: 170 | "not a vowel or consonant" 171 | } 172 | 173 | // Each case clause must have a statement of some kind. A comment will not suffice. 174 | // 175 | // Otherwise you will get a compilation error. The following won't compile because there is an 176 | // empty case statement: 177 | // 178 | // let anotherCharacter: Character = "a" 179 | // switch anotherCharacter 180 | // { 181 | // case "a": 182 | // case "A": 183 | // "the letter a" 184 | // default: 185 | // "not the letter a" 186 | // } 187 | 188 | // We can perform range matching for cases: 189 | let count = 3_000_000_000_000 190 | switch count 191 | { 192 | case 0: 193 | "no" 194 | case 1...3: 195 | "a few" 196 | case 4...9: 197 | "several" 198 | case 10...99: 199 | "tens of" 200 | case 100...999: 201 | "hundreds of" 202 | case 1000...999999: 203 | "thousands of" 204 | default: 205 | "millions and millions of" 206 | } 207 | 208 | // Matching against tuples 209 | // 210 | // In addition to matching Tuples, we can also use ranges inside Tuple values and even match 211 | // against partial Tuple values by using an "_" to ignore matches against a specific value within 212 | // the Tuple. 213 | let somePoint = (1,1) 214 | switch somePoint 215 | { 216 | case (0,0): 217 | "origin" 218 | 219 | // Match only against y=0 220 | case (_, 0): 221 | "On the X axis" 222 | 223 | // Match only against x=0 224 | case (0, _): 225 | "On the y axis" 226 | 227 | // Match x and y from -2 to +2 (inclusive) 228 | case (-2...2, -2...2): 229 | "On or inside the 2x2 box" 230 | 231 | // Everything else 232 | default: 233 | "Outisde the 2x2 box" 234 | } 235 | 236 | // Value bindings in switch statements 237 | // 238 | var anotherPoint = (2, 8) 239 | switch anotherPoint 240 | { 241 | // Bind 'x' to the first value (matching any x) of the tuple and match on y=0 242 | case (let x, 0): 243 | "On the x axis with an x value of \(x)" 244 | 245 | // Bind 'y' to the second value (matching any y) of the tuple and match against x=0 246 | case (0, let y): 247 | "On the y axis with an y value of \(y)" 248 | 249 | // Bind both values of the tuple, matching any x or y. Note the shorthand of the 'let' 250 | // outside of the parenthesis. This works with 'var' as well. 251 | // 252 | // Also notice that since this matches any x or y, we fulfill the requirement for an exhaustive 253 | // switch. 254 | case let (x, y): 255 | "Somewhere else on \(x), \(y)" 256 | } 257 | 258 | // We can also mix let/var for case statements. The following code block is the same as the 259 | // previous except that the final case statement, which mixes variable and constants for the x and 260 | // y components of the Tuple. 261 | switch anotherPoint 262 | { 263 | case (let x, 0): 264 | "On the x axis with an x value of \(x)" 265 | 266 | case (0, let y): 267 | "On the y axis with an y value of \(y)" 268 | 269 | case (var x, let y): 270 | ++x // We can modify the variable 'x', but not the constant 'y' 271 | "Somewhere else on \(x), \(y)" 272 | } 273 | 274 | // Where clauses allow us to perform more detailed conditions on case conditions. The where clauses 275 | // work on the values declared on the case line: 276 | let yetAnotherPoint = (1, -1) 277 | switch yetAnotherPoint 278 | { 279 | case let (x, y) where x == y: 280 | "On the line of x == y" 281 | 282 | case let (x, y) where x == -y: 283 | "On the line of x == -y" 284 | 285 | case let (x, y): 286 | "Just some arbitrary point" 287 | } 288 | 289 | // ------------------------------------------------------------------------------------------------ 290 | // Control transfer statements 291 | // 292 | // Swift supports extended versions of continue and break as well as an additional 'fallthrough' 293 | // statement for switch-case constructs. 294 | // 295 | // Since swift doesn't require a break statement to avoid falling through to the next case, we can 296 | // still use them to early-out of the current case without continuing work. The first statement 297 | // after the 'break' will be the next statement following the entire switch construct. 298 | let someValue = 9000 299 | switch someValue 300 | { 301 | case let x where (x & 1) == 1: 302 | if someValue < 100 303 | { 304 | "Odd number less than 100" 305 | break 306 | } 307 | "Odd number greater or equal to 100" 308 | 309 | case let x where (x & 1) == 0: 310 | if someValue < 100 311 | { 312 | "Even number less than 100" 313 | break 314 | } 315 | "Even number greater or equal to 100" 316 | 317 | default: 318 | "Unknown value" 319 | } 320 | 321 | // Since each case must have a statement and since we must have an exhaustive switch, we can use 322 | // the break statement to effectively nullify the use of a case: 323 | switch someValue 324 | { 325 | case Int.min...100: 326 | "Small number" 327 | 328 | case 101...1000: 329 | break // We don't care about medium numbers 330 | 331 | case 1001...100_00: 332 | "Big number" 333 | 334 | default: 335 | break // We don't care about the rest, either 336 | } 337 | 338 | // Since we don't need to break out of cases to avoid falling through automatically, we must 339 | // specifically express our intention to fall through using the 'fallthrough' keyword 340 | let integerToDescribe = 5 341 | var integerDescription = "\(integerToDescribe) is" 342 | switch integerToDescribe 343 | { 344 | case 2, 3, 5, 7, 11, 13, 17, 19: 345 | integerDescription += " a prime number, and also" 346 | fallthrough 347 | 348 | default: 349 | integerDescription += " an integer." 350 | } 351 | 352 | // Continue and Break statements have been extended in Swift to allow each to specify which 353 | // switch or loop construct to break out of, or continue to. 354 | // 355 | // To enable this, labels are used, similar to labels used by C's goto statement. 356 | // 357 | // The following will print each name until it reaches the letter 'a' then skip to the next name 358 | var result = "" 359 | nameLoop: for name in names 360 | { 361 | characterLoop: for character in name 362 | { 363 | theSwitch: switch character 364 | { 365 | case "a": 366 | // Break out of the theSwitch and characterLoop 367 | break characterLoop 368 | 369 | default: 370 | result += String(character) 371 | } 372 | } 373 | } 374 | result 375 | 376 | // Similarly, this prints all names without the letter 'a' in them: 377 | result = "" 378 | nameLoop: for name in names 379 | { 380 | characterLoop: for character in name 381 | { 382 | theSwitch: switch character 383 | { 384 | case "a": 385 | // Continue directly to the character loop, bypassing this character in this name 386 | continue characterLoop 387 | 388 | default: 389 | result += String(character) 390 | } 391 | } 392 | } 393 | result 394 | 395 | // Similarly, this prints all names until the letter 'x' is found, then aborts all processing by 396 | // breaking out of the outer loop: 397 | result = "" 398 | nameLoop: for name in names 399 | { 400 | characterLoop: for character in name 401 | { 402 | theSwitch: switch character 403 | { 404 | case "x": 405 | // Break completely out of the outer name loop 406 | break nameLoop 407 | 408 | default: 409 | result += String(character) 410 | } 411 | } 412 | } 413 | result 414 | -------------------------------------------------------------------------------- /5. Control Flow.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /6. Functions.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /6. Functions.playground/section-1.swift: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------------------------ 2 | // Things to know: 3 | // 4 | // * Like most other languages, functions contain units of code to perform a task, but there are 5 | // a few key featurs of functions in Swift. 6 | // 7 | // * Functions are types in Swift. This means that a function can receive another function as an 8 | // input parameter or return a function as a result. You can even declare variables that are 9 | // functions. 10 | // 11 | // * Functions can be nested, and the inner function can be returned by the outer function. To 12 | // enable this, the current state of any local variables (local to the outer function) that are 13 | // used by the inner function are "captured" for use by the inner function. Any time that inner 14 | // function is called, it uses this captured state. This is the case even when a nested function 15 | // is returned by the parent function. 16 | // ------------------------------------------------------------------------------------------------ 17 | 18 | // Here's a simple function that receives a Single string and returns a String 19 | // 20 | // Note that each parameter has a local name (for use within the function) and a standard type 21 | // annotation. The return value's type is at the end of the function, following the ->. 22 | func sayHello(personName: String) -> String 23 | { 24 | return "Hello, \(personName)" 25 | } 26 | 27 | // If we call the function, we'll receive the greeting 28 | sayHello("Peter Parker") 29 | 30 | // Multiple input parameters are separated by a comma 31 | func halfOpenRangeLength(start: Int, end: Int) -> Int 32 | { 33 | return end - start 34 | } 35 | 36 | // A function with no parameters simply has an empty set of parenthesis following the function 37 | // name: 38 | func sayHelloWorld() -> String 39 | { 40 | return "Hello, world" 41 | } 42 | 43 | // A funciton with no return value can be expressed in two different ways. The first is to replace 44 | // the return type with a set of empty parenthesis, which can be thought of as an empty Tuple. 45 | func sayGoodbye(name: String) -> () 46 | { 47 | "Goodbye, \(name)" 48 | } 49 | 50 | // We can also remove the return type (and the -> delimiter) alltogether: 51 | func sayGoodbyeToMyLittleFriend(name: String) 52 | { 53 | "Goodbye, \(name)" 54 | } 55 | 56 | // Functions can return Tuples, which enable them to return multiple values. 57 | // 58 | // The following function simply returns two hard-coded strings. 59 | func getApplicationNameAndVersion() -> (String, String) 60 | { 61 | return ("Modaferator", "v1.0") 62 | } 63 | 64 | // Since the return value is a Tuple, we can use the Tuple's naming feature to name the values 65 | // being returned: 66 | func getApplicationInfo() -> (name: String, version: String) 67 | { 68 | return ("Modaferator", "v1.0") 69 | } 70 | var appInfo = getApplicationInfo() 71 | appInfo.name 72 | appInfo.version 73 | 74 | // ------------------------------------------------------------------------------------------------ 75 | // External Parameter Names 76 | // 77 | // We can use Objective-C-like external parameter names so the caller must name the external 78 | // parameters when they call the function. The extenal name appears before the local name. 79 | func addSeventeen(toNumber value: Int) -> Int 80 | { 81 | return value + 17 82 | } 83 | addSeventeen(toNumber: 42) 84 | 85 | // If your internal and external names are the same, you can use a shorthand #name syntax to create 86 | // both names at once. 87 | // 88 | // The following declaration creates an internal parameter named "action" as well as an external 89 | // parameter named "action": 90 | func kangaroosCan(#action: String) -> String 91 | { 92 | return "A Kangaroo can \(action)" 93 | } 94 | 95 | // We can now use the external name ("action") to make the call: 96 | kangaroosCan(action: "jump") 97 | kangaroosCan(action: "carry children in their pouches") 98 | 99 | // We can also have default parameter values. Default parameter values must be placed at the end 100 | // of the parameter list. 101 | // 102 | // In the addMul routine, we'll add two numbers and multiply the result by an optional multiplier 103 | // value. We will default the multiplier to 1 so that if the parameter is not specified, the 104 | // multiplication won't affect the result. 105 | func addMul(firstAdder: Int, secondAdder: Int, multiplier: Int = 1) -> Int 106 | { 107 | return (firstAdder + secondAdder) * multiplier 108 | } 109 | 110 | // We can call with just two parameters to add them together 111 | addMul(1, 2) 112 | 113 | // Default parameter values and external names 114 | // 115 | // Swift automatically creates external parameter names for those parameters that have default 116 | // values. Since our declaration of addMul did not specify an external name (either explicitly or 117 | // using the shorthand method), Swift created one for us based on the name of the internal 118 | // parameter name. This acts as if we had defined the third parameter using the "#" shorthand. 119 | // 120 | // Therefore, when calling the function and specifying a value for the defaulted parameter, we 121 | // must provide the default parameter's external name: 122 | addMul(1, 2, multiplier: 9) 123 | 124 | // We can opt out of the automatic external name for default parameter values by specify an 125 | // external name of "_" like so: 126 | func anotherAddMul(firstAdder: Int, secondAdder: Int, _ multiplier: Int = 1) -> Int 127 | { 128 | return (firstAdder + secondAdder) * multiplier 129 | } 130 | 131 | // Here, we call without the third parameter as before: 132 | anotherAddMul(1, 2) 133 | 134 | // And now we can call with an un-named third parameter: 135 | anotherAddMul(1, 2, 9) 136 | 137 | // ------------------------------------------------------------------------------------------------ 138 | // Variadic Parameters 139 | // 140 | // Variadic parameters allow you to call functions with zero or more values of a specified type. 141 | // 142 | // Variadic parameters appear within the receiving function as an array of the given type. 143 | // 144 | // A function may only have at most one variadic and it must appear as the last parameter. 145 | func arithmeticMean(numbers: Double...) -> Double 146 | { 147 | var total = 0.0 148 | 149 | // The variadic, numbers, looks like an array to the internal function so we can just loop 150 | // through them 151 | for number in numbers 152 | { 153 | total += number 154 | } 155 | return numbers.count == 0 ? total : total / Double(numbers.count) 156 | } 157 | 158 | // Let's call it with a few parameter lengths. Note that we can call with no parameters, since that 159 | // meets the criteria of a variadic parameter (zero or more). 160 | arithmeticMean() 161 | arithmeticMean(1) 162 | arithmeticMean(1, 2) 163 | arithmeticMean(1, 2, 3) 164 | arithmeticMean(1, 2, 3, 4) 165 | arithmeticMean(1, 2, 3, 4, 5) 166 | arithmeticMean(1, 2, 3, 4, 5, 6) 167 | 168 | // If we want to use variadic parameters and default parameter values, we can do so by making sure 169 | // that the default parameters come before the variadic, at the end of the parameter list: 170 | func anotherArithmeticMean(initialTotal: Double = 0, numbers: Double...) -> Double 171 | { 172 | var total = initialTotal 173 | for number in numbers 174 | { 175 | total += number 176 | } 177 | return numbers.count == 0 ? total : total / Double(numbers.count) 178 | } 179 | 180 | // Here, we can still call with no parameters because of the default parameter 181 | anotherArithmeticMean() 182 | 183 | // Going forward, we must specify the default parameter's external name (because we didn't opt-out 184 | // of it.) Also, you can see why Swift attempts to enforce the use of external parameter names for 185 | // default parameter values. In this case, it helps us recognize where the defalt parameters leave 186 | // off and the variadic parameters begin: 187 | anotherArithmeticMean(initialTotal: 1) 188 | anotherArithmeticMean(initialTotal: 1, 2) 189 | anotherArithmeticMean(initialTotal: 1, 2, 3) 190 | anotherArithmeticMean(initialTotal: 1, 2, 3, 4) 191 | anotherArithmeticMean(initialTotal: 1, 2, 3, 4, 5) 192 | anotherArithmeticMean(initialTotal: 1, 2, 3, 4, 5, 6) 193 | 194 | // Variadic parameters with external parameter names only apply their external name to the first 195 | // variadic parameter specified in the function call (if present.) 196 | func yetAnotherArithmeticMean(initialTotal: Double = 0, values numbers: Double...) -> Double 197 | { 198 | var total = initialTotal 199 | for number in numbers 200 | { 201 | total += number 202 | } 203 | return numbers.count == 0 ? total : total / Double(numbers.count) 204 | } 205 | 206 | // And here we can see the impact on the function call of adding the external name "values" to the 207 | // variadic parameter: 208 | yetAnotherArithmeticMean() 209 | yetAnotherArithmeticMean(initialTotal: 1) 210 | yetAnotherArithmeticMean(initialTotal: 1, values: 2) 211 | yetAnotherArithmeticMean(initialTotal: 1, values: 2, 3) 212 | yetAnotherArithmeticMean(initialTotal: 1, values: 2, 3, 4) 213 | yetAnotherArithmeticMean(initialTotal: 1, values: 2, 3, 4, 5) 214 | yetAnotherArithmeticMean(initialTotal: 1, values: 2, 3, 4, 5, 6) 215 | 216 | // ------------------------------------------------------------------------------------------------ 217 | // Constant and variable parameters 218 | // 219 | // All function parameters are constant by default. To make them variable, add the var introducer: 220 | func padString(var str: String, pad: Character, count: Int) -> String 221 | { 222 | str = Array(count: count, repeatedValue: pad) + str 223 | return str 224 | } 225 | 226 | var paddedString = "padded with dots" 227 | padString(paddedString, ".", 10) 228 | 229 | // Note that the function does not modify the caller's copy of the string that was passed in 230 | // because the value is still passed by value: 231 | paddedString 232 | 233 | // ------------------------------------------------------------------------------------------------ 234 | // In-Out Parameters 235 | // 236 | // In-Out parameters allow us to force a parameter to be passed by reference so those changes can 237 | // persist after the function call. 238 | // 239 | // Note that inout parameters cannot be variadic or have default parameter values. 240 | // 241 | // We'll write a standard swap function to exercise this: 242 | func swap(inout a: Int, inout b: Int) 243 | { 244 | let tmp = a 245 | a = b 246 | b = tmp 247 | } 248 | 249 | // Let's call the swap function to see what happens. 250 | // 251 | // To ensure that the caller is aware that the parameter is an inout (and hence, can modify their 252 | // variable), we must prefix the parameter name with "&". 253 | // 254 | // Note that the variables that will be passed as references may not be defined as constants, 255 | // because it is expected that their values will change: 256 | var one = 1, two = 2 257 | swap(&one, &two) 258 | 259 | // And we can see that 'one' contains a 2 and 'two' contains a 1: 260 | one 261 | two 262 | 263 | // ------------------------------------------------------------------------------------------------ 264 | // Function types 265 | // 266 | // The type of a function is comprised of the specific parameter list (the number of parameters as 267 | // well as their type) and the return value's type. 268 | // 269 | // The following two functions have the same type. 270 | // 271 | // It's important to note that their type is described as: (Int, Int) -> Int 272 | func add(a: Int, b: Int) -> Int {return a+b} 273 | func mul(a: Int, b: Int) -> Int {return a*b} 274 | 275 | // A function that has no parameters or return value would have the type: () -> () 276 | func nop() -> () 277 | { 278 | return 279 | } 280 | 281 | // The type of the function below is the same as the one above: () -> () 282 | // 283 | // It is written in a shorter form, eliminating the return type entirely, but this syntactic 284 | // simplification doesn't alter the way that the function's type is notated: 285 | func doNothing() 286 | { 287 | return 288 | } 289 | 290 | // Using what we know about funciton types, we can define variables that are the type of a function 291 | let doMul: (Int, Int) -> Int = mul 292 | 293 | // We can now use the variable to perform the function: 294 | doMul(4, 5) 295 | 296 | // We can also name our parameters when assigning them to a variable, as well as taking advantage 297 | // of un-named parameters ("_"). 298 | // 299 | // This additional syntactic decoration has a purpose, but it doesn't affect the underlying 300 | // function type, which remains: (Int, Int) -> Int 301 | let doAddMul: (a: Int, b: Int, Int) -> Int = addMul 302 | 303 | // Calling the function now requires external names for the first two parameters 304 | doAddMul(a: 4, b: 2, 55) 305 | 306 | // We can pass function types as parameters to funcions, too. 307 | // 308 | // Here, our first parameter named 'doMulFunc' has the type "(Int, Int) -> Int" followed by two 309 | // parameters, 'a' and 'b' that are used as parameters to the 'doMulFunc' function: 310 | func doDoMul(doMulFunc: (Int, Int) -> Int, a: Int, b: Int) -> Int 311 | { 312 | return doMulFunc(a, b) 313 | } 314 | 315 | // We can now pass the function (along with a couple parameters to call it with) to another 316 | // function: 317 | doDoMul(doMul, 5, 5) 318 | 319 | // We can also return function types. 320 | // 321 | // The syntax looks a little wird because of the two sets of arrows. To read this, understand that 322 | // the first arrow specifies the return type, and the second arrow is simply part of the function 323 | // type being returned: 324 | func getDoMul() -> (Int, Int) -> Int 325 | { 326 | return doMul 327 | } 328 | let newDoMul = getDoMul() 329 | newDoMul(9, 5) 330 | 331 | // Or, an even shorter version that avoids the additional stored value: 332 | getDoMul()(5, 5) 333 | 334 | // Earlier we discussed how functions that do not return values still have a return type that 335 | // includes: -> () 336 | // 337 | // Here, we'll see this in action by returning a function that doesn't return a value: 338 | func getNop() -> () -> () 339 | { 340 | return nop 341 | } 342 | 343 | // And we'll call nop (note the second set of parenthesis to make the actual call to nop()) 344 | getNop()() 345 | 346 | // We can nest functions inside of other functions: 347 | func getFive() -> Int 348 | { 349 | func returnFive() -> Int 350 | { 351 | return 5 352 | } 353 | 354 | // Call returnFive() and return the result of that function 355 | return returnFive() 356 | } 357 | 358 | // Calling getFive will return the Int value 5: 359 | getFive() 360 | 361 | // You can return nested functions, too: 362 | func getReturnFive() -> () -> Int 363 | { 364 | func returnFive() -> Int 365 | { 366 | return 5 367 | } 368 | return returnFive 369 | } 370 | 371 | // Calling outerFunc2 will return a function capable of returning the Int value 5: 372 | let returnFive = getReturnFive() 373 | 374 | // Here we call the nested function: 375 | returnFive() 376 | -------------------------------------------------------------------------------- /6. Functions.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /7. Closures.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /7. Closures.playground/section-1.swift: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------------------------ 2 | // Things to know: 3 | // 4 | // * Closures are blocks of code. 5 | // 6 | // * The can be passed as parameters to functions much like Function Types. In fact, functions 7 | // are a special case of closures. 8 | // 9 | // * Closures of all types (including nested functions) employ a method of capturing the surrounding 10 | // context in which is is defined, allowing it to access constants and variables from that 11 | // context. 12 | // ------------------------------------------------------------------------------------------------ 13 | 14 | // Closures can use constant, variable, inout, variadics, tuples for their parameters. They can 15 | // return any value, including Tuples. They cannot, however, have default parameters. 16 | // 17 | // The basic syntax is: 18 | // 19 | // { (parameters) -> return_type in 20 | // ... statements ... 21 | // } 22 | // 23 | // Here's an example of a simple String comparison closure that might be used for sorting Strings: 24 | // 25 | // { (s1: String, s2: String) -> Bool in 26 | // return s1 < s2 27 | // } 28 | // 29 | // Here's an example using Swift's 'sorted' member function. It's important to note that this 30 | // function receives a single closure. 31 | // 32 | // These can be a little tricky to read if you're not used to them. To understand the syntax, pay 33 | // special attention to the curly braces that encapsulate the closure and the parenthesis just 34 | // outside of those curly braces: 35 | let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"] 36 | var reversed = [String]() 37 | reversed = names.sorted({ 38 | (s1: String, s2: String) -> Bool in 39 | return s1 > s2 40 | }) 41 | 42 | // ------------------------------------------------------------------------------------------------ 43 | // Inferring Type from Context 44 | // 45 | // Like functions, closures have a type. 46 | // 47 | // If the type is known (as is always the case when passing a closure as a parameter to a function) 48 | // then the return type of the closure can be inferred, allowing us to simplify the syntax of our 49 | // call to sort. 50 | // 51 | // The following call is identical to the one above with the exception that "-> Bool" was removed: 52 | reversed = names.sorted({ 53 | (s1: String, s2: String) in 54 | return s1 > s2 55 | }) 56 | 57 | // Just as the return type can be inferred, so can the parameter types. This allows us to simplify 58 | // the syntax a bit further by removing the type annotations from the closure's parameters. 59 | // 60 | // The following call is identical to the one above with the exception that the parameter type 61 | // annotations (": String") have been removed: 62 | reversed = names.sorted({ 63 | (s1, s2) in 64 | return s1 > s2 65 | }) 66 | 67 | // Since all types can be inferred and we're not using any type annotation on the parameters, 68 | // we can simplify a bit further by removing the paranthesis around the parameters. We'll also put 69 | // it all on a single line, since it's a bit more clear now: 70 | reversed = names.sorted({ s1, s2 in return s1 > s2 }) 71 | 72 | // If the closuere has only a single expression, then the return statement is also inferred. When 73 | // this is the case, the closure returns the value of the single expression: 74 | reversed = names.sorted({ s1, s2 in s1 > s2 }) 75 | 76 | // We're not done simplifying yet. It turns out we can get rid of the parameters as well. If we 77 | // remove the parameters, we can still access them because Swift provides shorthand names to 78 | // parameters passed to inline closures. To access the first parameter, use $0. The second 79 | // parameter would be $1 and so on. 80 | // 81 | // Here's what that would might like (this will not compile - yet): 82 | // 83 | // reversed = names.sorted({ s1, s2 in $0 > $1 }) 84 | // 85 | // This won't compile because you're not allowed to use shorthand names if you specify the 86 | // parameter list. Therefore, we need to remove those in order to get it to compile. This makes 87 | // for a very short inline closure: 88 | reversed = names.sorted({ $0 > $1 }) 89 | 90 | // Interestingly enough, the operator < for String types is defined as: 91 | // 92 | // (String, String) -> Bool 93 | // 94 | // Notice how this is the same as the closure's type for the sorted() routine? Wouldn't it be 95 | // nice if we could just pass in this operator? It turns out that for inline closures, Swift allows 96 | // exactly this. 97 | // 98 | // Here's what that looks like: 99 | reversed = names.sorted(>) 100 | 101 | // If you want to just sort a mutable copy of an array (in place) you can use the sort() method 102 | var mutableCopyOfNames = names 103 | 104 | mutableCopyOfNames.sort(>) 105 | 106 | mutableCopyOfNames 107 | 108 | // ------------------------------------------------------------------------------------------------ 109 | // Trailing Closures 110 | // 111 | // Trailing Closures refer to closures that are the last parameter to a function. This special-case 112 | // syntax allows a few other syntactic simplifications. In essence, you can move trailing closures 113 | // just outside of the parameter list. Swift's sorted() member function uses a trailing closure for 114 | // just this reason. 115 | // 116 | // Let's go back to our original call to sort with a fully-formed closure and move the closure 117 | // outside of the parameter list. This resembles a function definition, but it's a function call. 118 | reversed = names.sorted { 119 | (s1: String, s2: String) -> Bool in 120 | return s1 > s2 121 | } 122 | 123 | // Note that the opening brace for the closure must be on the same line as the function call's 124 | // ending paranthesis. This is the same functinon call with the starting brace for the closure 125 | // moved to the next line. This will not compile: 126 | // 127 | // reversed = sort(names) 128 | // { 129 | // (s1: String, s2: String) -> Bool in 130 | // return s1 > s2 131 | // } 132 | 133 | // Let's jump back to our simplified closure ({$0 > $1}) and apply the trailing closure principle: 134 | reversed = names.sorted {$0 > $1} 135 | 136 | // Another simplification: if a function receives just one closure as the only parameter, you can 137 | // remove the () from the function call. First, we'll need a function that receives just one 138 | // parameter, a closure: 139 | func returnValue(f: () -> Int) -> Int 140 | { 141 | // Simply return the value that the closure 'f' returns 142 | return f() 143 | } 144 | 145 | // Now let's call the function with the parenthesis removed and a trailing closure: 146 | returnValue {return 6} 147 | 148 | // And if we apply the simplification described earlier that implies the return statement for 149 | // single-expresssion closures, it simplifies to this oddly-looking line of code: 150 | returnValue {6} 151 | 152 | // ------------------------------------------------------------------------------------------------ 153 | // Capturing Values 154 | // 155 | // The idea of capturing is to allow a closure to access the variables and constants in their 156 | // surrounding context. 157 | // 158 | // For example, a nested function can access contstans and variables from the function in which 159 | // it is defined. If this nested function is returned, each time it is called, it will work within 160 | // that "captured" context. 161 | // 162 | // Here's an example that should help clear this up: 163 | func makeIncrementor(forIncrement amount: Int) -> () -> Int 164 | { 165 | var runningTotal = 0 166 | 167 | // runningTotal and amount are 'captured' for the nested function incrementor() 168 | func incrementor() -> Int 169 | { 170 | runningTotal += amount 171 | return runningTotal 172 | } 173 | 174 | // We return the nested function, which has captured it's environment 175 | return incrementor 176 | } 177 | 178 | // Let's get a copy of the incrementor: 179 | var incrementBy10 = makeIncrementor(forIncrement: 10) 180 | 181 | // Whenever we call this function, it will return a value incremented by 10: 182 | incrementBy10() // returns 10 183 | incrementBy10() // returns 20 184 | 185 | // We can get another copy of incrementor that works on increments of 3. 186 | var incrementBy3 = makeIncrementor(forIncrement: 3) 187 | incrementBy3() // returns 3 188 | incrementBy3() // returns 6 189 | 190 | // 'incrementBy10' and 'incrementBy3' each has its own captured context, so they work independently 191 | // of each other. 192 | incrementBy10() // returns 30 193 | 194 | // Closures are reference types, which allows us to assign them to a variable. When this happens, 195 | // the captured context comes along for the ride. 196 | var copyIncrementBy10 = incrementBy10 197 | copyIncrementBy10() // returns 40 198 | 199 | // If we request a new incremntor that increments by 10, it will have a separate and unique captured 200 | // context: 201 | var anotherIncrementBy10 = makeIncrementor(forIncrement: 10) 202 | anotherIncrementBy10() // returns 10 203 | 204 | // Our first incrementor is still using its own context: 205 | incrementBy10() // returns 50 206 | -------------------------------------------------------------------------------- /7. Closures.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /8. Enumerations.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /8. Enumerations.playground/section-1.swift: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------------------------ 2 | // Things to know: 3 | // 4 | // * Enumerations in Swift are different from their popular counterparts in C-like languages. 5 | // Rather that containing "one of a set of integer values" like most C-like languages, Swift's 6 | // enumerations can be thought of as holding one named type of a given set of named types. 7 | // 8 | // To clarify: Rather than holding an integer value that has been pre-defined integer value 9 | // (Error = -1, Success = 0) an enumeration in Swift only associates a name with a type (like 10 | // Int, String, Tuple, etc.) These elements of the enumeration can then be assigned "Associated 11 | // Values." For example, an enumeration can store an "Error" which is a Tuple with an Int value 12 | // for the error code and a String containing the message. Each time a function or piece of code 13 | // assignes or returns an Error(Int, String), it can set populate the Tuple with Int/String par 14 | // for the specific error condition. 15 | // 16 | // * Alternative to the enumeration storing named types, Swift enumerations can have a type. If 17 | // that type is Int, then they will behave more like their C-style counterparts. 18 | // ------------------------------------------------------------------------------------------------ 19 | 20 | // Here is the simple enumeration. 21 | // 22 | // Unlike their C counterparts, the members of the enumeration below are not integer values (0, 23 | // 1, 2, etc.) Instead, each member is a fully-fledged value in its own right. 24 | enum Planet 25 | { 26 | case Mercury 27 | case Venus 28 | case Earth 29 | case Mars 30 | case Jupiter 31 | case Saturn 32 | case Uranus 33 | case Neptune 34 | } 35 | 36 | // You can also combine members onto a single line if you prefer, or mix them up. This has no 37 | // effect on the enumeration itself. 38 | enum CompassPoint 39 | { 40 | case North, South 41 | case East, West 42 | } 43 | 44 | // Let's store an enumeration value into a variable. We'll let the compiler infer the type: 45 | var directionToHead = CompassPoint.West 46 | 47 | // Now that directionToHead has a CompassPoint type (which was inferred) we can set it to a 48 | // different CompassPoint value using a shorter syntax: 49 | directionToHead = .East 50 | 51 | // We can use a switch to match values from an enumeration. 52 | // 53 | // Remember that switches have to be exhaustive. But in this case, Swift knows that the CompassType 54 | // enumeration only has 4 values, so as long as we cover all 4, we don't need the default case. 55 | switch directionToHead 56 | { 57 | case .North: 58 | "North" 59 | case .South: 60 | "South" 61 | case .East: 62 | "East" 63 | case .West: 64 | "West" 65 | } 66 | 67 | // ------------------------------------------------------------------------------------------------ 68 | // Associated Values 69 | // 70 | // Associated values allows us to store information with each member of the switch using a Tuple. 71 | // 72 | // The following enumeration will store not only the type of a barcode (UPCA, QR Code) but also 73 | // the data of the barcode (this is likely a foreign concept for most.) 74 | enum Barcode 75 | { 76 | case UPCA(Int, Int, Int) // UPCA with associated value type (Int, Int, Int) 77 | case QRCode(String) // QRCode with associated value type of String 78 | } 79 | 80 | // Let's specify a UPCA code (letting the compiler infer the enum type of Barcode): 81 | var productBarcode = Barcode.UPCA(0, 8590951226, 3) 82 | 83 | // Let's change that to a QR code (still of a Barcode type) 84 | productBarcode = .QRCode("ABCDEFGHIJKLMNOP") 85 | 86 | // We use a switch to check the value and extract the associated value: 87 | switch productBarcode 88 | { 89 | case .UPCA(let numberSystem, let identifier, let check): 90 | "UPCA: \(numberSystem), \(identifier), \(check)" 91 | case .QRCode(let productCode): 92 | "QR: \(productCode)" 93 | } 94 | 95 | // Using the switch statement simplification (see the Switch statement section) to reduce the 96 | // number of occurrances of the 'let' introducer: 97 | switch productBarcode 98 | { 99 | // All constants 100 | case let .UPCA(numberSystem, identifier, check): 101 | "UPCA: \(numberSystem), \(identifier), \(check)" 102 | 103 | // All variables 104 | case var .QRCode(productCode): 105 | "QR: \(productCode)" 106 | } 107 | 108 | // ------------------------------------------------------------------------------------------------ 109 | // Raw values 110 | // 111 | // We can assign a type to an enumeration. If we use Int as the type, then we are effectively 112 | // making an enumeration that functions like its C counterpart: 113 | enum StatusCode: Int 114 | { 115 | case Error = -1 116 | case Success = 9 117 | case OtherResult = 1 118 | case YetAnotherResult // Unspecified values are auto-incremented from the previous value 119 | } 120 | 121 | // We can get the raw value of an enumeration value with the rawValue member: 122 | StatusCode.OtherResult.rawValue 123 | 124 | // We can give enumerations many types. Here's one of type Character: 125 | enum ASCIIControlCharacter: Character 126 | { 127 | case Tab = "\t" 128 | case LineFeed = "\n" 129 | case CarriageReturn = "\r" 130 | 131 | // Note that only Int type enumerations can auto-increment. Since this is a Character type, 132 | // the following line of code won't compile: 133 | // 134 | // case VerticalTab 135 | } 136 | 137 | // Alternatively, we could also use Strings 138 | enum FamilyPet: String 139 | { 140 | case Cat = "Cat" 141 | case Dog = "Dog" 142 | case Ferret = "Ferret" 143 | } 144 | 145 | // And we can get their raw value as well: 146 | FamilyPet.Ferret.rawValue 147 | 148 | // We can also generate the enumeration value from the raw value. Note that this is an optional 149 | // because not all raw values will have a matching enumeration: 150 | var pet = FamilyPet(rawValue: "Ferret") 151 | 152 | // Let's verify this: 153 | if pet != .None { "We have a pet!" } 154 | else { "No pet :(" } 155 | 156 | // An example of when a raw doesn't translate to an enum, leaving us with a nil optional: 157 | pet = FamilyPet(rawValue: "Snake") 158 | if pet != .None { "We have a pet" } 159 | else { "No pet :(" } 160 | 161 | -------------------------------------------------------------------------------- /8. Enumerations.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /9. Classes and Structures.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /9. Classes and Structures.playground/section-1.swift: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------------------------ 2 | // Things to know: 3 | // 4 | // Classes and structures can both: 5 | // 6 | // * Define properties to store values 7 | // * Define methods to provide functionality 8 | // * Define subscripts to provide access to their values using subscript syntax 9 | // * Define initializers to set up their initial state 10 | // * Be extended to expand their functionality beyond a default implementation 11 | // * Conform to protocols to provide standard functionality of a certain kind 12 | // 13 | // Only classes have: 14 | // 15 | // * Inheritance enables one class to inherit the characteristics of another. 16 | // * Type casting enables you to check and interpret the type of a class instance at runtime. 17 | // * Deinitializers enable an instance of a class to free up any resources it has assigned. 18 | // * Reference counting allows more than one reference to a class instance. 19 | // ------------------------------------------------------------------------------------------------ 20 | 21 | // First, let's create a basic structure with a couple of simple properties. 22 | // 23 | // Our structure must have all of its properties initialized, either with default values or through 24 | // initialization (described later.) For now, we'll just ensure they're all initialized with 25 | // default values. 26 | struct Resolution 27 | { 28 | var width = 1280 29 | var height = 1024 30 | } 31 | 32 | // Similarly, a basic class with a few properties, fully initialized. Notice that the first 33 | // property is an instance of the Resolution structure. 34 | // 35 | // Also, note that the final member, the 'name' property, does not need to be initialized because 36 | // optionals are initalized to nil by default. 37 | class VideoMode 38 | { 39 | var resolution = Resolution() 40 | var interlaced = false 41 | var frameRate = 0.0 42 | var name: String? 43 | } 44 | 45 | // Here are some instances of our structure and class: 46 | var someResolution = Resolution() 47 | var someVideoMode = VideoMode() 48 | 49 | // ------------------------------------------------------------------------------------------------ 50 | // Accessing properties 51 | // 52 | // We can access members of the class or structure using the dot operator: 53 | someResolution.width 54 | someVideoMode.resolution.width 55 | 56 | // In Objective-C, if an object contained a structure, the sub-properties (the properties of the 57 | // structure inside the object) could not be modified directly. Instead the entire structure would 58 | // have to be replaced completely. This is not the case for Swift. 59 | someVideoMode.resolution.width = 2880 60 | someVideoMode.resolution.height = 1800 61 | 62 | // ------------------------------------------------------------------------------------------------ 63 | // Structures and Enumerations are Value Types 64 | // 65 | // This means that when passing an instance of a structure or enumeration to a function (or 66 | // assigning an instance of a structure or enumeration to another value), the structure or 67 | // enumeration is copied. 68 | // 69 | // Let's create two independent copies of a Resolution structure 70 | let constantResolution = Resolution() 71 | var variableResolution = constantResolution 72 | 73 | // We can modify the variable resolution: 74 | variableResolution.width = 320 75 | variableResolution.height = 200 76 | 77 | // We can see that the original (from where the variable copy originated) is unchanged: 78 | constantResolution 79 | 80 | // Note that since structures and enumerations are value types, we are unable to modify the 81 | // contents of constant intances. 82 | // 83 | // The following will not compile: 84 | // 85 | // constantResolution.width = 320 86 | 87 | // ------------------------------------------------------------------------------------------------ 88 | // Classes are Reference Types: 89 | // 90 | // This means that when passing an instance of an object to a function (or assigning an instance 91 | // of an object to another value), the new value will hold a reference to the original object. 92 | // 93 | // Let's create an object and assign it's instance to another variable: 94 | let constantVideoMode = VideoMode() 95 | var variableVideoMode = constantVideoMode 96 | 97 | // If we modify the variable.. 98 | variableVideoMode.frameRate = 240 99 | 100 | // ...we can see that the other instance is also modified: 101 | constantVideoMode.frameRate 102 | 103 | // In addition to this, we can even modify the 'constantVideoMode' instance. This is the case 104 | // because it is a reference type and modifing the contents do not modify the reference itself. 105 | constantVideoMode.frameRate = 24 106 | 107 | // We cannot, however, modify the instance variable. 108 | // 109 | // This line of code will not compile: 110 | // 111 | // constantVideoMode = VideoMode 112 | 113 | // ------------------------------------------------------------------------------------------------ 114 | // Memberwise Initializers for Structure Types 115 | // 116 | // We can set the properties without the need to create a specialiized init routine. If a struct 117 | // (not a class) does not have any initializers, then Swift will provide a "Memberwise Initializer" 118 | // for us automatically. 119 | // 120 | // Here's what tha memberwise initializer looks like. It's pretty self-explanatory in that it is 121 | // an initializer that includes one externally named parameter for each property in the structure. 122 | let vga = Resolution(width: 640, height: 480) 123 | 124 | // ------------------------------------------------------------------------------------------------ 125 | // Identity operators 126 | // 127 | // Since classes are reference types, we can check to see if they are 'identical' with the 128 | // Identity (===) operator: 129 | someVideoMode === variableVideoMode 130 | constantVideoMode === variableVideoMode 131 | 132 | // Identical-to is not the same as equal to: 133 | // 134 | // The following line will not compile as it uses the equality operator and VideoMode hasn't 135 | // defined an equality operator: 136 | // 137 | // constantVideoMode == variableVideoMode 138 | -------------------------------------------------------------------------------- /9. Classes and Structures.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /99. Not The End.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /99. Not The End.playground/section-1.swift: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------------------------------------ 2 | // ____ _ _ 3 | // / ___|___ _ __ __ _ _ __ __ _| |_ ___| | 4 | // | | / _ \| '_ \ / _` | '__/ _` | __/ __| | 5 | // | |__| (_) | | | | (_| | | | (_| | |_\__ \_| 6 | // \____\___/|_| |_|\__, |_| \__,_|\__|___(_) 7 | // |___/ 8 | // 9 | // You've made it to the end! 10 | // 11 | // ------------------------------------------------------------------------------------------------ 12 | 13 | // ------------------------------------------------------------------------------------------------ 14 | // There's Still More To Learn 15 | // 16 | // These playgrounds are taken directly from the Language Guide section of Apple's book titled 17 | // "The Swift Programming Language". 18 | // 19 | // The book also includes a Language Reference, which discusses the language in a more terse form 20 | // with greater detail (including the grammar.) It's not as dry as reading the C++ Ansi Spec, but 21 | // it's still rather detailed. In fact, some of the information from these playgrounds came from 22 | // the Language Reference section. 23 | // 24 | // The good news is that having managed to get through these playgrounds, you'll probably find 25 | // the Language Reference to be rather quick reading, chock full of additional goodies that you 26 | // never knew about (because the Language Guide and these Playgrounds don't touch on.) 27 | // 28 | // For example, how would you code the assert function such that the first parameter is executed 29 | // and evaluated to a Bool for use in determining an error condition? 30 | var pi = 3.14159 31 | assert(pi > 3.14, "Pi is too small") 32 | 33 | // Do you know why this compiles? 34 | func doSomeMagic(#a: Int)(b: Int) -> Int 35 | { 36 | return a + b 37 | } 38 | 39 | // ...or why it can be executed like this? 40 | doSomeMagic(a: 10)(b: 10) 41 | 42 | // You'll also learn about Metatypes and did you know that Swift's operator precedence is slightly 43 | // different than C? 44 | // 45 | // This is clearly stuff you should know before submitting your forehead to the wall. 46 | // 47 | // You'll learn about these constants: 48 | __FILE__ + "(" + String(__LINE__) + "): " + __FUNCTION__ + ":" + String(__COLUMN__) 49 | 50 | // Furthermore, don't let somebody else's code confuse you when you see something like this in 51 | // their code and realize that it actually compiles! 52 | var ohrly = pi.dynamicType.infinity 53 | 54 | // Most importantly, you'll solidify your understanding of the concepts that were presented in 55 | // these playgrounds. 56 | // 57 | // Happy coding! 58 | // 59 | // - Paul Nettle 60 | -------------------------------------------------------------------------------- /99. Not The End.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | learn-swift 2 | =========== 3 | 4 | Learn Apple's Swift programming language interactively through these playgrounds. 5 | 6 | ###Target Audience 7 | 8 | Programmers familiar with C++ and/or Objective-C. 9 | 10 | ###What you'll need 11 | 12 | You will need XCode 6.0 GM (or later) or 6.1 Beta 2 (or later) and probably 13 | a Mac to run it on. 14 | 15 | ###Purpose & Goal 16 | 17 | More than a primer, these playgrounds are intended to get programmers up to 18 | speed on Swift as fast as possible so they can begin using Swift productively. 19 | 20 | These playgrounds only cover the language. They do not dig into the Swift 21 | Standard Library or other APIs. 22 | 23 | To increase your understanding, you are encouraged to experiment with them 24 | along the way. Play the 'what-if' game. These are live playgrounds which offer 25 | near-realtime feedback of edits. 26 | 27 | ###Source of Content 28 | 29 | I created these while working my way through the "Language Guide" section of 30 | Apple's book, "The Swift Programming Language". I feel the information from 31 | that section is represented here fairly completely and very concisely. Many 32 | of the samples come directly from the book. Some portions from the book's 33 | "Language Reference" section were also included where I felt a bit more 34 | information was helpful. 35 | 36 | If you don't already have the book, it's free. You should probably get it. 37 | 38 | ###Contributors 39 | 40 | Thanks to Rafał Wójcik for his quick work to update these playgrounds to 41 | incorporate the Swift language changes that came with XCode Beta 3. 42 | --------------------------------------------------------------------------------