diff --git a/10. Properties.playground/Documentation/section-0.html b/10. Properties.playground/Documentation/section-0.html new file mode 100644 index 0000000..7b4dc1f --- /dev/null +++ b/10. Properties.playground/Documentation/section-0.html @@ -0,0 +1,25 @@ + + + + + Section 1 + + + + + + +
+
+

Properties

+

Things to know:

+ +
+

Here's a structure with a couple simple stored properties:

+ +
+
+ + diff --git a/10. Properties.playground/Documentation/section-10.html b/10. Properties.playground/Documentation/section-10.html new file mode 100644 index 0000000..d21c548 --- /dev/null +++ b/10. Properties.playground/Documentation/section-10.html @@ -0,0 +1,19 @@ + + + + + Section 11 + + + + + + +
+
+

Notice how we haven't used the importer yet, so it is nil:

+ +
+
+ + diff --git a/10. Properties.playground/Documentation/section-12.html b/10. Properties.playground/Documentation/section-12.html new file mode 100644 index 0000000..fd31def --- /dev/null +++ b/10. Properties.playground/Documentation/section-12.html @@ -0,0 +1,19 @@ + + + + + Section 13 + + + + + + +
+
+

So now let's access it:

+ +
+
+ + diff --git a/10. Properties.playground/Documentation/section-14.html b/10. Properties.playground/Documentation/section-14.html new file mode 100644 index 0000000..a6af5e1 --- /dev/null +++ b/10. Properties.playground/Documentation/section-14.html @@ -0,0 +1,19 @@ + + + + + Section 15 + + + + + + +
+
+

And now we see the importer was created:

+ +
+
+ + diff --git a/10. Properties.playground/Documentation/section-16.html b/10. Properties.playground/Documentation/section-16.html new file mode 100644 index 0000000..2b43fae --- /dev/null +++ b/10. Properties.playground/Documentation/section-16.html @@ -0,0 +1,22 @@ + + + + + Section 17 + + + + + + +
+
+

Computed Properties

+

Computed properties don't store data, but rather use getters and setters for accessing values that are computed up on request.

+

Computed Properties are available for global as well as local variables.

+

We'll start with a few structures that we'll use to show how computed properties work.

+ +
+
+ + diff --git a/10. Properties.playground/Documentation/section-18.html b/10. Properties.playground/Documentation/section-18.html new file mode 100644 index 0000000..4e4f4f4 --- /dev/null +++ b/10. Properties.playground/Documentation/section-18.html @@ -0,0 +1,20 @@ + + + + + Section 19 + + + + + + +
+
+

The following structure includes a computed property with a Point type named 'center'. Notice that 'center' is variable (not constant) which is a requirement of computed properties.

+

Every computed property must have a getter, but does not need a setter. If we provide a setter, we need to know the new value being assigned to the computed property. We've called this value 'newCenter'. Note that this value does not have a type annotation because the computed property already knows that it is a Point type. Providing a type annotation would be an error.

+ +
+
+ + diff --git a/10. Properties.playground/Documentation/section-2.html b/10. Properties.playground/Documentation/section-2.html new file mode 100644 index 0000000..5090af6 --- /dev/null +++ b/10. Properties.playground/Documentation/section-2.html @@ -0,0 +1,20 @@ + + + + + Section 3 + + + + + + +
+
+

Structures must have all of their properties initialized upon instantiation.

+

This won't compile since the struct includes properties that havn't been initialized with default values:

+ +
+
+ + diff --git a/10. Properties.playground/Documentation/section-20.html b/10. Properties.playground/Documentation/section-20.html new file mode 100644 index 0000000..42ed8d6 --- /dev/null +++ b/10. Properties.playground/Documentation/section-20.html @@ -0,0 +1,19 @@ + + + + + Section 21 + + + + + + +
+
+

Here, we'll create a square from our Rect structure

+ +
+
+ + diff --git a/10. Properties.playground/Documentation/section-22.html b/10. Properties.playground/Documentation/section-22.html new file mode 100644 index 0000000..ae7aba0 --- /dev/null +++ b/10. Properties.playground/Documentation/section-22.html @@ -0,0 +1,19 @@ + + + + + Section 23 + + + + + + +
+
+

We can now get the center point, computed from the Rect's origin and size. Being a computed property, we can treat it just like any other peroperty.

+ +
+
+ + diff --git a/10. Properties.playground/Documentation/section-24.html b/10. Properties.playground/Documentation/section-24.html new file mode 100644 index 0000000..9b22e06 --- /dev/null +++ b/10. Properties.playground/Documentation/section-24.html @@ -0,0 +1,20 @@ + + + + + Section 25 + + + + + + +
+
+

Since we provided a setter, we can also set the center point as if it is a stored property. +This will effectively update the Rect's origin and size based on the specified center point.

+ +
+
+ + diff --git a/10. Properties.playground/Documentation/section-26.html b/10. Properties.playground/Documentation/section-26.html new file mode 100644 index 0000000..be6c6c7 --- /dev/null +++ b/10. Properties.playground/Documentation/section-26.html @@ -0,0 +1,19 @@ + + + + + Section 27 + + + + + + +
+
+

We can see that the origin has been updated from (0, 0) to (10, 10):

+ +
+
+ + diff --git a/10. Properties.playground/Documentation/section-28.html b/10. Properties.playground/Documentation/section-28.html new file mode 100644 index 0000000..c97910f --- /dev/null +++ b/10. Properties.playground/Documentation/section-28.html @@ -0,0 +1,21 @@ + + + + + Section 29 + + + + + + +
+
+

Shorthand Setter Declaration

+

The computed property's setter from the Rect structure provided a parameter on the setter named 'newCenter'. If we don't specify this parameter, Swift will automatically generate an input value named 'newValue' for us.

+

Here, AlternativeRect is the same declaration as Rect above, except that the setter uses Swift's default setter value, 'newValue':

+ +
+
+ + diff --git a/10. Properties.playground/Documentation/section-30.html b/10. Properties.playground/Documentation/section-30.html new file mode 100644 index 0000000..9260387 --- /dev/null +++ b/10. Properties.playground/Documentation/section-30.html @@ -0,0 +1,19 @@ + + + + + Section 31 + + + + + + +
+
+

We can also have a read-only computed property by simply omitting the setter:

+ +
+
+ + diff --git a/10. Properties.playground/Documentation/section-32.html b/10. Properties.playground/Documentation/section-32.html new file mode 100644 index 0000000..2ca90e0 --- /dev/null +++ b/10. Properties.playground/Documentation/section-32.html @@ -0,0 +1,19 @@ + + + + + Section 33 + + + + + + +
+
+

Alternatively, Swift allows us to shorten the syntax of a read-only computed property by omitting the get{} construct and inserting the code for the getter directly into the property declaration:

+ +
+
+ + diff --git a/10. Properties.playground/Documentation/section-34.html b/10. Properties.playground/Documentation/section-34.html new file mode 100644 index 0000000..32e82e1 --- /dev/null +++ b/10. Properties.playground/Documentation/section-34.html @@ -0,0 +1,19 @@ + + + + + Section 35 + + + + + + +
+
+

Let's declare our structure and read the 'volume' property

+ +
+
+ + diff --git a/10. Properties.playground/Documentation/section-36.html b/10. Properties.playground/Documentation/section-36.html new file mode 100644 index 0000000..1b016fe --- /dev/null +++ b/10. Properties.playground/Documentation/section-36.html @@ -0,0 +1,20 @@ + + + + + Section 37 + + + + + + +
+
+

Since the 'volume' property is read-only, if we tried to assign a value to it, it would would generate a compiler error.

+

The following line of code will not compile:

+ +
+
+ + diff --git a/10. Properties.playground/Documentation/section-38.html b/10. Properties.playground/Documentation/section-38.html new file mode 100644 index 0000000..aad30c0 --- /dev/null +++ b/10. Properties.playground/Documentation/section-38.html @@ -0,0 +1,23 @@ + + + + + Section 39 + + + + + + +
+
+

Property Observers

+

Property observers allow us to respond to changes in a property's value. We can declare an observer that contains our code that is run just before a property is set (optionally allowing us to alter the value being set) and an observer that is run just after a property has been modified.

+

Property observers are available for global as well as local variables.

+

These observers are not called when a property is first initialized, but only when it changes.

+

Similar to setters, each 'willSet' and 'didSet' will receive a parameter that represents (in the case of 'willSet') the value about to be assigned to the property and (in the case of 'didSet') the value that was previously stored in the property prior to modification.

+ +
+
+ + diff --git a/10. Properties.playground/Documentation/section-4.html b/10. Properties.playground/Documentation/section-4.html new file mode 100644 index 0000000..dcd952d --- /dev/null +++ b/10. Properties.playground/Documentation/section-4.html @@ -0,0 +1,19 @@ + + + + + Section 5 + + + + + + +
+
+

In order to instantiate a FixedLengthRange struct, we must use the memberwise initializer. Note that this will initialize a constant property and a variable property inside the struct:

+ +
+
+ + diff --git a/10. Properties.playground/Documentation/section-40.html b/10. Properties.playground/Documentation/section-40.html new file mode 100644 index 0000000..c3559ea --- /dev/null +++ b/10. Properties.playground/Documentation/section-40.html @@ -0,0 +1,19 @@ + + + + + Section 41 + + + + + + +
+
+

Let's create an instance of StepCounter so we can try out our observer

+ +
+
+ + diff --git a/10. Properties.playground/Documentation/section-42.html b/10. Properties.playground/Documentation/section-42.html new file mode 100644 index 0000000..b438d6a --- /dev/null +++ b/10. Properties.playground/Documentation/section-42.html @@ -0,0 +1,19 @@ + + + + + Section 43 + + + + + + +
+
+

The following will first call 'willSet' on the 'totalSteps' property, followed by a change to the value itself, followed by a call to 'didSet'

+ +
+
+ + diff --git a/10. Properties.playground/Documentation/section-44.html b/10. Properties.playground/Documentation/section-44.html new file mode 100644 index 0000000..a66a942 --- /dev/null +++ b/10. Properties.playground/Documentation/section-44.html @@ -0,0 +1,19 @@ + + + + + Section 45 + + + + + + +
+
+

Similar to setters, we can shorten our observers by omitting the parameter list for each. When we co this, Swift automatically provides parameters named 'newValue' and 'oldValue'

+ +
+
+ + diff --git a/10. Properties.playground/Documentation/section-46.html b/10. Properties.playground/Documentation/section-46.html new file mode 100644 index 0000000..eaf5b21 --- /dev/null +++ b/10. Properties.playground/Documentation/section-46.html @@ -0,0 +1,20 @@ + + + + + Section 47 + + + + + + +
+
+

We can also override the value being set by modifying the property itself within the 'didSet' observer. This only works in the 'didSet'. If you attempt to modify the property in 'willSet' you will receive a compiler warning.

+

Let's try wrapping our value to the range of 0...255

+ +
+
+ + diff --git a/10. Properties.playground/Documentation/section-48.html b/10. Properties.playground/Documentation/section-48.html new file mode 100644 index 0000000..c66d7e5 --- /dev/null +++ b/10. Properties.playground/Documentation/section-48.html @@ -0,0 +1,25 @@ + + + + + Section 49 + + + + + + +
+
+

Type Properties

+

Until now, we've been working with Instance Properties and Instance Methods, which are associated to an instance of a class, structure or enumeration. Each instance gets its own copy of the property or method.

+

Type properties are properties that are attached to the class, structure or enumeration's type itself and not any specific instance. All instances of a type will share the same Type Property.

+

These are similar to 'static' properties in C-like languages.

+

For Value types (structs, enums) we use the 'static' keyword. For Class types with the 'class' keyword.

+

Type properties can be in the form of stored properties or computed properties. As with normal stored or computed properties, all the same rules apply except that stored type properties must always have a default value. This exception is necessary because the initializer (which we'll learn about later) is associated to a specific instance and as such, cannot be reliable for initializing type properties.

+

Here is a class with a couple of type properties

+ +
+
+ + diff --git a/10. Properties.playground/Documentation/section-50.html b/10. Properties.playground/Documentation/section-50.html new file mode 100644 index 0000000..2764b14 --- /dev/null +++ b/10. Properties.playground/Documentation/section-50.html @@ -0,0 +1,19 @@ + + + + + Section 51 + + + + + + +
+
+

Similarly, here's an enumeration with a couple of type properties

+ +
+
+ + diff --git a/10. Properties.playground/Documentation/section-52.html b/10. Properties.playground/Documentation/section-52.html new file mode 100644 index 0000000..a812dee --- /dev/null +++ b/10. Properties.playground/Documentation/section-52.html @@ -0,0 +1,19 @@ + + + + + Section 53 + + + + + + +
+
+

Classes are a little different in that they cannot contain stored type properties, but may only contain computed type properties

+ +
+
+ + diff --git a/10. Properties.playground/Documentation/section-54.html b/10. Properties.playground/Documentation/section-54.html new file mode 100644 index 0000000..7b47965 --- /dev/null +++ b/10. Properties.playground/Documentation/section-54.html @@ -0,0 +1,18 @@ + + + + + Section 55 + + + + + + +
+
+ +
+
+ + diff --git a/10. Properties.playground/Documentation/section-6.html b/10. Properties.playground/Documentation/section-6.html new file mode 100644 index 0000000..f2aee9d --- /dev/null +++ b/10. Properties.playground/Documentation/section-6.html @@ -0,0 +1,23 @@ + + + + + Section 7 + + + + + + +
+
+

Lazy Stored Properties

+

A Lazy Stored Property is a value that is not calculated until its first use.

+

They are declared using the "lazy" attribute and may not be constant.

+

Global and local variables are all lazy, except that they don't need the lazy attribute.

+

Here, we'll define a couple classes to showcase Lazy Stored Properties. In this example, let's assume that DataImporter is a time-consuming process, and as such, we will want to use a lazy stored property whenever we use it. This way, if we end up never using it, we never pay the penalty of instantiating it.

+ +
+
+ + diff --git a/10. Properties.playground/Documentation/section-8.html b/10. Properties.playground/Documentation/section-8.html new file mode 100644 index 0000000..09b8c3b --- /dev/null +++ b/10. Properties.playground/Documentation/section-8.html @@ -0,0 +1,19 @@ + + + + + Section 9 + + + + + + +
+
+

Now let's instantiate the data manager and add some simple data to the class:

+ +
+
+ + diff --git a/10. Properties.playground/Documentation/stylesheet.css b/10. Properties.playground/Documentation/stylesheet.css new file mode 100644 index 0000000..43b0230 --- /dev/null +++ b/10. Properties.playground/Documentation/stylesheet.css @@ -0,0 +1,65 @@ +html { + font-size: 1em; +} + +body { + background-color: #fff; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: normal; + font-size: 1.0em; + line-height: 140%; + padding: 0 2em; +} + +code { + font-family: "Menlo", monospace; + font-size: 0.95em; +} + +section { + padding: 0.5em 0; + -webkit-overflow-scrolling: touch; +} + +a:link { + color: #00f; + text-decoration: none; + border-bottom: 1px solid #00f; +} + +h1 { + font-size: 1.5em; + padding: 0.7em 0 0 0; +} + +h2 { + font-size: 1.2em; + padding: 0.7em 0 0 0; +} + +h3 { + font-size: 1.0em; + padding: 0.7em 0 0 0; +} + +blockquote { + font-size: 0.9em; + margin-left: 0; + padding-left: 2em; + border-left: 8px solid #eee; +} + +.footnotes { + color: #666666; + margin-top: 2em; + font-size: 0.9em; + letter-spacing: -3px; +} + +.footnotes:before { + content: "――――"; +} + +.footnotes ol { + letter-spacing: normal; +} diff --git a/10. Properties.playground/contents.xcplayground b/10. Properties.playground/contents.xcplayground index 4937636..0929d85 100644 --- a/10. Properties.playground/contents.xcplayground +++ b/10. Properties.playground/contents.xcplayground @@ -1,7 +1,115 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file diff --git a/10. Properties.playground/section-1.swift b/10. Properties.playground/section-1.swift index e63507f..684195f 100644 --- a/10. Properties.playground/section-1.swift +++ b/10. Properties.playground/section-1.swift @@ -1,301 +1,5 @@ -// ------------------------------------------------------------------------------------------------ -// Things to know: -// -// * Properties store values in classes, structures and enumerations. -// ------------------------------------------------------------------------------------------------ - -// Here's a structure with a couple simple stored properties: struct FixedLengthRange { var firstValue: Int let length: Int -} - -// Structures must have all of their properties initialized upon instantiation. -// -// This won't compile since the struct includes properties that havn't been initialized with -// default values: -// -// var anotherRangeOfThreeItems = FixedLengthRange() -// -// In order to instantiate a FixedLengthRange struct, we must use the memberwise initializer. Note -// that this will initialize a constant property and a variable property inside the struct: -var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3) -rangeOfThreeItems.firstValue = 6 - -// ------------------------------------------------------------------------------------------------ -// Lazy Stored Properties -// -// A Lazy Stored Property is a value that is not calculated until its first use. -// -// They are declared using the "lazy" attribute and may not be constant. -// -// Global and local variables are all lazy, except that they don't need the lazy attribute. -// -// Here, we'll define a couple classes to showcase Lazy Stored Properties. In this example, let's -// assume that DataImporter is a time-consuming process, and as such, we will want to use a lazy -// stored property whenever we use it. This way, if we end up never using it, we never pay the -// penalty of instantiating it. -class DataImporter -{ - var filename = "data.txt" -} - -class DataManager -{ - lazy var importer = DataImporter() - var data = [String]() -} - -// Now let's instantiate the data manager and add some simple data to the class: -let manager = DataManager() -manager.data.append("Some data") -manager.data.append("Some more data") - -// Notice how we haven't used the importer yet, so it is nil: -manager - -// So now let's access it: -manager.importer.filename - -// And now we see the importer was created: -manager - -// ------------------------------------------------------------------------------------------------ -// Computed Properties -// -// Computed properties don't store data, but rather use getters and setters for accessing values -// that are computed up on request. -// -// Computed Properties are available for global as well as local variables. -// -// We'll start with a few structures that we'll use to show how computed properties work. -struct Point -{ - var x = 0.0, y = 0.0 -} -struct Size -{ - var width = 0.0, height = 0.0 -} - -// The following structure includes a computed property with a Point type named 'center'. Notice -// that 'center' is variable (not constant) which is a requirement of computed properties. -// -// Every computed property must have a getter, but does not need a setter. If we provide a setter, -// we need to know the new value being assigned to the computed property. We've called this -// value 'newCenter'. Note that this value does not have a type annotation because the computed -// property already knows that it is a Point type. Providing a type annotation would be an error. -struct Rect -{ - var origin = Point() - var size = Size() - var center: Point - { - get - { - let centerX = origin.x + (size.width / 2) - let centerY = origin.y + (size.height / 2) - return Point(x: centerX, y: centerY) - } - set(newCenter) - { - origin.x = newCenter.x - (size.width / 2) - origin.y = newCenter.y - (size.height / 2) - } - } -} - -// Here, we'll create a square from our Rect structure -var square = Rect(origin: Point(x: 0.0, y: 0.0), size: Size(width: 10.0, height: 10.0)) - -// We can now get the center point, computed from the Rect's origin and size. Being a computed -// property, we can treat it just like any other peroperty. -let initialSquareCenter = square.center - -// Since we provided a setter, we can also set the center point as if it is a stored property. -// This will effectively update the Rect's origin and size based on the specified center point. -square.center = Point(x: 15, y: 15) - -// We can see that the origin has been updated from (0, 0) to (10, 10): -square.origin - -// Shorthand Setter Declaration -// -// The computed property's setter from the Rect structure provided a parameter on the setter named -// 'newCenter'. If we don't specify this parameter, Swift will automatically generate an input -// value named 'newValue' for us. -// -// Here, AlternativeRect is the same declaration as Rect above, except that the setter uses -// Swift's default setter value, 'newValue': -struct AlternativeRect -{ - var origin = Point() - var size = Size() - var center: Point - { - get - { - let centerX = origin.x + (size.width / 2) - let centerY = origin.y + (size.height / 2) - return Point(x: centerX, y: centerY) - } - set - { - origin.x = newValue.x - (size.width / 2) - origin.y = newValue.y - (size.height / 2) - } - } -} - -// We can also have a read-only computed property by simply omitting the setter: -struct Cube -{ - var width = 0.0, height = 0.0, depth = 0.0 - var volume: Double - { - get - { - return width * height * depth - } - } -} - -// Alternatively, Swift allows us to shorten the syntax of a read-only computed property by -// omitting the get{} construct and inserting the code for the getter directly into the property -// declaration: -struct AnotherCube -{ - var width = 0.0, height = 0.0, depth = 0.0 - var volume: Double - { - return width * height * depth - } -} - -// Let's declare our structure and read the 'volume' property -var cube = AnotherCube(width: 4, height: 5, depth: 2) -cube.volume - -// Since the 'volume' property is read-only, if we tried to assign a value to it, it would -// would generate a compiler error. -// -// The following line of code will not compile: -// -// cube.volume = 8.0 - -// ------------------------------------------------------------------------------------------------ -// Property Observers -// -// Property observers allow us to respond to changes in a property's value. We can declare an -// observer that contains our code that is run just before a property is set (optionally allowing -// us to alter the value being set) and an observer that is run just after a property has been -// modified. -// -// Property observers are available for global as well as local variables. -// -// These observers are not called when a property is first initialized, but only when it changes. -// -// Similar to setters, each 'willSet' and 'didSet' will receive a parameter that represents (in -// the case of 'willSet') the value about to be assigned to the property and (in the case of -// 'didSet') the value that was previously stored in the property prior to modification. -class StepCounter -{ - var totalSteps: Int = 0 - { - willSet(newTotalSteps) - { - "About to step to \(newTotalSteps)" - } - didSet(oldTotalSteps) - { - "Just stepped from \(oldTotalSteps)" - } - } -} - -// Let's create an instance of StepCounter so we can try out our observer -let stepCounter = StepCounter() - -// The following will first call 'willSet' on the 'totalSteps' property, followed by a change to -// the value itself, followed by a call to 'didSet' -stepCounter.totalSteps = 200; - -// Similar to setters, we can shorten our observers by omitting the parameter list for each. When -// we co this, Swift automatically provides parameters named 'newValue' and 'oldValue' -class StepCounterShorter -{ - var totalSteps: Int = 0 - { - willSet{ "About to step to \(newValue)" } - didSet { "Just stepped from \(oldValue)" } - } -} - -// We can also override the value being set by modifying the property itself within the 'didSet' -// observer. This only works in the 'didSet'. If you attempt to modify the property in 'willSet' -// you will receive a compiler warning. -// -// Let's try wrapping our value to the range of 0...255 -class StepCounterShorterWithModify -{ - var totalSteps: Int = 0 - { - willSet{ "About to step to \(newValue)" } - didSet { totalSteps = totalSteps & 0xff } - } -} -var stepper = StepCounterShorterWithModify() -stepper.totalSteps = 345 -stepper.totalSteps // This reports totalSteps is now set to 89 - -// ------------------------------------------------------------------------------------------------ -// Type Properties -// -// Until now, we've been working with Instance Properties and Instance Methods, which are -// associated to an instance of a class, structure or enumeration. Each instance gets its own copy -// of the property or method. -// -// Type properties are properties that are attached to the class, structure or enumeration's type -// itself and not any specific instance. All instances of a type will share the same Type Property. -// -// These are similar to 'static' properties in C-like languages. -// -// For Value types (structs, enums) we use the 'static' keyword. For Class types with the 'class' -// keyword. -// -// Type properties can be in the form of stored properties or computed properties. As with normal -// stored or computed properties, all the same rules apply except that stored type properties must -// always have a default value. This exception is necessary because the initializer (which we'll -// learn about later) is associated to a specific instance and as such, cannot be reliable for -// initializing type properties. -// -// Here is a class with a couple of type properties -struct SomeStructure -{ - static var storedTypeProperty = "some value" - - // Here's a read-only computed type property using the short-hand read-only syntax - static var computedTypeProperty: Int { return 4 } -} - -// Similarly, here's an enumeration with a couple of type properties -enum SomeEnum -{ - static let storedTypeProperty = "some value" - - static var computedTypeProperty: Int { return 4 } -} - -// Classes are a little different in that they cannot contain stored type properties, but may -// only contain computed type properties -class SomeClass -{ - // The following line won't compile because classes aren't allowed stored type properties - // - // class var storedTypeProperty = "some value" - - // This is read-only, but you can also do read/write - class var computedTypeProperty: Int { return 4 } -} - +} \ No newline at end of file diff --git a/10. Properties.playground/section-11.swift b/10. Properties.playground/section-11.swift new file mode 100644 index 0000000..f83cc9d --- /dev/null +++ b/10. Properties.playground/section-11.swift @@ -0,0 +1 @@ +manager \ No newline at end of file diff --git a/10. Properties.playground/section-13.swift b/10. Properties.playground/section-13.swift new file mode 100644 index 0000000..6d16cec --- /dev/null +++ b/10. Properties.playground/section-13.swift @@ -0,0 +1 @@ +manager.importer.filename \ No newline at end of file diff --git a/10. Properties.playground/section-15.swift b/10. Properties.playground/section-15.swift new file mode 100644 index 0000000..f83cc9d --- /dev/null +++ b/10. Properties.playground/section-15.swift @@ -0,0 +1 @@ +manager \ No newline at end of file diff --git a/10. Properties.playground/section-17.swift b/10. Properties.playground/section-17.swift new file mode 100644 index 0000000..754c7d4 --- /dev/null +++ b/10. Properties.playground/section-17.swift @@ -0,0 +1,8 @@ +struct Point +{ + var x = 0.0, y = 0.0 +} +struct Size +{ + var width = 0.0, height = 0.0 +} \ No newline at end of file diff --git a/10. Properties.playground/section-19.swift b/10. Properties.playground/section-19.swift new file mode 100644 index 0000000..a6f80da --- /dev/null +++ b/10. Properties.playground/section-19.swift @@ -0,0 +1,19 @@ +struct Rect +{ + var origin = Point() + var size = Size() + var center: Point + { + get + { + let centerX = origin.x + (size.width / 2) + let centerY = origin.y + (size.height / 2) + return Point(x: centerX, y: centerY) + } + set(newCenter) + { + origin.x = newCenter.x - (size.width / 2) + origin.y = newCenter.y - (size.height / 2) + } + } +} \ No newline at end of file diff --git a/10. Properties.playground/section-21.swift b/10. Properties.playground/section-21.swift new file mode 100644 index 0000000..204b08c --- /dev/null +++ b/10. Properties.playground/section-21.swift @@ -0,0 +1 @@ +var square = Rect(origin: Point(x: 0.0, y: 0.0), size: Size(width: 10.0, height: 10.0)) \ No newline at end of file diff --git a/10. Properties.playground/section-23.swift b/10. Properties.playground/section-23.swift new file mode 100644 index 0000000..edb44e0 --- /dev/null +++ b/10. Properties.playground/section-23.swift @@ -0,0 +1 @@ +let initialSquareCenter = square.center \ No newline at end of file diff --git a/10. Properties.playground/section-25.swift b/10. Properties.playground/section-25.swift new file mode 100644 index 0000000..a748f31 --- /dev/null +++ b/10. Properties.playground/section-25.swift @@ -0,0 +1 @@ +square.center = Point(x: 15, y: 15) \ No newline at end of file diff --git a/10. Properties.playground/section-27.swift b/10. Properties.playground/section-27.swift new file mode 100644 index 0000000..dca76b8 --- /dev/null +++ b/10. Properties.playground/section-27.swift @@ -0,0 +1 @@ +square.origin \ No newline at end of file diff --git a/10. Properties.playground/section-29.swift b/10. Properties.playground/section-29.swift new file mode 100644 index 0000000..a7769ee --- /dev/null +++ b/10. Properties.playground/section-29.swift @@ -0,0 +1,19 @@ +struct AlternativeRect +{ + var origin = Point() + var size = Size() + var center: Point + { + get + { + let centerX = origin.x + (size.width / 2) + let centerY = origin.y + (size.height / 2) + return Point(x: centerX, y: centerY) + } + set + { + origin.x = newValue.x - (size.width / 2) + origin.y = newValue.y - (size.height / 2) + } + } +} \ No newline at end of file diff --git a/10. Properties.playground/section-3.swift b/10. Properties.playground/section-3.swift new file mode 100644 index 0000000..92ca6b2 --- /dev/null +++ b/10. Properties.playground/section-3.swift @@ -0,0 +1 @@ +// var anotherRangeOfThreeItems = FixedLengthRange() \ No newline at end of file diff --git a/10. Properties.playground/section-31.swift b/10. Properties.playground/section-31.swift new file mode 100644 index 0000000..412a56d --- /dev/null +++ b/10. Properties.playground/section-31.swift @@ -0,0 +1,11 @@ +struct Cube +{ + var width = 0.0, height = 0.0, depth = 0.0 + var volume: Double + { + get + { + return width * height * depth + } + } +} \ No newline at end of file diff --git a/10. Properties.playground/section-33.swift b/10. Properties.playground/section-33.swift new file mode 100644 index 0000000..8bf1fb6 --- /dev/null +++ b/10. Properties.playground/section-33.swift @@ -0,0 +1,8 @@ +struct AnotherCube +{ + var width = 0.0, height = 0.0, depth = 0.0 + var volume: Double + { + return width * height * depth + } +} \ No newline at end of file diff --git a/10. Properties.playground/section-35.swift b/10. Properties.playground/section-35.swift new file mode 100644 index 0000000..6b9c2ef --- /dev/null +++ b/10. Properties.playground/section-35.swift @@ -0,0 +1,2 @@ +var cube = AnotherCube(width: 4, height: 5, depth: 2) +cube.volume \ No newline at end of file diff --git a/10. Properties.playground/section-37.swift b/10. Properties.playground/section-37.swift new file mode 100644 index 0000000..efb1cad --- /dev/null +++ b/10. Properties.playground/section-37.swift @@ -0,0 +1 @@ +// cube.volume = 8.0 \ No newline at end of file diff --git a/10. Properties.playground/section-39.swift b/10. Properties.playground/section-39.swift new file mode 100644 index 0000000..0a67d24 --- /dev/null +++ b/10. Properties.playground/section-39.swift @@ -0,0 +1,14 @@ +class StepCounter +{ + var totalSteps: Int = 0 + { + willSet(newTotalSteps) + { + "About to step to \(newTotalSteps)" + } + didSet(oldTotalSteps) + { + "Just stepped from \(oldTotalSteps)" + } + } +} \ No newline at end of file diff --git a/10. Properties.playground/section-41.swift b/10. Properties.playground/section-41.swift new file mode 100644 index 0000000..2915ce9 --- /dev/null +++ b/10. Properties.playground/section-41.swift @@ -0,0 +1 @@ +let stepCounter = StepCounter() \ No newline at end of file diff --git a/10. Properties.playground/section-43.swift b/10. Properties.playground/section-43.swift new file mode 100644 index 0000000..ad8e702 --- /dev/null +++ b/10. Properties.playground/section-43.swift @@ -0,0 +1 @@ +stepCounter.totalSteps = 200; \ No newline at end of file diff --git a/10. Properties.playground/section-45.swift b/10. Properties.playground/section-45.swift new file mode 100644 index 0000000..84c5a48 --- /dev/null +++ b/10. Properties.playground/section-45.swift @@ -0,0 +1,8 @@ +class StepCounterShorter +{ + var totalSteps: Int = 0 + { + willSet{ "About to step to \(newValue)" } + didSet { "Just stepped from \(oldValue)" } + } +} \ No newline at end of file diff --git a/10. Properties.playground/section-47.swift b/10. Properties.playground/section-47.swift new file mode 100644 index 0000000..c13ecb9 --- /dev/null +++ b/10. Properties.playground/section-47.swift @@ -0,0 +1,11 @@ +class StepCounterShorterWithModify +{ + var totalSteps: Int = 0 + { + willSet{ "About to step to \(newValue)" } + didSet { totalSteps = totalSteps & 0xff } + } +} +var stepper = StepCounterShorterWithModify() +stepper.totalSteps = 345 +stepper.totalSteps // This reports totalSteps is now set to 89 \ No newline at end of file diff --git a/10. Properties.playground/section-49.swift b/10. Properties.playground/section-49.swift new file mode 100644 index 0000000..33c91ed --- /dev/null +++ b/10. Properties.playground/section-49.swift @@ -0,0 +1,7 @@ +struct SomeStructure +{ + static var storedTypeProperty = "some value" + + // Here's a read-only computed type property using the short-hand read-only syntax + static var computedTypeProperty: Int { return 4 } +} \ No newline at end of file diff --git a/10. Properties.playground/section-5.swift b/10. Properties.playground/section-5.swift new file mode 100644 index 0000000..997c3fb --- /dev/null +++ b/10. Properties.playground/section-5.swift @@ -0,0 +1,2 @@ +var rangeOfThreeItems = FixedLengthRange(firstValue: 0, length: 3) +rangeOfThreeItems.firstValue = 6 \ No newline at end of file diff --git a/10. Properties.playground/section-51.swift b/10. Properties.playground/section-51.swift new file mode 100644 index 0000000..98c315f --- /dev/null +++ b/10. Properties.playground/section-51.swift @@ -0,0 +1,6 @@ +enum SomeEnum +{ + static let storedTypeProperty = "some value" + + static var computedTypeProperty: Int { return 4 } +} \ No newline at end of file diff --git a/10. Properties.playground/section-53.swift b/10. Properties.playground/section-53.swift new file mode 100644 index 0000000..a8fb059 --- /dev/null +++ b/10. Properties.playground/section-53.swift @@ -0,0 +1,9 @@ +class SomeClass +{ + // The following line won't compile because classes aren't allowed stored type properties + // + // class var storedTypeProperty = "some value" + + // This is read-only, but you can also do read/write + class var computedTypeProperty: Int { return 4 } +} \ No newline at end of file diff --git a/10. Properties.playground/section-7.swift b/10. Properties.playground/section-7.swift new file mode 100644 index 0000000..cc40ece --- /dev/null +++ b/10. Properties.playground/section-7.swift @@ -0,0 +1,10 @@ +class DataImporter +{ + var filename = "data.txt" +} + +class DataManager +{ + lazy var importer = DataImporter() + var data = [String]() +} \ No newline at end of file diff --git a/10. Properties.playground/section-9.swift b/10. Properties.playground/section-9.swift new file mode 100644 index 0000000..525ad53 --- /dev/null +++ b/10. Properties.playground/section-9.swift @@ -0,0 +1,3 @@ +let manager = DataManager() +manager.data.append("Some data") +manager.data.append("Some more data") \ No newline at end of file diff --git a/11. Methods.playground/Documentation/section-0.html b/11. Methods.playground/Documentation/section-0.html new file mode 100644 index 0000000..ca58c29 --- /dev/null +++ b/11. Methods.playground/Documentation/section-0.html @@ -0,0 +1,27 @@ + + + + + Section 1 + + + + + + +
+
+

Methods

+

Things to know:

+
    +
  • Methods can be in the form of Instance Methods, which apply to a given instance of a class struct or enumeration and Type Methods, which apply to the type itself, like static methods in C-like languages.
  • +
+
+

Instance Methods

+

Instance methods work on instances of a class, structure or enumeration. In order to call an instance method, you must first instantiate the class, structure or enumeration and then place the call on that instance.

+

Here, we'll create a class with a single instance method:

+ +
+
+ + diff --git a/11. Methods.playground/Documentation/section-10.html b/11. Methods.playground/Documentation/section-10.html new file mode 100644 index 0000000..fc2d1a6 --- /dev/null +++ b/11. Methods.playground/Documentation/section-10.html @@ -0,0 +1,19 @@ + + + + + Section 11 + + + + + + +
+
+

We'll create a constant Point2...

+ +
+
+ + diff --git a/11. Methods.playground/Documentation/section-12.html b/11. Methods.playground/Documentation/section-12.html new file mode 100644 index 0000000..f93624e --- /dev/null +++ b/11. Methods.playground/Documentation/section-12.html @@ -0,0 +1,20 @@ + + + + + Section 13 + + + + + + +
+
+

Because 'fixedPoint' is constant, we are not allowed to call mutating memthods:

+

The following line won't compile:

+ +
+
+ + diff --git a/11. Methods.playground/Documentation/section-14.html b/11. Methods.playground/Documentation/section-14.html new file mode 100644 index 0000000..a2cbe58 --- /dev/null +++ b/11. Methods.playground/Documentation/section-14.html @@ -0,0 +1,19 @@ + + + + + Section 15 + + + + + + +
+
+

If you're working with a structure or enumeration (not a class), uou can assign to 'self' directly

+ +
+
+ + diff --git a/11. Methods.playground/Documentation/section-16.html b/11. Methods.playground/Documentation/section-16.html new file mode 100644 index 0000000..3a3885e --- /dev/null +++ b/11. Methods.playground/Documentation/section-16.html @@ -0,0 +1,19 @@ + + + + + Section 17 + + + + + + +
+
+

Assigning to 'self' in an enumeration is used to change to a different member of the same enumeration:

+ +
+
+ + diff --git a/11. Methods.playground/Documentation/section-18.html b/11. Methods.playground/Documentation/section-18.html new file mode 100644 index 0000000..d15c955 --- /dev/null +++ b/11. Methods.playground/Documentation/section-18.html @@ -0,0 +1,21 @@ + + + + + Section 19 + + + + + + +
+
+

Type Methods

+

Type methods are like C++'s static methods.

+

They can only access Type members.

+ +
+
+ + diff --git a/11. Methods.playground/Documentation/section-2.html b/11. Methods.playground/Documentation/section-2.html new file mode 100644 index 0000000..d81dff3 --- /dev/null +++ b/11. Methods.playground/Documentation/section-2.html @@ -0,0 +1,24 @@ + + + + + Section 3 + + + + + + +
+
+

Since this should be pretty clear, let's jump into parameters for methods with internal and external names.

+

The defaults for external names are different for methods than they are for global functions.

+

For methods, the default behavior is that the caller must always specify all but the first external parameter name when calling the method. Member authors need not specify the external names for those parameters as the default is to treat all parameters as if they had the "#" specifier, which creates an external parameter name that mirrors the local parameter name.

+

To override this default-external-names-for-second-and-beyond-parameters, specify an "_" as the external parameter name for all but the first parameter.

+

If you want the caller to also use external name for the first parameter, be sure to add your own '#' symbol to the local name or specify the external name explicitly.

+

Here's a class that exercises the various combinations of internal and external name usages:

+ +
+
+ + diff --git a/11. Methods.playground/Documentation/section-20.html b/11. Methods.playground/Documentation/section-20.html new file mode 100644 index 0000000..e81d4ad --- /dev/null +++ b/11. Methods.playground/Documentation/section-20.html @@ -0,0 +1,19 @@ + + + + + Section 21 + + + + + + +
+
+

To call a type method, use the type name, not the instance name:

+ +
+
+ + diff --git a/11. Methods.playground/Documentation/section-22.html b/11. Methods.playground/Documentation/section-22.html new file mode 100644 index 0000000..ab2d427 --- /dev/null +++ b/11. Methods.playground/Documentation/section-22.html @@ -0,0 +1,19 @@ + + + + + Section 23 + + + + + + +
+
+

If we attempt to use an instance to call a type method, we'll get an error

+ +
+
+ + diff --git a/11. Methods.playground/Documentation/section-24.html b/11. Methods.playground/Documentation/section-24.html new file mode 100644 index 0000000..e27f472 --- /dev/null +++ b/11. Methods.playground/Documentation/section-24.html @@ -0,0 +1,19 @@ + + + + + Section 25 + + + + + + +
+
+

The following line will not compile:

+ +
+
+ + diff --git a/11. Methods.playground/Documentation/section-26.html b/11. Methods.playground/Documentation/section-26.html new file mode 100644 index 0000000..fe28089 --- /dev/null +++ b/11. Methods.playground/Documentation/section-26.html @@ -0,0 +1,19 @@ + + + + + Section 27 + + + + + + +
+
+

For classes, type methods use the 'class' keyword rather than the 'static' keyword:

+ +
+
+ + diff --git a/11. Methods.playground/Documentation/section-28.html b/11. Methods.playground/Documentation/section-28.html new file mode 100644 index 0000000..b7ca2a3 --- /dev/null +++ b/11. Methods.playground/Documentation/section-28.html @@ -0,0 +1,19 @@ + + + + + Section 29 + + + + + + +
+
+

We call class type methods with the type name just as we do for structures and enumerations:

+ +
+
+ + diff --git a/11. Methods.playground/Documentation/section-4.html b/11. Methods.playground/Documentation/section-4.html new file mode 100644 index 0000000..c799516 --- /dev/null +++ b/11. Methods.playground/Documentation/section-4.html @@ -0,0 +1,19 @@ + + + + + Section 5 + + + + + + +
+
+

Now let's see how we call each of those functions

+ +
+
+ + diff --git a/11. Methods.playground/Documentation/section-6.html b/11. Methods.playground/Documentation/section-6.html new file mode 100644 index 0000000..04d8a7a --- /dev/null +++ b/11. Methods.playground/Documentation/section-6.html @@ -0,0 +1,19 @@ + + + + + Section 7 + + + + + + +
+
+

The 'self' property refers to the current instance of a class, structure or enumeration. For C++ developers, think of 'self' as 'this'.

+ +
+
+ + diff --git a/11. Methods.playground/Documentation/section-8.html b/11. Methods.playground/Documentation/section-8.html new file mode 100644 index 0000000..f7e61d2 --- /dev/null +++ b/11. Methods.playground/Documentation/section-8.html @@ -0,0 +1,20 @@ + + + + + Section 9 + + + + + + +
+
+

Mutation

+

Instance methods cannot by default modify properties of structures or enumerations. To enable this, mark them as 'mutating':

+ +
+
+ + diff --git a/11. Methods.playground/Documentation/stylesheet.css b/11. Methods.playground/Documentation/stylesheet.css new file mode 100644 index 0000000..43b0230 --- /dev/null +++ b/11. Methods.playground/Documentation/stylesheet.css @@ -0,0 +1,65 @@ +html { + font-size: 1em; +} + +body { + background-color: #fff; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: normal; + font-size: 1.0em; + line-height: 140%; + padding: 0 2em; +} + +code { + font-family: "Menlo", monospace; + font-size: 0.95em; +} + +section { + padding: 0.5em 0; + -webkit-overflow-scrolling: touch; +} + +a:link { + color: #00f; + text-decoration: none; + border-bottom: 1px solid #00f; +} + +h1 { + font-size: 1.5em; + padding: 0.7em 0 0 0; +} + +h2 { + font-size: 1.2em; + padding: 0.7em 0 0 0; +} + +h3 { + font-size: 1.0em; + padding: 0.7em 0 0 0; +} + +blockquote { + font-size: 0.9em; + margin-left: 0; + padding-left: 2em; + border-left: 8px solid #eee; +} + +.footnotes { + color: #666666; + margin-top: 2em; + font-size: 0.9em; + letter-spacing: -3px; +} + +.footnotes:before { + content: "――――"; +} + +.footnotes ol { + letter-spacing: normal; +} diff --git a/11. Methods.playground/contents.xcplayground b/11. Methods.playground/contents.xcplayground index 4937636..d5ede43 100644 --- a/11. Methods.playground/contents.xcplayground +++ b/11. Methods.playground/contents.xcplayground @@ -1,7 +1,65 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file diff --git a/11. Methods.playground/section-1.swift b/11. Methods.playground/section-1.swift index bbe6559..fda60d8 100644 --- a/11. Methods.playground/section-1.swift +++ b/11. Methods.playground/section-1.swift @@ -1,234 +1,7 @@ -// ------------------------------------------------------------------------------------------------ -// Things to know: -// -// * Methods can be in the form of Instance Methods, which apply to a given instance of a class -// struct or enumeration and Type Methods, which apply to the type itself, like static methods -// in C-like languages. -// ------------------------------------------------------------------------------------------------ - -// Instance Methods -// -// Instance methods work on instances of a class, structure or enumeration. In order to call an -// instance method, you must first instantiate the class, structure or enumeration and then place -// the call on that instance. -// -// Here, we'll create a class with a single instance method: class SomeClass { func doSomething() { // ... } -} - -// Since this should be pretty clear, let's jump into parameters for methods with internal and -// external names. -// -// The defaults for external names are different for methods than they are for global functions. -// -// For methods, the default behavior is that the caller must always specify all but the first -// external parameter name when calling the method. Member authors need not specify the external -// names for those parameters as the default is to treat all parameters as if they had the "#" -// specifier, which creates an external parameter name that mirrors the local parameter name. -// -// To override this default-external-names-for-second-and-beyond-parameters, specify an "_" as the -// external parameter name for all but the first parameter. -// -// If you want the caller to also use external name for the first parameter, be sure to add your -// own '#' symbol to the local name or specify the external name explicitly. -// -// Here's a class that exercises the various combinations of internal and external name usages: -class Counter -{ - var count = 0; - - // No parameters - func increment() - { - count++ - } - - // One parameter, no external parameter name needed by caller - func incrementBy(amount: Int) - { - count += amount - } - - // One parameter, overriding default behavior to requre caller to use external parameter name - // on first (and only) parameter - func addValueTo(value amount: Int) - { - count += amount - } - - // Two parameters. Since no external names are specified, default behavior is implied: Caller - // need not specify the first parameter's external name, but must specify all others: - func addTwiceWithExternalImplied(first: Int, second: Int) - { - count += first - count += second - } - - // Two parameters. Using explicit external parameter names on all parameters to force caller - // to use them for all parameters, including the first. - func addTwiceWithExternalSpecified(a first: Int, b second: Int) - { - count += first - count += second - } - - // Two parameters. Using the external parameter shorthand ("#") to force caller to use - // external parameter name on first parameter and defaulting to shared local/external names - // for the rest. - func addTwiceWithExternalSpecified2(#first: Int, second: Int) - { - count += first - count += second - } - - // Two parameters. Disabling all external names - func addTwiceWithExternalSpecified3(first: Int, _ second: Int) - { - count += first - count += second - } -} - -// Now let's see how we call each of those functions -var counter = Counter() -counter.increment() -counter.incrementBy(4) -counter.addValueTo(value: 4) -counter.addTwiceWithExternalImplied(50, second: 4) -counter.addTwiceWithExternalSpecified(a: 50, b: 4) -counter.addTwiceWithExternalSpecified2(first: 10, second: 10) -counter.addTwiceWithExternalSpecified3(10, 10) -counter.count - -// The 'self' property refers to the current instance of a class, structure or enumeration. For -// C++ developers, think of 'self' as 'this'. -class Point -{ - var x: Int = 10 - - func setX(x: Int) - { - // Using self to disambiguate from the local parameter - self.x = x - } -} - -// ------------------------------------------------------------------------------------------------ -// Mutation -// -// Instance methods cannot by default modify properties of structures or enumerations. To enable -// this, mark them as 'mutating': -struct Point2 -{ - var x: Int = 10 - - // Note the need for the keyword 'mutating' - mutating func setX(x: Int) - { - self.x = x - } -} - -// We'll create a constant Point2... -let fixedPoint = Point2(x: 3) - -// Because 'fixedPoint' is constant, we are not allowed to call mutating memthods: -// -// The following line won't compile: -// -// fixedPoint.setX(4) - -// If you're working with a structure or enumeration (not a class), uou can assign to 'self' -// directly -struct Point3 -{ - var x = 0 - - // This does not work with classes - mutating func replaceMe(newX: Int) - { - self = Point3(x: 3) - } -} - -// Assigning to 'self' in an enumeration is used to change to a different member of the same -// enumeration: -enum TriStateSwitch -{ - case Off, Low, High - mutating func next() - { - switch self - { - case Off: - self = Low - case Low: - self = High - case High: - self = Off - } - } -} - -// ------------------------------------------------------------------------------------------------ -// Type Methods -// -// Type methods are like C++'s static methods. -// -// They can only access Type members. -struct LevelTracker -{ - var currentLevel = 1 - static var highestUnlockedLevel = 1 - - static func unlockedLevel(level: Int) - { - if level > highestUnlockedLevel - { - highestUnlockedLevel = level - } - } - static func levelIsUnlocked(level: Int) -> Bool - { - return level <= highestUnlockedLevel - } - mutating func advanceToLevel(level: Int) -> Bool - { - if LevelTracker.levelIsUnlocked(level) - { - currentLevel = level - return true - } - else - { - return false - } - } -} - -// To call a type method, use the type name, not the instance name: -LevelTracker.levelIsUnlocked(3) - -// If we attempt to use an instance to call a type method, we'll get an error -var levelTracker = LevelTracker() - -// The following line will not compile: -// -// levelTracker.levelIsUnlocked(3) - -// For classes, type methods use the 'class' keyword rather than the 'static' keyword: -class SomeOtherClass -{ - class func isGreaterThan100(value: Int) -> Bool - { - return value > 100 - } -} - -// We call class type methods with the type name just as we do for structures and enumerations: -SomeOtherClass.isGreaterThan100(105) +} \ No newline at end of file diff --git a/11. Methods.playground/section-11.swift b/11. Methods.playground/section-11.swift new file mode 100644 index 0000000..2eb2e13 --- /dev/null +++ b/11. Methods.playground/section-11.swift @@ -0,0 +1 @@ +let fixedPoint = Point2(x: 3) \ No newline at end of file diff --git a/11. Methods.playground/section-13.swift b/11. Methods.playground/section-13.swift new file mode 100644 index 0000000..ea0fe06 --- /dev/null +++ b/11. Methods.playground/section-13.swift @@ -0,0 +1 @@ +// fixedPoint.setX(4) \ No newline at end of file diff --git a/11. Methods.playground/section-15.swift b/11. Methods.playground/section-15.swift new file mode 100644 index 0000000..56086e2 --- /dev/null +++ b/11. Methods.playground/section-15.swift @@ -0,0 +1,10 @@ +struct Point3 +{ + var x = 0 + + // This does not work with classes + mutating func replaceMe(newX: Int) + { + self = Point3(x: 3) + } +} \ No newline at end of file diff --git a/11. Methods.playground/section-17.swift b/11. Methods.playground/section-17.swift new file mode 100644 index 0000000..c3ef47c --- /dev/null +++ b/11. Methods.playground/section-17.swift @@ -0,0 +1,16 @@ +enum TriStateSwitch +{ + case Off, Low, High + mutating func next() + { + switch self + { + case Off: + self = Low + case Low: + self = High + case High: + self = Off + } + } +} \ No newline at end of file diff --git a/11. Methods.playground/section-19.swift b/11. Methods.playground/section-19.swift new file mode 100644 index 0000000..d2e8db1 --- /dev/null +++ b/11. Methods.playground/section-19.swift @@ -0,0 +1,29 @@ +struct LevelTracker +{ + var currentLevel = 1 + static var highestUnlockedLevel = 1 + + static func unlockedLevel(level: Int) + { + if level > highestUnlockedLevel + { + highestUnlockedLevel = level + } + } + static func levelIsUnlocked(level: Int) -> Bool + { + return level <= highestUnlockedLevel + } + mutating func advanceToLevel(level: Int) -> Bool + { + if LevelTracker.levelIsUnlocked(level) + { + currentLevel = level + return true + } + else + { + return false + } + } +} \ No newline at end of file diff --git a/11. Methods.playground/section-21.swift b/11. Methods.playground/section-21.swift new file mode 100644 index 0000000..6a41df5 --- /dev/null +++ b/11. Methods.playground/section-21.swift @@ -0,0 +1 @@ +LevelTracker.levelIsUnlocked(3) \ No newline at end of file diff --git a/11. Methods.playground/section-23.swift b/11. Methods.playground/section-23.swift new file mode 100644 index 0000000..268e27c --- /dev/null +++ b/11. Methods.playground/section-23.swift @@ -0,0 +1 @@ +var levelTracker = LevelTracker() \ No newline at end of file diff --git a/11. Methods.playground/section-25.swift b/11. Methods.playground/section-25.swift new file mode 100644 index 0000000..76e6ba2 --- /dev/null +++ b/11. Methods.playground/section-25.swift @@ -0,0 +1 @@ +// levelTracker.levelIsUnlocked(3) \ No newline at end of file diff --git a/11. Methods.playground/section-27.swift b/11. Methods.playground/section-27.swift new file mode 100644 index 0000000..badae96 --- /dev/null +++ b/11. Methods.playground/section-27.swift @@ -0,0 +1,7 @@ +class SomeOtherClass +{ + class func isGreaterThan100(value: Int) -> Bool + { + return value > 100 + } +} \ No newline at end of file diff --git a/11. Methods.playground/section-29.swift b/11. Methods.playground/section-29.swift new file mode 100644 index 0000000..1f6cf5f --- /dev/null +++ b/11. Methods.playground/section-29.swift @@ -0,0 +1 @@ +SomeOtherClass.isGreaterThan100(105) \ No newline at end of file diff --git a/11. Methods.playground/section-3.swift b/11. Methods.playground/section-3.swift new file mode 100644 index 0000000..cc4357c --- /dev/null +++ b/11. Methods.playground/section-3.swift @@ -0,0 +1,55 @@ +class Counter +{ + var count = 0; + + // No parameters + func increment() + { + count++ + } + + // One parameter, no external parameter name needed by caller + func incrementBy(amount: Int) + { + count += amount + } + + // One parameter, overriding default behavior to requre caller to use external parameter name + // on first (and only) parameter + func addValueTo(value amount: Int) + { + count += amount + } + + // Two parameters. Since no external names are specified, default behavior is implied: Caller + // need not specify the first parameter's external name, but must specify all others: + func addTwiceWithExternalImplied(first: Int, second: Int) + { + count += first + count += second + } + + // Two parameters. Using explicit external parameter names on all parameters to force caller + // to use them for all parameters, including the first. + func addTwiceWithExternalSpecified(a first: Int, b second: Int) + { + count += first + count += second + } + + // Two parameters. Using the external parameter shorthand ("#") to force caller to use + // external parameter name on first parameter and defaulting to shared local/external names + // for the rest. + func addTwiceWithExternalSpecified2(#first: Int, second: Int) + { + count += first + count += second + } + + // Two parameters. Disabling all external names + func addTwiceWithExternalSpecified3(first: Int, _ second: Int) + { + count += first + count += second + } +} \ No newline at end of file diff --git a/11. Methods.playground/section-5.swift b/11. Methods.playground/section-5.swift new file mode 100644 index 0000000..1706a88 --- /dev/null +++ b/11. Methods.playground/section-5.swift @@ -0,0 +1,9 @@ +var counter = Counter() +counter.increment() +counter.incrementBy(4) +counter.addValueTo(value: 4) +counter.addTwiceWithExternalImplied(50, second: 4) +counter.addTwiceWithExternalSpecified(a: 50, b: 4) +counter.addTwiceWithExternalSpecified2(first: 10, second: 10) +counter.addTwiceWithExternalSpecified3(10, 10) +counter.count \ No newline at end of file diff --git a/11. Methods.playground/section-7.swift b/11. Methods.playground/section-7.swift new file mode 100644 index 0000000..55de21a --- /dev/null +++ b/11. Methods.playground/section-7.swift @@ -0,0 +1,10 @@ +class Point +{ + var x: Int = 10 + + func setX(x: Int) + { + // Using self to disambiguate from the local parameter + self.x = x + } +} \ No newline at end of file diff --git a/11. Methods.playground/section-9.swift b/11. Methods.playground/section-9.swift new file mode 100644 index 0000000..8ff1302 --- /dev/null +++ b/11. Methods.playground/section-9.swift @@ -0,0 +1,10 @@ +struct Point2 +{ + var x: Int = 10 + + // Note the need for the keyword 'mutating' + mutating func setX(x: Int) + { + self.x = x + } +} \ No newline at end of file diff --git a/12. Subscripts.playground/Documentation/section-0.html b/12. Subscripts.playground/Documentation/section-0.html new file mode 100644 index 0000000..4762978 --- /dev/null +++ b/12. Subscripts.playground/Documentation/section-0.html @@ -0,0 +1,28 @@ + + + + + Section 1 + + + + + + +
+
+

Subscripts

+

Things to know:

+
    +
  • Subscripts allow you to declare functionality for instances to make use of the subscript operator ( [] ).
  • +
  • Subscripts are available for classes, structures and enumerations.
  • +
  • Subscripts are declared much like getters and setters for properties.
  • +
+
+

Subscripts are declared like getters and setters, and follow the same syntax rules for read-only and read-write variants. They also can employ the same syntactic simplifications available for getters and setters.

+

Here's a structure that utilizes a subscript so that we can see the syntax of the declaration.

+ +
+
+ + diff --git a/12. Subscripts.playground/Documentation/section-2.html b/12. Subscripts.playground/Documentation/section-2.html new file mode 100644 index 0000000..fb1a131 --- /dev/null +++ b/12. Subscripts.playground/Documentation/section-2.html @@ -0,0 +1,19 @@ + + + + + Section 3 + + + + + + +
+
+

We can now make use of our newly created subscripts

+ +
+
+ + diff --git a/12. Subscripts.playground/Documentation/section-4.html b/12. Subscripts.playground/Documentation/section-4.html new file mode 100644 index 0000000..d4f2038 --- /dev/null +++ b/12. Subscripts.playground/Documentation/section-4.html @@ -0,0 +1,20 @@ + + + + + Section 5 + + + + + + +
+
+

Subscripts can take any parameter type and variadic parameters, but cannot use inout or default parameters.

+

Here's a more complex example:

+ +
+
+ + diff --git a/12. Subscripts.playground/Documentation/section-6.html b/12. Subscripts.playground/Documentation/section-6.html new file mode 100644 index 0000000..2b0433a --- /dev/null +++ b/12. Subscripts.playground/Documentation/section-6.html @@ -0,0 +1,19 @@ + + + + + Section 7 + + + + + + +
+
+

We'll create a standard 4x4 identity matrix

+ +
+
+ + diff --git a/12. Subscripts.playground/Documentation/stylesheet.css b/12. Subscripts.playground/Documentation/stylesheet.css new file mode 100644 index 0000000..43b0230 --- /dev/null +++ b/12. Subscripts.playground/Documentation/stylesheet.css @@ -0,0 +1,65 @@ +html { + font-size: 1em; +} + +body { + background-color: #fff; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: normal; + font-size: 1.0em; + line-height: 140%; + padding: 0 2em; +} + +code { + font-family: "Menlo", monospace; + font-size: 0.95em; +} + +section { + padding: 0.5em 0; + -webkit-overflow-scrolling: touch; +} + +a:link { + color: #00f; + text-decoration: none; + border-bottom: 1px solid #00f; +} + +h1 { + font-size: 1.5em; + padding: 0.7em 0 0 0; +} + +h2 { + font-size: 1.2em; + padding: 0.7em 0 0 0; +} + +h3 { + font-size: 1.0em; + padding: 0.7em 0 0 0; +} + +blockquote { + font-size: 0.9em; + margin-left: 0; + padding-left: 2em; + border-left: 8px solid #eee; +} + +.footnotes { + color: #666666; + margin-top: 2em; + font-size: 0.9em; + letter-spacing: -3px; +} + +.footnotes:before { + content: "――――"; +} + +.footnotes ol { + letter-spacing: normal; +} diff --git a/12. Subscripts.playground/contents.xcplayground b/12. Subscripts.playground/contents.xcplayground index 4937636..de8f024 100644 --- a/12. Subscripts.playground/contents.xcplayground +++ b/12. Subscripts.playground/contents.xcplayground @@ -1,7 +1,21 @@ - + - + + + + + + + + + + + + + + + + - \ No newline at end of file diff --git a/12. Subscripts.playground/section-1.swift b/12. Subscripts.playground/section-1.swift index 55ded36..dbc927b 100644 --- a/12. Subscripts.playground/section-1.swift +++ b/12. Subscripts.playground/section-1.swift @@ -1,19 +1,3 @@ -// ------------------------------------------------------------------------------------------------ -// Things to know: -// -// * Subscripts allow you to declare functionality for instances to make use of the subscript -// operator ( [] ). -// -// * Subscripts are available for classes, structures and enumerations. -// -// * Subscripts are declared much like getters and setters for properties. -// ------------------------------------------------------------------------------------------------ - -// Subscripts are declared like getters and setters, and follow the same syntax rules for -// read-only and read-write variants. They also can employ the same syntactic simplifications -// available for getters and setters. -// -// Here's a structure that utilizes a subscript so that we can see the syntax of the declaration. struct TimesTable { let multiplier: Int @@ -29,54 +13,4 @@ struct TimesTable { return multiplier * Int(index) } -} - -// We can now make use of our newly created subscripts -let threeTimesTable = TimesTable(multiplier: 3) -threeTimesTable[3] -threeTimesTable[4.0] - -// Subscripts can take any parameter type and variadic parameters, but cannot use inout or default -// parameters. -// -// Here's a more complex example: -struct Matrix -{ - let rows: Int - let columns: Int - var grid: [Double] - - init (rows: Int, columns: Int) - { - self.rows = rows - self.columns = columns - grid = Array(count: rows * columns, repeatedValue: 0.0) - } - - func indexIsValidForRow(row: Int, column: Int) -> Bool - { - return row >= 0 && row < rows && column >= 0 && column < columns - } - - // Subscript with getter & setter as well as dual-parameter subscript - subscript(row: Int, column: Int) -> Double - { - get - { - assert(indexIsValidForRow(row, column: column), "Index out of range") - return grid[row * columns + column] - } - set - { - assert(indexIsValidForRow(row, column: column), "Index out of range") - grid[row * columns + column] = newValue - } - } -} - -// We'll create a standard 4x4 identity matrix -var matrix = Matrix(rows: 4, columns: 4) -matrix[0,0] = 1 -matrix[1,1] = 1 -matrix[2,2] = 1 -matrix[3,3] = 1 +} \ No newline at end of file diff --git a/12. Subscripts.playground/section-3.swift b/12. Subscripts.playground/section-3.swift new file mode 100644 index 0000000..bc59a18 --- /dev/null +++ b/12. Subscripts.playground/section-3.swift @@ -0,0 +1,3 @@ +let threeTimesTable = TimesTable(multiplier: 3) +threeTimesTable[3] +threeTimesTable[4.0] \ No newline at end of file diff --git a/12. Subscripts.playground/section-5.swift b/12. Subscripts.playground/section-5.swift new file mode 100644 index 0000000..d034987 --- /dev/null +++ b/12. Subscripts.playground/section-5.swift @@ -0,0 +1,33 @@ +struct Matrix +{ + let rows: Int + let columns: Int + var grid: [Double] + + init (rows: Int, columns: Int) + { + self.rows = rows + self.columns = columns + grid = Array(count: rows * columns, repeatedValue: 0.0) + } + + func indexIsValidForRow(row: Int, column: Int) -> Bool + { + return row >= 0 && row < rows && column >= 0 && column < columns + } + + // Subscript with getter & setter as well as dual-parameter subscript + subscript(row: Int, column: Int) -> Double + { + get + { + assert(indexIsValidForRow(row, column: column), "Index out of range") + return grid[row * columns + column] + } + set + { + assert(indexIsValidForRow(row, column: column), "Index out of range") + grid[row * columns + column] = newValue + } + } +} \ No newline at end of file diff --git a/12. Subscripts.playground/section-7.swift b/12. Subscripts.playground/section-7.swift new file mode 100644 index 0000000..1bd353f --- /dev/null +++ b/12. Subscripts.playground/section-7.swift @@ -0,0 +1,5 @@ +var matrix = Matrix(rows: 4, columns: 4) +matrix[0,0] = 1 +matrix[1,1] = 1 +matrix[2,2] = 1 +matrix[3,3] = 1 \ No newline at end of file diff --git a/13. Inheritance.playground/Documentation/section-0.html b/13. Inheritance.playground/Documentation/section-0.html new file mode 100644 index 0000000..f70b794 --- /dev/null +++ b/13. Inheritance.playground/Documentation/section-0.html @@ -0,0 +1,25 @@ + + + + + Section 1 + + + + + + +
+
+

Inheritance

+

Things to know:

+
    +
  • There is no default base class for Swift objects. Any class that doesn't derive from another class is a base class.
  • +
+
+

Let's start with a simple base class:

+ +
+
+ + diff --git a/13. Inheritance.playground/Documentation/section-10.html b/13. Inheritance.playground/Documentation/section-10.html new file mode 100644 index 0000000..051fea8 --- /dev/null +++ b/13. Inheritance.playground/Documentation/section-10.html @@ -0,0 +1,19 @@ + + + + + Section 11 + + + + + + +
+
+

Here, we'll check our description to see that it does indeed give us something different from the superclass' default description:

+ +
+
+ + diff --git a/13. Inheritance.playground/Documentation/section-12.html b/13. Inheritance.playground/Documentation/section-12.html new file mode 100644 index 0000000..5e34342 --- /dev/null +++ b/13. Inheritance.playground/Documentation/section-12.html @@ -0,0 +1,21 @@ + + + + + Section 13 + + + + + + +
+
+

Overriding Properties

+

We can override property getters and setters. This applies to any property, including stored and computed properties

+

When we do this, our overridden property must include the name of the property as well as the property's type.

+ +
+
+ + diff --git a/13. Inheritance.playground/Documentation/section-14.html b/13. Inheritance.playground/Documentation/section-14.html new file mode 100644 index 0000000..e8dceff --- /dev/null +++ b/13. Inheritance.playground/Documentation/section-14.html @@ -0,0 +1,19 @@ + + + + + Section 15 + + + + + + +
+
+

We can see our override in action

+ +
+
+ + diff --git a/13. Inheritance.playground/Documentation/section-16.html b/13. Inheritance.playground/Documentation/section-16.html new file mode 100644 index 0000000..3f4fb51 --- /dev/null +++ b/13. Inheritance.playground/Documentation/section-16.html @@ -0,0 +1,19 @@ + + + + + Section 17 + + + + + + +
+
+

We can also override property observers

+ +
+
+ + diff --git a/13. Inheritance.playground/Documentation/section-18.html b/13. Inheritance.playground/Documentation/section-18.html new file mode 100644 index 0000000..f92483e --- /dev/null +++ b/13. Inheritance.playground/Documentation/section-18.html @@ -0,0 +1,19 @@ + + + + + Section 19 + + + + + + +
+
+

Here is our overridden observers in action

+ +
+
+ + diff --git a/13. Inheritance.playground/Documentation/section-2.html b/13. Inheritance.playground/Documentation/section-2.html new file mode 100644 index 0000000..60fddb0 --- /dev/null +++ b/13. Inheritance.playground/Documentation/section-2.html @@ -0,0 +1,20 @@ + + + + + Section 3 + + + + + + +
+
+

Subclasses

+

Now let's subclass the Vehicle to create a two-wheeled vehicle called a Bicycle

+ +
+
+ + diff --git a/13. Inheritance.playground/Documentation/section-20.html b/13. Inheritance.playground/Documentation/section-20.html new file mode 100644 index 0000000..b76c51e --- /dev/null +++ b/13. Inheritance.playground/Documentation/section-20.html @@ -0,0 +1,22 @@ + + + + + Section 21 + + + + + + +
+
+

Preventing Overrides

+

We can prevent a subclass from overriding a particular method or property using the 'final' keyword.

+

final can be applied to: class, var, func, class methods and subscripts

+

Here, we'll prevent an entire class from being subclassed by applying the . Because of this, the finals inside the class are not needed, but are present for descriptive purposes. These additional finals may not compile in the future, but they do today:

+ +
+
+ + diff --git a/13. Inheritance.playground/Documentation/section-4.html b/13. Inheritance.playground/Documentation/section-4.html new file mode 100644 index 0000000..46be430 --- /dev/null +++ b/13. Inheritance.playground/Documentation/section-4.html @@ -0,0 +1,19 @@ + + + + + Section 5 + + + + + + +
+
+

We can call a member from the superclass

+ +
+
+ + diff --git a/13. Inheritance.playground/Documentation/section-6.html b/13. Inheritance.playground/Documentation/section-6.html new file mode 100644 index 0000000..0b56829 --- /dev/null +++ b/13. Inheritance.playground/Documentation/section-6.html @@ -0,0 +1,19 @@ + + + + + Section 7 + + + + + + +
+
+

Subclasses can also be subclassed

+ +
+
+ + diff --git a/13. Inheritance.playground/Documentation/section-8.html b/13. Inheritance.playground/Documentation/section-8.html new file mode 100644 index 0000000..b057004 --- /dev/null +++ b/13. Inheritance.playground/Documentation/section-8.html @@ -0,0 +1,19 @@ + + + + + Section 9 + + + + + + +
+
+

Here, we'll create a car that includes a new description by overriding the superclass' instance method

+ +
+
+ + diff --git a/13. Inheritance.playground/Documentation/stylesheet.css b/13. Inheritance.playground/Documentation/stylesheet.css new file mode 100644 index 0000000..43b0230 --- /dev/null +++ b/13. Inheritance.playground/Documentation/stylesheet.css @@ -0,0 +1,65 @@ +html { + font-size: 1em; +} + +body { + background-color: #fff; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: normal; + font-size: 1.0em; + line-height: 140%; + padding: 0 2em; +} + +code { + font-family: "Menlo", monospace; + font-size: 0.95em; +} + +section { + padding: 0.5em 0; + -webkit-overflow-scrolling: touch; +} + +a:link { + color: #00f; + text-decoration: none; + border-bottom: 1px solid #00f; +} + +h1 { + font-size: 1.5em; + padding: 0.7em 0 0 0; +} + +h2 { + font-size: 1.2em; + padding: 0.7em 0 0 0; +} + +h3 { + font-size: 1.0em; + padding: 0.7em 0 0 0; +} + +blockquote { + font-size: 0.9em; + margin-left: 0; + padding-left: 2em; + border-left: 8px solid #eee; +} + +.footnotes { + color: #666666; + margin-top: 2em; + font-size: 0.9em; + letter-spacing: -3px; +} + +.footnotes:before { + content: "――――"; +} + +.footnotes ol { + letter-spacing: normal; +} diff --git a/13. Inheritance.playground/contents.xcplayground b/13. Inheritance.playground/contents.xcplayground index 4937636..da0d9fd 100644 --- a/13. Inheritance.playground/contents.xcplayground +++ b/13. Inheritance.playground/contents.xcplayground @@ -1,7 +1,49 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file diff --git a/13. Inheritance.playground/section-1.swift b/13. Inheritance.playground/section-1.swift index b17743d..0bdcaa0 100644 --- a/13. Inheritance.playground/section-1.swift +++ b/13. Inheritance.playground/section-1.swift @@ -1,11 +1,3 @@ -// ------------------------------------------------------------------------------------------------ -// Things to know: -// -// * There is no default base class for Swift objects. Any class that doesn't derive from -// another class is a base class. -// ------------------------------------------------------------------------------------------------ - -// Let's start with a simple base class: class Vehicle { var numberOfWheels: Int @@ -22,143 +14,4 @@ class Vehicle numberOfWheels = 0 maxPassengers = 1 } -} - -// ------------------------------------------------------------------------------------------------ -// Subclasses -// -// Now let's subclass the Vehicle to create a two-wheeled vehicle called a Bicycle -class Bicycle: Vehicle -{ - // We'll make this a 2-wheeled vehicle - override init() - { - super.init() - numberOfWheels = 2 - } -} - -// We can call a member from the superclass -let bicycle = Bicycle() -bicycle.description() - -// Subclasses can also be subclassed -class Tandem: Bicycle -{ - // This bicycle has 2 passengers - override init() - { - super.init() - maxPassengers = 2 - } -} - -// Here, we'll create a car that includes a new description by overriding the superclass' instance -// method -class Car: Vehicle -{ - // Adding a new property - var speed: Double = 0.0 - - // New initialization, starting with super.init() - override init() - { - super.init() - maxPassengers = 5 - numberOfWheels = 4 - } - - // Using the override keyword to specify that we're overriding a function up the inheritance - // chain. It is a compilation error to leave out 'override' if a method exists up the chain. - override func description() -> String - { - // We start with the default implementation of description then tack our stuff onto it - return super.description() + "; " + "traveling at \(speed) mph" - } -} - -// Here, we'll check our description to see that it does indeed give us something different from -// the superclass' default description: -let car = Car() -car.speed = 55 -car.description() - -// ------------------------------------------------------------------------------------------------ -// Overriding Properties -// -// We can override property getters and setters. This applies to any property, including stored and -// computed properties -// -// When we do this, our overridden property must include the name of the property as well as the -// property's type. -class SpeedLimitedCar: Car -{ - // Make sure to specify the name and type - override var speed: Double - { - get - { - return super.speed - } - // We need a setter since the super's property is read/write - // - // However, if the super was read-only, we wouldn't need this setter unless we wanted to - // add write capability. - set - { - super.speed = min(newValue, 40.0) - } - } -} - -// We can see our override in action -var speedLimitedCar = SpeedLimitedCar() -speedLimitedCar.speed = 60 -speedLimitedCar.speed - -// We can also override property observers -class AutomaticCar: Car -{ - var gear = 1 - override var speed: Double - { - // Set the gear based on changes to speed - didSet { gear = Int(speed / 10.0) + 1 } - - // Since we're overriding the property observer, we're not allowed to override the - // getter/setter. - // - // The following lines will not compile: - // - // get { return super.speed } - // set { super.speed = newValue } - } -} - -// Here is our overridden observers in action -var automaticCar = AutomaticCar() -automaticCar.speed = 35.0 -automaticCar.gear - -// ------------------------------------------------------------------------------------------------ -// Preenting Overrides -// -// We can prevent a subclass from overriding a particular method or property using the 'final' -// keyword. -// -// final can be applied to: class, var, func, class methods and subscripts -// -// Here, we'll prevent an entire class from being subclassed by applying the . Because of this, -// the finals inside the class are not needed, but are present for descriptive purposes. These -// additional finals may not compile in the future, but they do today: -final class AnotherAutomaticCar: Car -{ - // This variable cannot be overridden - final var gear = 1 - - // We can even prevent overridden functions from being further refined - final override var speed: Double - { - didSet { gear = Int(speed / 10.0) + 1 } - } -} +} \ No newline at end of file diff --git a/13. Inheritance.playground/section-11.swift b/13. Inheritance.playground/section-11.swift new file mode 100644 index 0000000..ee6e53f --- /dev/null +++ b/13. Inheritance.playground/section-11.swift @@ -0,0 +1,3 @@ +let car = Car() +car.speed = 55 +car.description() \ No newline at end of file diff --git a/13. Inheritance.playground/section-13.swift b/13. Inheritance.playground/section-13.swift new file mode 100644 index 0000000..f540613 --- /dev/null +++ b/13. Inheritance.playground/section-13.swift @@ -0,0 +1,19 @@ +class SpeedLimitedCar: Car +{ + // Make sure to specify the name and type + override var speed: Double + { + get + { + return super.speed + } + // We need a setter since the super's property is read/write + // + // However, if the super was read-only, we wouldn't need this setter unless we wanted to + // add write capability. + set + { + super.speed = min(newValue, 40.0) + } + } +} \ No newline at end of file diff --git a/13. Inheritance.playground/section-15.swift b/13. Inheritance.playground/section-15.swift new file mode 100644 index 0000000..f8889fb --- /dev/null +++ b/13. Inheritance.playground/section-15.swift @@ -0,0 +1,3 @@ +var speedLimitedCar = SpeedLimitedCar() +speedLimitedCar.speed = 60 +speedLimitedCar.speed \ No newline at end of file diff --git a/13. Inheritance.playground/section-17.swift b/13. Inheritance.playground/section-17.swift new file mode 100644 index 0000000..53e0362 --- /dev/null +++ b/13. Inheritance.playground/section-17.swift @@ -0,0 +1,17 @@ +class AutomaticCar: Car +{ + var gear = 1 + override var speed: Double + { + // Set the gear based on changes to speed + didSet { gear = Int(speed / 10.0) + 1 } + + // Since we're overriding the property observer, we're not allowed to override the + // getter/setter. + // + // The following lines will not compile: + // + // get { return super.speed } + // set { super.speed = newValue } + } +} \ No newline at end of file diff --git a/13. Inheritance.playground/section-19.swift b/13. Inheritance.playground/section-19.swift new file mode 100644 index 0000000..a466f2e --- /dev/null +++ b/13. Inheritance.playground/section-19.swift @@ -0,0 +1,3 @@ +var automaticCar = AutomaticCar() +automaticCar.speed = 35.0 +automaticCar.gear \ No newline at end of file diff --git a/13. Inheritance.playground/section-21.swift b/13. Inheritance.playground/section-21.swift new file mode 100644 index 0000000..ce37602 --- /dev/null +++ b/13. Inheritance.playground/section-21.swift @@ -0,0 +1,11 @@ +final class AnotherAutomaticCar: Car +{ + // This variable cannot be overridden + final var gear = 1 + + // We can even prevent overridden functions from being further refined + final override var speed: Double + { + didSet { gear = Int(speed / 10.0) + 1 } + } +} \ No newline at end of file diff --git a/13. Inheritance.playground/section-3.swift b/13. Inheritance.playground/section-3.swift new file mode 100644 index 0000000..5f9e09e --- /dev/null +++ b/13. Inheritance.playground/section-3.swift @@ -0,0 +1,9 @@ +class Bicycle: Vehicle +{ + // We'll make this a 2-wheeled vehicle + override init() + { + super.init() + numberOfWheels = 2 + } +} \ No newline at end of file diff --git a/13. Inheritance.playground/section-5.swift b/13. Inheritance.playground/section-5.swift new file mode 100644 index 0000000..919eb2e --- /dev/null +++ b/13. Inheritance.playground/section-5.swift @@ -0,0 +1,2 @@ +let bicycle = Bicycle() +bicycle.description() \ No newline at end of file diff --git a/13. Inheritance.playground/section-7.swift b/13. Inheritance.playground/section-7.swift new file mode 100644 index 0000000..362b4c4 --- /dev/null +++ b/13. Inheritance.playground/section-7.swift @@ -0,0 +1,9 @@ +class Tandem: Bicycle +{ + // This bicycle has 2 passengers + override init() + { + super.init() + maxPassengers = 2 + } +} \ No newline at end of file diff --git a/13. Inheritance.playground/section-9.swift b/13. Inheritance.playground/section-9.swift new file mode 100644 index 0000000..78b583b --- /dev/null +++ b/13. Inheritance.playground/section-9.swift @@ -0,0 +1,21 @@ +class Car: Vehicle +{ + // Adding a new property + var speed: Double = 0.0 + + // New initialization, starting with super.init() + override init() + { + super.init() + maxPassengers = 5 + numberOfWheels = 4 + } + + // Using the override keyword to specify that we're overriding a function up the inheritance + // chain. It is a compilation error to leave out 'override' if a method exists up the chain. + override func description() -> String + { + // We start with the default implementation of description then tack our stuff onto it + return super.description() + "; " + "traveling at \(speed) mph" + } +} \ No newline at end of file diff --git a/14a. Initialization.playground/Documentation/section-0.html b/14a. Initialization.playground/Documentation/section-0.html new file mode 100644 index 0000000..899cc30 --- /dev/null +++ b/14a. Initialization.playground/Documentation/section-0.html @@ -0,0 +1,28 @@ + + + + + Section 1 + + + + + + +
+
+

Initialization

+

Things to know:

+
    +
  • Swift provides an initializer (which partially resembles a function) to ensure that every property in a class, structure or enumeration is fully initialized.
  • +
  • Optionals do not need to be initialized as they are automatically initialized to nil if no default value is provided.
  • +
+
+

Here's a basic use of an initializer.

+

The Fahrenheit class will create a 'temperature' property that does not have a default value. Because there is no default value, the property must be initialized in the initializer.

+

Without the initializer, we would get an error whenever we tried to instantiate the Fahrenheit class.

+ +
+
+ + diff --git a/14a. Initialization.playground/Documentation/section-10.html b/14a. Initialization.playground/Documentation/section-10.html new file mode 100644 index 0000000..41021cf --- /dev/null +++ b/14a. Initialization.playground/Documentation/section-10.html @@ -0,0 +1,20 @@ + + + + + Section 11 + + + + + + +
+
+

External parameter names are automatically generated for the initializer. In order to opt-out of this automated convenience, you can specify "_" for the extnenal name explicitly.

+

Here's a class that defines two initializers - one that makes use of the automatic external name generation and one that opts out:

+ +
+
+ + diff --git a/14a. Initialization.playground/Documentation/section-12.html b/14a. Initialization.playground/Documentation/section-12.html new file mode 100644 index 0000000..69fa0a8 --- /dev/null +++ b/14a. Initialization.playground/Documentation/section-12.html @@ -0,0 +1,19 @@ + + + + + Section 13 + + + + + + +
+
+

Here, we can see our efforts pay off:

+ +
+
+ + diff --git a/14a. Initialization.playground/Documentation/section-14.html b/14a. Initialization.playground/Documentation/section-14.html new file mode 100644 index 0000000..2def6bd --- /dev/null +++ b/14a. Initialization.playground/Documentation/section-14.html @@ -0,0 +1,19 @@ + + + + + Section 15 + + + + + + +
+
+

Optional properties do not need to be initialized:

+ +
+
+ + diff --git a/14a. Initialization.playground/Documentation/section-16.html b/14a. Initialization.playground/Documentation/section-16.html new file mode 100644 index 0000000..4058922 --- /dev/null +++ b/14a. Initialization.playground/Documentation/section-16.html @@ -0,0 +1,19 @@ + + + + + Section 17 + + + + + + +
+
+

Constants need initialization as well. In the following example, our constant has a default value. However, if we initialize the class with the init(text: String) initializer, we can modify the default value to use the one passed in:

+ +
+
+ + diff --git a/14a. Initialization.playground/Documentation/section-18.html b/14a. Initialization.playground/Documentation/section-18.html new file mode 100644 index 0000000..a95e96b --- /dev/null +++ b/14a. Initialization.playground/Documentation/section-18.html @@ -0,0 +1,19 @@ + + + + + Section 19 + + + + + + +
+
+

Here, we initialize the class with a blank initializer (calling init()) to let text's default value initialize the stored value

+ +
+
+ + diff --git a/14a. Initialization.playground/Documentation/section-2.html b/14a. Initialization.playground/Documentation/section-2.html new file mode 100644 index 0000000..d7a7e0f --- /dev/null +++ b/14a. Initialization.playground/Documentation/section-2.html @@ -0,0 +1,19 @@ + + + + + Section 3 + + + + + + +
+
+

Since the class can fully initialize itself, we can safely instantiate it with no error:

+ +
+
+ + diff --git a/14a. Initialization.playground/Documentation/section-20.html b/14a. Initialization.playground/Documentation/section-20.html new file mode 100644 index 0000000..08acc67 --- /dev/null +++ b/14a. Initialization.playground/Documentation/section-20.html @@ -0,0 +1,19 @@ + + + + + Section 21 + + + + + + +
+
+

Here, we'll us an aalternat initializer to specify a different value for 'text'

+ +
+
+ + diff --git a/14a. Initialization.playground/Documentation/section-22.html b/14a. Initialization.playground/Documentation/section-22.html new file mode 100644 index 0000000..9e757cd --- /dev/null +++ b/14a. Initialization.playground/Documentation/section-22.html @@ -0,0 +1,21 @@ + + + + + Section 23 + + + + + + +
+
+

Default Initializer

+

If all properties have default values (including optionals defaulting to nil) AND you do not create your own initlializer AND there is no superclass, Swift will create a default initializer (with no parameters) for you. This initializer sets all properties to their default values.

+

If you create your own initializer, Swift will not create the default initializer. If you want your custom initializers as well as the default initializer, then put your initializers in an extension.

+ +
+
+ + diff --git a/14a. Initialization.playground/Documentation/section-24.html b/14a. Initialization.playground/Documentation/section-24.html new file mode 100644 index 0000000..c0a4db1 --- /dev/null +++ b/14a. Initialization.playground/Documentation/section-24.html @@ -0,0 +1,21 @@ + + + + + Section 25 + + + + + + +
+
+

Memberwise Initializers for Structure Types

+

Similar to the default initializer for classes, structures that do not implement an initializer but have default values for their stored properties will get a default memberwise initializer.

+

As with classes, if you want your custom initializers as well as the default memberwise initializer, then put your initializers in an extension.

+ +
+
+ + diff --git a/14a. Initialization.playground/Documentation/section-26.html b/14a. Initialization.playground/Documentation/section-26.html new file mode 100644 index 0000000..c5f63fb --- /dev/null +++ b/14a. Initialization.playground/Documentation/section-26.html @@ -0,0 +1,19 @@ + + + + + Section 27 + + + + + + +
+
+

Here, we call the default memberwise initializer that Swift created for us

+ +
+
+ + diff --git a/14a. Initialization.playground/Documentation/section-28.html b/14a. Initialization.playground/Documentation/section-28.html new file mode 100644 index 0000000..14fb4fa --- /dev/null +++ b/14a. Initialization.playground/Documentation/section-28.html @@ -0,0 +1,22 @@ + + + + + Section 29 + + + + + + +
+
+

Initializer Delegatgion for Value Types

+

Sometimes, it's convenient to have multiple initializers that call other initializers to do some of the heavy lifting.

+

In the following example, we have a class 'Rect' that has multiple initializers. One that initializes our rect given an origin and size, while another that calls upon the origin/size initializer from calculations based on the a center point.

+

This concept is further extended in "Initializer Chaining", covered later.

+ +
+
+ + diff --git a/14a. Initialization.playground/Documentation/section-30.html b/14a. Initialization.playground/Documentation/section-30.html new file mode 100644 index 0000000..d849d09 --- /dev/null +++ b/14a. Initialization.playground/Documentation/section-30.html @@ -0,0 +1,19 @@ + + + + + Section 31 + + + + + + +
+
+

Here, we call the three initializers:

+ +
+
+ + diff --git a/14a. Initialization.playground/Documentation/section-4.html b/14a. Initialization.playground/Documentation/section-4.html new file mode 100644 index 0000000..198283d --- /dev/null +++ b/14a. Initialization.playground/Documentation/section-4.html @@ -0,0 +1,19 @@ + + + + + Section 5 + + + + + + +
+
+

Since the temperature is always defined as "32.0", it is cleaner and preferred to use a default value instead of setting it inside the initializer:

+ +
+
+ + diff --git a/14a. Initialization.playground/Documentation/section-6.html b/14a. Initialization.playground/Documentation/section-6.html new file mode 100644 index 0000000..4f8a3d8 --- /dev/null +++ b/14a. Initialization.playground/Documentation/section-6.html @@ -0,0 +1,20 @@ + + + + + Section 7 + + + + + + +
+
+

Initializers can also include parameters. Here's an example of using a parameter to initialize the class's temperature to a given value.

+

The following class contains two initializers:

+ +
+
+ + diff --git a/14a. Initialization.playground/Documentation/section-8.html b/14a. Initialization.playground/Documentation/section-8.html new file mode 100644 index 0000000..65b7bca --- /dev/null +++ b/14a. Initialization.playground/Documentation/section-8.html @@ -0,0 +1,19 @@ + + + + + Section 9 + + + + + + +
+
+

Now let's try our new initializers

+ +
+
+ + diff --git a/14a. Initialization.playground/Documentation/stylesheet.css b/14a. Initialization.playground/Documentation/stylesheet.css new file mode 100644 index 0000000..43b0230 --- /dev/null +++ b/14a. Initialization.playground/Documentation/stylesheet.css @@ -0,0 +1,65 @@ +html { + font-size: 1em; +} + +body { + background-color: #fff; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: normal; + font-size: 1.0em; + line-height: 140%; + padding: 0 2em; +} + +code { + font-family: "Menlo", monospace; + font-size: 0.95em; +} + +section { + padding: 0.5em 0; + -webkit-overflow-scrolling: touch; +} + +a:link { + color: #00f; + text-decoration: none; + border-bottom: 1px solid #00f; +} + +h1 { + font-size: 1.5em; + padding: 0.7em 0 0 0; +} + +h2 { + font-size: 1.2em; + padding: 0.7em 0 0 0; +} + +h3 { + font-size: 1.0em; + padding: 0.7em 0 0 0; +} + +blockquote { + font-size: 0.9em; + margin-left: 0; + padding-left: 2em; + border-left: 8px solid #eee; +} + +.footnotes { + color: #666666; + margin-top: 2em; + font-size: 0.9em; + letter-spacing: -3px; +} + +.footnotes:before { + content: "――――"; +} + +.footnotes ol { + letter-spacing: normal; +} diff --git a/14a. Initialization.playground/contents.xcplayground b/14a. Initialization.playground/contents.xcplayground index 4937636..0e6428a 100644 --- a/14a. Initialization.playground/contents.xcplayground +++ b/14a. Initialization.playground/contents.xcplayground @@ -1,7 +1,69 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file diff --git a/14a. Initialization.playground/section-1.swift b/14a. Initialization.playground/section-1.swift index 8638c0a..8158664 100644 --- a/14a. Initialization.playground/section-1.swift +++ b/14a. Initialization.playground/section-1.swift @@ -1,20 +1,3 @@ -// ------------------------------------------------------------------------------------------------ -// Things to know: -// -// * Swift provides an initializer (which partially resembles a function) to ensure that every -// property in a class, structure or enumeration is fully initialized. -// -// * Optionals do not need to be initialized as they are automatically initialized to nil if no -// default value is provided. -// ------------------------------------------------------------------------------------------------ - -// Here's a basic use of an initializer. -// -// The Fahrenheit class will create a 'temperature' property that does not have a default value. -// Because there is no default value, the property must be initialized in the initializer. -// -// Without the initializer, we would get an error whenever we tried to instantiate the Fahrenheit -// class. struct Fahrenheit { var temperature: Double @@ -23,210 +6,4 @@ struct Fahrenheit { temperature = 32.0 } -} - -// Since the class can fully initialize itself, we can safely instantiate it with no error: -var f = Fahrenheit() - -// Since the temperature is always defined as "32.0", it is cleaner and preferred to use a default -// value instead of setting it inside the initializer: -struct AnotherFahrenheit -{ - var temperature: Double = 32.0 -} - -// Initializers can also include parameters. Here's an example of using a parameter to initialize -// the class's temperature to a given value. -// -// The following class contains two initializers: -struct Celsius -{ - var temperatureInCelsius: Double = 0.0 - - // Initialize our temperature from Fahrenheit - init(fromFahrenheit fahrenheit: Double) - { - temperatureInCelsius = (fahrenheit - 32.0) / 1.8 - } - - // Initialize our temperature from Kelvin - init(kelvin: Double) - { - temperatureInCelsius = kelvin - 273.15 - } -} - -// Now let's try our new initializers -let boilingPotOfWater = Celsius(fromFahrenheit: 212.0) -let freezingPointOfWater = Celsius(kelvin: 273.15) - -// External parameter names are automatically generated for the initializer. In order to opt-out -// of this automated convenience, you can specify "_" for the extnenal name explicitly. -// -// Here's a class that defines two initializers - one that makes use of the automatic external -// name generation and one that opts out: -struct Color -{ - let red = 0.0, green = 0.0, blue = 0.0 - - // This initializer will make use of automatically generated exernal names - init(red: Double, green: Double, blue: Double) - { - self.red = red - self.green = green - self.blue = blue - } - - // This initializer opts out by explicitly declaring external names with "_" - init(_ red: Double, _ blue: Double) - { - self.red = red - self.green = 0 - self.blue = blue - } -} - -// Here, we can see our efforts pay off: -let magenta = Color(red: 1.0, green: 0.0, blue: 1.0) -let purple = Color(1.0, 0.5) - -// Optional properties do not need to be initialized: -class SurveyQuestion -{ - var text: String - - // Response is optional, and is automatically initialized to nil - var response: String? - - init(text: String) - { - // We only need to initialize 'text' - self.text = text - } - - func ask() -> String - { - return text - } -} - -// Constants need initialization as well. In the following example, our constant has a default -// value. However, if we initialize the class with the init(text: String) initializer, we can -// modify the default value to use the one passed in: -class SurveyQuestion2 -{ - // Default value of "No question" - let text: String = "No question" - - var response: String? - - init(text: String) - { - // Initializing the constant, 'text', even though it has a default value, we can modify - // that default value here - self.text = text - } - - init() - { - // We do nothing here and let the default value for 'text' define its value - } - - func ask() -> String - { - return text - } -} - -// Here, we initialize the class with a blank initializer (calling init()) to let text's default -// value initialize the stored value -let noQuestion = SurveyQuestion2() - -// Here, we'll us an aalternat initializer to specify a different value for 'text' -let beetsQuestion = SurveyQuestion2(text: "Do you like beets?") - -// ------------------------------------------------------------------------------------------------ -// Default Initializer -// -// If all properties have default values (including optionals defaulting to nil) AND you do not -// create your own initlializer AND there is no superclass, Swift will create a default -// initializer (with no parameters) for you. This initializer sets all properties to their -// default values. -// -// If you create your own initializer, Swift will not create the default initializer. If you want -// your custom initializers as well as the default initializer, then put your initializers in an -// extension. -class ShoppingListItem -{ - var name: String? - var quantity = 1 - var purchased = false - - // No init(...) initializer -} - -// ------------------------------------------------------------------------------------------------ -// Memberwise Initializers for Structure Types -// -// Similar to the default initializer for classes, structures that do not implement an initializer -// but have default values for their stored properties will get a default memberwise initializer. -// -// As with classes, if you want your custom initializers as well as the default memberwise -// initializer, then put your initializers in an extension. -struct Size -{ - var width = 0.0 - var height = 0.0 -} - -// Here, we call the default memberwise initializer that Swift created for us -let twoByTwo = Size(width: 2.0, height: 2.0) - -// ------------------------------------------------------------------------------------------------ -// Initializer Delegatgion for Value Types -// -// Sometimes, it's convenient to have multiple initializers that call other initializers to do -// some of the heavy lifting. -// -// In the following example, we have a class 'Rect' that has multiple initializers. One that -// initializes our rect given an origin and size, while another that calls upon the origin/size -// initializer from calculations based on the a center point. -// -// This concept is further extended in "Initializer Chaining", covered later. -struct Point -{ - var x = 0.0 - var y = 0.0 -} - -struct Rect -{ - var origin = Point() - var size = Size() - - // We create a basic initializer to use the default values Since we define other initializers, - // the system won't create this for us, so we need to define it ourselves. Since we're using - // the defaults, it is an empty closure. - init() {} - - // Init from origin/size - init(origin: Point, size: Size) - { - self.origin = origin - self.size = size - } - - // Init from center/size - note how we use the init(origin:size) to perform actual - // initialization - init(center: Point, size: Size) - { - let originX = center.x - size.width / 2 - let originY = center.y - size.height / 2 - self.init(origin: Point(x: originX, y: originY), size: size) - } -} - -// Here, we call the three initializers: -let basicRect = Rect() -let originRect = Rect(origin: Point(x: 2.0, y: 2.0), size: Size(width: 5.0, height: 5.0)) -let centerRect = Rect(center: Point(x: 4.0, y: 4.0), size: Size(width: 3.0, height: 3.0)) +} \ No newline at end of file diff --git a/14a. Initialization.playground/section-11.swift b/14a. Initialization.playground/section-11.swift new file mode 100644 index 0000000..c0869fb --- /dev/null +++ b/14a. Initialization.playground/section-11.swift @@ -0,0 +1,20 @@ +struct Color +{ + let red = 0.0, green = 0.0, blue = 0.0 + + // This initializer will make use of automatically generated exernal names + init(red: Double, green: Double, blue: Double) + { + self.red = red + self.green = green + self.blue = blue + } + + // This initializer opts out by explicitly declaring external names with "_" + init(_ red: Double, _ blue: Double) + { + self.red = red + self.green = 0 + self.blue = blue + } +} \ No newline at end of file diff --git a/14a. Initialization.playground/section-13.swift b/14a. Initialization.playground/section-13.swift new file mode 100644 index 0000000..352130b --- /dev/null +++ b/14a. Initialization.playground/section-13.swift @@ -0,0 +1,2 @@ +let magenta = Color(red: 1.0, green: 0.0, blue: 1.0) +let purple = Color(1.0, 0.5) \ No newline at end of file diff --git a/14a. Initialization.playground/section-15.swift b/14a. Initialization.playground/section-15.swift new file mode 100644 index 0000000..fc39216 --- /dev/null +++ b/14a. Initialization.playground/section-15.swift @@ -0,0 +1,18 @@ +class SurveyQuestion +{ + var text: String + + // Response is optional, and is automatically initialized to nil + var response: String? + + init(text: String) + { + // We only need to initialize 'text' + self.text = text + } + + func ask() -> String + { + return text + } +} \ No newline at end of file diff --git a/14a. Initialization.playground/section-17.swift b/14a. Initialization.playground/section-17.swift new file mode 100644 index 0000000..0e84f23 --- /dev/null +++ b/14a. Initialization.playground/section-17.swift @@ -0,0 +1,24 @@ +class SurveyQuestion2 +{ + // Default value of "No question" + let text: String = "No question" + + var response: String? + + init(text: String) + { + // Initializing the constant, 'text', even though it has a default value, we can modify + // that default value here + self.text = text + } + + init() + { + // We do nothing here and let the default value for 'text' define its value + } + + func ask() -> String + { + return text + } +} \ No newline at end of file diff --git a/14a. Initialization.playground/section-19.swift b/14a. Initialization.playground/section-19.swift new file mode 100644 index 0000000..e0a3e37 --- /dev/null +++ b/14a. Initialization.playground/section-19.swift @@ -0,0 +1 @@ +let noQuestion = SurveyQuestion2() \ No newline at end of file diff --git a/14a. Initialization.playground/section-21.swift b/14a. Initialization.playground/section-21.swift new file mode 100644 index 0000000..88bcce5 --- /dev/null +++ b/14a. Initialization.playground/section-21.swift @@ -0,0 +1 @@ +let beetsQuestion = SurveyQuestion2(text: "Do you like beets?") \ No newline at end of file diff --git a/14a. Initialization.playground/section-23.swift b/14a. Initialization.playground/section-23.swift new file mode 100644 index 0000000..3a9b82a --- /dev/null +++ b/14a. Initialization.playground/section-23.swift @@ -0,0 +1,8 @@ +class ShoppingListItem +{ + var name: String? + var quantity = 1 + var purchased = false + + // No init(...) initializer +} \ No newline at end of file diff --git a/14a. Initialization.playground/section-25.swift b/14a. Initialization.playground/section-25.swift new file mode 100644 index 0000000..16f06ab --- /dev/null +++ b/14a. Initialization.playground/section-25.swift @@ -0,0 +1,5 @@ +struct Size +{ + var width = 0.0 + var height = 0.0 +} \ No newline at end of file diff --git a/14a. Initialization.playground/section-27.swift b/14a. Initialization.playground/section-27.swift new file mode 100644 index 0000000..62a6508 --- /dev/null +++ b/14a. Initialization.playground/section-27.swift @@ -0,0 +1 @@ +let twoByTwo = Size(width: 2.0, height: 2.0) \ No newline at end of file diff --git a/14a. Initialization.playground/section-29.swift b/14a. Initialization.playground/section-29.swift new file mode 100644 index 0000000..36186e7 --- /dev/null +++ b/14a. Initialization.playground/section-29.swift @@ -0,0 +1,32 @@ +struct Point +{ + var x = 0.0 + var y = 0.0 +} + +struct Rect +{ + var origin = Point() + var size = Size() + + // We create a basic initializer to use the default values Since we define other initializers, + // the system won't create this for us, so we need to define it ourselves. Since we're using + // the defaults, it is an empty closure. + init() {} + + // Init from origin/size + init(origin: Point, size: Size) + { + self.origin = origin + self.size = size + } + + // Init from center/size - note how we use the init(origin:size) to perform actual + // initialization + init(center: Point, size: Size) + { + let originX = center.x - size.width / 2 + let originY = center.y - size.height / 2 + self.init(origin: Point(x: originX, y: originY), size: size) + } +} \ No newline at end of file diff --git a/14a. Initialization.playground/section-3.swift b/14a. Initialization.playground/section-3.swift new file mode 100644 index 0000000..3245b42 --- /dev/null +++ b/14a. Initialization.playground/section-3.swift @@ -0,0 +1 @@ +var f = Fahrenheit() \ No newline at end of file diff --git a/14a. Initialization.playground/section-31.swift b/14a. Initialization.playground/section-31.swift new file mode 100644 index 0000000..0fb77de --- /dev/null +++ b/14a. Initialization.playground/section-31.swift @@ -0,0 +1,3 @@ +let basicRect = Rect() +let originRect = Rect(origin: Point(x: 2.0, y: 2.0), size: Size(width: 5.0, height: 5.0)) +let centerRect = Rect(center: Point(x: 4.0, y: 4.0), size: Size(width: 3.0, height: 3.0)) \ No newline at end of file diff --git a/14a. Initialization.playground/section-5.swift b/14a. Initialization.playground/section-5.swift new file mode 100644 index 0000000..d17aa67 --- /dev/null +++ b/14a. Initialization.playground/section-5.swift @@ -0,0 +1,4 @@ +struct AnotherFahrenheit +{ + var temperature: Double = 32.0 +} \ No newline at end of file diff --git a/14a. Initialization.playground/section-7.swift b/14a. Initialization.playground/section-7.swift new file mode 100644 index 0000000..17de92d --- /dev/null +++ b/14a. Initialization.playground/section-7.swift @@ -0,0 +1,16 @@ +struct Celsius +{ + var temperatureInCelsius: Double = 0.0 + + // Initialize our temperature from Fahrenheit + init(fromFahrenheit fahrenheit: Double) + { + temperatureInCelsius = (fahrenheit - 32.0) / 1.8 + } + + // Initialize our temperature from Kelvin + init(kelvin: Double) + { + temperatureInCelsius = kelvin - 273.15 + } +} \ No newline at end of file diff --git a/14a. Initialization.playground/section-9.swift b/14a. Initialization.playground/section-9.swift new file mode 100644 index 0000000..b1fc23a --- /dev/null +++ b/14a. Initialization.playground/section-9.swift @@ -0,0 +1,2 @@ +let boilingPotOfWater = Celsius(fromFahrenheit: 212.0) +let freezingPointOfWater = Celsius(kelvin: 273.15) \ No newline at end of file diff --git a/14b. Initializer Chaining.playground/Documentation/section-0.html b/14b. Initializer Chaining.playground/Documentation/section-0.html new file mode 100644 index 0000000..e3725db --- /dev/null +++ b/14b. Initializer Chaining.playground/Documentation/section-0.html @@ -0,0 +1,29 @@ + + + + + Section 1 + + + + + + +
+
+

Initializer Chaining

+

Things to know:

+
    +
  • Initializer Chaining refers to the way in which initialization takes place along the class hierarchy.
  • +
  • Designated Initializers are those that are responsible for ensuring that the instance of a class, structure or enumeration is properly initialized. These are the initializers you've seen so far. Designated initializers are also resonsible for calling the superclass' initializer.
  • +
  • Convenience Initializers are initializers that are provided for specialized initialization and must call one of the designated initializers within a class in order to fulfill the requirement of properly initializing a class.
  • +
+
+

Designated Initializers are the normal initializers ("init(...)") functions that you've seen so far. If the system creates an initializer for you, it will be a designated initializer.

+

Convenience Initializers allow you to offer different code-paths through initialization, which call upon the designated initializers to do the heavy lifting. They use the keyword "convenience". They are actually different than designated initializers because Swift uses the difference (designated vs. convenience) to perform two-phase initialization, which we'll cover shortly.

+

Here's what a simple case looks like with a designated and convenience initializer:

+ +
+
+ + diff --git a/14b. Initializer Chaining.playground/Documentation/section-10.html b/14b. Initializer Chaining.playground/Documentation/section-10.html new file mode 100644 index 0000000..0136b35 --- /dev/null +++ b/14b. Initializer Chaining.playground/Documentation/section-10.html @@ -0,0 +1,19 @@ + + + + + Section 11 + + + + + + +
+
+

Let's initialize our new ShoppingListItem using the super's initializer

+ +
+
+ + diff --git a/14b. Initializer Chaining.playground/Documentation/section-12.html b/14b. Initializer Chaining.playground/Documentation/section-12.html new file mode 100644 index 0000000..abd345f --- /dev/null +++ b/14b. Initializer Chaining.playground/Documentation/section-12.html @@ -0,0 +1,19 @@ + + + + + Section 13 + + + + + + +
+
+

Here, we can create an array of ShippingListItems

+ +
+
+ + diff --git a/14b. Initializer Chaining.playground/Documentation/section-14.html b/14b. Initializer Chaining.playground/Documentation/section-14.html new file mode 100644 index 0000000..a1c8a8d --- /dev/null +++ b/14b. Initializer Chaining.playground/Documentation/section-14.html @@ -0,0 +1,21 @@ + + + + + Section 15 + + + + + + +
+
+

For individual properties, we can set their default value with a closure or function instead of just a literal value.

+

In the example below, we use a closure to calculate the default value for 'estimatedPi'. Note the parenthesis at the end of the closure. Without them, we would be asking to set 'estimatedPI' to a closure type rather than to the result of the closure. Also, the parenthesis tell it to execute the closure immediately.

+

Also note that these closures used for default values for properties are not allowed to access any of the other properties of the class because it's assumed that they have not yet been initialized.

+ +
+
+ + diff --git a/14b. Initializer Chaining.playground/Documentation/section-16.html b/14b. Initializer Chaining.playground/Documentation/section-16.html new file mode 100644 index 0000000..873012b --- /dev/null +++ b/14b. Initializer Chaining.playground/Documentation/section-16.html @@ -0,0 +1,19 @@ + + + + + Section 17 + + + + + + +
+
+

Here's a more pracitcal example. Note that since the closure is required to return the type of the property it is initializing, it must create a temporary of the same type which is initialized and then returned.

+ +
+
+ + diff --git a/14b. Initializer Chaining.playground/Documentation/section-18.html b/14b. Initializer Chaining.playground/Documentation/section-18.html new file mode 100644 index 0000000..0a95e8d --- /dev/null +++ b/14b. Initializer Chaining.playground/Documentation/section-18.html @@ -0,0 +1,19 @@ + + + + + Section 19 + + + + + + +
+
+

We can now check our work

+ +
+
+ + diff --git a/14b. Initializer Chaining.playground/Documentation/section-2.html b/14b. Initializer Chaining.playground/Documentation/section-2.html new file mode 100644 index 0000000..2f7079b --- /dev/null +++ b/14b. Initializer Chaining.playground/Documentation/section-2.html @@ -0,0 +1,19 @@ + + + + + Section 3 + + + + + + +
+
+

Here we make use of our two initializers

+ +
+
+ + diff --git a/14b. Initializer Chaining.playground/Documentation/section-4.html b/14b. Initializer Chaining.playground/Documentation/section-4.html new file mode 100644 index 0000000..be151c6 --- /dev/null +++ b/14b. Initializer Chaining.playground/Documentation/section-4.html @@ -0,0 +1,30 @@ + + + + + Section 5 + + + + + + +
+
+

Two-Phase Initialization

+

Two-phase initialization is a new concept enforced by Swift. Think of it like this:

+
    +
  • Phase 1: Subclasses MUST FIRST initialize any stored properties that are part of their subclass. They must do this before they are allowed to cusomize a super's stored property. Subclasses MUST THEN call the super's initializer, which will repeat this process until the top-most superclass (the base class) is reached.
  • +
+

At this point we can assume that the class is fully initialized (but not necessarily customized by the subclasses that may need to alter the initialization results.

+
    +
  • Phase 2: In the base class' initializer, we can customize its properties before it returns to the caller (a subclass.) That subclass is then allowed to write to any/all stored properties defined by itself or the superclass chain. This continues until you get the the bottom-most subclass.
  • +
+

A note about convenience initializers:

+

Convenience initializers must always call into a designated initializer from the current class. A designated initializer for a subclass must then into a super's iniitializer, which may also be a convenience initializer, because that convenience initializer will be required to eventually call a designated initializer before going higher up the chain.

+

Let's derive a class from Food so we can see this in action:

+ +
+
+ + diff --git a/14b. Initializer Chaining.playground/Documentation/section-6.html b/14b. Initializer Chaining.playground/Documentation/section-6.html new file mode 100644 index 0000000..d345ccf --- /dev/null +++ b/14b. Initializer Chaining.playground/Documentation/section-6.html @@ -0,0 +1,19 @@ + + + + + Section 7 + + + + + + +
+
+

Now we can call our various initializers to see them in action:

+ +
+
+ + diff --git a/14b. Initializer Chaining.playground/Documentation/section-8.html b/14b. Initializer Chaining.playground/Documentation/section-8.html new file mode 100644 index 0000000..6aaa77c --- /dev/null +++ b/14b. Initializer Chaining.playground/Documentation/section-8.html @@ -0,0 +1,21 @@ + + + + + Section 9 + + + + + + +
+
+

Inheriting a full set of the super's initializers

+

In the following class, we don't specify any initializer, but that's OK because we have default values for the stored property (purchased).

+

Not providing any initializer at all allows us to gain a full set of the super's initializers.

+ +
+
+ + diff --git a/14b. Initializer Chaining.playground/Documentation/stylesheet.css b/14b. Initializer Chaining.playground/Documentation/stylesheet.css new file mode 100644 index 0000000..43b0230 --- /dev/null +++ b/14b. Initializer Chaining.playground/Documentation/stylesheet.css @@ -0,0 +1,65 @@ +html { + font-size: 1em; +} + +body { + background-color: #fff; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: normal; + font-size: 1.0em; + line-height: 140%; + padding: 0 2em; +} + +code { + font-family: "Menlo", monospace; + font-size: 0.95em; +} + +section { + padding: 0.5em 0; + -webkit-overflow-scrolling: touch; +} + +a:link { + color: #00f; + text-decoration: none; + border-bottom: 1px solid #00f; +} + +h1 { + font-size: 1.5em; + padding: 0.7em 0 0 0; +} + +h2 { + font-size: 1.2em; + padding: 0.7em 0 0 0; +} + +h3 { + font-size: 1.0em; + padding: 0.7em 0 0 0; +} + +blockquote { + font-size: 0.9em; + margin-left: 0; + padding-left: 2em; + border-left: 8px solid #eee; +} + +.footnotes { + color: #666666; + margin-top: 2em; + font-size: 0.9em; + letter-spacing: -3px; +} + +.footnotes:before { + content: "――――"; +} + +.footnotes ol { + letter-spacing: normal; +} diff --git a/14b. Initializer Chaining.playground/contents.xcplayground b/14b. Initializer Chaining.playground/contents.xcplayground index 4937636..2a1d9b7 100644 --- a/14b. Initializer Chaining.playground/contents.xcplayground +++ b/14b. Initializer Chaining.playground/contents.xcplayground @@ -1,7 +1,45 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file diff --git a/14b. Initializer Chaining.playground/section-1.swift b/14b. Initializer Chaining.playground/section-1.swift index d4ed131..9d01b12 100644 --- a/14b. Initializer Chaining.playground/section-1.swift +++ b/14b. Initializer Chaining.playground/section-1.swift @@ -1,29 +1,3 @@ -// ------------------------------------------------------------------------------------------------ -// Things to know: -// -// * Initializer Chaining refers to the way in which initialization takes place along the class -// hierarchy. -// -// * Designated Initializers are those that are responsible for ensuring that the instance of a -// class, structure or enumeration is properly initialized. These are the initializers you've -// seen so far. Designated initializers are also resonsible for calling the superclass' -// initializer. -// -// * Convenience Initializers are initializers that are provided for specialized initialization -// and must call one of the designated initializers within a class in order to fulfill the -// requirement of properly initializing a class. -// ------------------------------------------------------------------------------------------------ - -// Designated Initializers are the normal initializers ("init(...)") functions that you've seen -// so far. If the system creates an initializer for you, it will be a designated initializer. -// -// Convenience Initializers allow you to offer different code-paths through initialization, which -// call upon the designated initializers to do the heavy lifting. They use the keyword -// "convenience". They are actually different than designated initializers because Swift uses the -// difference (designated vs. convenience) to perform two-phase initialization, which we'll cover -// shortly. -// -// Here's what a simple case looks like with a designated and convenience initializer: class Food { var name: String @@ -41,154 +15,4 @@ class Food // Must call the designated in same class self.init(name: "[unnamed]") } -} - -// Here we make use of our two initializers -let namedMeat = Food(name: "Bacon") -let mysteryMeat = Food() - -// ------------------------------------------------------------------------------------------------ -// Two-Phase Initialization -// -// Two-phase initialization is a new concept enforced by Swift. Think of it like this: -// -// Phase 1: Subclasses MUST FIRST initialize any stored properties that are part of their subclass. -// They must do this before they are allowed to cusomize a super's stored property. -// -// Subclasses MUST THEN call the super's initializer, which will repeat this process -// until the top-most superclass (the base class) is reached. -// -// At this point we can assume that the class is fully initialized (but not necessarily -// customized by the subclasses that may need to alter the initialization results. -// -// Phase 2: In the base class' initializer, we can customize its properties before it returns to -// the caller (a subclass.) That subclass is then allowed to write to any/all stored -// properties defined by itself or the superclass chain. This continues until you get the -// the bottom-most subclass. -// -// A note about convenience initializers: -// -// Convenience initializers must always call into a designated initializer from the current class. -// A designated initializer for a subclass must then into a super's iniitializer, which may also -// be a convenience initializer, because that convenience initializer will be required to -// eventually call a designated initializer before going higher up the chain. -// -// Let's derive a class from Food so we can see this in action: -class RecipeIngredient: Food -{ - var quantity: Int - - // This is a designated initializer (because it has no 'convenience' keyword) - init(name: String, quantity: Int) - { - // We must initialize our new stored properties first (this is Phase 1) - self.quantity = quantity - - // Next, we must call super's initializer (still, Phase 1) - super.init(name: name) - - // Only after the super's initializer is called, can we customize any properties that - // originated from someplace up the class hierarchy chain. - self.name = "Ingredient: " + name - } - - // Here, we'll create a convenience initializer that simply provides a default quantity - // value of 1. Note that in order for this to compile, we are required to call a designated - // initializer. - convenience override init(name: String) - { - self.init(name: name, quantity: 1) - } -} - -// Now we can call our various initializers to see them in action: -let oneMysteryItem = RecipeIngredient() -let oneBacon = RecipeIngredient(name: "Bacon") -let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6) - -// ------------------------------------------------------------------------------------------------ -// Inheriting a full set of the super's initializers -// -// In the following class, we don't specify any initializer, but that's OK because we have default -// values for the stored property (purchased). -// -// Not providing any initializer at all allows us to gain a full set of the super's initializers. -class ShoppingListItem: RecipeIngredient -{ - var purchased = false - var description: String - { - var output = "\(quantity) x \(name)" - output += purchased ? " ✔" : " ✘" - return output - } -} - -// Let's initialize our new ShoppingListItem using the super's initializer -let lotsOfCheese = ShoppingListItem(name: "cheese", quantity: 99) - -// Here, we can create an array of ShippingListItems -var breakfastList = [ - ShoppingListItem(), - ShoppingListItem(name: "Bacon"), - ShoppingListItem(name: "Eggs", quantity: 6), -] - -// ------------------------------------------------------------------------------------------------ -// For individual properties, we can set their default value with a closure or function instead -// of just a literal value. -// -// In the example below, we use a closure to calculate the default value for 'estimatedPi'. Note -// the parenthesis at the end of the closure. Without them, we would be asking to set 'estimatedPI' -// to a closure type rather than to the result of the closure. Also, the parenthesis tell it to -// execute the closure immediately. -// -// Also note that these closures used for default values for properties are not allowed to access -// any of the other properties of the class because it's assumed that they have not yet been -// initialized. -class ClassWithPI -{ - let estimatedPI: Double = - { - let constant1 = 22.0 - let constant2 = 7.0 - - // Must return the type specified by the property - return constant1 / constant2 - }() -} - -// Here's a more pracitcal example. Note that since the closure is required to return the type -// of the property it is initializing, it must create a temporary of the same type which is -// initialized and then returned. -struct CheckerBoard -{ - let boardColors: [Bool] = - { - var temporaryBoard = [Bool]() - var isBlack = false - for i in 1...10 - { - for j in 1...10 - { - temporaryBoard.append(isBlack) - isBlack = !isBlack - } - - isBlack = !isBlack - } - - // Return the temporary in order to set 'boardColors' - return temporaryBoard - }() - - func squareIsBlackAtRow(row: Int, column: Int) -> Bool - { - return boardColors[(row * 10) + column] - } -} - -// We can now check our work -var board = CheckerBoard() -board.squareIsBlackAtRow(1, column: 1) // Should be false -board.squareIsBlackAtRow(1, column: 2) // Should be true +} \ No newline at end of file diff --git a/14b. Initializer Chaining.playground/section-11.swift b/14b. Initializer Chaining.playground/section-11.swift new file mode 100644 index 0000000..a21335c --- /dev/null +++ b/14b. Initializer Chaining.playground/section-11.swift @@ -0,0 +1 @@ +let lotsOfCheese = ShoppingListItem(name: "cheese", quantity: 99) \ No newline at end of file diff --git a/14b. Initializer Chaining.playground/section-13.swift b/14b. Initializer Chaining.playground/section-13.swift new file mode 100644 index 0000000..9dac256 --- /dev/null +++ b/14b. Initializer Chaining.playground/section-13.swift @@ -0,0 +1,5 @@ +var breakfastList = [ + ShoppingListItem(), + ShoppingListItem(name: "Bacon"), + ShoppingListItem(name: "Eggs", quantity: 6), +] \ No newline at end of file diff --git a/14b. Initializer Chaining.playground/section-15.swift b/14b. Initializer Chaining.playground/section-15.swift new file mode 100644 index 0000000..2f84f9c --- /dev/null +++ b/14b. Initializer Chaining.playground/section-15.swift @@ -0,0 +1,11 @@ +class ClassWithPI +{ + let estimatedPI: Double = + { + let constant1 = 22.0 + let constant2 = 7.0 + + // Must return the type specified by the property + return constant1 / constant2 + }() +} \ No newline at end of file diff --git a/14b. Initializer Chaining.playground/section-17.swift b/14b. Initializer Chaining.playground/section-17.swift new file mode 100644 index 0000000..eb09738 --- /dev/null +++ b/14b. Initializer Chaining.playground/section-17.swift @@ -0,0 +1,26 @@ +struct CheckerBoard +{ + let boardColors: [Bool] = + { + var temporaryBoard = [Bool]() + var isBlack = false + for i in 1...10 + { + for j in 1...10 + { + temporaryBoard.append(isBlack) + isBlack = !isBlack + } + + isBlack = !isBlack + } + + // Return the temporary in order to set 'boardColors' + return temporaryBoard + }() + + func squareIsBlackAtRow(row: Int, column: Int) -> Bool + { + return boardColors[(row * 10) + column] + } +} \ No newline at end of file diff --git a/14b. Initializer Chaining.playground/section-19.swift b/14b. Initializer Chaining.playground/section-19.swift new file mode 100644 index 0000000..48cb61a --- /dev/null +++ b/14b. Initializer Chaining.playground/section-19.swift @@ -0,0 +1,3 @@ +var board = CheckerBoard() +board.squareIsBlackAtRow(1, column: 1) // Should be false +board.squareIsBlackAtRow(1, column: 2) // Should be true \ No newline at end of file diff --git a/14b. Initializer Chaining.playground/section-3.swift b/14b. Initializer Chaining.playground/section-3.swift new file mode 100644 index 0000000..66d2a12 --- /dev/null +++ b/14b. Initializer Chaining.playground/section-3.swift @@ -0,0 +1,2 @@ +let namedMeat = Food(name: "Bacon") +let mysteryMeat = Food() \ No newline at end of file diff --git a/14b. Initializer Chaining.playground/section-5.swift b/14b. Initializer Chaining.playground/section-5.swift new file mode 100644 index 0000000..d0f86ed --- /dev/null +++ b/14b. Initializer Chaining.playground/section-5.swift @@ -0,0 +1,26 @@ +class RecipeIngredient: Food +{ + var quantity: Int + + // This is a designated initializer (because it has no 'convenience' keyword) + init(name: String, quantity: Int) + { + // We must initialize our new stored properties first (this is Phase 1) + self.quantity = quantity + + // Next, we must call super's initializer (still, Phase 1) + super.init(name: name) + + // Only after the super's initializer is called, can we customize any properties that + // originated from someplace up the class hierarchy chain. + self.name = "Ingredient: " + name + } + + // Here, we'll create a convenience initializer that simply provides a default quantity + // value of 1. Note that in order for this to compile, we are required to call a designated + // initializer. + convenience override init(name: String) + { + self.init(name: name, quantity: 1) + } +} \ No newline at end of file diff --git a/14b. Initializer Chaining.playground/section-7.swift b/14b. Initializer Chaining.playground/section-7.swift new file mode 100644 index 0000000..5ad22fb --- /dev/null +++ b/14b. Initializer Chaining.playground/section-7.swift @@ -0,0 +1,3 @@ +let oneMysteryItem = RecipeIngredient() +let oneBacon = RecipeIngredient(name: "Bacon") +let sixEggs = RecipeIngredient(name: "Eggs", quantity: 6) \ No newline at end of file diff --git a/14b. Initializer Chaining.playground/section-9.swift b/14b. Initializer Chaining.playground/section-9.swift new file mode 100644 index 0000000..d23616c --- /dev/null +++ b/14b. Initializer Chaining.playground/section-9.swift @@ -0,0 +1,10 @@ +class ShoppingListItem: RecipeIngredient +{ + var purchased = false + var description: String + { + var output = "\(quantity) x \(name)" + output += purchased ? " ✔" : " ✘" + return output + } +} \ No newline at end of file diff --git a/15. Deinitialization.playground/Documentation/section-0.html b/15. Deinitialization.playground/Documentation/section-0.html new file mode 100644 index 0000000..243e6eb --- /dev/null +++ b/15. Deinitialization.playground/Documentation/section-0.html @@ -0,0 +1,28 @@ + + + + + Section 1 + + + + + + +
+
+

Deinitialization

+

Things to know:

+
    +
  • Deinitializers are called automatically before a class instance is deallocated,so you can access all properties in the deinitializer.
  • +
  • You cannot call them directly.
  • +
  • The superclass' deinitializer is called before the subclass'.
  • +
  • Swift uses ARC to manage memory, so deinitializers shouldn't always be necessary. However, you might open a file and need to close it when a class is deallocated.
  • +
+
+

Let's create a couple classes to work with...

+ +
+
+ + diff --git a/15. Deinitialization.playground/Documentation/section-2.html b/15. Deinitialization.playground/Documentation/section-2.html new file mode 100644 index 0000000..cb434d0 --- /dev/null +++ b/15. Deinitialization.playground/Documentation/section-2.html @@ -0,0 +1,19 @@ + + + + + Section 3 + + + + + + +
+
+

Let's exercise the Player class a bit and create a new player with 100 coins in his purse (pulled from the Bank.)

+ +
+
+ + diff --git a/15. Deinitialization.playground/Documentation/section-4.html b/15. Deinitialization.playground/Documentation/section-4.html new file mode 100644 index 0000000..6b836ee --- /dev/null +++ b/15. Deinitialization.playground/Documentation/section-4.html @@ -0,0 +1,19 @@ + + + + + Section 5 + + + + + + +
+
+

The Player now wins 2000 coins!

+ +
+
+ + diff --git a/15. Deinitialization.playground/Documentation/section-6.html b/15. Deinitialization.playground/Documentation/section-6.html new file mode 100644 index 0000000..771cd62 --- /dev/null +++ b/15. Deinitialization.playground/Documentation/section-6.html @@ -0,0 +1,19 @@ + + + + + Section 7 + + + + + + +
+
+

When we cause playerOne to be deallocated, the deinitializer is called

+ +
+
+ + diff --git a/15. Deinitialization.playground/Documentation/section-8.html b/15. Deinitialization.playground/Documentation/section-8.html new file mode 100644 index 0000000..37602ae --- /dev/null +++ b/15. Deinitialization.playground/Documentation/section-8.html @@ -0,0 +1,19 @@ + + + + + Section 9 + + + + + + +
+
+

This should print 12000 coins, but the playgrounds don't appear to do this correctly. If you put this code into a project and compile/run it (with minor changes to print variables using println) then you will see that the bank does indeed have 12000 coins.

+ +
+
+ + diff --git a/15. Deinitialization.playground/Documentation/stylesheet.css b/15. Deinitialization.playground/Documentation/stylesheet.css new file mode 100644 index 0000000..43b0230 --- /dev/null +++ b/15. Deinitialization.playground/Documentation/stylesheet.css @@ -0,0 +1,65 @@ +html { + font-size: 1em; +} + +body { + background-color: #fff; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: normal; + font-size: 1.0em; + line-height: 140%; + padding: 0 2em; +} + +code { + font-family: "Menlo", monospace; + font-size: 0.95em; +} + +section { + padding: 0.5em 0; + -webkit-overflow-scrolling: touch; +} + +a:link { + color: #00f; + text-decoration: none; + border-bottom: 1px solid #00f; +} + +h1 { + font-size: 1.5em; + padding: 0.7em 0 0 0; +} + +h2 { + font-size: 1.2em; + padding: 0.7em 0 0 0; +} + +h3 { + font-size: 1.0em; + padding: 0.7em 0 0 0; +} + +blockquote { + font-size: 0.9em; + margin-left: 0; + padding-left: 2em; + border-left: 8px solid #eee; +} + +.footnotes { + color: #666666; + margin-top: 2em; + font-size: 0.9em; + letter-spacing: -3px; +} + +.footnotes:before { + content: "――――"; +} + +.footnotes ol { + letter-spacing: normal; +} diff --git a/15. Deinitialization.playground/contents.xcplayground b/15. Deinitialization.playground/contents.xcplayground index 4937636..3dea9bb 100644 --- a/15. Deinitialization.playground/contents.xcplayground +++ b/15. Deinitialization.playground/contents.xcplayground @@ -1,7 +1,25 @@ - + - + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file diff --git a/15. Deinitialization.playground/section-1.swift b/15. Deinitialization.playground/section-1.swift index fec04d3..25f9944 100644 --- a/15. Deinitialization.playground/section-1.swift +++ b/15. Deinitialization.playground/section-1.swift @@ -1,19 +1,3 @@ -// ------------------------------------------------------------------------------------------------ -// Things to know: -// -// * Deinitializers are called automatically before a class instance is -// deallocated,so you can access all properties in the deinitializer. -// -// * You cannot call them directly. -// -// * The superclass' deinitializer is called before the subclass'. -// -// * Swift uses ARC to manage memory, so deinitializers shouldn't always -// be necessary. However, you might open a file and need to close it -// when a class is deallocated. -// ------------------------------------------------------------------------------------------------ - -// Let's create a couple classes to work with... struct Bank { static var coinsInBank = 10_000 @@ -48,24 +32,4 @@ class Player { Bank.receiveCoins(coinsInPurse) } -} - -// Let's exercise the Player class a bit and create a new player with 100 -// coins in his purse (pulled from the Bank.) -var playerOne: Player? = Player(coins: 100) -playerOne!.coinsInPurse -Bank.coinsInBank - -// The Player now wins 2000 coins! -playerOne!.winCoins(2_000) -playerOne!.coinsInPurse -Bank.coinsInBank - -// When we cause playerOne to be deallocated, the deinitializer is called -playerOne = nil - -// This should print 12000 coins, but the playgrounds don't appear to do -// this correctly. If you put this code into a project and compile/run -// it (with minor changes to print variables using println) then you -// will see that the bank does indeed have 12000 coins. -Bank.coinsInBank +} \ No newline at end of file diff --git a/15. Deinitialization.playground/section-3.swift b/15. Deinitialization.playground/section-3.swift new file mode 100644 index 0000000..ce53ce4 --- /dev/null +++ b/15. Deinitialization.playground/section-3.swift @@ -0,0 +1,3 @@ +var playerOne: Player? = Player(coins: 100) +playerOne!.coinsInPurse +Bank.coinsInBank \ No newline at end of file diff --git a/15. Deinitialization.playground/section-5.swift b/15. Deinitialization.playground/section-5.swift new file mode 100644 index 0000000..c513e94 --- /dev/null +++ b/15. Deinitialization.playground/section-5.swift @@ -0,0 +1,3 @@ +playerOne!.winCoins(2_000) +playerOne!.coinsInPurse +Bank.coinsInBank \ No newline at end of file diff --git a/15. Deinitialization.playground/section-7.swift b/15. Deinitialization.playground/section-7.swift new file mode 100644 index 0000000..1068e3c --- /dev/null +++ b/15. Deinitialization.playground/section-7.swift @@ -0,0 +1 @@ +playerOne = nil \ No newline at end of file diff --git a/15. Deinitialization.playground/section-9.swift b/15. Deinitialization.playground/section-9.swift new file mode 100644 index 0000000..5543ebf --- /dev/null +++ b/15. Deinitialization.playground/section-9.swift @@ -0,0 +1 @@ +Bank.coinsInBank \ No newline at end of file diff --git a/16. ARC.playground/Documentation/section-0.html b/16. ARC.playground/Documentation/section-0.html new file mode 100644 index 0000000..bd13d07 --- /dev/null +++ b/16. ARC.playground/Documentation/section-0.html @@ -0,0 +1,28 @@ + + + + + Section 1 + + + + + + +
+
+

ARC

+

Things to know:

+
    +
  • Automatic Reference Counting allows Swift to track and manage your app's memory usage. It automatically frees up memory from unused instances that are no longer in use.
  • +
  • Reference counting only applies to classes as structures and enumerations are value types.
  • +
  • Whenever a class instance is stored (to a property, constant or variable) a "strong reference" is made. A strong reference ensures that the reference is not deallocated for as long as the strong reference remains.
  • +
+
+

We can't really see ARC in actino within a Playground, but we can still follow along what would normally happen.

+

We'll start by creating a class to work with

+ +
+
+ + diff --git a/16. ARC.playground/Documentation/section-10.html b/16. ARC.playground/Documentation/section-10.html new file mode 100644 index 0000000..11f6277 --- /dev/null +++ b/16. ARC.playground/Documentation/section-10.html @@ -0,0 +1,19 @@ + + + + + Section 11 + + + + + + +
+
+

If we clear out this reference, we will drop the reference count once more to 0, causing the object to be cleaned up by ARC:

+ +
+
+ + diff --git a/16. ARC.playground/Documentation/section-12.html b/16. ARC.playground/Documentation/section-12.html new file mode 100644 index 0000000..70b383a --- /dev/null +++ b/16. ARC.playground/Documentation/section-12.html @@ -0,0 +1,21 @@ + + + + + Section 13 + + + + + + +
+
+

Strong Reference Cycles between class instances

+

If two classes hold a reference to each other, then they create a "Strong Reference Cycle".

+

Here's an example of two classes that are capable of holding references to one another, but do not do so initially (their references are optional and defaulted to nil):

+ +
+
+ + diff --git a/16. ARC.playground/Documentation/section-14.html b/16. ARC.playground/Documentation/section-14.html new file mode 100644 index 0000000..ef3bdd2 --- /dev/null +++ b/16. ARC.playground/Documentation/section-14.html @@ -0,0 +1,19 @@ + + + + + Section 15 + + + + + + +
+
+

We can create a tenant and an apartment which are not associated to each other. After these two lines of code, each instance will have a reference count of 1.

+ +
+
+ + diff --git a/16. ARC.playground/Documentation/section-16.html b/16. ARC.playground/Documentation/section-16.html new file mode 100644 index 0000000..64b3e31 --- /dev/null +++ b/16. ARC.playground/Documentation/section-16.html @@ -0,0 +1,21 @@ + + + + + Section 17 + + + + + + +
+
+

Let's link them up.

+

This will create a strong reference cycle because each instance will have a reference to the other. The end result is that each instance will now have a reference count of 2. For example, the two strong references for the Tenant instances are held by 'bill' and the 'tenant' property inside the 'number73' apartment.

+

Note the "!" symbols for forced unwrapping (covered in an earlier section):

+ +
+
+ + diff --git a/16. ARC.playground/Documentation/section-18.html b/16. ARC.playground/Documentation/section-18.html new file mode 100644 index 0000000..a215ce8 --- /dev/null +++ b/16. ARC.playground/Documentation/section-18.html @@ -0,0 +1,20 @@ + + + + + Section 19 + + + + + + +
+
+

If we try to clean up, the memory will not be deallocated by ARC. Let's follow along what actually happens a step at a time.

+

First, we set 'bill' to be nil, which drops the strong reference count for this instance of Tenant down to 1. Since there is still a strong reference held to this instance, it is never deallocated (and the deinit() is also never called on that instance of Person.)

+ +
+
+ + diff --git a/16. ARC.playground/Documentation/section-2.html b/16. ARC.playground/Documentation/section-2.html new file mode 100644 index 0000000..1b0c33a --- /dev/null +++ b/16. ARC.playground/Documentation/section-2.html @@ -0,0 +1,19 @@ + + + + + Section 3 + + + + + + +
+
+

We'll want to create a person and then remove our reference to it, which means we'll need to use an optional Person type stored in a variable:

+ +
+
+ + diff --git a/16. ARC.playground/Documentation/section-20.html b/16. ARC.playground/Documentation/section-20.html new file mode 100644 index 0000000..4eaed33 --- /dev/null +++ b/16. ARC.playground/Documentation/section-20.html @@ -0,0 +1,19 @@ + + + + + Section 21 + + + + + + +
+
+

Next we do the same for 'number73' dropping the strong reference count for this instance of Apartment down to 1. Similarly, it is not deallocated or deinitialized.

+ +
+
+ + diff --git a/16. ARC.playground/Documentation/section-22.html b/16. ARC.playground/Documentation/section-22.html new file mode 100644 index 0000000..4e25420 --- /dev/null +++ b/16. ARC.playground/Documentation/section-22.html @@ -0,0 +1,26 @@ + + + + + Section 23 + + + + + + +
+
+

At this point, we have two instances that still exist in memory, but cannot be cleaned up because we don't have any references to them in order to solve the problem.

+

Resolving Strong Reference Cycles between Class Instances

+

Swift provides two methods to resolve strong reference cycles: weak and unowned references.

+

Weak references allow an instance to be held without actually having a strong hold on it (and hence, not incrementing the reference count for the target object.)

+

Use weak references when it's OK for a reference to become nil sometime during its lifetime. Since the Apartment can have no tenant at some point during its lifetime, a weak reference is the right way to go.

+

Weak references must always be optional types (because they may be required to be nil.) When an object holds a weak reference to an object, if that object is ever deallocated, Swift will locate all the weak references to it and set those references to nil.

+

Weak references are declared using the 'weak' keyword.

+

Let's fix our Apartment class. Note that we only have to break the cycle. It's perfectly fine to let the Tenant continue to hold a strong reference to our apartment. We will also create a new Tenant class (we'll just give it a new name, "NamedTenant"), but only so that we can change the apartment type to reference our fixed Apartment class.

+ +
+
+ + diff --git a/16. ARC.playground/Documentation/section-24.html b/16. ARC.playground/Documentation/section-24.html new file mode 100644 index 0000000..068db99 --- /dev/null +++ b/16. ARC.playground/Documentation/section-24.html @@ -0,0 +1,20 @@ + + + + + Section 25 + + + + + + +
+
+

Here is our new tenant and his new apartment.

+

This will create a single strong reference to each:

+ +
+
+ + diff --git a/16. ARC.playground/Documentation/section-26.html b/16. ARC.playground/Documentation/section-26.html new file mode 100644 index 0000000..82c4495 --- /dev/null +++ b/16. ARC.playground/Documentation/section-26.html @@ -0,0 +1,19 @@ + + + + + Section 27 + + + + + + +
+
+

Let's link them up like we did before. Note that this time we're not creating a new strong reference to the NamedTenant so the reference count will remain 1. The FixedApartment however, will have a reference count of 2 (because the NamedTenant will hold a strong reference to it.)

+ +
+
+ + diff --git a/16. ARC.playground/Documentation/section-28.html b/16. ARC.playground/Documentation/section-28.html new file mode 100644 index 0000000..7f64ca3 --- /dev/null +++ b/16. ARC.playground/Documentation/section-28.html @@ -0,0 +1,20 @@ + + + + + Section 29 + + + + + + +
+
+

At this point, we have one strong reference to the NamedTenant and two strong references to FixedApartment.

+

Let's set jerry to nil, which will drop his reference count to 0 causing it to get deallocated. Once this happens, it is also deinitialized.

+ +
+
+ + diff --git a/16. ARC.playground/Documentation/section-30.html b/16. ARC.playground/Documentation/section-30.html new file mode 100644 index 0000000..95c9615 --- /dev/null +++ b/16. ARC.playground/Documentation/section-30.html @@ -0,0 +1,20 @@ + + + + + Section 31 + + + + + + +
+
+

With 'jerry' deallocated, the strong reference it once held to FixedApartment is also cleaned up leaving only one strong reference remaining to the FixedApartment class.

+

If we clear 'number74' then we'll remove the last remaining strong reference:

+ +
+
+ + diff --git a/16. ARC.playground/Documentation/section-32.html b/16. ARC.playground/Documentation/section-32.html new file mode 100644 index 0000000..62e2737 --- /dev/null +++ b/16. ARC.playground/Documentation/section-32.html @@ -0,0 +1,22 @@ + + + + + Section 33 + + + + + + +
+
+

Unowned References

+

Unowned refernces are similar to weak references in that they do not hold a strong reference to an instance. However, the key difference is that if the object the reference is deallocated they will not be set to nil like weak references to. Therefore, it's important to ensure that any unowned references will always have a value. If this were to happen, accessing the unowned reference will trigger a runtime error. In fact, Swift guraantees that your app will crash in this scenario.

+

Unowned references are created using the 'unowned' keyword and they must not be optional.

+

We'll showcase this with a Customer and Credit Card. This is a good example case because a customer may have the credit card, or they may close the account, but once a Credit Card has been created, it will always have a customer.

+ +
+
+ + diff --git a/16. ARC.playground/Documentation/section-34.html b/16. ARC.playground/Documentation/section-34.html new file mode 100644 index 0000000..52f9082 --- /dev/null +++ b/16. ARC.playground/Documentation/section-34.html @@ -0,0 +1,22 @@ + + + + + Section 35 + + + + + + +
+
+

Unowned References and Implicitly Unwrapped Optional Properties

+

We've covered two common scenarios of cyclic references, but there is a third case. Consider the case of a country and its capital city. Unlike the case where a customer may have a credit card, or the case where an apartment may have a tenant, a country will always have a capital city and a capital city will always have a tenant.

+

The solution is to use an unowned property in one class and an implicitly unwrapped optional property in the other class. This allows both properties to be accessed directly (without optional unwrapping) once initialization is complete, while avoiding the reference cycle.

+

Let's see how this is done:

+ +
+
+ + diff --git a/16. ARC.playground/Documentation/section-36.html b/16. ARC.playground/Documentation/section-36.html new file mode 100644 index 0000000..199d9fd --- /dev/null +++ b/16. ARC.playground/Documentation/section-36.html @@ -0,0 +1,19 @@ + + + + + Section 37 + + + + + + +
+
+

We can define a Country with a capital city

+ +
+
+ + diff --git a/16. ARC.playground/Documentation/section-38.html b/16. ARC.playground/Documentation/section-38.html new file mode 100644 index 0000000..3977f00 --- /dev/null +++ b/16. ARC.playground/Documentation/section-38.html @@ -0,0 +1,28 @@ + + + + + Section 39 + + + + + + +
+
+

Here's how and why this works.

+

The relationship between Customer:CreditCard is very similar to the relationship between Country:City. The two key differences are that (1) the country initializes its own city and the country does not need to reference the city through the optional binding or forced unwrapping because the Country defines the city with the implicitly unwrapped optional property (using the exclamation mark on the type annotation (City!).

+

The City uses an unowned Country property in the same way (and for the same reasons) as the CreditCard uses an unowned property of a Customer.

+

The Country still uses an optional (though implicitly unwrapped) for the same reason that the Customer uses an optional to store a CreditCard. If we look at Country's initializer, we see that it initializes a capitalCity by passing 'self' to the City initializer. Normally, an initializer cannot reference its own 'self' until it has fully initialized the object. In this case, the Country can access its own 'self' because once 'name' has been initialized, the object is considered fully initialized. This is the case because 'capitalCity' is an optional.

+

We take this just a step further by declaring 'capitalCity' to be an implicitly unwrapped optinoal property so that we can avoid having to deal with unwrapping 'capitalCity' whenever we want to access it.

+

Strong Reference Cycles for Closures

+

We've seen how classes can reference each other creating a cyclic reference because classes are reference types. However, classes aren't the only way to create a cyclic reference. These problematic references can also happen with closures because they, too, are reference types.

+

This happens when a closure captures an instance of a class (simply because it uses the class reference within the closure) and a class maintains a reference to the closure. Note that the references that a closure captures are automatically strong references.

+

Let's see how this problem can manifest. We'll create a class that represents an HTML element which includes a variable (asHTML) which stores a reference to a closure.

+

Quick note: The asHTML variable is defined as lazy so that it can reference 'self' within the closure. Try removing the 'lazy' and you'll see that you get an error trying to access 'self'. This is an error because we're not allowed to access 'self' during Phase 1 initialization. By making 'asHTML' lazy, we solve this problem by deferring its initialization until later.

+ +
+
+ + diff --git a/16. ARC.playground/Documentation/section-4.html b/16. ARC.playground/Documentation/section-4.html new file mode 100644 index 0000000..4b72297 --- /dev/null +++ b/16. ARC.playground/Documentation/section-4.html @@ -0,0 +1,20 @@ + + + + + Section 5 + + + + + + +
+
+

We now have a single strong reference to a single Person object.

+

If we assign 'person' to another variable or constant, we'll increse the reference conunt by 1 for a total of 2:

+ +
+
+ + diff --git a/16. ARC.playground/Documentation/section-40.html b/16. ARC.playground/Documentation/section-40.html new file mode 100644 index 0000000..0c84a83 --- /dev/null +++ b/16. ARC.playground/Documentation/section-40.html @@ -0,0 +1,19 @@ + + + + + Section 41 + + + + + + +
+
+

Let's use the HTMLElement. We'll make sure we declare it as optional so we can set it to 'nil' later.

+ +
+
+ + diff --git a/16. ARC.playground/Documentation/section-42.html b/16. ARC.playground/Documentation/section-42.html new file mode 100644 index 0000000..d1c4647 --- /dev/null +++ b/16. ARC.playground/Documentation/section-42.html @@ -0,0 +1,20 @@ + + + + + Section 43 + + + + + + +
+
+

At this point, we've created a strong reference cycle between the HTMLElement instance and the asHTML closure because the closure references the object which owns [a reference to] it.

+

We can set paragraph to nil, but the HTMLElement will not get deallocated:

+ +
+
+ + diff --git a/16. ARC.playground/Documentation/section-44.html b/16. ARC.playground/Documentation/section-44.html new file mode 100644 index 0000000..6696019 --- /dev/null +++ b/16. ARC.playground/Documentation/section-44.html @@ -0,0 +1,20 @@ + + + + + Section 45 + + + + + + +
+
+

The solution here is to use a "Closure Capture List" as part of the closure's definition. This essentially allows us to modify the default behavior of closures using strong references for captured instances.

+

Here's how we define a capture list:

+ +
+
+ + diff --git a/16. ARC.playground/Documentation/section-46.html b/16. ARC.playground/Documentation/section-46.html new file mode 100644 index 0000000..d639227 --- /dev/null +++ b/16. ARC.playground/Documentation/section-46.html @@ -0,0 +1,19 @@ + + + + + Section 47 + + + + + + +
+
+

Some closures can used simplified syntax if their parameters are inferred while other closures may not have any parameters. In both cases the method for declaring the capture list doesn't change much. Simply include the capture list followed by the 'in' keyword:

+ +
+
+ + diff --git a/16. ARC.playground/Documentation/section-48.html b/16. ARC.playground/Documentation/section-48.html new file mode 100644 index 0000000..a6ad07a --- /dev/null +++ b/16. ARC.playground/Documentation/section-48.html @@ -0,0 +1,19 @@ + + + + + Section 49 + + + + + + +
+
+

Let's see how we can use this to resolve the HTMLElement problem. We'll create a new class, FixedHTMLElement which is identical to the previous with the exception of the addition of the line: "[unowned self] in"

+ +
+
+ + diff --git a/16. ARC.playground/Documentation/section-50.html b/16. ARC.playground/Documentation/section-50.html new file mode 100644 index 0000000..0410fcb --- /dev/null +++ b/16. ARC.playground/Documentation/section-50.html @@ -0,0 +1,19 @@ + + + + + Section 51 + + + + + + +
+
+

Playgrounds do not allow us to test/prove this, so feel free to plug this into a compiled application to see it in action.

+ +
+
+ + diff --git a/16. ARC.playground/Documentation/section-6.html b/16. ARC.playground/Documentation/section-6.html new file mode 100644 index 0000000..0fe800d --- /dev/null +++ b/16. ARC.playground/Documentation/section-6.html @@ -0,0 +1,19 @@ + + + + + Section 7 + + + + + + +
+
+

With a reference count of 2, we can set our original reference to nil. This will drop our reference count down to 1.

+ +
+
+ + diff --git a/16. ARC.playground/Documentation/section-8.html b/16. ARC.playground/Documentation/section-8.html new file mode 100644 index 0000000..b7c7a75 --- /dev/null +++ b/16. ARC.playground/Documentation/section-8.html @@ -0,0 +1,19 @@ + + + + + Section 9 + + + + + + +
+
+

The copyOfPerson still exists and holds a strong reference to our instance:

+ +
+
+ + diff --git a/16. ARC.playground/Documentation/stylesheet.css b/16. ARC.playground/Documentation/stylesheet.css new file mode 100644 index 0000000..43b0230 --- /dev/null +++ b/16. ARC.playground/Documentation/stylesheet.css @@ -0,0 +1,65 @@ +html { + font-size: 1em; +} + +body { + background-color: #fff; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: normal; + font-size: 1.0em; + line-height: 140%; + padding: 0 2em; +} + +code { + font-family: "Menlo", monospace; + font-size: 0.95em; +} + +section { + padding: 0.5em 0; + -webkit-overflow-scrolling: touch; +} + +a:link { + color: #00f; + text-decoration: none; + border-bottom: 1px solid #00f; +} + +h1 { + font-size: 1.5em; + padding: 0.7em 0 0 0; +} + +h2 { + font-size: 1.2em; + padding: 0.7em 0 0 0; +} + +h3 { + font-size: 1.0em; + padding: 0.7em 0 0 0; +} + +blockquote { + font-size: 0.9em; + margin-left: 0; + padding-left: 2em; + border-left: 8px solid #eee; +} + +.footnotes { + color: #666666; + margin-top: 2em; + font-size: 0.9em; + letter-spacing: -3px; +} + +.footnotes:before { + content: "――――"; +} + +.footnotes ol { + letter-spacing: normal; +} diff --git a/16. ARC.playground/contents.xcplayground b/16. ARC.playground/contents.xcplayground index 4937636..0674f23 100644 --- a/16. ARC.playground/contents.xcplayground +++ b/16. ARC.playground/contents.xcplayground @@ -1,7 +1,107 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file diff --git a/16. ARC.playground/section-1.swift b/16. ARC.playground/section-1.swift index e131215..845fcbc 100644 --- a/16. ARC.playground/section-1.swift +++ b/16. ARC.playground/section-1.swift @@ -1,20 +1,3 @@ -// ------------------------------------------------------------------------------------------------ -// Things to know: -// -// * Automatic Reference Counting allows Swift to track and manage your app's memory usage. It -// automatically frees up memory from unused instances that are no longer in use. -// -// * Reference counting only applies to classes as structures and enumerations are value types. -// -// * Whenever a class instance is stored (to a property, constant or variable) a -// "strong reference" is made. A strong reference ensures that the reference is not deallocated -// for as long as the strong reference remains. -// ------------------------------------------------------------------------------------------------ - -// We can't really see ARC in actino within a Playground, but we can still follow along what -// would normally happen. -// -// We'll start by creating a class to work with class Person { let name: String @@ -22,350 +5,4 @@ class Person { self.name = name } -} - -// We'll want to create a person and then remove our reference to it, which means we'll need to -// use an optional Person type stored in a variable: -var person: Person? = Person(name: "Bill") - -// We now have a single strong reference to a single Person object. -// -// If we assign 'person' to another variable or constant, we'll increse the reference conunt by 1 -// for a total of 2: -var copyOfPerson = person - -// With a reference count of 2, we can set our original reference to nil. This will drop our -// reference count down to 1. -person = nil - -// The copyOfPerson still exists and holds a strong reference to our instance: -copyOfPerson - -// If we clear out this reference, we will drop the reference count once more to 0, causing the -// object to be cleaned up by ARC: -copyOfPerson = nil - -// ------------------------------------------------------------------------------------------------ -// Strong Reference Cycles between class instances -// -// If two classes hold a reference to each other, then they create a "Strong Reference Cycle". -// -// Here's an example of two classes that are capable of holding references to one another, but -// do not do so initially (their references are optional and defaulted to nil): -class Tenant -{ - let name: String - var apartment: Apartment? - - init(name: String) { self.name = name } -} -class Apartment -{ - let number: Int - var tenant: Tenant? - - init (number: Int) { self.number = number } -} - -// We can create a tenant and an apartment which are not associated to each other. After these -// two lines of code, each instance will have a reference count of 1. -var bill: Tenant? = Tenant(name: "Bill") -var number73: Apartment? = Apartment(number: 73) - -// Let's link them up. -// -// This will create a strong reference cycle because each instance will have a reference to the -// other. The end result is that each instance will now have a reference count of 2. For example, -// the two strong references for the Tenant instances are held by 'bill' and the 'tenant' -// property inside the 'number73' apartment. -// -// Note the "!" symbols for forced unwrapping (covered in an earlier section): -bill!.apartment = number73 -number73!.tenant = bill - -// If we try to clean up, the memory will not be deallocated by ARC. Let's follow along what -// actually happens a step at a time. -// -// First, we set 'bill' to be nil, which drops the strong reference count for this instance of -// Tenant down to 1. Since there is still a strong reference held to this instance, it is never -// deallocated (and the deinit() is also never called on that instance of Person.) -bill = nil - -// Next we do the same for 'number73' dropping the strong reference count for this instance of -// Apartment down to 1. Similarly, it is not deallocated or deinitialized. -number73 = nil - -// At this point, we have two instances that still exist in memory, but cannot be cleaned up -// because we don't have any references to them in order to solve the problem. - -// ------------------------------------------------------------------------------------------------ -// Resolving Strong Reference Cycles between Class Instances -// -// Swift provides two methods to resolve strong reference cycles: weak and unowned references. - -// ------------------------------------------------------------------------------------------------ -// Weak references allow an instance to be held without actually having a strong hold on it (and -// hence, not incrementing the reference count for the target object.) -// -// Use weak references when it's OK for a reference to become nil sometime during its lifetime. -// Since the Apartment can have no tenant at some point during its lifetime, a weak reference -// is the right way to go. -// -// Weak references must always be optional types (because they may be required to be nil.) When -// an object holds a weak reference to an object, if that object is ever deallocated, Swift will -// locate all the weak references to it and set those references to nil. -// -// Weak references are declared using the 'weak' keyword. -// -// Let's fix our Apartment class. Note that we only have to break the cycle. It's perfectly -// fine to let the Tenant continue to hold a strong reference to our apartment. We will also -// create a new Tenant class (we'll just give it a new name, "NamedTenant"), but only so that we -// can change the apartment type to reference our fixed Apartment class. -class NamedTenant -{ - let name: String - var apartment: FixedApartment? - - init(name: String) { self.name = name } -} -class FixedApartment -{ - let number: Int - weak var tenant: NamedTenant? - - init (number: Int) { self.number = number } -} - -// Here is our new tenant and his new apartment. -// -// This will create a single strong reference to each: -var jerry: NamedTenant? = NamedTenant(name: "Jerry") -var number74: FixedApartment? = FixedApartment(number: 74) - -// Let's link them up like we did before. Note that this time we're not creating a new strong -// reference to the NamedTenant so the reference count will remain 1. The FixedApartment -// however, will have a reference count of 2 (because the NamedTenant will hold a strong reference -// to it.) -jerry!.apartment = number74 -number74!.tenant = jerry - -// At this point, we have one strong reference to the NamedTenant and two strong references to -// FixedApartment. -// -// Let's set jerry to nil, which will drop his reference count to 0 causing it to get -// deallocated. Once this happens, it is also deinitialized. -jerry = nil - -// With 'jerry' deallocated, the strong reference it once held to FixedApartment is also cleaned -// up leaving only one strong reference remaining to the FixedApartment class. -// -// If we clear 'number74' then we'll remove the last remaining strong reference: -number74 = nil - -// ------------------------------------------------------------------------------------------------ -// Unowned References -// -// Unowned refernces are similar to weak references in that they do not hold a strong reference -// to an instance. However, the key difference is that if the object the reference is deallocated -// they will not be set to nil like weak references to. Therefore, it's important to ensure that -// any unowned references will always have a value. If this were to happen, accessing the unowned -// reference will trigger a runtime error. In fact, Swift guraantees that your app will crash in -// this scenario. -// -// Unowned references are created using the 'unowned' keyword and they must not be optional. -// -// We'll showcase this with a Customer and Credit Card. This is a good example case because a -// customer may have the credit card, or they may close the account, but once a Credit Card -// has been created, it will always have a customer. -class Customer -{ - let name: String - var card: CreditCard? - init (name: String) - { - self.name = name - } -} - -class CreditCard -{ - let number: Int - unowned let customer: Customer - - // Since 'customer' is not optional, it must be set in the initializer - init (number: Int, customer: Customer) - { - self.number = number - self.customer = customer - } -} - -// ------------------------------------------------------------------------------------------------ -// Unowned References and Implicitly Unwrapped Optional Properties -// -// We've covered two common scenarios of cyclic references, but there is a third case. Consider -// the case of a country and its capital city. Unlike the case where a customer may have a credit -// card, or the case where an apartment may have a tenant, a country will always have a capital -// city and a capital city will always have a tenant. -// -// The solution is to use an unowned property in one class and an implicitly unwrapped optional -// property in the other class. This allows both properties to be accessed directly (without -// optional unwrapping) once initialization is complete, while avoiding the reference cycle. -// -// Let's see how this is done: -class Country -{ - let name: String - let capitalCity: City! - - init(name: String, capitalName: String) - { - self.name = name - self.capitalCity = City(name: capitalName, country: self) - } -} - -class City -{ - let name: String - unowned let country: Country - - init(name: String, country: Country) - { - self.name = name - self.country = country - } -} - -// We can define a Country with a capital city -var america = Country(name: "USA", capitalName: "Washington DC") - -// Here's how and why this works. -// -// The relationship between Customer:CreditCard is very similar to the relationship between -// Country:City. The two key differences are that (1) the country initializes its own city and the -// country does not need to reference the city through the optional binding or forced unwrapping -// because the Country defines the city with the implicitly unwrapped optional property (using the -// exclamation mark on the type annotation (City!). -// -// The City uses an unowned Country property in the same way (and for the same reasons) as the -// CreditCard uses an unowned property of a Customer. -// -// The Country still uses an optional (though implicitly unwrapped) for the same reason that the -// Customer uses an optional to store a CreditCard. If we look at Country's initializer, we see -// that it initializes a capitalCity by passing 'self' to the City initializer. Normally, an -// initializer cannot reference its own 'self' until it has fully initialized the object. In this -// case, the Country can access its own 'self' because once 'name' has been initialized, the object -// is considered fully initialized. This is the case because 'capitalCity' is an optional. -// -// We take this just a step further by declaring 'capitalCity' to be an implicitly unwrapped -// optinoal property so that we can avoid having to deal with unwrapping 'capitalCity' whenever we -// want to access it. - -// ------------------------------------------------------------------------------------------------ -// Strong Reference Cycles for Closures -// -// We've seen how classes can reference each other creating a cyclic reference because classes are -// reference types. However, classes aren't the only way to create a cyclic reference. These -// problematic references can also happen with closures because they, too, are reference types. -// -// This happens when a closure captures an instance of a class (simply because it uses the class -// reference within the closure) and a class maintains a reference to the closure. Note that the -// references that a closure captures are automatically strong references. -// -// Let's see how this problem can manifest. We'll create a class that represents an HTML element -// which includes a variable (asHTML) which stores a reference to a closure. -// -// Quick note: The asHTML variable is defined as lazy so that it can reference 'self' within the -// closure. Try removing the 'lazy' and you'll see that you get an error trying to access 'self'. -// This is an error because we're not allowed to access 'self' during Phase 1 initialization. By -// making 'asHTML' lazy, we solve this problem by deferring its initialization until later. -class HTMLElement -{ - let name: String - let text: String? - - lazy var asHTML: () -> String = - { - if let text = self.text - { - return "<\(self.name)>\(text)" - } - else - { - return "<\(self.name) />" - } - } - - init(name: String, text: String? = nil) - { - self.name = name - self.text = text - } -} - -// Let's use the HTMLElement. We'll make sure we declare it as optional so we can set it to 'nil' -// later. -var paragraph: HTMLElement? = HTMLElement(name: "p", text: "Hello, world") -paragraph!.asHTML() - -// At this point, we've created a strong reference cycle between the HTMLElement instance and the -// asHTML closure because the closure references the object which owns [a reference to] it. -// -// We can set paragraph to nil, but the HTMLElement will not get deallocated: -paragraph = nil - -// The solution here is to use a "Closure Capture List" as part of the closure's definition. This -// essentially allows us to modify the default behavior of closures using strong references for -// captured instances. -// -// Here's how we define a capture list: -// -// lazy var someClosure: (Int, String) -> String = -// { -// [unowned self] (index: Int, stringToProcess: String) -> String in -// -// // ... code here ... -// } -// -// Some closures can used simplified syntax if their parameters are inferred while other closures -// may not have any parameters. In both cases the method for declaring the capture list doesn't -// change much. Simply include the capture list followed by the 'in' keyword: -// -// lazy var someClosure: () -> String = -// { -// [unowned self] in -// -// // ... code here ... -// } -// -// Let's see how we can use this to resolve the HTMLElement problem. We'll create a new class, -// FixedHTMLElement which is identical to the previous with the exception of the addition of the -// line: "[unowned self] in" -class FixedHTMLElement -{ - let name: String - let text: String? - - lazy var asHTML: () -> String = - { - [unowned self] in - if let text = self.text - { - return "<\(self.name)>\(text)" - } - else - { - return "<\(self.name) />" - } - } - - init(name: String, text: String? = nil) - { - self.name = name - self.text = text - } -} - -// Playgrounds do not allow us to test/prove this, so feel free to plug this into a compiled -// application to see it in action. +} \ No newline at end of file diff --git a/16. ARC.playground/section-11.swift b/16. ARC.playground/section-11.swift new file mode 100644 index 0000000..b3c1cbf --- /dev/null +++ b/16. ARC.playground/section-11.swift @@ -0,0 +1 @@ +copyOfPerson = nil \ No newline at end of file diff --git a/16. ARC.playground/section-13.swift b/16. ARC.playground/section-13.swift new file mode 100644 index 0000000..caa0419 --- /dev/null +++ b/16. ARC.playground/section-13.swift @@ -0,0 +1,14 @@ +class Tenant +{ + let name: String + var apartment: Apartment? + + init(name: String) { self.name = name } +} +class Apartment +{ + let number: Int + var tenant: Tenant? + + init (number: Int) { self.number = number } +} \ No newline at end of file diff --git a/16. ARC.playground/section-15.swift b/16. ARC.playground/section-15.swift new file mode 100644 index 0000000..228a8ce --- /dev/null +++ b/16. ARC.playground/section-15.swift @@ -0,0 +1,2 @@ +var bill: Tenant? = Tenant(name: "Bill") +var number73: Apartment? = Apartment(number: 73) \ No newline at end of file diff --git a/16. ARC.playground/section-17.swift b/16. ARC.playground/section-17.swift new file mode 100644 index 0000000..c5f00f0 --- /dev/null +++ b/16. ARC.playground/section-17.swift @@ -0,0 +1,2 @@ +bill!.apartment = number73 +number73!.tenant = bill \ No newline at end of file diff --git a/16. ARC.playground/section-19.swift b/16. ARC.playground/section-19.swift new file mode 100644 index 0000000..34c574b --- /dev/null +++ b/16. ARC.playground/section-19.swift @@ -0,0 +1 @@ +bill = nil \ No newline at end of file diff --git a/16. ARC.playground/section-21.swift b/16. ARC.playground/section-21.swift new file mode 100644 index 0000000..80fa20d --- /dev/null +++ b/16. ARC.playground/section-21.swift @@ -0,0 +1 @@ +number73 = nil \ No newline at end of file diff --git a/16. ARC.playground/section-23.swift b/16. ARC.playground/section-23.swift new file mode 100644 index 0000000..1924d13 --- /dev/null +++ b/16. ARC.playground/section-23.swift @@ -0,0 +1,14 @@ +class NamedTenant +{ + let name: String + var apartment: FixedApartment? + + init(name: String) { self.name = name } +} +class FixedApartment +{ + let number: Int + weak var tenant: NamedTenant? + + init (number: Int) { self.number = number } +} \ No newline at end of file diff --git a/16. ARC.playground/section-25.swift b/16. ARC.playground/section-25.swift new file mode 100644 index 0000000..bd5d9ef --- /dev/null +++ b/16. ARC.playground/section-25.swift @@ -0,0 +1,2 @@ +var jerry: NamedTenant? = NamedTenant(name: "Jerry") +var number74: FixedApartment? = FixedApartment(number: 74) \ No newline at end of file diff --git a/16. ARC.playground/section-27.swift b/16. ARC.playground/section-27.swift new file mode 100644 index 0000000..3f306e7 --- /dev/null +++ b/16. ARC.playground/section-27.swift @@ -0,0 +1,2 @@ +jerry!.apartment = number74 +number74!.tenant = jerry \ No newline at end of file diff --git a/16. ARC.playground/section-29.swift b/16. ARC.playground/section-29.swift new file mode 100644 index 0000000..c3966d6 --- /dev/null +++ b/16. ARC.playground/section-29.swift @@ -0,0 +1 @@ +jerry = nil \ No newline at end of file diff --git a/16. ARC.playground/section-3.swift b/16. ARC.playground/section-3.swift new file mode 100644 index 0000000..32b3a75 --- /dev/null +++ b/16. ARC.playground/section-3.swift @@ -0,0 +1 @@ +var person: Person? = Person(name: "Bill") \ No newline at end of file diff --git a/16. ARC.playground/section-31.swift b/16. ARC.playground/section-31.swift new file mode 100644 index 0000000..ee7117e --- /dev/null +++ b/16. ARC.playground/section-31.swift @@ -0,0 +1 @@ +number74 = nil \ No newline at end of file diff --git a/16. ARC.playground/section-33.swift b/16. ARC.playground/section-33.swift new file mode 100644 index 0000000..26d8b30 --- /dev/null +++ b/16. ARC.playground/section-33.swift @@ -0,0 +1,22 @@ +class Customer +{ + let name: String + var card: CreditCard? + init (name: String) + { + self.name = name + } +} + +class CreditCard +{ + let number: Int + unowned let customer: Customer + + // Since 'customer' is not optional, it must be set in the initializer + init (number: Int, customer: Customer) + { + self.number = number + self.customer = customer + } +} \ No newline at end of file diff --git a/16. ARC.playground/section-35.swift b/16. ARC.playground/section-35.swift new file mode 100644 index 0000000..2b8a1a8 --- /dev/null +++ b/16. ARC.playground/section-35.swift @@ -0,0 +1,23 @@ +class Country +{ + let name: String + let capitalCity: City! + + init(name: String, capitalName: String) + { + self.name = name + self.capitalCity = City(name: capitalName, country: self) + } +} + +class City +{ + let name: String + unowned let country: Country + + init(name: String, country: Country) + { + self.name = name + self.country = country + } +} \ No newline at end of file diff --git a/16. ARC.playground/section-37.swift b/16. ARC.playground/section-37.swift new file mode 100644 index 0000000..df6f2c8 --- /dev/null +++ b/16. ARC.playground/section-37.swift @@ -0,0 +1 @@ +var america = Country(name: "USA", capitalName: "Washington DC") \ No newline at end of file diff --git a/16. ARC.playground/section-39.swift b/16. ARC.playground/section-39.swift new file mode 100644 index 0000000..0ad0573 --- /dev/null +++ b/16. ARC.playground/section-39.swift @@ -0,0 +1,23 @@ +class HTMLElement +{ + let name: String + let text: String? + + lazy var asHTML: () -> String = + { + if let text = self.text + { + return "<\(self.name)>\(text)" + } + else + { + return "<\(self.name) />" + } + } + + init(name: String, text: String? = nil) + { + self.name = name + self.text = text + } +} \ No newline at end of file diff --git a/16. ARC.playground/section-41.swift b/16. ARC.playground/section-41.swift new file mode 100644 index 0000000..05bb6cc --- /dev/null +++ b/16. ARC.playground/section-41.swift @@ -0,0 +1,2 @@ +var paragraph: HTMLElement? = HTMLElement(name: "p", text: "Hello, world") +paragraph!.asHTML() \ No newline at end of file diff --git a/16. ARC.playground/section-43.swift b/16. ARC.playground/section-43.swift new file mode 100644 index 0000000..980aa4b --- /dev/null +++ b/16. ARC.playground/section-43.swift @@ -0,0 +1 @@ +paragraph = nil \ No newline at end of file diff --git a/16. ARC.playground/section-45.swift b/16. ARC.playground/section-45.swift new file mode 100644 index 0000000..2e29613 --- /dev/null +++ b/16. ARC.playground/section-45.swift @@ -0,0 +1,6 @@ +// lazy var someClosure: (Int, String) -> String = +// { +// [unowned self] (index: Int, stringToProcess: String) -> String in +// +// // ... code here ... +// } \ No newline at end of file diff --git a/16. ARC.playground/section-47.swift b/16. ARC.playground/section-47.swift new file mode 100644 index 0000000..56629d2 --- /dev/null +++ b/16. ARC.playground/section-47.swift @@ -0,0 +1,6 @@ +// lazy var someClosure: () -> String = +// { +// [unowned self] in +// +// // ... code here ... +// } \ No newline at end of file diff --git a/16. ARC.playground/section-49.swift b/16. ARC.playground/section-49.swift new file mode 100644 index 0000000..88349d1 --- /dev/null +++ b/16. ARC.playground/section-49.swift @@ -0,0 +1,24 @@ +class FixedHTMLElement +{ + let name: String + let text: String? + + lazy var asHTML: () -> String = + { + [unowned self] in + if let text = self.text + { + return "<\(self.name)>\(text)" + } + else + { + return "<\(self.name) />" + } + } + + init(name: String, text: String? = nil) + { + self.name = name + self.text = text + } +} \ No newline at end of file diff --git a/16. ARC.playground/section-5.swift b/16. ARC.playground/section-5.swift new file mode 100644 index 0000000..f1d173a --- /dev/null +++ b/16. ARC.playground/section-5.swift @@ -0,0 +1 @@ +var copyOfPerson = person \ No newline at end of file diff --git a/16. ARC.playground/section-7.swift b/16. ARC.playground/section-7.swift new file mode 100644 index 0000000..12d9e59 --- /dev/null +++ b/16. ARC.playground/section-7.swift @@ -0,0 +1 @@ +person = nil \ No newline at end of file diff --git a/16. ARC.playground/section-9.swift b/16. ARC.playground/section-9.swift new file mode 100644 index 0000000..4719968 --- /dev/null +++ b/16. ARC.playground/section-9.swift @@ -0,0 +1 @@ +copyOfPerson \ No newline at end of file diff --git a/17. Optional Chaining.playground/Documentation/section-0.html b/17. Optional Chaining.playground/Documentation/section-0.html new file mode 100644 index 0000000..111b815 --- /dev/null +++ b/17. Optional Chaining.playground/Documentation/section-0.html @@ -0,0 +1,25 @@ + + + + + Section 1 + + + + + + +
+
+

Optional Chaining

+

Things to know:

+
    +
  • Optional Chaining is the process of safely referencing a series of optionals (which contain optionals, which contain optionals, etc.) without having to perform the optional unwrapping checks at each step along the way.
  • +
+
+

Consider a case of forced unwrapping like "someOptional!.someProperty". We already know that this is only safe if we know that the optional will never be nil. For times that we don't know this, we must check the optional before we can reference it. This can become cumbersome for situations where many optionals are chained together as properties of properties. Let's create a series of optionals to show this:

+ +
+
+ + diff --git a/17. Optional Chaining.playground/Documentation/section-10.html b/17. Optional Chaining.playground/Documentation/section-10.html new file mode 100644 index 0000000..0f13bcc --- /dev/null +++ b/17. Optional Chaining.playground/Documentation/section-10.html @@ -0,0 +1,20 @@ + + + + + Section 11 + + + + + + +
+
+

The solusion here is to use optional chaining, which replaces the forced unwrapping "!" with a "?" character. If at any point along this chain, any optional is nil, evaluation is aborted and the entire result becomes nil.

+

Let's see this entire chain, one step at a time working backwards. This let's us see exactly where the optional chain becomes a valid value:

+ +
+
+ + diff --git a/17. Optional Chaining.playground/Documentation/section-12.html b/17. Optional Chaining.playground/Documentation/section-12.html new file mode 100644 index 0000000..1bd39c0 --- /dev/null +++ b/17. Optional Chaining.playground/Documentation/section-12.html @@ -0,0 +1,19 @@ + + + + + Section 13 + + + + + + +
+
+

Optional chaining can be mixed with non-optionals as well as forced unwrapping and other contexts (subcripts, function calls, return values, etc.) We won't bother to create the series of classes and instances for the following example, but it should serve as a valid example of how to use optional chaining syntax in various situations. Given the proper context this line of code would compile and run as expected.

+ +
+
+ + diff --git a/17. Optional Chaining.playground/Documentation/section-14.html b/17. Optional Chaining.playground/Documentation/section-14.html new file mode 100644 index 0000000..6184b2d --- /dev/null +++ b/17. Optional Chaining.playground/Documentation/section-14.html @@ -0,0 +1,19 @@ + + + + + Section 15 + + + + + + +
+
+

This line could be read as: optional person's second optional music preference's favorite song's optional artist's name.

+ +
+
+ + diff --git a/17. Optional Chaining.playground/Documentation/section-2.html b/17. Optional Chaining.playground/Documentation/section-2.html new file mode 100644 index 0000000..cc43bb6 --- /dev/null +++ b/17. Optional Chaining.playground/Documentation/section-2.html @@ -0,0 +1,19 @@ + + + + + Section 3 + + + + + + +
+
+

Here, we'll create a working chain:

+ +
+
+ + diff --git a/17. Optional Chaining.playground/Documentation/section-4.html b/17. Optional Chaining.playground/Documentation/section-4.html new file mode 100644 index 0000000..17efa85 --- /dev/null +++ b/17. Optional Chaining.playground/Documentation/section-4.html @@ -0,0 +1,19 @@ + + + + + Section 5 + + + + + + +
+
+

We can access this chain with forced unwrapping. In this controlled environment, this will work but it's probably not advisable in a real world situation unless you're sure that each member of the chain is sure to never be nil.

+ +
+
+ + diff --git a/17. Optional Chaining.playground/Documentation/section-6.html b/17. Optional Chaining.playground/Documentation/section-6.html new file mode 100644 index 0000000..b1c3157 --- /dev/null +++ b/17. Optional Chaining.playground/Documentation/section-6.html @@ -0,0 +1,19 @@ + + + + + Section 7 + + + + + + +
+
+

Let's break the chain, removing the user's music preferences:

+ +
+
+ + diff --git a/17. Optional Chaining.playground/Documentation/section-8.html b/17. Optional Chaining.playground/Documentation/section-8.html new file mode 100644 index 0000000..f3bb358 --- /dev/null +++ b/17. Optional Chaining.playground/Documentation/section-8.html @@ -0,0 +1,20 @@ + + + + + Section 9 + + + + + + +
+
+

With a broken chain, if we try to reference the arist like before, we will get a runtime error.

+

The following line will compile, but will crash:

+ +
+
+ + diff --git a/17. Optional Chaining.playground/Documentation/stylesheet.css b/17. Optional Chaining.playground/Documentation/stylesheet.css new file mode 100644 index 0000000..43b0230 --- /dev/null +++ b/17. Optional Chaining.playground/Documentation/stylesheet.css @@ -0,0 +1,65 @@ +html { + font-size: 1em; +} + +body { + background-color: #fff; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: normal; + font-size: 1.0em; + line-height: 140%; + padding: 0 2em; +} + +code { + font-family: "Menlo", monospace; + font-size: 0.95em; +} + +section { + padding: 0.5em 0; + -webkit-overflow-scrolling: touch; +} + +a:link { + color: #00f; + text-decoration: none; + border-bottom: 1px solid #00f; +} + +h1 { + font-size: 1.5em; + padding: 0.7em 0 0 0; +} + +h2 { + font-size: 1.2em; + padding: 0.7em 0 0 0; +} + +h3 { + font-size: 1.0em; + padding: 0.7em 0 0 0; +} + +blockquote { + font-size: 0.9em; + margin-left: 0; + padding-left: 2em; + border-left: 8px solid #eee; +} + +.footnotes { + color: #666666; + margin-top: 2em; + font-size: 0.9em; + letter-spacing: -3px; +} + +.footnotes:before { + content: "――――"; +} + +.footnotes ol { + letter-spacing: normal; +} diff --git a/17. Optional Chaining.playground/contents.xcplayground b/17. Optional Chaining.playground/contents.xcplayground index 4937636..ff08ea4 100644 --- a/17. Optional Chaining.playground/contents.xcplayground +++ b/17. Optional Chaining.playground/contents.xcplayground @@ -1,7 +1,35 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file diff --git a/17. Optional Chaining.playground/section-1.swift b/17. Optional Chaining.playground/section-1.swift index f08addb..57e4f54 100644 --- a/17. Optional Chaining.playground/section-1.swift +++ b/17. Optional Chaining.playground/section-1.swift @@ -1,16 +1,3 @@ -// ------------------------------------------------------------------------------------------------ -// Things to know: -// -// * Optional Chaining is the process of safely referencing a series of optionals (which contain -// optionals, which contain optionals, etc.) without having to perform the optional unwrapping -// checks at each step along the way. -// ------------------------------------------------------------------------------------------------ - -// Consider a case of forced unwrapping like "someOptional!.someProperty". We already know that -// this is only safe if we know that the optional will never be nil. For times that we don't know -// this, we must check the optional before we can reference it. This can become cumbersome for -// situations where many optionals are chained together as properties of properties. Let's create -// a series of optionals to show this: class Artist { let name: String @@ -43,49 +30,4 @@ class Person self.name = name self.musicPreferences = musicPreferences } -} - -// Here, we'll create a working chain: -var someArtist: Artist? = Artist(name: "Somebody with talent") -var favSong: Song? = Song(name: "Something with a beat", artist: someArtist) -var musicPrefs: MusicPreferences? = MusicPreferences(favoriteSong: favSong) -var person: Person? = Person(name: "Bill", musicPreferences: musicPrefs) - -// We can access this chain with forced unwrapping. In this controlled environment, this will work -// but it's probably not advisable in a real world situation unless you're sure that each member -// of the chain is sure to never be nil. -person!.musicPreferences!.favoriteSong!.artist! - -// Let's break the chain, removing the user's music preferences: -if var p = person -{ - p.musicPreferences = nil -} - -// With a broken chain, if we try to reference the arist like before, we will get a runtime error. -// -// The following line will compile, but will crash: -// -// person!.musicPreferences!.favoriteSong!.artist! -// -// The solusion here is to use optional chaining, which replaces the forced unwrapping "!" with -// a "?" character. If at any point along this chain, any optional is nil, evaluation is aborted -// and the entire result becomes nil. -// -// Let's see this entire chain, one step at a time working backwards. This let's us see exactly -// where the optional chain becomes a valid value: -person?.musicPreferences?.favoriteSong?.artist -person?.musicPreferences?.favoriteSong -person?.musicPreferences -person - -// Optional chaining can be mixed with non-optionals as well as forced unwrapping and other -// contexts (subcripts, function calls, return values, etc.) We won't bother to create the -// series of classes and instances for the following example, but it should serve as a valid -// example of how to use optional chaining syntax in various situations. Given the proper context -// this line of code would compile and run as expected. -// -// person?.musicPreferences?[2].getFavoriteSong()?.artist?.name -// -// This line could be read as: optional person's second optional music preference's favorite song's -// optional artist's name. +} \ No newline at end of file diff --git a/17. Optional Chaining.playground/section-11.swift b/17. Optional Chaining.playground/section-11.swift new file mode 100644 index 0000000..d237b0b --- /dev/null +++ b/17. Optional Chaining.playground/section-11.swift @@ -0,0 +1,4 @@ +person?.musicPreferences?.favoriteSong?.artist +person?.musicPreferences?.favoriteSong +person?.musicPreferences +person \ No newline at end of file diff --git a/17. Optional Chaining.playground/section-13.swift b/17. Optional Chaining.playground/section-13.swift new file mode 100644 index 0000000..f548766 --- /dev/null +++ b/17. Optional Chaining.playground/section-13.swift @@ -0,0 +1 @@ +// person?.musicPreferences?[2].getFavoriteSong()?.artist?.name \ No newline at end of file diff --git a/17. Optional Chaining.playground/section-3.swift b/17. Optional Chaining.playground/section-3.swift new file mode 100644 index 0000000..6baab96 --- /dev/null +++ b/17. Optional Chaining.playground/section-3.swift @@ -0,0 +1,4 @@ +var someArtist: Artist? = Artist(name: "Somebody with talent") +var favSong: Song? = Song(name: "Something with a beat", artist: someArtist) +var musicPrefs: MusicPreferences? = MusicPreferences(favoriteSong: favSong) +var person: Person? = Person(name: "Bill", musicPreferences: musicPrefs) \ No newline at end of file diff --git a/17. Optional Chaining.playground/section-5.swift b/17. Optional Chaining.playground/section-5.swift new file mode 100644 index 0000000..b924078 --- /dev/null +++ b/17. Optional Chaining.playground/section-5.swift @@ -0,0 +1 @@ +person!.musicPreferences!.favoriteSong!.artist! \ No newline at end of file diff --git a/17. Optional Chaining.playground/section-7.swift b/17. Optional Chaining.playground/section-7.swift new file mode 100644 index 0000000..28abdcf --- /dev/null +++ b/17. Optional Chaining.playground/section-7.swift @@ -0,0 +1,4 @@ +if var p = person +{ + p.musicPreferences = nil +} \ No newline at end of file diff --git a/17. Optional Chaining.playground/section-9.swift b/17. Optional Chaining.playground/section-9.swift new file mode 100644 index 0000000..fec7d3a --- /dev/null +++ b/17. Optional Chaining.playground/section-9.swift @@ -0,0 +1 @@ +// person!.musicPreferences!.favoriteSong!.artist! \ No newline at end of file diff --git a/18. Type Casting.playground/Documentation/section-0.html b/18. Type Casting.playground/Documentation/section-0.html new file mode 100644 index 0000000..0c46573 --- /dev/null +++ b/18. Type Casting.playground/Documentation/section-0.html @@ -0,0 +1,28 @@ + + + + + Section 1 + + + + + + +
+
+

Type Casting

+

Things to know:

+
    +
  • Type casting allows us to check the type of an instance and/or treat that instance as a +different type from somewhere else in its own hieararchy.
  • +
  • Type casting also allows us to determine if a type conforms to a protocol.
  • +
  • Additionally we can create
  • +
+
+

Let's start by creating a few types to work with:

+ +
+
+ + diff --git a/18. Type Casting.playground/Documentation/section-10.html b/18. Type Casting.playground/Documentation/section-10.html new file mode 100644 index 0000000..954baf7 --- /dev/null +++ b/18. Type Casting.playground/Documentation/section-10.html @@ -0,0 +1,22 @@ + + + + + Section 11 + + + + + + +
+
+

Downcasting

+

As with other languages, downcasting refers to casting an object to a subclass within its hierarchy. This is done with the Type Cast Operator, "as".

+

Because not all objects can be downcasted, the result of a downcast may be nil. Because of this, we have two options for downcasting. We can downcast to an optional using the "as?" operator or downcast with forced unwrapping of the optional with the standard "as" operator. As with forced unwrapping, only do this if you know for certain that the downcast will succeed.

+

Let's see this in action using optional binding and the "as?" operator. Remember that our library has type MediaItem[], so if we cast an element of that array to either a Movie or Song, will will be downcasting the instance.

+ +
+
+ + diff --git a/18. Type Casting.playground/Documentation/section-12.html b/18. Type Casting.playground/Documentation/section-12.html new file mode 100644 index 0000000..c89f751 --- /dev/null +++ b/18. Type Casting.playground/Documentation/section-12.html @@ -0,0 +1,25 @@ + + + + + Section 13 + + + + + + +
+
+

Type Casting for Any and AnyObject

+
    +
  • AnyObject allows us to store an instance to any class type.
  • +
  • Any allows us to store a reference to any type at all, excluding functino types.
  • +
+

We should strive to use Any and AnyObject only when we must (like when an API function returns arrays of values of any object type.) Otherwise, it's important that we use explicit types to conform to Swift's type safety.

+

Let's see AnyObject in action. We'll define an array of type AnyObject[] and populate it with some movies:

+ +
+
+ + diff --git a/18. Type Casting.playground/Documentation/section-14.html b/18. Type Casting.playground/Documentation/section-14.html new file mode 100644 index 0000000..d653dc5 --- /dev/null +++ b/18. Type Casting.playground/Documentation/section-14.html @@ -0,0 +1,20 @@ + + + + + Section 15 + + + + + + +
+
+

Here, we know that someObjects[] only contains Movie instances, so we'll use our forced version of the typecast operator, "as". Note, however, that if somebody modifies the code later and adds an instance of a non-Movie type (which they can do), we'll crash. This is why it's important to limit our use of AnyObject and Any to only those cases where we absolutely need it.

+

Let's see how we would use the someObjects array:

+ +
+
+ + diff --git a/18. Type Casting.playground/Documentation/section-16.html b/18. Type Casting.playground/Documentation/section-16.html new file mode 100644 index 0000000..0125e1c --- /dev/null +++ b/18. Type Casting.playground/Documentation/section-16.html @@ -0,0 +1,19 @@ + + + + + Section 17 + + + + + + +
+
+

Alternatively, we can downcast the array itself rather than each item:

+ +
+
+ + diff --git a/18. Type Casting.playground/Documentation/section-18.html b/18. Type Casting.playground/Documentation/section-18.html new file mode 100644 index 0000000..576a88f --- /dev/null +++ b/18. Type Casting.playground/Documentation/section-18.html @@ -0,0 +1,19 @@ + + + + + Section 19 + + + + + + +
+
+

Finally, we can avoid the additional local variable and performt he downcast right inside the loop structure:

+ +
+
+ + diff --git a/18. Type Casting.playground/Documentation/section-2.html b/18. Type Casting.playground/Documentation/section-2.html new file mode 100644 index 0000000..0d19a41 --- /dev/null +++ b/18. Type Casting.playground/Documentation/section-2.html @@ -0,0 +1,19 @@ + + + + + Section 3 + + + + + + +
+
+

We'll create a library of Movies and Songs. Note that Swift will infer the type of the array to be MediaItem[].

+ +
+
+ + diff --git a/18. Type Casting.playground/Documentation/section-20.html b/18. Type Casting.playground/Documentation/section-20.html new file mode 100644 index 0000000..b4ccf2d --- /dev/null +++ b/18. Type Casting.playground/Documentation/section-20.html @@ -0,0 +1,20 @@ + + + + + Section 21 + + + + + + +
+
+

Any allows us store references to any type at all (not including functions), which can include integers, floating points, strings or objects.

+

Let's see this in action. We'll create an array of type Any[] and fill it with random bits and pieces of stuff:

+ +
+
+ + diff --git a/18. Type Casting.playground/Documentation/section-22.html b/18. Type Casting.playground/Documentation/section-22.html new file mode 100644 index 0000000..aa6e6ab --- /dev/null +++ b/18. Type Casting.playground/Documentation/section-22.html @@ -0,0 +1,20 @@ + + + + + Section 23 + + + + + + +
+
+

We can now use a switch statement with a mix of "is" and "as" operators to parse through the things array and get information from it.

+

We'll use a switch statement to check each type (and even include a few where clauses to get a bit more specific about the value inside the type.) Note that when we do this, we use the forced version of the 'as' operator, which is safe in the context of a 'case' because a matched case means that it's guaranteed to be a valid value of the given type.

+ +
+
+ + diff --git a/18. Type Casting.playground/Documentation/section-4.html b/18. Type Casting.playground/Documentation/section-4.html new file mode 100644 index 0000000..1de97cb --- /dev/null +++ b/18. Type Casting.playground/Documentation/section-4.html @@ -0,0 +1,20 @@ + + + + + Section 5 + + + + + + +
+
+

Checking type

+

We can check the type of an object very simply with the 'is' operator, also known as the Type Check Operator.

+ +
+
+ + diff --git a/18. Type Casting.playground/Documentation/section-6.html b/18. Type Casting.playground/Documentation/section-6.html new file mode 100644 index 0000000..d7ca2e7 --- /dev/null +++ b/18. Type Casting.playground/Documentation/section-6.html @@ -0,0 +1,19 @@ + + + + + Section 7 + + + + + + +
+
+

Let's see this in action. Lets loop through the library and count up the movies and songs:

+ +
+
+ + diff --git a/18. Type Casting.playground/Documentation/section-8.html b/18. Type Casting.playground/Documentation/section-8.html new file mode 100644 index 0000000..9e0af26 --- /dev/null +++ b/18. Type Casting.playground/Documentation/section-8.html @@ -0,0 +1,19 @@ + + + + + Section 9 + + + + + + +
+
+

Our final Movie and Song counts:

+ +
+
+ + diff --git a/18. Type Casting.playground/Documentation/stylesheet.css b/18. Type Casting.playground/Documentation/stylesheet.css new file mode 100644 index 0000000..43b0230 --- /dev/null +++ b/18. Type Casting.playground/Documentation/stylesheet.css @@ -0,0 +1,65 @@ +html { + font-size: 1em; +} + +body { + background-color: #fff; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: normal; + font-size: 1.0em; + line-height: 140%; + padding: 0 2em; +} + +code { + font-family: "Menlo", monospace; + font-size: 0.95em; +} + +section { + padding: 0.5em 0; + -webkit-overflow-scrolling: touch; +} + +a:link { + color: #00f; + text-decoration: none; + border-bottom: 1px solid #00f; +} + +h1 { + font-size: 1.5em; + padding: 0.7em 0 0 0; +} + +h2 { + font-size: 1.2em; + padding: 0.7em 0 0 0; +} + +h3 { + font-size: 1.0em; + padding: 0.7em 0 0 0; +} + +blockquote { + font-size: 0.9em; + margin-left: 0; + padding-left: 2em; + border-left: 8px solid #eee; +} + +.footnotes { + color: #666666; + margin-top: 2em; + font-size: 0.9em; + letter-spacing: -3px; +} + +.footnotes:before { + content: "――――"; +} + +.footnotes ol { + letter-spacing: normal; +} diff --git a/18. Type Casting.playground/contents.xcplayground b/18. Type Casting.playground/contents.xcplayground index 4937636..925673a 100644 --- a/18. Type Casting.playground/contents.xcplayground +++ b/18. Type Casting.playground/contents.xcplayground @@ -1,7 +1,53 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file diff --git a/18. Type Casting.playground/section-1.swift b/18. Type Casting.playground/section-1.swift index 0d8ce0d..d0feec9 100644 --- a/18. Type Casting.playground/section-1.swift +++ b/18. Type Casting.playground/section-1.swift @@ -1,15 +1,3 @@ -// ------------------------------------------------------------------------------------------------ -// Things to know: -// -// * Type casting allows us to check the type of an instance and/or treat that instance as a -// different type from somewhere else in its own hieararchy. -// -// * Type casting also allows us to determine if a type conforms to a protocol. -// -// * Additionally we can create -// ------------------------------------------------------------------------------------------------ - -// Let's start by creating a few types to work with: class MediaItem { var name: String @@ -34,155 +22,4 @@ class Song: MediaItem self.artist = artist super.init(name: name) } -} - -// We'll create a library of Movies and Songs. Note that Swift will infer the type of the array to -// be MediaItem[]. -let library = -[ - Movie(name: "Casablanca", director: "Michael Curtiz"), - Song(name: "Blue Suede Shoes", artist: "Elvis Presley"), - Movie(name: "Citizen Kane", director: "Orson Welles"), - Song(name: "The One And Only", artist: "Chesney Hawkes"), - Song(name: "Never Gunna Give You Up", artist: "Rick Astley") -] - -// ------------------------------------------------------------------------------------------------ -// Checking type -// -// We can check the type of an object very simply with the 'is' operator, also known as the Type -// Check Operator. -library[0] is Movie // true -library[0] is Song // false - -// Let's see this in action. Lets loop through the library and count up the movies and songs: -var movieCount = 0 -var songCount = 0 -for item in library -{ - if item is Movie { ++movieCount } - else if item is Song { ++songCount } -} - -// Our final Movie and Song counts: -movieCount -songCount - -// ------------------------------------------------------------------------------------------------ -// Downcasting -// -// As with other languages, downcasting refers to casting an object to a subclass within its -// hierarchy. This is done with the Type Cast Operator, "as". -// -// Because not all objects can be downcasted, the result of a downcast may be nil. Because of this, -// we have two options for downcasting. We can downcast to an optional using the "as?" operator -// or downcast with forced unwrapping of the optional with the standard "as" operator. As with -// forced unwrapping, only do this if you know for certain that the downcast will succeed. -// -// Let's see this in action using optional binding and the "as?" operator. Remember that our -// library has type MediaItem[], so if we cast an element of that array to either a Movie or Song, -// will will be downcasting the instance. -for item in library -{ - if let movie = item as? Movie - { - "Movie: '\(movie.name)' was directed by \(movie.director)" - } - else if let song = item as? Song - { - "Song: '\(song.name)' was performed by \(song.artist)" - } -} - -// ------------------------------------------------------------------------------------------------ -// Type Casting for Any and AnyObject -// -// * AnyObject allows us to store an instance to any class type. -// -// * Any allows us to store a reference to any type at all, excluding functino types. -// -// We should strive to use Any and AnyObject only when we must (like when an API function returns -// arrays of values of any object type.) Otherwise, it's important that we use explicit types to -// conform to Swift's type safety. -// -// Let's see AnyObject in action. We'll define an array of type AnyObject[] and populate it with -// some movies: -let someObjects: [AnyObject] = -[ - Movie(name: "2001: A Space Odyssey", director: "Stanley Kubrick"), - Movie(name: "Moon", director: "Duncan Jones"), - Movie(name: "Alien", director: "Ridley Scott"), -] - -// Here, we know that someObjects[] only contains Movie instances, so we'll use our forced version -// of the typecast operator, "as". Note, however, that if somebody modifies the code later and adds -// an instance of a non-Movie type (which they can do), we'll crash. This is why it's important -// to limit our use of AnyObject and Any to only those cases where we absolutely need it. -// -// Let's see how we would use the someObjects array: -for object: AnyObject in someObjects -{ - let movie = object as Movie - "Movie: '\(movie.name)' was directed by \(movie.director)" -} - -// Alternatively, we can downcast the array itself rather than each item: -var someMovies = someObjects as [Movie] -for movie in someMovies -{ - "Movie: '\(movie.name)' was directed by \(movie.director)" -} - -// Finally, we can avoid the additional local variable and performt he downcast right inside -// the loop structure: -for movie in someObjects as [Movie] -{ - "Movie: '\(movie.name)' was directed by \(movie.director)" -} - -// Any allows us store references to any type at all (not including functions), which can include -// integers, floating points, strings or objects. -// -// Let's see this in action. We'll create an array of type Any[] and fill it with random bits and -// pieces of stuff: -var things = [Any]() - -things.append(0) -things.append(0.0) -things.append(42) -things.append(3.14159) -things.append("Hello") -things.append((3.0, 5.0)) -things.append(Movie(name: "Ghostbusters", director: "Ivan Reitman")) - -// We can now use a switch statement with a mix of "is" and "as" operators to parse through the -// things array and get information from it. -// -// We'll use a switch statement to check each type (and even include a few where clauses to get a -// bit more specific about the value inside the type.) Note that when we do this, we use the forced -// version of the 'as' operator, which is safe in the context of a 'case' because a matched case -// means that it's guaranteed to be a valid value of the given type. -for thing in things -{ - switch thing - { - case 0 as Int: - "zero as an Int" - case 0 as Double: - "zero as a Double" - case let someInt as Int: - "an integer value of \(someInt)" - case let someDouble as Double where someDouble > 0: - "a positive Double value of \(someDouble)" - case is Double: - "some other double that I don't want to print" - case let someString as String: - "a string value of \(someString)" - case let (x, y) as (Double, Double): - "a Tuple used to store an X/Y floating point coordinate: \(x), \(y)" - case let movie as Movie: - "A movie called '\(movie.name)'" - default: - "Something else" - } -} +} \ No newline at end of file diff --git a/18. Type Casting.playground/section-11.swift b/18. Type Casting.playground/section-11.swift new file mode 100644 index 0000000..092f51b --- /dev/null +++ b/18. Type Casting.playground/section-11.swift @@ -0,0 +1,11 @@ +for item in library +{ + if let movie = item as? Movie + { + "Movie: '\(movie.name)' was directed by \(movie.director)" + } + else if let song = item as? Song + { + "Song: '\(song.name)' was performed by \(song.artist)" + } +} \ No newline at end of file diff --git a/18. Type Casting.playground/section-13.swift b/18. Type Casting.playground/section-13.swift new file mode 100644 index 0000000..aad2b36 --- /dev/null +++ b/18. Type Casting.playground/section-13.swift @@ -0,0 +1,6 @@ +let someObjects: [AnyObject] = +[ + Movie(name: "2001: A Space Odyssey", director: "Stanley Kubrick"), + Movie(name: "Moon", director: "Duncan Jones"), + Movie(name: "Alien", director: "Ridley Scott"), +] \ No newline at end of file diff --git a/18. Type Casting.playground/section-15.swift b/18. Type Casting.playground/section-15.swift new file mode 100644 index 0000000..5770aa5 --- /dev/null +++ b/18. Type Casting.playground/section-15.swift @@ -0,0 +1,5 @@ +for object: AnyObject in someObjects +{ + let movie = object as Movie + "Movie: '\(movie.name)' was directed by \(movie.director)" +} \ No newline at end of file diff --git a/18. Type Casting.playground/section-17.swift b/18. Type Casting.playground/section-17.swift new file mode 100644 index 0000000..2dc4199 --- /dev/null +++ b/18. Type Casting.playground/section-17.swift @@ -0,0 +1,5 @@ +var someMovies = someObjects as [Movie] +for movie in someMovies +{ + "Movie: '\(movie.name)' was directed by \(movie.director)" +} \ No newline at end of file diff --git a/18. Type Casting.playground/section-19.swift b/18. Type Casting.playground/section-19.swift new file mode 100644 index 0000000..418f0e9 --- /dev/null +++ b/18. Type Casting.playground/section-19.swift @@ -0,0 +1,4 @@ +for movie in someObjects as [Movie] +{ + "Movie: '\(movie.name)' was directed by \(movie.director)" +} \ No newline at end of file diff --git a/18. Type Casting.playground/section-21.swift b/18. Type Casting.playground/section-21.swift new file mode 100644 index 0000000..1e1f4a6 --- /dev/null +++ b/18. Type Casting.playground/section-21.swift @@ -0,0 +1,9 @@ +var things = [Any]() + +things.append(0) +things.append(0.0) +things.append(42) +things.append(3.14159) +things.append("Hello") +things.append((3.0, 5.0)) +things.append(Movie(name: "Ghostbusters", director: "Ivan Reitman")) \ No newline at end of file diff --git a/18. Type Casting.playground/section-23.swift b/18. Type Casting.playground/section-23.swift new file mode 100644 index 0000000..8fc536b --- /dev/null +++ b/18. Type Casting.playground/section-23.swift @@ -0,0 +1,24 @@ +for thing in things +{ + switch thing + { + case 0 as Int: + "zero as an Int" + case 0 as Double: + "zero as a Double" + case let someInt as Int: + "an integer value of \(someInt)" + case let someDouble as Double where someDouble > 0: + "a positive Double value of \(someDouble)" + case is Double: + "some other double that I don't want to print" + case let someString as String: + "a string value of \(someString)" + case let (x, y) as (Double, Double): + "a Tuple used to store an X/Y floating point coordinate: \(x), \(y)" + case let movie as Movie: + "A movie called '\(movie.name)'" + default: + "Something else" + } +} \ No newline at end of file diff --git a/18. Type Casting.playground/section-3.swift b/18. Type Casting.playground/section-3.swift new file mode 100644 index 0000000..f8d7a22 --- /dev/null +++ b/18. Type Casting.playground/section-3.swift @@ -0,0 +1,8 @@ +let library = +[ + Movie(name: "Casablanca", director: "Michael Curtiz"), + Song(name: "Blue Suede Shoes", artist: "Elvis Presley"), + Movie(name: "Citizen Kane", director: "Orson Welles"), + Song(name: "The One And Only", artist: "Chesney Hawkes"), + Song(name: "Never Gunna Give You Up", artist: "Rick Astley") +] \ No newline at end of file diff --git a/18. Type Casting.playground/section-5.swift b/18. Type Casting.playground/section-5.swift new file mode 100644 index 0000000..dabc3b7 --- /dev/null +++ b/18. Type Casting.playground/section-5.swift @@ -0,0 +1,2 @@ +library[0] is Movie // true +library[0] is Song // false \ No newline at end of file diff --git a/18. Type Casting.playground/section-7.swift b/18. Type Casting.playground/section-7.swift new file mode 100644 index 0000000..448a636 --- /dev/null +++ b/18. Type Casting.playground/section-7.swift @@ -0,0 +1,7 @@ +var movieCount = 0 +var songCount = 0 +for item in library +{ + if item is Movie { ++movieCount } + else if item is Song { ++songCount } +} \ No newline at end of file diff --git a/18. Type Casting.playground/section-9.swift b/18. Type Casting.playground/section-9.swift new file mode 100644 index 0000000..dbffb65 --- /dev/null +++ b/18. Type Casting.playground/section-9.swift @@ -0,0 +1,2 @@ +movieCount +songCount \ No newline at end of file diff --git a/19. Nested Types.playground/Documentation/section-0.html b/19. Nested Types.playground/Documentation/section-0.html new file mode 100644 index 0000000..74f79f2 --- /dev/null +++ b/19. Nested Types.playground/Documentation/section-0.html @@ -0,0 +1,28 @@ + + + + + Section 1 + + + + + + +
+
+

Nested Types

+

Things to know:

+
    +
  • Nested types are utility classes and structures that are declared within other classes, structures and enumerations.
  • +
+
+

Let's take a look at how we might define a Blackjack Card using nested types.

+

Each card has a suit (Spades, Hearts, Diamonds, Clubs) and a rank (Ace, King, Queen, etc.) This is a natural use of nested types because the suit and rank enumerations only apply to a Blackjack card and so they are scoped to within that that type.

+

Additionally, since the rank of an Ace can be valued at either an 11 or a 1, we can create a nested structure within the Rank enumeration that allows us to multiple values for a given rank.

+

Let's see how this might be implemented:

+ +
+
+ + diff --git a/19. Nested Types.playground/Documentation/section-2.html b/19. Nested Types.playground/Documentation/section-2.html new file mode 100644 index 0000000..e00c9e0 --- /dev/null +++ b/19. Nested Types.playground/Documentation/section-2.html @@ -0,0 +1,20 @@ + + + + + Section 3 + + + + + + +
+
+

Let's initialize a BlackJack card for the Ace of Spades. Note that BlackjackCard doesn't define any initializers, so we're able to use the default memberwise initializer.

+

Also note that since the initializer knows thet type of each member being initialized (both of which are enumerations) we can use the shorthand method (.Something) for each member's initial value.

+ +
+
+ + diff --git a/19. Nested Types.playground/Documentation/section-4.html b/19. Nested Types.playground/Documentation/section-4.html new file mode 100644 index 0000000..59cec1b --- /dev/null +++ b/19. Nested Types.playground/Documentation/section-4.html @@ -0,0 +1,19 @@ + + + + + Section 5 + + + + + + +
+
+

To access the nested type, we can drill down into the type using type names:

+ +
+
+ + diff --git a/19. Nested Types.playground/Documentation/stylesheet.css b/19. Nested Types.playground/Documentation/stylesheet.css new file mode 100644 index 0000000..43b0230 --- /dev/null +++ b/19. Nested Types.playground/Documentation/stylesheet.css @@ -0,0 +1,65 @@ +html { + font-size: 1em; +} + +body { + background-color: #fff; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: normal; + font-size: 1.0em; + line-height: 140%; + padding: 0 2em; +} + +code { + font-family: "Menlo", monospace; + font-size: 0.95em; +} + +section { + padding: 0.5em 0; + -webkit-overflow-scrolling: touch; +} + +a:link { + color: #00f; + text-decoration: none; + border-bottom: 1px solid #00f; +} + +h1 { + font-size: 1.5em; + padding: 0.7em 0 0 0; +} + +h2 { + font-size: 1.2em; + padding: 0.7em 0 0 0; +} + +h3 { + font-size: 1.0em; + padding: 0.7em 0 0 0; +} + +blockquote { + font-size: 0.9em; + margin-left: 0; + padding-left: 2em; + border-left: 8px solid #eee; +} + +.footnotes { + color: #666666; + margin-top: 2em; + font-size: 0.9em; + letter-spacing: -3px; +} + +.footnotes:before { + content: "――――"; +} + +.footnotes ol { + letter-spacing: normal; +} diff --git a/19. Nested Types.playground/contents.xcplayground b/19. Nested Types.playground/contents.xcplayground index 4937636..96d8b2e 100644 --- a/19. Nested Types.playground/contents.xcplayground +++ b/19. Nested Types.playground/contents.xcplayground @@ -1,7 +1,17 @@ - + - + + + + + + + + + + + + - \ No newline at end of file diff --git a/19. Nested Types.playground/section-1.swift b/19. Nested Types.playground/section-1.swift index 02b8065..38fb0ca 100644 --- a/19. Nested Types.playground/section-1.swift +++ b/19. Nested Types.playground/section-1.swift @@ -1,21 +1,3 @@ -// ------------------------------------------------------------------------------------------------ -// Things to know: -// -// * Nested types are utility classes and structures that are declared within other classes, -// structures and enumerations. -// ------------------------------------------------------------------------------------------------ - -// Let's take a look at how we might define a Blackjack Card using nested types. -// -// Each card has a suit (Spades, Hearts, Diamonds, Clubs) and a rank (Ace, King, Queen, etc.) This -// is a natural use of nested types because the suit and rank enumerations only apply to a -// Blackjack card and so they are scoped to within that that type. -// -// Additionally, since the rank of an Ace can be valued at either an 11 or a 1, we can create -// a nested structure within the Rank enumeration that allows us to multiple values for a given -// rank. -// -// Let's see how this might be implemented: struct BlackjackCard { // Nested Suit enumeration @@ -72,16 +54,4 @@ struct BlackjackCard } return output } -} - -// Let's initialize a BlackJack card for the Ace of Spades. Note that BlackjackCard doesn't define -// any initializers, so we're able to use the default memberwise initializer. -// -// Also note that since the initializer knows thet type of each member being initialized (both of -// which are enumerations) we can use the shorthand method (.Something) for each member's initial -// value. -let theAceOfSpades = BlackjackCard(rank: .Ace, suit: .Spades) -theAceOfSpades.description - -// To access the nested type, we can drill down into the type using type names: -let heartsSymbol = String( BlackjackCard.Suit.Hearts.toRaw() ) +} \ No newline at end of file diff --git a/19. Nested Types.playground/section-3.swift b/19. Nested Types.playground/section-3.swift new file mode 100644 index 0000000..fbe7409 --- /dev/null +++ b/19. Nested Types.playground/section-3.swift @@ -0,0 +1,2 @@ +let theAceOfSpades = BlackjackCard(rank: .Ace, suit: .Spades) +theAceOfSpades.description \ No newline at end of file diff --git a/19. Nested Types.playground/section-5.swift b/19. Nested Types.playground/section-5.swift new file mode 100644 index 0000000..013af30 --- /dev/null +++ b/19. Nested Types.playground/section-5.swift @@ -0,0 +1 @@ +let heartsSymbol = String( BlackjackCard.Suit.Hearts.toRaw() ) \ No newline at end of file diff --git a/1a. The Basics.playground/Documentation/section-0.html b/1a. The Basics.playground/Documentation/section-0.html new file mode 100644 index 0000000..8089aa8 --- /dev/null +++ b/1a. The Basics.playground/Documentation/section-0.html @@ -0,0 +1,30 @@ + + + + + Section 1 + + + + + + +
+
+

The Basics

+

Things to know:

+
    +
  • Swift is Apple's new programming language for iOS and OSX. If you know C or Objective-C, then + these playgrounds should serve as a solid primer for making the switch to Swift.
  • +
  • Some experience programming in a C-like langauge is expected. If not, then I'm sorry but +you're just not the target audience.
  • +
+
+

Constants & Variables

+

These are known as "Stored Values" in Swift

+

Use the let keyword to define a constant

+ +
+
+ + diff --git a/1a. The Basics.playground/Documentation/section-10.html b/1a. The Basics.playground/Documentation/section-10.html new file mode 100644 index 0000000..1a40286 --- /dev/null +++ b/1a. The Basics.playground/Documentation/section-10.html @@ -0,0 +1,19 @@ + + + + + Section 11 + + + + + + +
+
+

You can combine them on a single line with a comma

+ +
+
+ + diff --git a/1a. The Basics.playground/Documentation/section-12.html b/1a. The Basics.playground/Documentation/section-12.html new file mode 100644 index 0000000..2073d7b --- /dev/null +++ b/1a. The Basics.playground/Documentation/section-12.html @@ -0,0 +1,26 @@ + + + + + Section 13 + + + + + + +
+
+

Specifying the type with type annotations

+

The built-in types in Swift are: Int, Double, Float, Bool, String, Array, Dictionary +There are variations of these (like UInt16), but those are the basic types. Note that all of +these types are capitalized.

+

Because of inference (assuming 42 is an Int an "some text" is a String), type annotations are +usually pretty rare in Swift

+

Here's how you specify the type. If we didn't specify the type as Double, tye type would have +been inferred to be Int.

+ +
+
+ + diff --git a/1a. The Basics.playground/Documentation/section-14.html b/1a. The Basics.playground/Documentation/section-14.html new file mode 100644 index 0000000..bde5dd1 --- /dev/null +++ b/1a. The Basics.playground/Documentation/section-14.html @@ -0,0 +1,22 @@ + + + + + Section 15 + + + + + + +
+
+

Constant & Variable names cannot contain any mathematical symbols, arrows private-use (or +invalid) Unicode code points or line-and-box drawing characters. Nor can they begin with a +number. Otherwise, it's open season for naming your variables! (yes, really!)

+

Here are some oddly named, but perfectly valid, constants:

+ +
+
+ + diff --git a/1a. The Basics.playground/Documentation/section-16.html b/1a. The Basics.playground/Documentation/section-16.html new file mode 100644 index 0000000..c3e8f82 --- /dev/null +++ b/1a. The Basics.playground/Documentation/section-16.html @@ -0,0 +1,19 @@ + + + + + Section 17 + + + + + + +
+
+

You can print a value using println (this doesn't do anything in a playground, though)

+ +
+
+ + diff --git a/1a. The Basics.playground/Documentation/section-18.html b/1a. The Basics.playground/Documentation/section-18.html new file mode 100644 index 0000000..43463de --- /dev/null +++ b/1a. The Basics.playground/Documentation/section-18.html @@ -0,0 +1,21 @@ + + + + + Section 19 + + + + + + +
+
+

Since println doesn't work in Playgrounds, we'll just put the raw string on the line +which is an expression that evaluates to itself, printing the result in the right-hand +pane in the playground, like so:

+ +
+
+ + diff --git a/1a. The Basics.playground/Documentation/section-2.html b/1a. The Basics.playground/Documentation/section-2.html new file mode 100644 index 0000000..5871b47 --- /dev/null +++ b/1a. The Basics.playground/Documentation/section-2.html @@ -0,0 +1,20 @@ + + + + + Section 3 + + + + + + +
+
+

Use the var keyword to define a variable

+

Tip: only use variables when you need a stored value that changes. Otherwise, prefer constants.

+ +
+
+ + diff --git a/1a. The Basics.playground/Documentation/section-20.html b/1a. The Basics.playground/Documentation/section-20.html new file mode 100644 index 0000000..5d6f5d6 --- /dev/null +++ b/1a. The Basics.playground/Documentation/section-20.html @@ -0,0 +1,23 @@ + + + + + Section 21 + + + + + + +
+
+
+

A note about variable names

+

As with most languages, you cannot give an identifier (such as a variable or constant name, +class name, etc.) the same name as a keyword. For example, you can't define a constant named "let":

+

The following line of code will not compile:

+ +
+
+ + diff --git a/1a. The Basics.playground/Documentation/section-22.html b/1a. The Basics.playground/Documentation/section-22.html new file mode 100644 index 0000000..9d1b449 --- /dev/null +++ b/1a. The Basics.playground/Documentation/section-22.html @@ -0,0 +1,20 @@ + + + + + Section 23 + + + + + + +
+
+

However, sometimes it would be convenient to do so and Swift provides a means to enable this +by surrounding the identifier with backticks (`). Here's an example:

+ +
+
+ + diff --git a/1a. The Basics.playground/Documentation/section-24.html b/1a. The Basics.playground/Documentation/section-24.html new file mode 100644 index 0000000..639a147 --- /dev/null +++ b/1a. The Basics.playground/Documentation/section-24.html @@ -0,0 +1,19 @@ + + + + + Section 25 + + + + + + +
+
+

We can now use let like any normal variable:

+ +
+
+ + diff --git a/1a. The Basics.playground/Documentation/section-26.html b/1a. The Basics.playground/Documentation/section-26.html new file mode 100644 index 0000000..98bbcb8 --- /dev/null +++ b/1a. The Basics.playground/Documentation/section-26.html @@ -0,0 +1,19 @@ + + + + + Section 27 + + + + + + +
+
+

This works for any keyword:

+ +
+
+ + diff --git a/1a. The Basics.playground/Documentation/section-28.html b/1a. The Basics.playground/Documentation/section-28.html new file mode 100644 index 0000000..ca6c863 --- /dev/null +++ b/1a. The Basics.playground/Documentation/section-28.html @@ -0,0 +1,19 @@ + + + + + Section 29 + + + + + + +
+
+

Additionally, it's important to know that this works on non-colliding identifier names:

+ +
+
+ + diff --git a/1a. The Basics.playground/Documentation/section-30.html b/1a. The Basics.playground/Documentation/section-30.html new file mode 100644 index 0000000..fb7df79 --- /dev/null +++ b/1a. The Basics.playground/Documentation/section-30.html @@ -0,0 +1,19 @@ + + + + + Section 31 + + + + + + +
+
+

Also note that myConstant and myConstant refer to the same constant:

+ +
+
+ + diff --git a/1a. The Basics.playground/Documentation/section-32.html b/1a. The Basics.playground/Documentation/section-32.html new file mode 100644 index 0000000..608e0c6 --- /dev/null +++ b/1a. The Basics.playground/Documentation/section-32.html @@ -0,0 +1,22 @@ + + + + + Section 33 + + + + + + +
+
+
+

Comments

+

You've probably already figured this out, but anything after the "//" is a comment. There's more +to comments, though:

+ +
+
+ + diff --git a/1a. The Basics.playground/Documentation/section-34.html b/1a. The Basics.playground/Documentation/section-34.html new file mode 100644 index 0000000..8586934 --- /dev/null +++ b/1a. The Basics.playground/Documentation/section-34.html @@ -0,0 +1,22 @@ + + + + + Section 35 + + + + + + +
+
+
+

Semicolons

+

Semicolons on the end of a line are optional, but the preferred style for Swift is to not use +them to terminate lines.

+ +
+
+ + diff --git a/1a. The Basics.playground/Documentation/section-36.html b/1a. The Basics.playground/Documentation/section-36.html new file mode 100644 index 0000000..59f6c74 --- /dev/null +++ b/1a. The Basics.playground/Documentation/section-36.html @@ -0,0 +1,20 @@ + + + + + Section 37 + + + + + + +
+
+

However, if you want to put two lines of code on one line, you'll need the semicolon to separate +them.

+ +
+
+ + diff --git a/1a. The Basics.playground/Documentation/section-38.html b/1a. The Basics.playground/Documentation/section-38.html new file mode 100644 index 0000000..ad87440 --- /dev/null +++ b/1a. The Basics.playground/Documentation/section-38.html @@ -0,0 +1,22 @@ + + + + + Section 39 + + + + + + +
+
+
+

Integers

+

There are multiple types of integers. Signed and unsigned with sizes of 8, 16, 32 and 64 bits. +Here are a couple samples:

+ +
+
+ + diff --git a/1a. The Basics.playground/Documentation/section-4.html b/1a. The Basics.playground/Documentation/section-4.html new file mode 100644 index 0000000..ccfd63d --- /dev/null +++ b/1a. The Basics.playground/Documentation/section-4.html @@ -0,0 +1,19 @@ + + + + + Section 5 + + + + + + +
+
+

Constants cannot change. This line wouldn't compile:

+ +
+
+ + diff --git a/1a. The Basics.playground/Documentation/section-40.html b/1a. The Basics.playground/Documentation/section-40.html new file mode 100644 index 0000000..2bd7052 --- /dev/null +++ b/1a. The Basics.playground/Documentation/section-40.html @@ -0,0 +1,23 @@ + + + + + Section 41 + + + + + + +
+
+

There is also Int and UInt, the defaults. These will default to the size of the current +platform's native word size. On 32-bit platforms, Int will be equivalent to Int32/UInt32. On +64-bit platforms it is equivalent to Int64/UInt64.

+

Similarly, there is

+

Tip: For code interoperability, prefer Int over its counterparts.

+ +
+
+ + diff --git a/1a. The Basics.playground/Documentation/section-42.html b/1a. The Basics.playground/Documentation/section-42.html new file mode 100644 index 0000000..12b2d48 --- /dev/null +++ b/1a. The Basics.playground/Documentation/section-42.html @@ -0,0 +1,19 @@ + + + + + Section 43 + + + + + + +
+
+

To find the bounds of any integer, try ".min" or ".max"

+ +
+
+ + diff --git a/1a. The Basics.playground/Documentation/section-44.html b/1a. The Basics.playground/Documentation/section-44.html new file mode 100644 index 0000000..e55ef05 --- /dev/null +++ b/1a. The Basics.playground/Documentation/section-44.html @@ -0,0 +1,21 @@ + + + + + Section 45 + + + + + + +
+
+
+

Floating point numbers

+

Double is a 64-bit floating point numbers and Float is a 32-bit floating point number

+ +
+
+ + diff --git a/1a. The Basics.playground/Documentation/section-46.html b/1a. The Basics.playground/Documentation/section-46.html new file mode 100644 index 0000000..f061632 --- /dev/null +++ b/1a. The Basics.playground/Documentation/section-46.html @@ -0,0 +1,23 @@ + + + + + Section 47 + + + + + + +
+
+
+

Type Safety and Type Inference

+

Swift is a strongly typed language, and as such, every stored value MUST have a type and can +only be used where that specific type is expected.

+

Integer literals are inferred to be Int

+ +
+
+ + diff --git a/1a. The Basics.playground/Documentation/section-48.html b/1a. The Basics.playground/Documentation/section-48.html new file mode 100644 index 0000000..10391a6 --- /dev/null +++ b/1a. The Basics.playground/Documentation/section-48.html @@ -0,0 +1,19 @@ + + + + + Section 49 + + + + + + +
+
+

Floating point literals are always inferred to be Double

+ +
+
+ + diff --git a/1a. The Basics.playground/Documentation/section-50.html b/1a. The Basics.playground/Documentation/section-50.html new file mode 100644 index 0000000..1858768 --- /dev/null +++ b/1a. The Basics.playground/Documentation/section-50.html @@ -0,0 +1,19 @@ + + + + + Section 51 + + + + + + +
+
+

If you want a Float instead, you must use type annotation

+ +
+
+ + diff --git a/1a. The Basics.playground/Documentation/section-52.html b/1a. The Basics.playground/Documentation/section-52.html new file mode 100644 index 0000000..b62ca2d --- /dev/null +++ b/1a. The Basics.playground/Documentation/section-52.html @@ -0,0 +1,19 @@ + + + + + Section 53 + + + + + + +
+
+

String literals are inferred to be String type

+ +
+
+ + diff --git a/1a. The Basics.playground/Documentation/section-54.html b/1a. The Basics.playground/Documentation/section-54.html new file mode 100644 index 0000000..5a51389 --- /dev/null +++ b/1a. The Basics.playground/Documentation/section-54.html @@ -0,0 +1,19 @@ + + + + + Section 55 + + + + + + +
+
+

Here's a bool

+ +
+
+ + diff --git a/1a. The Basics.playground/Documentation/section-56.html b/1a. The Basics.playground/Documentation/section-56.html new file mode 100644 index 0000000..53ed65e --- /dev/null +++ b/1a. The Basics.playground/Documentation/section-56.html @@ -0,0 +1,19 @@ + + + + + Section 57 + + + + + + +
+
+

These lines won't compile because we are specifying a type that doesn't match the given value

+ +
+
+ + diff --git a/1a. The Basics.playground/Documentation/section-58.html b/1a. The Basics.playground/Documentation/section-58.html new file mode 100644 index 0000000..1f55606 --- /dev/null +++ b/1a. The Basics.playground/Documentation/section-58.html @@ -0,0 +1,21 @@ + + + + + Section 59 + + + + + + +
+
+
+

Numeric literals

+

You can specify numbers in a few interesting ways

+ +
+
+ + diff --git a/1a. The Basics.playground/Documentation/section-6.html b/1a. The Basics.playground/Documentation/section-6.html new file mode 100644 index 0000000..9c83744 --- /dev/null +++ b/1a. The Basics.playground/Documentation/section-6.html @@ -0,0 +1,19 @@ + + + + + Section 7 + + + + + + +
+
+

Variables can change:

+ +
+
+ + diff --git a/1a. The Basics.playground/Documentation/section-60.html b/1a. The Basics.playground/Documentation/section-60.html new file mode 100644 index 0000000..2fca177 --- /dev/null +++ b/1a. The Basics.playground/Documentation/section-60.html @@ -0,0 +1,20 @@ + + + + + Section 61 + + + + + + +
+
+

Floating point numbers can be specified in a few different ways as well. Here are a few raw +examples (not assigned to variables):

+ +
+
+ + diff --git a/1a. The Basics.playground/Documentation/section-62.html b/1a. The Basics.playground/Documentation/section-62.html new file mode 100644 index 0000000..9ccbd16 --- /dev/null +++ b/1a. The Basics.playground/Documentation/section-62.html @@ -0,0 +1,19 @@ + + + + + Section 63 + + + + + + +
+
+

We can pad our literals as well:

+ +
+
+ + diff --git a/1a. The Basics.playground/Documentation/section-64.html b/1a. The Basics.playground/Documentation/section-64.html new file mode 100644 index 0000000..9e7802f --- /dev/null +++ b/1a. The Basics.playground/Documentation/section-64.html @@ -0,0 +1,21 @@ + + + + + Section 65 + + + + + + +
+
+

Numeric type conversion

+

I don't need this casino to bring just in case and bring this just anything is just +A number that won't fit in the given type will not compile

+ +
+
+ + diff --git a/1a. The Basics.playground/Documentation/section-66.html b/1a. The Basics.playground/Documentation/section-66.html new file mode 100644 index 0000000..703fb7f --- /dev/null +++ b/1a. The Basics.playground/Documentation/section-66.html @@ -0,0 +1,19 @@ + + + + + Section 67 + + + + + + +
+
+

Since the default type for numeric values is Int, you need to specify a different type

+ +
+
+ + diff --git a/1a. The Basics.playground/Documentation/section-68.html b/1a. The Basics.playground/Documentation/section-68.html new file mode 100644 index 0000000..4e3c3d3 --- /dev/null +++ b/1a. The Basics.playground/Documentation/section-68.html @@ -0,0 +1,19 @@ + + + + + Section 69 + + + + + + +
+
+

This will infer a UInt16 based on the types of both operands

+ +
+
+ + diff --git a/1a. The Basics.playground/Documentation/section-70.html b/1a. The Basics.playground/Documentation/section-70.html new file mode 100644 index 0000000..08dd329 --- /dev/null +++ b/1a. The Basics.playground/Documentation/section-70.html @@ -0,0 +1,19 @@ + + + + + Section 71 + + + + + + +
+
+

Conversions between integer and floating point types must be made explicit

+ +
+
+ + diff --git a/1a. The Basics.playground/Documentation/section-72.html b/1a. The Basics.playground/Documentation/section-72.html new file mode 100644 index 0000000..ae352e5 --- /dev/null +++ b/1a. The Basics.playground/Documentation/section-72.html @@ -0,0 +1,20 @@ + + + + + Section 73 + + + + + + +
+
+

The inverse is also true - conversion from floating point to integer must be explicit

+

Conversions to integer types from floating point simply truncate the fractional part. So

+ +
+
+ + diff --git a/1a. The Basics.playground/Documentation/section-74.html b/1a. The Basics.playground/Documentation/section-74.html new file mode 100644 index 0000000..d422b91 --- /dev/null +++ b/1a. The Basics.playground/Documentation/section-74.html @@ -0,0 +1,20 @@ + + + + + Section 75 + + + + + + +
+
+

Literal numerics work a little differently since the literal values don't have an explicit +type assigned to them. Their type is only inferred at the point they are evaluated.

+ +
+
+ + diff --git a/1a. The Basics.playground/Documentation/section-8.html b/1a. The Basics.playground/Documentation/section-8.html new file mode 100644 index 0000000..6845cf0 --- /dev/null +++ b/1a. The Basics.playground/Documentation/section-8.html @@ -0,0 +1,20 @@ + + + + + Section 9 + + + + + + +
+
+

You also can't redeclare a variable or constant once it has been declared. These lines +won't compile:

+ +
+
+ + diff --git a/1a. The Basics.playground/Documentation/stylesheet.css b/1a. The Basics.playground/Documentation/stylesheet.css new file mode 100644 index 0000000..43b0230 --- /dev/null +++ b/1a. The Basics.playground/Documentation/stylesheet.css @@ -0,0 +1,65 @@ +html { + font-size: 1em; +} + +body { + background-color: #fff; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: normal; + font-size: 1.0em; + line-height: 140%; + padding: 0 2em; +} + +code { + font-family: "Menlo", monospace; + font-size: 0.95em; +} + +section { + padding: 0.5em 0; + -webkit-overflow-scrolling: touch; +} + +a:link { + color: #00f; + text-decoration: none; + border-bottom: 1px solid #00f; +} + +h1 { + font-size: 1.5em; + padding: 0.7em 0 0 0; +} + +h2 { + font-size: 1.2em; + padding: 0.7em 0 0 0; +} + +h3 { + font-size: 1.0em; + padding: 0.7em 0 0 0; +} + +blockquote { + font-size: 0.9em; + margin-left: 0; + padding-left: 2em; + border-left: 8px solid #eee; +} + +.footnotes { + color: #666666; + margin-top: 2em; + font-size: 0.9em; + letter-spacing: -3px; +} + +.footnotes:before { + content: "――――"; +} + +.footnotes ol { + letter-spacing: normal; +} diff --git a/1a. The Basics.playground/contents.xcplayground b/1a. The Basics.playground/contents.xcplayground index 4937636..a4d9610 100644 --- a/1a. The Basics.playground/contents.xcplayground +++ b/1a. The Basics.playground/contents.xcplayground @@ -1,7 +1,157 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file diff --git a/1a. The Basics.playground/section-1.swift b/1a. The Basics.playground/section-1.swift index 1dd68fd..c39f95a 100644 --- a/1a. The Basics.playground/section-1.swift +++ b/1a. The Basics.playground/section-1.swift @@ -1,240 +1 @@ -// ------------------------------------------------------------------------------------------------ -// Things to know: -// -// * Swift is Apple's new programming language for iOS and OSX. If you know C or Objective-C, then -// these playgrounds should serve as a solid primer for making the switch to Swift. -// -// * Some experience programming in a C-like langauge is expected. If not, then I'm sorry but -// you're just not the target audience. -// ------------------------------------------------------------------------------------------------ - -// ------------------------------------------------------------------------------------------------ -// Constants & Variables - These are known as "Stored Values" in Swift - -// Use the 'let' keyword to define a constant -let maximumNumberOfLoginAttempts = 10 - -// Use the 'var' keyword to define a variable -// -// Tip: only use variables when you need a stored value that changes. Otherwise, prefer constants. -var currentLoginAttempt = 0 - -// Constants cannot change. This line wouldn't compile: -// maximumNumberOfLoginAttempts = 9 - -// Variables can change: -currentLoginAttempt += 1 - -// You also can't redeclare a variable or constant once it has been declared. These lines -// won't compile: -// let maximumNumberOfLoginAttempts = 10 -// var currentLoginAttempt = "Some string which is not an Int" - -// You can combine them on a single line with a comma -let a = 10, b = 20, c = 30 -var x = 0.0, y = 0.0, z = 0.0 - -// Specifying the type with type annotations -// -// The built-in types in Swift are: Int, Double, Float, Bool, String, Array, Dictionary -// There are variations of these (like UInt16), but those are the basic types. Note that all of -// these types are capitalized. -// -// Because of inference (assuming 42 is an Int an "some text" is a String), type annotations are -// usually pretty rare in Swift -// -// Here's how you specify the type. If we didn't specify the type as Double, tye type would have -// been inferred to be Int. -var SomeDouble: Double = 4 - -// Constant & Variable names cannot contain any mathematical symbols, arrows private-use (or -// invalid) Unicode code points or line-and-box drawing characters. Nor can they begin with a -// number. Otherwise, it's open season for naming your variables! (yes, really!) -// -// Here are some oddly named, but perfectly valid, constants: -let π = 3.14159 -let 你好 = "你好世界" -let 🐶🐮 = "dogcow" - -// You can print a value using println -let fiveHundred = 500 -println("The current value of fiveHundred is: \(fiveHundred)") - -// Since we're using Playgrounds, we'll just put the raw string on the line which is an expression -// that evaluates to itself, printing the result in the right-hand pane in the playground, like so: -"The current value of fiveHundred is: \(fiveHundred)" - -// ------------------------------------------------------------------------------------------------ -// A note about variable names -// -// As with most languages, you cannot give an identifier (such as a variable or constant name, -// class name, etc.) the same name as a keyword. For example, you can't define a constant named -// "let": -// -// The following line of code will not compile: -// -// let let = 0 -// -// However, sometimes it would be convenient to do so and Swift provides a means to enable this -// by surrounding the identifier with backticks (`). Here's an example: -let `let` = 42.0 - -// We can now use `let` like any normal variable: -x = `let` - -// This works for any keyword: -let `class` = "class" -let `do` = "do" -let `for` = "for" - -// Additionally, it's important to know that this works on non-colliding identifier names: -let `myConstant` = 123.456 - -// Also note that `myConstant` and myConstant refer to the same constant: -myConstant - -// ------------------------------------------------------------------------------------------------ -// Comments -// -// You've probably already figured this out, but anything after the "//" is a comment. There's more -// to comments, though: - -/* This is a comment - that spans multiple lines */ - -// The multi-line comments are handy because they can nest, which allows you to safely comment out -// blocks of code, even if they have multi-line comments in them: - -/* - // Some variable - var someVar = 10 - - /* A function - * - * This is a common way to comment functions, but it makes it difficult to comment out these - * blocks. - */ - func doSomething() - { - return - } -*/ - -// ------------------------------------------------------------------------------------------------ -// Semicolons -// -// Semicolons on the end of a line are optional, but the preferred style for Swift is to not use -// them to terminate lines. -var foo1 = 0 -var foo2 = 0; // optional semicolon - -// However, if you want to put two lines of code on one line, you'll need the semicolon to separate -// them. -foo1 = 1; foo2 = 2 - -// ------------------------------------------------------------------------------------------------ -// Integers -// -// There are multiple types of integers. Signed and unsigned with sizes of 8, 16, 32 and 64 bits. -// Here are a couple samples: -let meaningOfLife: UInt8 = 42 // Unsigned 8-bit integer -let randomNumber: Int32 = -34 // Signed 32-bit integer - -// There is also Int and UInt, the defaults. These will default to the size of the current -// platform's native word size. On 32-bit platforms, Int will be equivalent to Int32/UInt32. On -// 64-bit platforms it is equivalent to Int64/UInt64. -// -// Similarly, there is -// -// Tip: For code interoperability, prefer Int over its counterparts. -let tirePressurePSI = 52 - -// To find the bounds of any integer, try ".min" or ".max" -UInt8.min -UInt8.max -Int32.min -Int32.max - -// ------------------------------------------------------------------------------------------------ -// Floating point numbers -// -// Double is a 64-bit floating point numbers and Float is a 32-bit floating point number -let pi: Double = 3.14159 -let pie: Float = 100 // ... becase it's 100% delicious! - -// ------------------------------------------------------------------------------------------------ -// Type Safety and Type Inference -// -// Swift is a strongly typed language, and as such, every stored value MUST have a type and can -// only be used where that specific type is expected. -// -// Integer literals are inferred to be Int -let someInt = 1234 - -// Floating point literals are always inferred to be Double -let someDouble = 1234.56 - -// If you want a Float instead, you must use type annotation -let someFloat: Float = 1234.56 - -// String literals are inferred to be String type -let someString = "This will be a String" - -// Here's a bool -let someBool = true - -// These lines won't compile because we are specifying a type that doesn't match the given value -// let someBool: Bool = 19 -// let someInteger: Int = "45" -// let someOtherInt: Int = 45.6 - -// ------------------------------------------------------------------------------------------------ -// Numeric literals -// -// You can specify numbers in a few interesting ways -let decimalInteger = 17 -let binaryInteger = 0b10001 // 17 in binary notation -let octalInteger = 0o21 // ...also 17 (Octal, baby!) -let hexInteger = 0x11 // ...and 17 in Hexidecimal - -// Floating point numbers can be specified in a few different ways as well. Here are a few raw -// examples (not assigned to variables): -1.25e2 // Scientific notation -1.25e-2 -0xFp2 // Break this down into "0xF", "p", "2". Read as 15 (0xF) to the power of (p) 2, which is 60 -0xFp-2 -0xC.3p0 - -// We can pad our literals as well: -000123.456 // Zero padding -0__123.456 // Underscores are just ignored - -// Numeric type conversion - -// A number that won't fit in the given type will not compile -// let cannotBeNegative: UInt8 = -1 -// let tooBig: Int8 = Int8.max + 1 - -// Since the default type for numeric values is Int, you need to specify a different type -let simpleInt = 2_000 // Int -let twoThousand: UInt16 = 2_000 // Specified as UInt16 -let one: UInt8 = 1 // Specified as UInt8 - -// This will infer a UInt16 based on the types of both operands -let twoThousandAndOne = twoThousand + UInt16(one) - -// Conversions between integer and floating point types must be made explicit -let three = 3 // Inferred to be Int -let pointOneFourOneFiveNine = 0.14159 // Inferred to be Double -let doublePi = Double(three) + pointOneFourOneFiveNine // Explicit conversion of Int to Double - -// The inverse is also true - conversion from floating point to integer must be explicit -// -// Conversions to integer types from floating point simply truncate the fractional part. So -// doublePi becomes 3 and -doublePi becomes -3 -let integerPi = Int(doublePi) -let negativePi = Int(-doublePi) - -// Literal numerics work a little differently since the literal values don't have an explicit -// type assigned to them. Their type is only inferred at the point they are evaluated. -let someValue = 3 + 0.14159 +let maximumNumberOfLoginAttempts = 10 \ No newline at end of file diff --git a/1a. The Basics.playground/section-11.swift b/1a. The Basics.playground/section-11.swift new file mode 100644 index 0000000..e641f51 --- /dev/null +++ b/1a. The Basics.playground/section-11.swift @@ -0,0 +1,2 @@ +let a = 10, b = 20, c = 30 +var x = 0.0, y = 0.0, z = 0.0 \ No newline at end of file diff --git a/1a. The Basics.playground/section-13.swift b/1a. The Basics.playground/section-13.swift new file mode 100644 index 0000000..b9ef6ef --- /dev/null +++ b/1a. The Basics.playground/section-13.swift @@ -0,0 +1 @@ +var SomeDouble: Double = 4 \ No newline at end of file diff --git a/1a. The Basics.playground/section-15.swift b/1a. The Basics.playground/section-15.swift new file mode 100644 index 0000000..e7960b2 --- /dev/null +++ b/1a. The Basics.playground/section-15.swift @@ -0,0 +1,3 @@ +let π = 3.14159 +let 你好 = "你好世界" +let 🐶🐮 = "dogcow" \ No newline at end of file diff --git a/1a. The Basics.playground/section-17.swift b/1a. The Basics.playground/section-17.swift new file mode 100644 index 0000000..f3a540b --- /dev/null +++ b/1a. The Basics.playground/section-17.swift @@ -0,0 +1,2 @@ +let fiveHundred = 500 +println("The current value of fiveHundred is: \(fiveHundred)") \ No newline at end of file diff --git a/1a. The Basics.playground/section-19.swift b/1a. The Basics.playground/section-19.swift new file mode 100644 index 0000000..0be03e4 --- /dev/null +++ b/1a. The Basics.playground/section-19.swift @@ -0,0 +1 @@ +"The current value of fiveHundred is: \(fiveHundred)" \ No newline at end of file diff --git a/1a. The Basics.playground/section-21.swift b/1a. The Basics.playground/section-21.swift new file mode 100644 index 0000000..196ff1a --- /dev/null +++ b/1a. The Basics.playground/section-21.swift @@ -0,0 +1 @@ +// let let = 0 \ No newline at end of file diff --git a/1a. The Basics.playground/section-23.swift b/1a. The Basics.playground/section-23.swift new file mode 100644 index 0000000..a6981c3 --- /dev/null +++ b/1a. The Basics.playground/section-23.swift @@ -0,0 +1 @@ +let `let` = 42.0 \ No newline at end of file diff --git a/1a. The Basics.playground/section-25.swift b/1a. The Basics.playground/section-25.swift new file mode 100644 index 0000000..d458aa4 --- /dev/null +++ b/1a. The Basics.playground/section-25.swift @@ -0,0 +1 @@ +x = `let` \ No newline at end of file diff --git a/1a. The Basics.playground/section-27.swift b/1a. The Basics.playground/section-27.swift new file mode 100644 index 0000000..f4b70a2 --- /dev/null +++ b/1a. The Basics.playground/section-27.swift @@ -0,0 +1,3 @@ +let `class` = "class" +let `do` = "do" +let `for` = "for" \ No newline at end of file diff --git a/1a. The Basics.playground/section-29.swift b/1a. The Basics.playground/section-29.swift new file mode 100644 index 0000000..99450bf --- /dev/null +++ b/1a. The Basics.playground/section-29.swift @@ -0,0 +1 @@ +let `myConstant` = 123.456 \ No newline at end of file diff --git a/1a. The Basics.playground/section-3.swift b/1a. The Basics.playground/section-3.swift new file mode 100644 index 0000000..3bfd508 --- /dev/null +++ b/1a. The Basics.playground/section-3.swift @@ -0,0 +1 @@ +var currentLoginAttempt = 0 \ No newline at end of file diff --git a/1a. The Basics.playground/section-31.swift b/1a. The Basics.playground/section-31.swift new file mode 100644 index 0000000..2eaa9a5 --- /dev/null +++ b/1a. The Basics.playground/section-31.swift @@ -0,0 +1 @@ +myConstant \ No newline at end of file diff --git a/1a. The Basics.playground/section-33.swift b/1a. The Basics.playground/section-33.swift new file mode 100644 index 0000000..1b25939 --- /dev/null +++ b/1a. The Basics.playground/section-33.swift @@ -0,0 +1,20 @@ +/* This is a comment + that spans multiple lines */ + +// The multi-line comments are handy because they can nest, which allows you to safely comment out +// blocks of code, even if they have multi-line comments in them: + +/* + // Some variable + var someVar = 10 + + /* A function + * + * This is a common way to comment functions, but it makes it difficult to comment out these + * blocks. + */ + func doSomething() + { + return + } +*/ \ No newline at end of file diff --git a/1a. The Basics.playground/section-35.swift b/1a. The Basics.playground/section-35.swift new file mode 100644 index 0000000..5ec424e --- /dev/null +++ b/1a. The Basics.playground/section-35.swift @@ -0,0 +1,2 @@ +var foo1 = 0 +var foo2 = 0; // optional semicolon \ No newline at end of file diff --git a/1a. The Basics.playground/section-37.swift b/1a. The Basics.playground/section-37.swift new file mode 100644 index 0000000..e8b1169 --- /dev/null +++ b/1a. The Basics.playground/section-37.swift @@ -0,0 +1 @@ +foo1 = 1; foo2 = 2 \ No newline at end of file diff --git a/1a. The Basics.playground/section-39.swift b/1a. The Basics.playground/section-39.swift new file mode 100644 index 0000000..ba4a632 --- /dev/null +++ b/1a. The Basics.playground/section-39.swift @@ -0,0 +1,2 @@ +let meaningOfLife: UInt8 = 42 // Unsigned 8-bit integer +let randomNumber: Int32 = -34 // Signed 32-bit integer \ No newline at end of file diff --git a/1a. The Basics.playground/section-41.swift b/1a. The Basics.playground/section-41.swift new file mode 100644 index 0000000..b660a64 --- /dev/null +++ b/1a. The Basics.playground/section-41.swift @@ -0,0 +1 @@ +let tirePressurePSI = 52 \ No newline at end of file diff --git a/1a. The Basics.playground/section-43.swift b/1a. The Basics.playground/section-43.swift new file mode 100644 index 0000000..a779982 --- /dev/null +++ b/1a. The Basics.playground/section-43.swift @@ -0,0 +1,4 @@ +UInt8.min +UInt8.max +Int32.min +Int32.max \ No newline at end of file diff --git a/1a. The Basics.playground/section-45.swift b/1a. The Basics.playground/section-45.swift new file mode 100644 index 0000000..b666cb9 --- /dev/null +++ b/1a. The Basics.playground/section-45.swift @@ -0,0 +1,2 @@ +let pi: Double = 3.14159 +let pie: Float = 100 // ... becase it's 100% delicious! \ No newline at end of file diff --git a/1a. The Basics.playground/section-47.swift b/1a. The Basics.playground/section-47.swift new file mode 100644 index 0000000..39ced39 --- /dev/null +++ b/1a. The Basics.playground/section-47.swift @@ -0,0 +1 @@ +let someInt = 1234 \ No newline at end of file diff --git a/1a. The Basics.playground/section-49.swift b/1a. The Basics.playground/section-49.swift new file mode 100644 index 0000000..65a9f97 --- /dev/null +++ b/1a. The Basics.playground/section-49.swift @@ -0,0 +1 @@ +let someDouble = 1234.56 \ No newline at end of file diff --git a/1a. The Basics.playground/section-5.swift b/1a. The Basics.playground/section-5.swift new file mode 100644 index 0000000..012b354 --- /dev/null +++ b/1a. The Basics.playground/section-5.swift @@ -0,0 +1 @@ +// maximumNumberOfLoginAttempts = 9 \ No newline at end of file diff --git a/1a. The Basics.playground/section-51.swift b/1a. The Basics.playground/section-51.swift new file mode 100644 index 0000000..7d69a70 --- /dev/null +++ b/1a. The Basics.playground/section-51.swift @@ -0,0 +1 @@ +let someFloat: Float = 1234.56 \ No newline at end of file diff --git a/1a. The Basics.playground/section-53.swift b/1a. The Basics.playground/section-53.swift new file mode 100644 index 0000000..67c3dd3 --- /dev/null +++ b/1a. The Basics.playground/section-53.swift @@ -0,0 +1 @@ +let someString = "This will be a String" \ No newline at end of file diff --git a/1a. The Basics.playground/section-55.swift b/1a. The Basics.playground/section-55.swift new file mode 100644 index 0000000..522a2d7 --- /dev/null +++ b/1a. The Basics.playground/section-55.swift @@ -0,0 +1 @@ +let someBool = true \ No newline at end of file diff --git a/1a. The Basics.playground/section-57.swift b/1a. The Basics.playground/section-57.swift new file mode 100644 index 0000000..1440839 --- /dev/null +++ b/1a. The Basics.playground/section-57.swift @@ -0,0 +1,3 @@ +//let someBool: Bool = 19 +//let someInteger: Int = "45" +//let someOtherInt: Int = 45.6 \ No newline at end of file diff --git a/1a. The Basics.playground/section-59.swift b/1a. The Basics.playground/section-59.swift new file mode 100644 index 0000000..09e022f --- /dev/null +++ b/1a. The Basics.playground/section-59.swift @@ -0,0 +1,4 @@ +let decimalInteger = 17 +let binaryInteger = 0b10001 // 17 in binary notation +let octalInteger = 0o21 // ...also 17 (Octal, baby!) +let hexInteger = 0x11 // ...and 17 in Hexidecimal \ No newline at end of file diff --git a/1a. The Basics.playground/section-61.swift b/1a. The Basics.playground/section-61.swift new file mode 100644 index 0000000..b381127 --- /dev/null +++ b/1a. The Basics.playground/section-61.swift @@ -0,0 +1,5 @@ +1.25e2 // Scientific notation +1.25e-2 +0xFp2 // Break this down into "0xF", "p", "2". Read as 15 (0xF) to the power of (p) 2, which is 60 +0xFp-2 +0xC.3p0 \ No newline at end of file diff --git a/1a. The Basics.playground/section-63.swift b/1a. The Basics.playground/section-63.swift new file mode 100644 index 0000000..e3668ae --- /dev/null +++ b/1a. The Basics.playground/section-63.swift @@ -0,0 +1,2 @@ +000123.456 // Zero padding +0__123.456 // Underscores are just ignored \ No newline at end of file diff --git a/1a. The Basics.playground/section-65.swift b/1a. The Basics.playground/section-65.swift new file mode 100644 index 0000000..f4d5295 --- /dev/null +++ b/1a. The Basics.playground/section-65.swift @@ -0,0 +1,2 @@ +// let cannotBeNegative: UInt8 = -1 +// let tooBig: Int8 = Int8.max + 1 \ No newline at end of file diff --git a/1a. The Basics.playground/section-67.swift b/1a. The Basics.playground/section-67.swift new file mode 100644 index 0000000..e065dba --- /dev/null +++ b/1a. The Basics.playground/section-67.swift @@ -0,0 +1,3 @@ +let simpleInt = 2_000 // Int +let twoThousand: UInt16 = 2_000 // Specified as UInt16 +let one: UInt8 = 1 // Specified as UInt8 \ No newline at end of file diff --git a/1a. The Basics.playground/section-69.swift b/1a. The Basics.playground/section-69.swift new file mode 100644 index 0000000..a32c638 --- /dev/null +++ b/1a. The Basics.playground/section-69.swift @@ -0,0 +1 @@ +let twoThousandAndOne = twoThousand + UInt16(one) \ No newline at end of file diff --git a/1a. The Basics.playground/section-7.swift b/1a. The Basics.playground/section-7.swift new file mode 100644 index 0000000..9a68dd8 --- /dev/null +++ b/1a. The Basics.playground/section-7.swift @@ -0,0 +1 @@ +currentLoginAttempt += 1 \ No newline at end of file diff --git a/1a. The Basics.playground/section-71.swift b/1a. The Basics.playground/section-71.swift new file mode 100644 index 0000000..7f8ac38 --- /dev/null +++ b/1a. The Basics.playground/section-71.swift @@ -0,0 +1,3 @@ +let three = 3 // Inferred to be Int +let pointOneFourOneFiveNine = 0.14159 // Inferred to be Double +let doublePi = Double(three) + pointOneFourOneFiveNine // Explicit conversion of Int to Double \ No newline at end of file diff --git a/1a. The Basics.playground/section-73.swift b/1a. The Basics.playground/section-73.swift new file mode 100644 index 0000000..94cd8eb --- /dev/null +++ b/1a. The Basics.playground/section-73.swift @@ -0,0 +1,3 @@ +// doublePi becomes 3 and -doublePi becomes -3 +let integerPi = Int(doublePi) +let negativePi = Int(-doublePi) \ No newline at end of file diff --git a/1a. The Basics.playground/section-75.swift b/1a. The Basics.playground/section-75.swift new file mode 100644 index 0000000..8daf620 --- /dev/null +++ b/1a. The Basics.playground/section-75.swift @@ -0,0 +1 @@ +let someValue = 3 + 0.14159 \ No newline at end of file diff --git a/1a. The Basics.playground/section-9.swift b/1a. The Basics.playground/section-9.swift new file mode 100644 index 0000000..cadcd65 --- /dev/null +++ b/1a. The Basics.playground/section-9.swift @@ -0,0 +1,2 @@ +// let maximumNumberOfLoginAttempts = 10 +// var currentLoginAttempt = "Some string which is not an Int" \ No newline at end of file diff --git a/1a. The Basics.playground/timeline.xctimeline b/1a. The Basics.playground/timeline.xctimeline deleted file mode 100644 index ae188c4..0000000 --- a/1a. The Basics.playground/timeline.xctimeline +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - diff --git a/1b. Type alliases.playground/Documentation/section-0.html b/1b. Type alliases.playground/Documentation/section-0.html new file mode 100644 index 0000000..a5bb7a2 --- /dev/null +++ b/1b. Type alliases.playground/Documentation/section-0.html @@ -0,0 +1,25 @@ + + + + + Section 1 + + + + + + +
+
+

Type Aliases

+

Things to know:

+
    +
  • Type Aliases allow you to provide a different name for types, similar to 'typedef' in C.
  • +
+
+

Create an alias for UInt16 called "AudioSample"

+ +
+
+ + diff --git a/1b. Type alliases.playground/Documentation/section-2.html b/1b. Type alliases.playground/Documentation/section-2.html new file mode 100644 index 0000000..054c625 --- /dev/null +++ b/1b. Type alliases.playground/Documentation/section-2.html @@ -0,0 +1,19 @@ + + + + + Section 3 + + + + + + +
+
+

This actually calls UInt16.min

+ +
+
+ + diff --git a/1b. Type alliases.playground/Documentation/section-4.html b/1b. Type alliases.playground/Documentation/section-4.html new file mode 100644 index 0000000..72efeb3 --- /dev/null +++ b/1b. Type alliases.playground/Documentation/section-4.html @@ -0,0 +1,19 @@ + + + + + Section 5 + + + + + + +
+
+

We can also typealias custom types

+ +
+
+ + diff --git a/1b. Type alliases.playground/Documentation/stylesheet.css b/1b. Type alliases.playground/Documentation/stylesheet.css new file mode 100644 index 0000000..43b0230 --- /dev/null +++ b/1b. Type alliases.playground/Documentation/stylesheet.css @@ -0,0 +1,65 @@ +html { + font-size: 1em; +} + +body { + background-color: #fff; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: normal; + font-size: 1.0em; + line-height: 140%; + padding: 0 2em; +} + +code { + font-family: "Menlo", monospace; + font-size: 0.95em; +} + +section { + padding: 0.5em 0; + -webkit-overflow-scrolling: touch; +} + +a:link { + color: #00f; + text-decoration: none; + border-bottom: 1px solid #00f; +} + +h1 { + font-size: 1.5em; + padding: 0.7em 0 0 0; +} + +h2 { + font-size: 1.2em; + padding: 0.7em 0 0 0; +} + +h3 { + font-size: 1.0em; + padding: 0.7em 0 0 0; +} + +blockquote { + font-size: 0.9em; + margin-left: 0; + padding-left: 2em; + border-left: 8px solid #eee; +} + +.footnotes { + color: #666666; + margin-top: 2em; + font-size: 0.9em; + letter-spacing: -3px; +} + +.footnotes:before { + content: "――――"; +} + +.footnotes ol { + letter-spacing: normal; +} diff --git a/1b. Type alliases.playground/contents.xcplayground b/1b. Type alliases.playground/contents.xcplayground index 4937636..96d8b2e 100644 --- a/1b. Type alliases.playground/contents.xcplayground +++ b/1b. Type alliases.playground/contents.xcplayground @@ -1,7 +1,17 @@ - + - + + + + + + + + + + + + - \ No newline at end of file diff --git a/1b. Type alliases.playground/section-1.swift b/1b. Type alliases.playground/section-1.swift index 4c999bc..f364f5d 100644 --- a/1b. Type alliases.playground/section-1.swift +++ b/1b. Type alliases.playground/section-1.swift @@ -1,22 +1 @@ -// ------------------------------------------------------------------------------------------------ -// Things to know: -// -// * Type Aliases allow you to provide a different name for types, -// similar to 'typedef' in C. -// ------------------------------------------------------------------------------------------------ - -// Create an alias for UInt16 called "AudioSample" -typealias AudioSample = UInt16 - -// This actually calls UInt16.min -var maxAmplituedFound = AudioSample.min - -// We can also typealias custom types -struct MySimpleStruct -{ - static let a = 99 -} - -typealias MyAliasedName = MySimpleStruct -MyAliasedName.a - +typealias AudioSample = UInt16 \ No newline at end of file diff --git a/1b. Type alliases.playground/section-3.swift b/1b. Type alliases.playground/section-3.swift new file mode 100644 index 0000000..8e67c2e --- /dev/null +++ b/1b. Type alliases.playground/section-3.swift @@ -0,0 +1 @@ +var maxAmplituedFound = AudioSample.min \ No newline at end of file diff --git a/1b. Type alliases.playground/section-5.swift b/1b. Type alliases.playground/section-5.swift new file mode 100644 index 0000000..54d031e --- /dev/null +++ b/1b. Type alliases.playground/section-5.swift @@ -0,0 +1,7 @@ +struct MySimpleStruct +{ + static let a = 99 +} + +typealias MyAliasedName = MySimpleStruct +MyAliasedName.a \ No newline at end of file diff --git a/1c. Tuples.playground/Documentation/section-0.html b/1c. Tuples.playground/Documentation/section-0.html new file mode 100644 index 0000000..66d3489 --- /dev/null +++ b/1c. Tuples.playground/Documentation/section-0.html @@ -0,0 +1,27 @@ + + + + + Section 1 + + + + + + +
+
+

Tuples

+

Things to know:

+
    +
  • Tuples are groups of values combined into a single, compound value
  • +
+
+

Defining a Tuple

+

Use parenthesis around the comma-delimited list of values

+

This Tuple doesn't specify types, so it relies on inference

+ +
+
+ + diff --git a/1c. Tuples.playground/Documentation/section-10.html b/1c. Tuples.playground/Documentation/section-10.html new file mode 100644 index 0000000..047d343 --- /dev/null +++ b/1c. Tuples.playground/Documentation/section-10.html @@ -0,0 +1,20 @@ + + + + + Section 11 + + + + + + +
+
+

Named tuples

+

Alternatively, you can name the elements of a Tuple

+ +
+
+ + diff --git a/1c. Tuples.playground/Documentation/section-12.html b/1c. Tuples.playground/Documentation/section-12.html new file mode 100644 index 0000000..8f35989 --- /dev/null +++ b/1c. Tuples.playground/Documentation/section-12.html @@ -0,0 +1,19 @@ + + + + + Section 13 + + + + + + +
+
+

When you name the elements you effectively assign names to their indices, so the dot operator works with names or integers:

+ +
+
+ + diff --git a/1c. Tuples.playground/Documentation/section-14.html b/1c. Tuples.playground/Documentation/section-14.html new file mode 100644 index 0000000..71543f2 --- /dev/null +++ b/1c. Tuples.playground/Documentation/section-14.html @@ -0,0 +1,18 @@ + + + + + Section 15 + + + + + + +
+
+ +
+
+ + diff --git a/1c. Tuples.playground/Documentation/section-2.html b/1c. Tuples.playground/Documentation/section-2.html new file mode 100644 index 0000000..3987b35 --- /dev/null +++ b/1c. Tuples.playground/Documentation/section-2.html @@ -0,0 +1,19 @@ + + + + + Section 3 + + + + + + +
+
+

We can also specify the type in order to avoid inferrence

+ +
+
+ + diff --git a/1c. Tuples.playground/Documentation/section-4.html b/1c. Tuples.playground/Documentation/section-4.html new file mode 100644 index 0000000..d6fafdf --- /dev/null +++ b/1c. Tuples.playground/Documentation/section-4.html @@ -0,0 +1,20 @@ + + + + + Section 5 + + + + + + +
+
+

Decomposing tuples

+

Decomposing tuples looks like this

+ +
+
+ + diff --git a/1c. Tuples.playground/Documentation/section-6.html b/1c. Tuples.playground/Documentation/section-6.html new file mode 100644 index 0000000..197cf9e --- /dev/null +++ b/1c. Tuples.playground/Documentation/section-6.html @@ -0,0 +1,19 @@ + + + + + Section 7 + + + + + + +
+
+

We can also decompose into variables instead of constants, but you probably figured that out

+ +
+
+ + diff --git a/1c. Tuples.playground/Documentation/section-8.html b/1c. Tuples.playground/Documentation/section-8.html new file mode 100644 index 0000000..0ba4ea3 --- /dev/null +++ b/1c. Tuples.playground/Documentation/section-8.html @@ -0,0 +1,19 @@ + + + + + Section 9 + + + + + + +
+
+

You can also access them with the dot operator followed by their index:

+ +
+
+ + diff --git a/1c. Tuples.playground/Documentation/stylesheet.css b/1c. Tuples.playground/Documentation/stylesheet.css new file mode 100644 index 0000000..43b0230 --- /dev/null +++ b/1c. Tuples.playground/Documentation/stylesheet.css @@ -0,0 +1,65 @@ +html { + font-size: 1em; +} + +body { + background-color: #fff; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: normal; + font-size: 1.0em; + line-height: 140%; + padding: 0 2em; +} + +code { + font-family: "Menlo", monospace; + font-size: 0.95em; +} + +section { + padding: 0.5em 0; + -webkit-overflow-scrolling: touch; +} + +a:link { + color: #00f; + text-decoration: none; + border-bottom: 1px solid #00f; +} + +h1 { + font-size: 1.5em; + padding: 0.7em 0 0 0; +} + +h2 { + font-size: 1.2em; + padding: 0.7em 0 0 0; +} + +h3 { + font-size: 1.0em; + padding: 0.7em 0 0 0; +} + +blockquote { + font-size: 0.9em; + margin-left: 0; + padding-left: 2em; + border-left: 8px solid #eee; +} + +.footnotes { + color: #666666; + margin-top: 2em; + font-size: 0.9em; + letter-spacing: -3px; +} + +.footnotes:before { + content: "――――"; +} + +.footnotes ol { + letter-spacing: normal; +} diff --git a/1c. Tuples.playground/contents.xcplayground b/1c. Tuples.playground/contents.xcplayground index 4937636..ff08ea4 100644 --- a/1c. Tuples.playground/contents.xcplayground +++ b/1c. Tuples.playground/contents.xcplayground @@ -1,7 +1,35 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file diff --git a/1c. Tuples.playground/section-1.swift b/1c. Tuples.playground/section-1.swift index 456f8c6..c4e7598 100644 --- a/1c. Tuples.playground/section-1.swift +++ b/1c. Tuples.playground/section-1.swift @@ -1,36 +1 @@ -// ------------------------------------------------------------------------------------------------ -// Things to know: -// -// * Tuples are groups of values combined into a single, compound value -// ------------------------------------------------------------------------------------------------ - -// Defining a Tuple - use parenthesis around the comma-delimited list of values -// -// This Tuple doesn't specify types, so it relies on inference -let httpError404 = (404, "Not found") - -// We can also specify the type in order to avoid inferrence -let someOtherTuple = (Double(100), Bool(false)) - -// Decomposing typles looks like this -let (statusCode, statusMessage) = httpError404 -statusCode -statusMessage - -// We can also decompose into variables instead of constants, but you probably figured that out -var (varStatusCode, varStatusMessage) = httpError404 -varStatusCode -varStatusMessage - -// You can also access them with the dot operator followed by their index: -httpError404.0 -httpError404.1 - -// Alternatively, you can name the elements of a Tuple -let namedTuple = (statusCode: 404, message: "Not found") - -// When you name the elements you effectively assign names to their indices, so the dot operator -// works with names or integers: -namedTuple.statusCode == namedTuple.0 -namedTuple.message == namedTuple.1 - +let httpError404 = (404, "Not found") \ No newline at end of file diff --git a/1c. Tuples.playground/section-11.swift b/1c. Tuples.playground/section-11.swift new file mode 100644 index 0000000..10d4a1f --- /dev/null +++ b/1c. Tuples.playground/section-11.swift @@ -0,0 +1 @@ +let namedTuple = (statusCode: 404, message: "Not found") \ No newline at end of file diff --git a/1c. Tuples.playground/section-13.swift b/1c. Tuples.playground/section-13.swift new file mode 100644 index 0000000..1558cc2 --- /dev/null +++ b/1c. Tuples.playground/section-13.swift @@ -0,0 +1,2 @@ +namedTuple.statusCode == namedTuple.0 +namedTuple.message == namedTuple.1 \ No newline at end of file diff --git a/1c. Tuples.playground/section-3.swift b/1c. Tuples.playground/section-3.swift new file mode 100644 index 0000000..1762a80 --- /dev/null +++ b/1c. Tuples.playground/section-3.swift @@ -0,0 +1,2 @@ +let someOtherTuple = (Double(100), Bool(false)) +let a: (Double, Bool) = (100, false) \ No newline at end of file diff --git a/1c. Tuples.playground/section-5.swift b/1c. Tuples.playground/section-5.swift new file mode 100644 index 0000000..096173b --- /dev/null +++ b/1c. Tuples.playground/section-5.swift @@ -0,0 +1,3 @@ +let (statusCode, statusMessage) = httpError404 +statusCode +statusMessage \ No newline at end of file diff --git a/1c. Tuples.playground/section-7.swift b/1c. Tuples.playground/section-7.swift new file mode 100644 index 0000000..21b9fde --- /dev/null +++ b/1c. Tuples.playground/section-7.swift @@ -0,0 +1,3 @@ +var (varStatusCode, varStatusMessage) = httpError404 +varStatusCode +varStatusMessage \ No newline at end of file diff --git a/1c. Tuples.playground/section-9.swift b/1c. Tuples.playground/section-9.swift new file mode 100644 index 0000000..e7be6b2 --- /dev/null +++ b/1c. Tuples.playground/section-9.swift @@ -0,0 +1,2 @@ +httpError404.0 +httpError404.1 \ No newline at end of file diff --git a/1d. Optionals.playground/Documentation/section-0.html b/1d. Optionals.playground/Documentation/section-0.html new file mode 100644 index 0000000..2dad945 --- /dev/null +++ b/1d. Optionals.playground/Documentation/section-0.html @@ -0,0 +1,26 @@ + + + + + Section 1 + + + + + + +
+
+

Optionals

+

Things to know:

+
    +
  • An optional value is a stored value that can either hold a value or "no value at all"
  • +
  • This is similar to assigning a stored value to "nil" but Optionals work with any type of stored value, including Int, Bool, etc.
  • +
+
+

An optional declaration adds a "?" immediately after the explicit type. The following line defines a value 'someOptional' that can either hold an Int or no value at all. In this case we set an optional Int value to .None (similar to nil)

+ +
+
+ + diff --git a/1d. Optionals.playground/Documentation/section-10.html b/1d. Optionals.playground/Documentation/section-10.html new file mode 100644 index 0000000..4c9e10e --- /dev/null +++ b/1d. Optionals.playground/Documentation/section-10.html @@ -0,0 +1,19 @@ + + + + + Section 11 + + + + + + +
+
+

If we assign it to a constant, the type of that constant will be an Optional Int (Int?)

+ +
+
+ + diff --git a/1d. Optionals.playground/Documentation/section-12.html b/1d. Optionals.playground/Documentation/section-12.html new file mode 100644 index 0000000..7fcf3e8 --- /dev/null +++ b/1d. Optionals.playground/Documentation/section-12.html @@ -0,0 +1,21 @@ + + + + + Section 13 + + + + + + +
+
+

Alternate syntax for Optionals

+

The use of a "?" for the syntax of an optional is syntactic sugar for the use of the Generic Optional type defined in Swift's standard library. We haven't gotten into Generics yet, but let's not let that stop us from learning this little detail.

+

These two lines are of equivalent types:

+ +
+
+ + diff --git a/1d. Optionals.playground/Documentation/section-14.html b/1d. Optionals.playground/Documentation/section-14.html new file mode 100644 index 0000000..490b505 --- /dev/null +++ b/1d. Optionals.playground/Documentation/section-14.html @@ -0,0 +1,21 @@ + + + + + Section 15 + + + + + + +
+
+

Unwrapping

+

The difference between Int and Int? is important. Optionals essentially "wrap" their contents which means that Int and Int? are two different types (with the latter being wrapped in an optional.

+

We can't explicity convert to an Int because that's not the same as an Int?. The following line won't compile:

+ +
+
+ + diff --git a/1d. Optionals.playground/Documentation/section-16.html b/1d. Optionals.playground/Documentation/section-16.html new file mode 100644 index 0000000..33316f7 --- /dev/null +++ b/1d. Optionals.playground/Documentation/section-16.html @@ -0,0 +1,19 @@ + + + + + Section 17 + + + + + + +
+
+

One way to do this is to "force unwrap" the value using the "!" symbol, like this:

+ +
+
+ + diff --git a/1d. Optionals.playground/Documentation/section-18.html b/1d. Optionals.playground/Documentation/section-18.html new file mode 100644 index 0000000..1c935b0 --- /dev/null +++ b/1d. Optionals.playground/Documentation/section-18.html @@ -0,0 +1,19 @@ + + + + + Section 19 + + + + + + +
+
+

Implicit unwrapping isn't very safe because if the optional doesn't hold a value, it will generate a runtime error. To verify that is's safe, you can check the optional with an if statement.

+ +
+
+ + diff --git a/1d. Optionals.playground/Documentation/section-2.html b/1d. Optionals.playground/Documentation/section-2.html new file mode 100644 index 0000000..cd29004 --- /dev/null +++ b/1d. Optionals.playground/Documentation/section-2.html @@ -0,0 +1,20 @@ + + + + + Section 3 + + + + + + +
+
+

Let's try to convert a String to an Int. Using the String's toInt() method, we'll try to convert a string to a numeric value. Since not all strings can be converted to an Integer, the toInt() returns an optional, "Int?". This way we can recognize failed conversions without having to trap exceptions or use other arcane methods to recognize the failure.

+

Here's an optional in action

+ +
+
+ + diff --git a/1d. Optionals.playground/Documentation/section-20.html b/1d. Optionals.playground/Documentation/section-20.html new file mode 100644 index 0000000..525183f --- /dev/null +++ b/1d. Optionals.playground/Documentation/section-20.html @@ -0,0 +1,21 @@ + + + + + Section 21 + + + + + + +
+
+

Optional Binding

+

We can conditionally store the unwrapped value to a stored value if the optional holds a value.

+

In the following block, we'll optionally bind the Int value to a constant named 'intValue'

+ +
+
+ + diff --git a/1d. Optionals.playground/Documentation/section-22.html b/1d. Optionals.playground/Documentation/section-22.html new file mode 100644 index 0000000..fc8fb67 --- /dev/null +++ b/1d. Optionals.playground/Documentation/section-22.html @@ -0,0 +1,19 @@ + + + + + Section 23 + + + + + + +
+
+

We can still use optional binding to bind to another optional value, if we do so explicitly by specifying the type of the stored value that we're binding to.

+ +
+
+ + diff --git a/1d. Optionals.playground/Documentation/section-24.html b/1d. Optionals.playground/Documentation/section-24.html new file mode 100644 index 0000000..aca08d3 --- /dev/null +++ b/1d. Optionals.playground/Documentation/section-24.html @@ -0,0 +1,19 @@ + + + + + Section 25 + + + + + + +
+
+

Setting an optional to 'nil' sets it to be contain "no value"

+ +
+
+ + diff --git a/1d. Optionals.playground/Documentation/section-26.html b/1d. Optionals.playground/Documentation/section-26.html new file mode 100644 index 0000000..9ff73f1 --- /dev/null +++ b/1d. Optionals.playground/Documentation/section-26.html @@ -0,0 +1,19 @@ + + + + + Section 27 + + + + + + +
+
+

Now if we check it, we see that it holds no value:

+ +
+
+ + diff --git a/1d. Optionals.playground/Documentation/section-28.html b/1d. Optionals.playground/Documentation/section-28.html new file mode 100644 index 0000000..40fb6c4 --- /dev/null +++ b/1d. Optionals.playground/Documentation/section-28.html @@ -0,0 +1,19 @@ + + + + + Section 29 + + + + + + +
+
+

We can also try optional binding on an empty optional

+ +
+
+ + diff --git a/1d. Optionals.playground/Documentation/section-30.html b/1d. Optionals.playground/Documentation/section-30.html new file mode 100644 index 0000000..b536b1f --- /dev/null +++ b/1d. Optionals.playground/Documentation/section-30.html @@ -0,0 +1,20 @@ + + + + + Section 31 + + + + + + +
+
+

Because of the introduction of optionals, you can no longer use nil for non-optional variables or constants.

+

The following line will not compile

+ +
+
+ + diff --git a/1d. Optionals.playground/Documentation/section-32.html b/1d. Optionals.playground/Documentation/section-32.html new file mode 100644 index 0000000..c43d9ea --- /dev/null +++ b/1d. Optionals.playground/Documentation/section-32.html @@ -0,0 +1,19 @@ + + + + + Section 33 + + + + + + +
+
+

The following line will compile, because the String is optional

+ +
+
+ + diff --git a/1d. Optionals.playground/Documentation/section-34.html b/1d. Optionals.playground/Documentation/section-34.html new file mode 100644 index 0000000..dd33a37 --- /dev/null +++ b/1d. Optionals.playground/Documentation/section-34.html @@ -0,0 +1,19 @@ + + + + + Section 35 + + + + + + +
+
+

If we create an optional without an initializer, it is automatically initialized to nil. In future sections, we'll learn that all values must be initialized before they are used. Because of this behavior, this variable is considered initialized, even though it holds no value:

+ +
+
+ + diff --git a/1d. Optionals.playground/Documentation/section-36.html b/1d. Optionals.playground/Documentation/section-36.html new file mode 100644 index 0000000..680ac93 --- /dev/null +++ b/1d. Optionals.playground/Documentation/section-36.html @@ -0,0 +1,21 @@ + + + + + Section 37 + + + + + + +
+
+

Another way to implicitly unwrap an optional is during declaration of a new stored value

+

Here, we create an optional that we are pretty sure will always hold a value, so we give Swift permission to automatically unwrap it whenever it needs to.

+

Note the type, "String!"

+ +
+
+ + diff --git a/1d. Optionals.playground/Documentation/section-38.html b/1d. Optionals.playground/Documentation/section-38.html new file mode 100644 index 0000000..63bc77c --- /dev/null +++ b/1d. Optionals.playground/Documentation/section-38.html @@ -0,0 +1,20 @@ + + + + + Section 39 + + + + + + +
+
+

Although assumedString is still an optional (and can be treated as one), it will also automatically unwrap if we try to use it like a normal String.

+

Note that we perform no unwrapping in order to assign the value to a normal String

+ +
+
+ + diff --git a/1d. Optionals.playground/Documentation/section-4.html b/1d. Optionals.playground/Documentation/section-4.html new file mode 100644 index 0000000..99cbfff --- /dev/null +++ b/1d. Optionals.playground/Documentation/section-4.html @@ -0,0 +1,19 @@ + + + + + Section 5 + + + + + + +
+
+

Notice how failedConversion is 'nil', even though it's an Int

+ +
+
+ + diff --git a/1d. Optionals.playground/Documentation/section-40.html b/1d. Optionals.playground/Documentation/section-40.html new file mode 100644 index 0000000..7f8c6da --- /dev/null +++ b/1d. Optionals.playground/Documentation/section-40.html @@ -0,0 +1,19 @@ + + + + + Section 41 + + + + + + +
+
+

Since assumedString is still an optional, we can still set it to nil. This is dangerous to do because we assumed it is always going to hold a value (and we gave permission to automatically unwrap it.) So by doing this, we are taking a risk:

+ +
+
+ + diff --git a/1d. Optionals.playground/Documentation/section-42.html b/1d. Optionals.playground/Documentation/section-42.html new file mode 100644 index 0000000..5b3c4f5 --- /dev/null +++ b/1d. Optionals.playground/Documentation/section-42.html @@ -0,0 +1,20 @@ + + + + + Section 43 + + + + + + +
+
+

BE SURE that your implicitly unwrapped optionals actually hold values!

+

The following line will compile, but will generate a runtime error because of the automatic unwrapping.

+ +
+
+ + diff --git a/1d. Optionals.playground/Documentation/section-44.html b/1d. Optionals.playground/Documentation/section-44.html new file mode 100644 index 0000000..9c75db7 --- /dev/null +++ b/1d. Optionals.playground/Documentation/section-44.html @@ -0,0 +1,19 @@ + + + + + Section 45 + + + + + + +
+
+

Like any other optional, we can still check if it holds a value:

+ +
+
+ + diff --git a/1d. Optionals.playground/Documentation/section-6.html b/1d. Optionals.playground/Documentation/section-6.html new file mode 100644 index 0000000..287d5b1 --- /dev/null +++ b/1d. Optionals.playground/Documentation/section-6.html @@ -0,0 +1,19 @@ + + + + + Section 7 + + + + + + +
+
+

Let's carry on with a successful conversion

+ +
+
+ + diff --git a/1d. Optionals.playground/Documentation/section-8.html b/1d. Optionals.playground/Documentation/section-8.html new file mode 100644 index 0000000..c47cb86 --- /dev/null +++ b/1d. Optionals.playground/Documentation/section-8.html @@ -0,0 +1,19 @@ + + + + + Section 9 + + + + + + +
+
+

This one worked

+ +
+
+ + diff --git a/1d. Optionals.playground/Documentation/stylesheet.css b/1d. Optionals.playground/Documentation/stylesheet.css new file mode 100644 index 0000000..43b0230 --- /dev/null +++ b/1d. Optionals.playground/Documentation/stylesheet.css @@ -0,0 +1,65 @@ +html { + font-size: 1em; +} + +body { + background-color: #fff; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: normal; + font-size: 1.0em; + line-height: 140%; + padding: 0 2em; +} + +code { + font-family: "Menlo", monospace; + font-size: 0.95em; +} + +section { + padding: 0.5em 0; + -webkit-overflow-scrolling: touch; +} + +a:link { + color: #00f; + text-decoration: none; + border-bottom: 1px solid #00f; +} + +h1 { + font-size: 1.5em; + padding: 0.7em 0 0 0; +} + +h2 { + font-size: 1.2em; + padding: 0.7em 0 0 0; +} + +h3 { + font-size: 1.0em; + padding: 0.7em 0 0 0; +} + +blockquote { + font-size: 0.9em; + margin-left: 0; + padding-left: 2em; + border-left: 8px solid #eee; +} + +.footnotes { + color: #666666; + margin-top: 2em; + font-size: 0.9em; + letter-spacing: -3px; +} + +.footnotes:before { + content: "――――"; +} + +.footnotes ol { + letter-spacing: normal; +} diff --git a/1d. Optionals.playground/contents.xcplayground b/1d. Optionals.playground/contents.xcplayground index 4937636..812a6e4 100644 --- a/1d. Optionals.playground/contents.xcplayground +++ b/1d. Optionals.playground/contents.xcplayground @@ -1,7 +1,97 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file diff --git a/1d. Optionals.playground/section-1.swift b/1d. Optionals.playground/section-1.swift index 157c904..0c365fe 100644 --- a/1d. Optionals.playground/section-1.swift +++ b/1d. Optionals.playground/section-1.swift @@ -1,186 +1 @@ -// ------------------------------------------------------------------------------------------------ -// Things to know: -// -// * An optional value is a stored value that can either hold a value or "no value at all" -// -// * This is similar to assigning a stored value to "nil" but Optionals work with any type of -// stored value, including Int, Bool, etc. -// ------------------------------------------------------------------------------------------------ - -// An optional declaration adds a "?" immediately after the explicit type. The following line -// defines a value 'someOptional' that can either hold an Int or no value at all. In this case -// we set an optional Int value to .None (similar to nil) -let someOptional: Int? = .None - -// Let's try to convert a String to an Int -// -// Using the String's toInt() method, we'll try to convert a string to a numeric value. Since not -// all strings can be converted to an Integer, the toInt() returns an optional, "Int?". This way -// we can recognize failed conversions without having to trap exceptions or use other arcane -// methods to recognize the failure. -// -// Here's an optional in action -let notNumber = "abc" -let failedConversion = notNumber.toInt() - -// Notice how failedConversion is 'nil', even though it's an Int -failedConversion - -// Let's carry on with a successful conversion -let possibleNumber = "123" -var optionalConvertedNumber = possibleNumber.toInt() - -// This one worked -optionalConvertedNumber - -// If we assign it to a constant, the type of that constant will be an Optional Int (Int?) -let unwrapped = optionalConvertedNumber // 'unwrapped' is another optional - -// ------------------------------------------------------------------------------------------------ -// Alternate syntax for Optionals -// -// The use of a "?" for the syntax of an optional is syntactic sugar for the use of the Generic -// Optional type defined in Swift's standard library. We haven't gotten into Generics yet, but -// let's not let that stop us from learning this little detail. -// -// These two lines are of equivalent types: -let optionalA: String? = .None -let optionalB: Optional = .None - -// ------------------------------------------------------------------------------------------------ -// Unwrapping -// -// The difference between Int and Int? is important. Optionals essentially "wrap" their contents -// which means that Int and Int? are two different types (with the latter being wrapped in an -// optional. -// -// We can't explicity convert to an Int because that's not the same as an Int?. The following -// line won't compile: -// -// let unwrappedInt: Int = optionalConvertedNumber - -// One way to do this is to "force unwrap" the value using the "!" symbol, like this: -let unwrappedInt = optionalConvertedNumber! - -// Implicit unwrapping isn't very safe because if the optional doesn't hold a value, it will -// generate a runtime error. To verify that is's safe, you can check the optional with an if -// statement. -if optionalConvertedNumber != .None -{ - // It's now safe to force-unwrap because we KNOW it has a value - let anotherUnwrappedInt = optionalConvertedNumber! -} -else -{ - // The optional holds "no value" - "Nothing to see here, go away" -} - -// ------------------------------------------------------------------------------------------------ -// Optional Binding -// -// We can conditionally store the unwrapped value to a stored value if the optional holds a value. -// -// In the following block, we'll optionally bind the Int value to a constant named 'intValue' -if let intValue = optionalConvertedNumber -{ - // No need to use the "!" suffix as intValue is not optional - intValue - - // In fact, since 'intValue' is an Int (not an Int?) we can't use the force-unwrap. This line - // of code won't compile: - // intValue! -} -else -{ - // Note that 'intValue' doesn't exist in this "else" scope - "No value" -} - -// We can still use optional binding to bind to another optional value, if we do so explicitly -// by specifying the type of the stored value that we're binding to. -if let optionalIntValue:Int? = optionalConvertedNumber -{ - // 'optionalIntValue' is still an optional, but it's known to be safe. We can still check - // it here, though, because it's still an optional. If it weren't optional, this if statement - // wouldn't compile: - if optionalIntValue != .None - { - // 'optionalIntValue' is optional, so we still use the force-unwrap here: - "intValue is optional, but has the value \(optionalIntValue!)" - } -} - -// Setting an optional to 'nil' sets it to be contain "no value" -optionalConvertedNumber = nil - -// Now if we check it, we see that it holds no value: -if optionalConvertedNumber != .None -{ - "optionalConvertedNumber holds a value (\(optionalConvertedNumber))! (this should not happen)" -} -else -{ - "optionalConvertedNumber holds no value" -} - -// We can also try optional binding on an empty optional -if let optionalIntValue = optionalConvertedNumber -{ - "optionalIntValue holds a value (\(optionalIntValue))! (this should not happen)" -} -else -{ - "optionalIntValue holds no value" -} - -// Because of the introduction of optionals, you can no longer use nil for non-optional -// variables or constants. -// -// The following line will not compile -// -// var failure: String = nil // Won't compile - -// The following line will compile, because the String is optional -var noString: String? = nil - -// If we create an optional without an initializer, it is automatically initialized to nil. In -// future sections, we'll learn that all values must be initialized before they are used. Because -// of this behavior, this variable is considered initialized, even though it holds no value: -var emptyOptional: String? - -// Another way to implicitly unwrap an optional is during declaration of a new stored value -// -// Here, we create an optional that we are pretty sure will always hold a value, so we give Swift -// permission to automatically unwrap it whenever it needs to. -// -// Note the type, "String!" -var assumedString: String! = "An implicitly unwrapped optional string" - -// Although assumedString is still an optional (and can be treated as one), it will also -// automatically unwrap if we try to use it like a normal String. -// -// Note that we perform no unwrapping in order to assign the value to a normal String -let copyOfAssumedString: String = assumedString - -// Since assumedString is still an optional, we can still set it to nil. This is dangerous to do -// because we assumed it is always going to hold a value (and we gave permission to automatically -// unwrap it.) So by doing this, we are taking a risk: -assumedString = nil - -// BE SURE that your implicitly unwrapped optionals actually hold values! -// -// The following line will compile, but will generate a runtime error because of the automatic -// unwrapping. -// -// let errorString: String = assumedString - -// Like any other optional, we can still check if it holds a value: -if assumedString -{ - "We have a value" -} -else -{ - "No value" -} +let someOptional: Int? = nil \ No newline at end of file diff --git a/1d. Optionals.playground/section-11.swift b/1d. Optionals.playground/section-11.swift new file mode 100644 index 0000000..fe14a16 --- /dev/null +++ b/1d. Optionals.playground/section-11.swift @@ -0,0 +1 @@ +let unwrapped = optionalConvertedNumber // 'unwrapped' is another optional \ No newline at end of file diff --git a/1d. Optionals.playground/section-13.swift b/1d. Optionals.playground/section-13.swift new file mode 100644 index 0000000..21216f4 --- /dev/null +++ b/1d. Optionals.playground/section-13.swift @@ -0,0 +1,2 @@ +let optionalA: String? = .None +let optionalB: Optional = .None \ No newline at end of file diff --git a/1d. Optionals.playground/section-15.swift b/1d. Optionals.playground/section-15.swift new file mode 100644 index 0000000..c601239 --- /dev/null +++ b/1d. Optionals.playground/section-15.swift @@ -0,0 +1 @@ +// let unwrappedInt: Int = optionalConvertedNumber \ No newline at end of file diff --git a/1d. Optionals.playground/section-17.swift b/1d. Optionals.playground/section-17.swift new file mode 100644 index 0000000..8470e56 --- /dev/null +++ b/1d. Optionals.playground/section-17.swift @@ -0,0 +1 @@ +let unwrappedInt = optionalConvertedNumber! \ No newline at end of file diff --git a/1d. Optionals.playground/section-19.swift b/1d. Optionals.playground/section-19.swift new file mode 100644 index 0000000..d065a13 --- /dev/null +++ b/1d. Optionals.playground/section-19.swift @@ -0,0 +1,10 @@ +if optionalConvertedNumber != .None +{ + // It's now safe to force-unwrap because we KNOW it has a value + let anotherUnwrappedInt = optionalConvertedNumber! +} +else +{ + // The optional holds "no value" + "Nothing to see here, go away" +} \ No newline at end of file diff --git a/1d. Optionals.playground/section-21.swift b/1d. Optionals.playground/section-21.swift new file mode 100644 index 0000000..0648c1c --- /dev/null +++ b/1d. Optionals.playground/section-21.swift @@ -0,0 +1,14 @@ +if let intValue = optionalConvertedNumber +{ + // No need to use the "!" suffix as intValue is not optional + intValue + + // In fact, since 'intValue' is an Int (not an Int?) we can't use the force-unwrap. This line + // of code won't compile: + // intValue! +} +else +{ + // Note that 'intValue' doesn't exist in this "else" scope + "No value" +} \ No newline at end of file diff --git a/1d. Optionals.playground/section-23.swift b/1d. Optionals.playground/section-23.swift new file mode 100644 index 0000000..fc6bbf6 --- /dev/null +++ b/1d. Optionals.playground/section-23.swift @@ -0,0 +1,11 @@ +if let optionalIntValue:Int? = optionalConvertedNumber +{ + // 'optionalIntValue' is still an optional, but it's known to be safe. We can still check + // it here, though, because it's still an optional. If it weren't optional, this if statement + // wouldn't compile: + if optionalIntValue != .None + { + // 'optionalIntValue' is optional, so we still use the force-unwrap here: + "intValue is optional, but has the value \(optionalIntValue!)" + } +} \ No newline at end of file diff --git a/1d. Optionals.playground/section-25.swift b/1d. Optionals.playground/section-25.swift new file mode 100644 index 0000000..3c75f74 --- /dev/null +++ b/1d. Optionals.playground/section-25.swift @@ -0,0 +1 @@ +optionalConvertedNumber = nil \ No newline at end of file diff --git a/1d. Optionals.playground/section-27.swift b/1d. Optionals.playground/section-27.swift new file mode 100644 index 0000000..3143308 --- /dev/null +++ b/1d. Optionals.playground/section-27.swift @@ -0,0 +1,8 @@ +if optionalConvertedNumber != .None +{ + "optionalConvertedNumber holds a value (\(optionalConvertedNumber))! (this should not happen)" +} +else +{ + "optionalConvertedNumber holds no value" +} \ No newline at end of file diff --git a/1d. Optionals.playground/section-29.swift b/1d. Optionals.playground/section-29.swift new file mode 100644 index 0000000..880b18e --- /dev/null +++ b/1d. Optionals.playground/section-29.swift @@ -0,0 +1,8 @@ +if let optionalIntValue = optionalConvertedNumber +{ + "optionalIntValue holds a value (\(optionalIntValue))! (this should not happen)" +} +else +{ + "optionalIntValue holds no value" +} \ No newline at end of file diff --git a/1d. Optionals.playground/section-3.swift b/1d. Optionals.playground/section-3.swift new file mode 100644 index 0000000..a24e848 --- /dev/null +++ b/1d. Optionals.playground/section-3.swift @@ -0,0 +1,2 @@ +let notNumber = "abc" +let failedConversion = notNumber.toInt() \ No newline at end of file diff --git a/1d. Optionals.playground/section-31.swift b/1d. Optionals.playground/section-31.swift new file mode 100644 index 0000000..2000986 --- /dev/null +++ b/1d. Optionals.playground/section-31.swift @@ -0,0 +1 @@ +// var failure: String = nil // Won't compile \ No newline at end of file diff --git a/1d. Optionals.playground/section-33.swift b/1d. Optionals.playground/section-33.swift new file mode 100644 index 0000000..a26ad69 --- /dev/null +++ b/1d. Optionals.playground/section-33.swift @@ -0,0 +1 @@ +var noString: String? = nil \ No newline at end of file diff --git a/1d. Optionals.playground/section-35.swift b/1d. Optionals.playground/section-35.swift new file mode 100644 index 0000000..821d354 --- /dev/null +++ b/1d. Optionals.playground/section-35.swift @@ -0,0 +1 @@ +var emptyOptional: String? \ No newline at end of file diff --git a/1d. Optionals.playground/section-37.swift b/1d. Optionals.playground/section-37.swift new file mode 100644 index 0000000..d5d3f16 --- /dev/null +++ b/1d. Optionals.playground/section-37.swift @@ -0,0 +1 @@ +var assumedString: String! = "An implicitly unwrapped optional string" \ No newline at end of file diff --git a/1d. Optionals.playground/section-39.swift b/1d. Optionals.playground/section-39.swift new file mode 100644 index 0000000..a0162da --- /dev/null +++ b/1d. Optionals.playground/section-39.swift @@ -0,0 +1 @@ +let copyOfAssumedString: String = assumedString \ No newline at end of file diff --git a/1d. Optionals.playground/section-41.swift b/1d. Optionals.playground/section-41.swift new file mode 100644 index 0000000..edc5ecc --- /dev/null +++ b/1d. Optionals.playground/section-41.swift @@ -0,0 +1 @@ +assumedString = nil \ No newline at end of file diff --git a/1d. Optionals.playground/section-43.swift b/1d. Optionals.playground/section-43.swift new file mode 100644 index 0000000..ae32b4d --- /dev/null +++ b/1d. Optionals.playground/section-43.swift @@ -0,0 +1 @@ +// let errorString: String = assumedString \ No newline at end of file diff --git a/1d. Optionals.playground/section-45.swift b/1d. Optionals.playground/section-45.swift new file mode 100644 index 0000000..1ab5567 --- /dev/null +++ b/1d. Optionals.playground/section-45.swift @@ -0,0 +1,8 @@ +if (assumedString != nil) +{ + "We have a value" +} +else +{ + "No value" +} \ No newline at end of file diff --git a/1d. Optionals.playground/section-5.swift b/1d. Optionals.playground/section-5.swift new file mode 100644 index 0000000..2d40ec7 --- /dev/null +++ b/1d. Optionals.playground/section-5.swift @@ -0,0 +1 @@ +failedConversion \ No newline at end of file diff --git a/1d. Optionals.playground/section-7.swift b/1d. Optionals.playground/section-7.swift new file mode 100644 index 0000000..829059e --- /dev/null +++ b/1d. Optionals.playground/section-7.swift @@ -0,0 +1,2 @@ +let possibleNumber = "123" +var optionalConvertedNumber = possibleNumber.toInt() \ No newline at end of file diff --git a/1d. Optionals.playground/section-9.swift b/1d. Optionals.playground/section-9.swift new file mode 100644 index 0000000..0d099e8 --- /dev/null +++ b/1d. Optionals.playground/section-9.swift @@ -0,0 +1 @@ +optionalConvertedNumber \ No newline at end of file diff --git a/1e. Assertions.playground/Documentation/section-0.html b/1e. Assertions.playground/Documentation/section-0.html new file mode 100644 index 0000000..3d58633 --- /dev/null +++ b/1e. Assertions.playground/Documentation/section-0.html @@ -0,0 +1,26 @@ + + + + + Section 1 + + + + + + +
+
+

Assertions

+

Things to know:

+
    +
  • Assertions only trigger in debug mode and not in published builds
  • +
  • Assertions cause your app to terminate on the line and the debugger jumps to the line
  • +
+
+

Let's start with a value...

+ +
+
+ + diff --git a/1e. Assertions.playground/Documentation/section-2.html b/1e. Assertions.playground/Documentation/section-2.html new file mode 100644 index 0000000..98b434f --- /dev/null +++ b/1e. Assertions.playground/Documentation/section-2.html @@ -0,0 +1,19 @@ + + + + + Section 3 + + + + + + +
+
+

You can assert with a message

+ +
+
+ + diff --git a/1e. Assertions.playground/Documentation/section-4.html b/1e. Assertions.playground/Documentation/section-4.html new file mode 100644 index 0000000..26622ed --- /dev/null +++ b/1e. Assertions.playground/Documentation/section-4.html @@ -0,0 +1,19 @@ + + + + + Section 5 + + + + + + +
+
+

You can assert without the message

+ +
+
+ + diff --git a/1e. Assertions.playground/Documentation/section-6.html b/1e. Assertions.playground/Documentation/section-6.html new file mode 100644 index 0000000..998dc98 --- /dev/null +++ b/1e. Assertions.playground/Documentation/section-6.html @@ -0,0 +1,18 @@ + + + + + Section 7 + + + + + + +
+
+ +
+
+ + diff --git a/1e. Assertions.playground/Documentation/stylesheet.css b/1e. Assertions.playground/Documentation/stylesheet.css new file mode 100644 index 0000000..43b0230 --- /dev/null +++ b/1e. Assertions.playground/Documentation/stylesheet.css @@ -0,0 +1,65 @@ +html { + font-size: 1em; +} + +body { + background-color: #fff; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: normal; + font-size: 1.0em; + line-height: 140%; + padding: 0 2em; +} + +code { + font-family: "Menlo", monospace; + font-size: 0.95em; +} + +section { + padding: 0.5em 0; + -webkit-overflow-scrolling: touch; +} + +a:link { + color: #00f; + text-decoration: none; + border-bottom: 1px solid #00f; +} + +h1 { + font-size: 1.5em; + padding: 0.7em 0 0 0; +} + +h2 { + font-size: 1.2em; + padding: 0.7em 0 0 0; +} + +h3 { + font-size: 1.0em; + padding: 0.7em 0 0 0; +} + +blockquote { + font-size: 0.9em; + margin-left: 0; + padding-left: 2em; + border-left: 8px solid #eee; +} + +.footnotes { + color: #666666; + margin-top: 2em; + font-size: 0.9em; + letter-spacing: -3px; +} + +.footnotes:before { + content: "――――"; +} + +.footnotes ol { + letter-spacing: normal; +} diff --git a/1e. Assertions.playground/contents.xcplayground b/1e. Assertions.playground/contents.xcplayground index 4937636..dc73c16 100644 --- a/1e. Assertions.playground/contents.xcplayground +++ b/1e. Assertions.playground/contents.xcplayground @@ -1,7 +1,19 @@ - + - + + + + + + + + + + + + + + - \ No newline at end of file diff --git a/1e. Assertions.playground/section-1.swift b/1e. Assertions.playground/section-1.swift index 460e628..d58a126 100644 --- a/1e. Assertions.playground/section-1.swift +++ b/1e. Assertions.playground/section-1.swift @@ -1,17 +1 @@ -// ------------------------------------------------------------------------------------------------ -// Things to know: -// -// * Assertions only trigger in debug mode and not in published builds -// -// * Assertions cause your app to terminate on the line and the debugger jumps to the line -// ------------------------------------------------------------------------------------------------ - -// Let's start with a value... -let age = 3 - -// You can assert with a message -assert(age >= 0, "A person's age cannot be negative") - -// You can assert without the message -assert(age >= 0) - +let age = 3 \ No newline at end of file diff --git a/1e. Assertions.playground/section-3.swift b/1e. Assertions.playground/section-3.swift new file mode 100644 index 0000000..6c8f745 --- /dev/null +++ b/1e. Assertions.playground/section-3.swift @@ -0,0 +1 @@ +assert(age >= 0, "A person's age cannot be negative") \ No newline at end of file diff --git a/1e. Assertions.playground/section-5.swift b/1e. Assertions.playground/section-5.swift new file mode 100644 index 0000000..1b81835 --- /dev/null +++ b/1e. Assertions.playground/section-5.swift @@ -0,0 +1 @@ +assert(age >= 0) \ No newline at end of file diff --git a/2. Basic operations.playground/Documentation/section-0.html b/2. Basic operations.playground/Documentation/section-0.html new file mode 100644 index 0000000..b3d5660 --- /dev/null +++ b/2. Basic operations.playground/Documentation/section-0.html @@ -0,0 +1,30 @@ + + + + + Section 1 + + + + + + +
+
+

Basic Operations

+

Things to know:

+
    +
  • Assuming knowledge of C here, so a lot will be left out that is the the same, such as "let i = 1 + 2"
  • +
  • Unary operators work on a single target. Of them, you have prefix operators such as !b or postfix operators such as i++.
  • +
  • Binary operators work on two targets and are infix operators because they go between two values, such as a + b
  • +
  • Ternary operators work on three targets. There is only one ternary: a ? b : c.
  • +
+
+

We have our standard assignment operator (=). Note that the assignment operator in Swift does +not actually return a value, so the statment "if (x = y) {}" will not work. This helps prevent +accidental assignments during conditionals.

+ +
+
+ + diff --git a/2. Basic operations.playground/Documentation/section-10.html b/2. Basic operations.playground/Documentation/section-10.html new file mode 100644 index 0000000..fc6ae4a --- /dev/null +++ b/2. Basic operations.playground/Documentation/section-10.html @@ -0,0 +1,20 @@ + + + + + Section 11 + + + + + + +
+
+

Unary, Binary and Ternary operators

+

Unary prefix operators appear before their taget. Here we increment a then negate it:

+ +
+
+ + diff --git a/2. Basic operations.playground/Documentation/section-12.html b/2. Basic operations.playground/Documentation/section-12.html new file mode 100644 index 0000000..caf0da1 --- /dev/null +++ b/2. Basic operations.playground/Documentation/section-12.html @@ -0,0 +1,19 @@ + + + + + Section 13 + + + + + + +
+
+

You can also use the uniary + operator, though it doesn't do anything

+ +
+
+ + diff --git a/2. Basic operations.playground/Documentation/section-14.html b/2. Basic operations.playground/Documentation/section-14.html new file mode 100644 index 0000000..392129b --- /dev/null +++ b/2. Basic operations.playground/Documentation/section-14.html @@ -0,0 +1,19 @@ + + + + + Section 15 + + + + + + +
+
+

We have the compound assigment operator

+ +
+
+ + diff --git a/2. Basic operations.playground/Documentation/section-16.html b/2. Basic operations.playground/Documentation/section-16.html new file mode 100644 index 0000000..307731f --- /dev/null +++ b/2. Basic operations.playground/Documentation/section-16.html @@ -0,0 +1,19 @@ + + + + + Section 17 + + + + + + +
+
+

The logical NOT

+ +
+
+ + diff --git a/2. Basic operations.playground/Documentation/section-18.html b/2. Basic operations.playground/Documentation/section-18.html new file mode 100644 index 0000000..2d1b268 --- /dev/null +++ b/2. Basic operations.playground/Documentation/section-18.html @@ -0,0 +1,19 @@ + + + + + Section 19 + + + + + + +
+
+

Unary postfix operators appear after their target: i++

+ +
+
+ + diff --git a/2. Basic operations.playground/Documentation/section-2.html b/2. Basic operations.playground/Documentation/section-2.html new file mode 100644 index 0000000..739b678 --- /dev/null +++ b/2. Basic operations.playground/Documentation/section-2.html @@ -0,0 +1,19 @@ + + + + + Section 3 + + + + + + +
+
+

Assignment can also take multiple values (for Tuples):

+ +
+
+ + diff --git a/2. Basic operations.playground/Documentation/section-20.html b/2. Basic operations.playground/Documentation/section-20.html new file mode 100644 index 0000000..f4d55aa --- /dev/null +++ b/2. Basic operations.playground/Documentation/section-20.html @@ -0,0 +1,19 @@ + + + + + Section 21 + + + + + + +
+
+

Binary operators are infix because they appear between to targets

+ +
+
+ + diff --git a/2. Basic operations.playground/Documentation/section-22.html b/2. Basic operations.playground/Documentation/section-22.html new file mode 100644 index 0000000..be7afd7 --- /dev/null +++ b/2. Basic operations.playground/Documentation/section-22.html @@ -0,0 +1,19 @@ + + + + + Section 23 + + + + + + +
+
+

String concatenation uses the + operator:

+ +
+
+ + diff --git a/2. Basic operations.playground/Documentation/section-24.html b/2. Basic operations.playground/Documentation/section-24.html new file mode 100644 index 0000000..6be0825 --- /dev/null +++ b/2. Basic operations.playground/Documentation/section-24.html @@ -0,0 +1,19 @@ + + + + + Section 25 + + + + + + +
+
+

You can add characters, too, which form a string

+ +
+
+ + diff --git a/2. Basic operations.playground/Documentation/section-26.html b/2. Basic operations.playground/Documentation/section-26.html new file mode 100644 index 0000000..fd966cc --- /dev/null +++ b/2. Basic operations.playground/Documentation/section-26.html @@ -0,0 +1,19 @@ + + + + + Section 27 + + + + + + +
+
+

You can also add characters to a string

+ +
+
+ + diff --git a/2. Basic operations.playground/Documentation/section-28.html b/2. Basic operations.playground/Documentation/section-28.html new file mode 100644 index 0000000..64b10a3 --- /dev/null +++ b/2. Basic operations.playground/Documentation/section-28.html @@ -0,0 +1,19 @@ + + + + + Section 29 + + + + + + +
+
+

Ternary operators work on three targets:

+ +
+
+ + diff --git a/2. Basic operations.playground/Documentation/section-30.html b/2. Basic operations.playground/Documentation/section-30.html new file mode 100644 index 0000000..3636b54 --- /dev/null +++ b/2. Basic operations.playground/Documentation/section-30.html @@ -0,0 +1,20 @@ + + + + + Section 31 + + + + + + +
+
+

Identity operators

+

We can test if the object reference refers to the same instance of an object (as opposed to two objects that are "equivalent" based on some compare logic.) We do this with the === operator:

+ +
+
+ + diff --git a/2. Basic operations.playground/Documentation/section-32.html b/2. Basic operations.playground/Documentation/section-32.html new file mode 100644 index 0000000..0def40e --- /dev/null +++ b/2. Basic operations.playground/Documentation/section-32.html @@ -0,0 +1,19 @@ + + + + + Section 33 + + + + + + +
+
+

String comparisons are case sensitive

+ +
+
+ + diff --git a/2. Basic operations.playground/Documentation/section-34.html b/2. Basic operations.playground/Documentation/section-34.html new file mode 100644 index 0000000..49377f6 --- /dev/null +++ b/2. Basic operations.playground/Documentation/section-34.html @@ -0,0 +1,20 @@ + + + + + Section 35 + + + + + + +
+
+

Logical operators

+

Comparisons use the logical operators with AND, OR and NOT

+ +
+
+ + diff --git a/2. Basic operations.playground/Documentation/section-4.html b/2. Basic operations.playground/Documentation/section-4.html new file mode 100644 index 0000000..3ba10a8 --- /dev/null +++ b/2. Basic operations.playground/Documentation/section-4.html @@ -0,0 +1,19 @@ + + + + + Section 5 + + + + + + +
+
+

Aside from the standard mathematics operators (+, -, /, *), there is also the remainder operator (%) which is not to be confused with modulo. They work differently because of the way remainders are calculated for negative numbers and can be used with floating point values.

+ +
+
+ + diff --git a/2. Basic operations.playground/Documentation/section-6.html b/2. Basic operations.playground/Documentation/section-6.html new file mode 100644 index 0000000..5942545 --- /dev/null +++ b/2. Basic operations.playground/Documentation/section-6.html @@ -0,0 +1,21 @@ + + + + + Section 7 + + + + + + +
+
+

Range operators

+

The range operator with two dots means up to but NOT including the final value.

+

This is called the "Half-Closed Range Operator"

+ +
+
+ + diff --git a/2. Basic operations.playground/Documentation/section-8.html b/2. Basic operations.playground/Documentation/section-8.html new file mode 100644 index 0000000..7f33e58 --- /dev/null +++ b/2. Basic operations.playground/Documentation/section-8.html @@ -0,0 +1,20 @@ + + + + + Section 9 + + + + + + +
+
+

The range operator with three dots is inclusive with last value like

+

This is called the "Closed Range Operator"

+ +
+
+ + diff --git a/2. Basic operations.playground/Documentation/stylesheet.css b/2. Basic operations.playground/Documentation/stylesheet.css new file mode 100644 index 0000000..43b0230 --- /dev/null +++ b/2. Basic operations.playground/Documentation/stylesheet.css @@ -0,0 +1,65 @@ +html { + font-size: 1em; +} + +body { + background-color: #fff; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: normal; + font-size: 1.0em; + line-height: 140%; + padding: 0 2em; +} + +code { + font-family: "Menlo", monospace; + font-size: 0.95em; +} + +section { + padding: 0.5em 0; + -webkit-overflow-scrolling: touch; +} + +a:link { + color: #00f; + text-decoration: none; + border-bottom: 1px solid #00f; +} + +h1 { + font-size: 1.5em; + padding: 0.7em 0 0 0; +} + +h2 { + font-size: 1.2em; + padding: 0.7em 0 0 0; +} + +h3 { + font-size: 1.0em; + padding: 0.7em 0 0 0; +} + +blockquote { + font-size: 0.9em; + margin-left: 0; + padding-left: 2em; + border-left: 8px solid #eee; +} + +.footnotes { + color: #666666; + margin-top: 2em; + font-size: 0.9em; + letter-spacing: -3px; +} + +.footnotes:before { + content: "――――"; +} + +.footnotes ol { + letter-spacing: normal; +} diff --git a/2. Basic operations.playground/contents.xcplayground b/2. Basic operations.playground/contents.xcplayground index 4937636..9ca3d07 100644 --- a/2. Basic operations.playground/contents.xcplayground +++ b/2. Basic operations.playground/contents.xcplayground @@ -1,7 +1,77 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file diff --git a/2. Basic operations.playground/section-1.swift b/2. Basic operations.playground/section-1.swift index 5dab6f2..81ada47 100644 --- a/2. Basic operations.playground/section-1.swift +++ b/2. Basic operations.playground/section-1.swift @@ -1,114 +1,2 @@ -// ------------------------------------------------------------------------------------------------ -// Things to know: -// -// * Assuming knowledge of C here, so a lot will be left out that is the the same, such as -// "let i = 1 + 2" -// -// * Unary operators work on a single target. Of them, you have prefix operators such as !b or -// postfix operators such as i++. -// -// * Binary operators work on two targets and are infix operators because they go between two -// values, such as a + b -// -// * Ternary operators work on three targets. There is only one ternary: a ? b : c. -// ------------------------------------------------------------------------------------------------ - -// We have our standard assignment operator (=). Note that the assignment operator in Swift does -// not actually return a value, so the statment "if (x = y) {}" will not work. This helps prevent -// accidental assignments during conditionals. var a = 10.0 -var b = 3.0 - -// Assignment can also take multiple values (for Tuples): -let (x, y) = (5, 6) -x -y - -// Aside from the standard mathematics operators (+, -, /, *), there is also the remainder operator -// (%) which is not to be confused with modulo. They work differently because of the way remainders -// are calculated for negative numbers and can be used with floating point values. -var c = a / b // Floatng point result -var d = a % b // Floating point remainder - -// ------------------------------------------------------------------------------------------------ -// Range operators -// -// The range operator with two dots means up to but NOT including the final value. -// -// This is called the "Half-Closed Range Operator" -for i in 1..<10 -{ - i // prints 1 through 9 -} - -// The range operator with three dots is inclusive with last value like -// -// This is called the "Closed Range Operator" -for i in 1...10 -{ - i // prints 1 through 10 -} - -// ------------------------------------------------------------------------------------------------ -// Unary, Binary and Ternary operators -// -// Unary prefix operators appear before their taget. Here we increment a then negate it: -++a -a = -a - -// You can also use the uniary + operator, though it doesn't do anything -a = +a - -// We have the compound assigment operator -a += 10 - -// The logical NOT -var truefalse = true -truefalse = !truefalse - -// Unary postfix operators appear after their target: i++ -a-- -a - -// Binary operators are infix because they appear between to targets -a + b - -// String concatenation uses the + operator: -"hello, " + "world" - -// You can add characters, too, which form a string -let dog: Character = "🐶" -let cow: Character = "🐮" // Why is this an "Enum Value" ? -let dogCow = dog + cow - -// You can also add characters to a string -let str = "abc" -let chr: Character = "d" -chr + str + chr - -// Ternary operators work on three targets: -truefalse ? a : b - -// ------------------------------------------------------------------------------------------------ -// Identity operators -// -// We can test if the object reference refers to the same instance of an object (as opposed to two -// objects that are "equivalent" based on some compare logic.) We do this with the === operator: -class myclass {} -var c1 = myclass() -var c2 = myclass() -c1 === c2 -c1 === c1 - -// String comparisons are case sensitive -"abc" == "abc" -"abc" == "ABC" - -// ------------------------------------------------------------------------------------------------ -// Logical operators -// -// Comparisons use the logical operators with AND, OR and NOT -if (true && false) || !(false && true) -{ - "true" -} +var b = 3.0 \ No newline at end of file diff --git a/2. Basic operations.playground/section-11.swift b/2. Basic operations.playground/section-11.swift new file mode 100644 index 0000000..b641424 --- /dev/null +++ b/2. Basic operations.playground/section-11.swift @@ -0,0 +1,2 @@ +++a +a = -a \ No newline at end of file diff --git a/2. Basic operations.playground/section-13.swift b/2. Basic operations.playground/section-13.swift new file mode 100644 index 0000000..7d00de3 --- /dev/null +++ b/2. Basic operations.playground/section-13.swift @@ -0,0 +1 @@ +a = +a \ No newline at end of file diff --git a/2. Basic operations.playground/section-15.swift b/2. Basic operations.playground/section-15.swift new file mode 100644 index 0000000..acf173d --- /dev/null +++ b/2. Basic operations.playground/section-15.swift @@ -0,0 +1 @@ +a += 10 \ No newline at end of file diff --git a/2. Basic operations.playground/section-17.swift b/2. Basic operations.playground/section-17.swift new file mode 100644 index 0000000..df2a1cc --- /dev/null +++ b/2. Basic operations.playground/section-17.swift @@ -0,0 +1,2 @@ +var truefalse = true +truefalse = !truefalse \ No newline at end of file diff --git a/2. Basic operations.playground/section-19.swift b/2. Basic operations.playground/section-19.swift new file mode 100644 index 0000000..7e31d5f --- /dev/null +++ b/2. Basic operations.playground/section-19.swift @@ -0,0 +1,2 @@ +a-- +a \ No newline at end of file diff --git a/2. Basic operations.playground/section-21.swift b/2. Basic operations.playground/section-21.swift new file mode 100644 index 0000000..2fdfd51 --- /dev/null +++ b/2. Basic operations.playground/section-21.swift @@ -0,0 +1 @@ +a + b \ No newline at end of file diff --git a/2. Basic operations.playground/section-23.swift b/2. Basic operations.playground/section-23.swift new file mode 100644 index 0000000..0cc8e1a --- /dev/null +++ b/2. Basic operations.playground/section-23.swift @@ -0,0 +1 @@ +"hello, " + "world" \ No newline at end of file diff --git a/2. Basic operations.playground/section-25.swift b/2. Basic operations.playground/section-25.swift new file mode 100644 index 0000000..4804015 --- /dev/null +++ b/2. Basic operations.playground/section-25.swift @@ -0,0 +1,3 @@ +let dog: Character = "🐶" +let cow: Character = "🐮" +let dogCow = dog + cow \ No newline at end of file diff --git a/2. Basic operations.playground/section-27.swift b/2. Basic operations.playground/section-27.swift new file mode 100644 index 0000000..3654eee --- /dev/null +++ b/2. Basic operations.playground/section-27.swift @@ -0,0 +1,3 @@ +let str = "abc" +let chr: Character = "d" +String(chr) + str + String(chr) \ No newline at end of file diff --git a/2. Basic operations.playground/section-29.swift b/2. Basic operations.playground/section-29.swift new file mode 100644 index 0000000..0673f55 --- /dev/null +++ b/2. Basic operations.playground/section-29.swift @@ -0,0 +1 @@ +truefalse ? a : b \ No newline at end of file diff --git a/2. Basic operations.playground/section-3.swift b/2. Basic operations.playground/section-3.swift new file mode 100644 index 0000000..dfcac72 --- /dev/null +++ b/2. Basic operations.playground/section-3.swift @@ -0,0 +1,3 @@ +let (x, y) = (5, 6) +x +y \ No newline at end of file diff --git a/2. Basic operations.playground/section-31.swift b/2. Basic operations.playground/section-31.swift new file mode 100644 index 0000000..ff973ef --- /dev/null +++ b/2. Basic operations.playground/section-31.swift @@ -0,0 +1,5 @@ +class myclass {} +var c1 = myclass() +var c2 = myclass() +c1 === c2 +c1 === c1 \ No newline at end of file diff --git a/2. Basic operations.playground/section-33.swift b/2. Basic operations.playground/section-33.swift new file mode 100644 index 0000000..5d375aa --- /dev/null +++ b/2. Basic operations.playground/section-33.swift @@ -0,0 +1,2 @@ +"abc" == "abc" +"abc" == "ABC" \ No newline at end of file diff --git a/2. Basic operations.playground/section-35.swift b/2. Basic operations.playground/section-35.swift new file mode 100644 index 0000000..8dee5de --- /dev/null +++ b/2. Basic operations.playground/section-35.swift @@ -0,0 +1,4 @@ +if (true && false) || !(false && true) +{ + "true" +} \ No newline at end of file diff --git a/2. Basic operations.playground/section-5.swift b/2. Basic operations.playground/section-5.swift new file mode 100644 index 0000000..bf00965 --- /dev/null +++ b/2. Basic operations.playground/section-5.swift @@ -0,0 +1,2 @@ +var c = a / b // Floatng point result +var d = a % b // Floating point remainder \ No newline at end of file diff --git a/2. Basic operations.playground/section-7.swift b/2. Basic operations.playground/section-7.swift new file mode 100644 index 0000000..4e378ad --- /dev/null +++ b/2. Basic operations.playground/section-7.swift @@ -0,0 +1,4 @@ +for i in 1..<10 +{ + i // prints 1 through 9 +} \ No newline at end of file diff --git a/2. Basic operations.playground/section-9.swift b/2. Basic operations.playground/section-9.swift new file mode 100644 index 0000000..6f4b827 --- /dev/null +++ b/2. Basic operations.playground/section-9.swift @@ -0,0 +1,4 @@ +for i in 1...10 +{ + i // prints 1 through 10 +} \ No newline at end of file diff --git a/20. Extensions.playground/Documentation/section-0.html b/20. Extensions.playground/Documentation/section-0.html new file mode 100644 index 0000000..2a54f8a --- /dev/null +++ b/20. Extensions.playground/Documentation/section-0.html @@ -0,0 +1,38 @@ + + + + + Section 1 + + + + + + +
+
+

Extensions

+

Things to know:

+
    +
  • Similar to Objective-C categories, extensions allow you to add functionality to an existing type (class, struct, enumeration.)
  • +
  • You do not need access to the original source code to extend a type.
  • +
  • Extensions can be applied to built-in types as well, including String, Int, Double, etc.
  • +
  • With extensions, you can:
      +
    • Add computed properties (including static)
    • +
    • Define instance methods and type methods
    • +
    • Provide new convenience initializers
    • +
    • Define subscripts
    • +
    • Define and use new nested types
    • +
    • Make an existing type conform to a protocol
    • +
    +
  • +
  • Extensions do not support adding stored properties or property observers to a type.
  • +
  • Extensions apply all instances of a type, even if they were created before the extension was defined.
  • +
+
+

Let's take a look at how extensions are declared. Note that, unlike Objective-C categories, extensions are not named:

+ +
+
+ + diff --git a/20. Extensions.playground/Documentation/section-10.html b/20. Extensions.playground/Documentation/section-10.html new file mode 100644 index 0000000..fff407d --- /dev/null +++ b/20. Extensions.playground/Documentation/section-10.html @@ -0,0 +1,19 @@ + + + + + Section 11 + + + + + + +
+
+

Since we didn't provide any initializers, we can use Swift's default memberwise initializer for the Rect.

+ +
+
+ + diff --git a/20. Extensions.playground/Documentation/section-12.html b/20. Extensions.playground/Documentation/section-12.html new file mode 100644 index 0000000..5266708 --- /dev/null +++ b/20. Extensions.playground/Documentation/section-12.html @@ -0,0 +1,19 @@ + + + + + Section 13 + + + + + + +
+
+

Let's extend Rect to add a new convenience initializer. Note that we're still responsible for ensuring that the instance is fully initialized.

+ +
+
+ + diff --git a/20. Extensions.playground/Documentation/section-14.html b/20. Extensions.playground/Documentation/section-14.html new file mode 100644 index 0000000..fbe5d42 --- /dev/null +++ b/20. Extensions.playground/Documentation/section-14.html @@ -0,0 +1,19 @@ + + + + + Section 15 + + + + + + +
+
+

Let's try out our new initializer:

+ +
+
+ + diff --git a/20. Extensions.playground/Documentation/section-16.html b/20. Extensions.playground/Documentation/section-16.html new file mode 100644 index 0000000..bcf74cd --- /dev/null +++ b/20. Extensions.playground/Documentation/section-16.html @@ -0,0 +1,19 @@ + + + + + Section 17 + + + + + + +
+
+

Remember that if a class has an initializer, Swift will not provide the default memberwise initializer. However, since we added an initializer via an Extension, we still have access to Swift's memberwise initializer:

+ +
+
+ + diff --git a/20. Extensions.playground/Documentation/section-18.html b/20. Extensions.playground/Documentation/section-18.html new file mode 100644 index 0000000..6c28edb --- /dev/null +++ b/20. Extensions.playground/Documentation/section-18.html @@ -0,0 +1,21 @@ + + + + + Section 19 + + + + + + +
+
+

Methods

+

As you might expect, we can add methods to an existing type as well. Here's a clever little extention to perform a task (a closure) multiple times, equal to the value stored in the Int.

+

Note that the integer value is stored in 'self'.

+ +
+
+ + diff --git a/20. Extensions.playground/Documentation/section-2.html b/20. Extensions.playground/Documentation/section-2.html new file mode 100644 index 0000000..9931ece --- /dev/null +++ b/20. Extensions.playground/Documentation/section-2.html @@ -0,0 +1,20 @@ + + + + + Section 3 + + + + + + +
+
+

Computed properties

+

Computed properties are a poweful use of extensions. Below, we'll add native conversion from various measurements (Km, mm, feet, etc.) to the Double class.

+ +
+
+ + diff --git a/20. Extensions.playground/Documentation/section-20.html b/20. Extensions.playground/Documentation/section-20.html new file mode 100644 index 0000000..7d5c9c7 --- /dev/null +++ b/20. Extensions.playground/Documentation/section-20.html @@ -0,0 +1,19 @@ + + + + + Section 21 + + + + + + +
+
+

Let's call our new member using the shorthand syntax for trailing closures:

+ +
+
+ + diff --git a/20. Extensions.playground/Documentation/section-22.html b/20. Extensions.playground/Documentation/section-22.html new file mode 100644 index 0000000..72b7b0c --- /dev/null +++ b/20. Extensions.playground/Documentation/section-22.html @@ -0,0 +1,20 @@ + + + + + Section 23 + + + + + + +
+
+

Instance methods can mutate the instance itself.

+

Note the use of the 'mutating' keyword.

+ +
+
+ + diff --git a/20. Extensions.playground/Documentation/section-24.html b/20. Extensions.playground/Documentation/section-24.html new file mode 100644 index 0000000..7959bd8 --- /dev/null +++ b/20. Extensions.playground/Documentation/section-24.html @@ -0,0 +1,20 @@ + + + + + Section 25 + + + + + + +
+
+

Subscripts

+

Let's add a subscript to Int:

+ +
+
+ + diff --git a/20. Extensions.playground/Documentation/section-26.html b/20. Extensions.playground/Documentation/section-26.html new file mode 100644 index 0000000..2aa23d4 --- /dev/null +++ b/20. Extensions.playground/Documentation/section-26.html @@ -0,0 +1,19 @@ + + + + + Section 27 + + + + + + +
+
+

And we can call our subscript directly on an Int, including a literal Int type:

+ +
+
+ + diff --git a/20. Extensions.playground/Documentation/section-28.html b/20. Extensions.playground/Documentation/section-28.html new file mode 100644 index 0000000..87b9144 --- /dev/null +++ b/20. Extensions.playground/Documentation/section-28.html @@ -0,0 +1,20 @@ + + + + + Section 29 + + + + + + +
+
+

Nested types

+

We can also add nested types to an existing type:

+ +
+
+ + diff --git a/20. Extensions.playground/Documentation/section-30.html b/20. Extensions.playground/Documentation/section-30.html new file mode 100644 index 0000000..9c80c6f --- /dev/null +++ b/20. Extensions.playground/Documentation/section-30.html @@ -0,0 +1,19 @@ + + + + + Section 31 + + + + + + +
+
+

Let's test out our new extension with nested types:

+ +
+
+ + diff --git a/20. Extensions.playground/Documentation/section-4.html b/20. Extensions.playground/Documentation/section-4.html new file mode 100644 index 0000000..0b5138e --- /dev/null +++ b/20. Extensions.playground/Documentation/section-4.html @@ -0,0 +1,19 @@ + + + + + Section 5 + + + + + + +
+
+

We can call upon Double's new computed property to convert inches to meters

+ +
+
+ + diff --git a/20. Extensions.playground/Documentation/section-6.html b/20. Extensions.playground/Documentation/section-6.html new file mode 100644 index 0000000..dc7088a --- /dev/null +++ b/20. Extensions.playground/Documentation/section-6.html @@ -0,0 +1,19 @@ + + + + + Section 7 + + + + + + +
+
+

Similarly, we'll convert three feet to meters

+ +
+
+ + diff --git a/20. Extensions.playground/Documentation/section-8.html b/20. Extensions.playground/Documentation/section-8.html new file mode 100644 index 0000000..ee94d0c --- /dev/null +++ b/20. Extensions.playground/Documentation/section-8.html @@ -0,0 +1,21 @@ + + + + + Section 9 + + + + + + +
+
+

Initializers

+

Extensions can be used to add new convenience initializers to a class, but they cannot add new designated initializers.

+

Let's see this in action:

+ +
+
+ + diff --git a/20. Extensions.playground/Documentation/stylesheet.css b/20. Extensions.playground/Documentation/stylesheet.css new file mode 100644 index 0000000..43b0230 --- /dev/null +++ b/20. Extensions.playground/Documentation/stylesheet.css @@ -0,0 +1,65 @@ +html { + font-size: 1em; +} + +body { + background-color: #fff; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: normal; + font-size: 1.0em; + line-height: 140%; + padding: 0 2em; +} + +code { + font-family: "Menlo", monospace; + font-size: 0.95em; +} + +section { + padding: 0.5em 0; + -webkit-overflow-scrolling: touch; +} + +a:link { + color: #00f; + text-decoration: none; + border-bottom: 1px solid #00f; +} + +h1 { + font-size: 1.5em; + padding: 0.7em 0 0 0; +} + +h2 { + font-size: 1.2em; + padding: 0.7em 0 0 0; +} + +h3 { + font-size: 1.0em; + padding: 0.7em 0 0 0; +} + +blockquote { + font-size: 0.9em; + margin-left: 0; + padding-left: 2em; + border-left: 8px solid #eee; +} + +.footnotes { + color: #666666; + margin-top: 2em; + font-size: 0.9em; + letter-spacing: -3px; +} + +.footnotes:before { + content: "――――"; +} + +.footnotes ol { + letter-spacing: normal; +} diff --git a/20. Extensions.playground/contents.xcplayground b/20. Extensions.playground/contents.xcplayground index 4937636..0e6428a 100644 --- a/20. Extensions.playground/contents.xcplayground +++ b/20. Extensions.playground/contents.xcplayground @@ -1,7 +1,69 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file diff --git a/20. Extensions.playground/section-1.swift b/20. Extensions.playground/section-1.swift index 32a6b0a..7d69bc0 100644 --- a/20. Extensions.playground/section-1.swift +++ b/20. Extensions.playground/section-1.swift @@ -1,201 +1,4 @@ -// ------------------------------------------------------------------------------------------------ -// Things to know: -// -// * Similar to Objective-C categories, extensions allow you to add functionality to an existing -// type (class, struct, enumeration.) -// -// * You do not need access to the original source code to extend a type. -// -// * Extensions can be applied to built-in types as well, including String, Int, Double, etc. -// -// * With extensions, you can: -// -// o Add computed properties (including static) -// o Define instance methods and type methods -// o Provide new convenience initializers -// o Define subscripts -// o Define and use new nested types -// o Make an existing type conform to a protocol -// -// * Extensions do not support adding stored properties or property observers to a type. -// -// * Extensions apply all instances of a type, even if they were created before the extension was -// defined. -// ------------------------------------------------------------------------------------------------ - -// Let's take a look at how extensions are declared. Note that, unlike Objective-C categories, -// extensions are not named: extension Int { // ... code here -} - -// ------------------------------------------------------------------------------------------------ -// Computed properties -// -// Computed properties are a poweful use of extensions. Below, we'll add native conversion from -// various measurements (Km, mm, feet, etc.) to the Double class. -extension Double -{ - var kmToMeters: Double { return self * 1_000.0 } - var cmToMeters: Double { return self / 100.0 } - var mmToMeters: Double { return self / 1_000.0 } - var inchToMeters: Double { return self / 39.3701 } - var ftToMeters: Double { return self / 3.28084 } -} - -// We can call upon Double's new computed property to convert inches to meters -let oneInchInMeters = 1.inchToMeters - -// Similarly, we'll convert three feet to meters -let threeFeetInMeters = 3.ftToMeters - -// ------------------------------------------------------------------------------------------------ -// Initializers -// -// Extensions can be used to add new convenience initializers to a class, but they cannot add -// new designated initializers. -// -// Let's see this in action: -struct Size -{ - var width = 0.0 - var height = 0.0 -} -struct Point -{ - var x = 0.0 - var y = 0.0 -} -struct Rect -{ - var origin = Point() - var size = Size() -} - -// Since we didn't provide any initializers, we can use Swift's default memberwise initializer for -// the Rect. -var memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0), size: Size(width: 5.0, height: 5.0)) - -// Let's extend Rect to add a new convenience initializer. Note that we're still responsible for -// ensuring that the instance is fully initialized. -extension Rect -{ - init (center: Point, size: Size) - { - let originX = center.x - (size.width / 2) - let originY = center.y - (size.height / 2) - self.init(origin: Point(x: originX, y: originY), size: size) - } -} - -// Let's try out our new initializer: -let centerRect = Rect(center: Point(x: 4.0, y: 4.0), size: Size(width: 3.0, height: 3.0)) - -// Remember that if a class has an initializer, Swift will not provide the default memberwise -// initializer. However, since we added an initializer via an Extension, we still have access -// to Swift's memberwise initializer: -var anotherRect = Rect(origin: Point(x: 1.0, y: 1.0), size: Size(width: 3.0, height: 2.0)) - -// ------------------------------------------------------------------------------------------------ -// Methods -// -// As you might expect, we can add methods to an existing type as well. Here's a clever little -// extention to perform a task (a closure) multiple times, equal to the value stored in the Int. -// -// Note that the integer value is stored in 'self'. -extension Int -{ - func repititions(task: () -> ()) - { - for i in 0.. Int - { - var decimalBase = 1 - for _ in 0 ..< digitIndex - { - decimalBase *= 10 - } - return self / decimalBase % 10 - } -} - -// And we can call our subscript directly on an Int, including a literal Int type: -123456789[0] -123456789[1] -123456789[2] -123456789[3] -123456789[4] -123456789[5] -123456789[6] - -// ------------------------------------------------------------------------------------------------ -// Nested types -// -// We can also add nested types to an existing type: -extension Character -{ - enum Kind - { - case Vowel, Consonant, Other - } - var kind: Kind - { - switch String(self) - { - case "a", "e", "i", "o", "u": - return .Vowel - case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m", - "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z": - return .Consonant - default: - return .Other - } - } -} - -// Let's test out our new extension with nested types: -Character("a").kind == .Vowel -Character("h").kind == .Consonant -Character("+").kind == .Other - - - - - - - - - - - - +} \ No newline at end of file diff --git a/20. Extensions.playground/section-11.swift b/20. Extensions.playground/section-11.swift new file mode 100644 index 0000000..3725951 --- /dev/null +++ b/20. Extensions.playground/section-11.swift @@ -0,0 +1 @@ +var memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0), size: Size(width: 5.0, height: 5.0)) \ No newline at end of file diff --git a/20. Extensions.playground/section-13.swift b/20. Extensions.playground/section-13.swift new file mode 100644 index 0000000..01e706e --- /dev/null +++ b/20. Extensions.playground/section-13.swift @@ -0,0 +1,9 @@ +extension Rect +{ + init (center: Point, size: Size) + { + let originX = center.x - (size.width / 2) + let originY = center.y - (size.height / 2) + self.init(origin: Point(x: originX, y: originY), size: size) + } +} \ No newline at end of file diff --git a/20. Extensions.playground/section-15.swift b/20. Extensions.playground/section-15.swift new file mode 100644 index 0000000..4cc461c --- /dev/null +++ b/20. Extensions.playground/section-15.swift @@ -0,0 +1 @@ +let centerRect = Rect(center: Point(x: 4.0, y: 4.0), size: Size(width: 3.0, height: 3.0)) \ No newline at end of file diff --git a/20. Extensions.playground/section-17.swift b/20. Extensions.playground/section-17.swift new file mode 100644 index 0000000..1f3dd72 --- /dev/null +++ b/20. Extensions.playground/section-17.swift @@ -0,0 +1 @@ +var anotherRect = Rect(origin: Point(x: 1.0, y: 1.0), size: Size(width: 3.0, height: 2.0)) \ No newline at end of file diff --git a/20. Extensions.playground/section-19.swift b/20. Extensions.playground/section-19.swift new file mode 100644 index 0000000..8d7d9bb --- /dev/null +++ b/20. Extensions.playground/section-19.swift @@ -0,0 +1,10 @@ +extension Int +{ + func repititions(task: () -> ()) + { + for i in 0.. Int + { + var decimalBase = 1 + for _ in 0 ..< digitIndex + { + decimalBase *= 10 + } + return self / decimalBase % 10 + } +} \ No newline at end of file diff --git a/20. Extensions.playground/section-27.swift b/20. Extensions.playground/section-27.swift new file mode 100644 index 0000000..502b2be --- /dev/null +++ b/20. Extensions.playground/section-27.swift @@ -0,0 +1,7 @@ +123456789[0] +123456789[1] +123456789[2] +123456789[3] +123456789[4] +123456789[5] +123456789[6] \ No newline at end of file diff --git a/20. Extensions.playground/section-29.swift b/20. Extensions.playground/section-29.swift new file mode 100644 index 0000000..6a97c25 --- /dev/null +++ b/20. Extensions.playground/section-29.swift @@ -0,0 +1,20 @@ +extension Character +{ + enum Kind + { + case Vowel, Consonant, Other + } + var kind: Kind + { + switch String(self) + { + case "a", "e", "i", "o", "u": + return .Vowel + case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m", + "n", "p", "q", "r", "s", "t", "v", "w", "x", "y", "z": + return .Consonant + default: + return .Other + } + } +} \ No newline at end of file diff --git a/20. Extensions.playground/section-3.swift b/20. Extensions.playground/section-3.swift new file mode 100644 index 0000000..3899d4b --- /dev/null +++ b/20. Extensions.playground/section-3.swift @@ -0,0 +1,8 @@ +extension Double +{ + var kmToMeters: Double { return self * 1_000.0 } + var cmToMeters: Double { return self / 100.0 } + var mmToMeters: Double { return self / 1_000.0 } + var inchToMeters: Double { return self / 39.3701 } + var ftToMeters: Double { return self / 3.28084 } +} \ No newline at end of file diff --git a/20. Extensions.playground/section-31.swift b/20. Extensions.playground/section-31.swift new file mode 100644 index 0000000..343e87c --- /dev/null +++ b/20. Extensions.playground/section-31.swift @@ -0,0 +1,3 @@ +Character("a").kind == .Vowel +Character("h").kind == .Consonant +Character("+").kind == .Other \ No newline at end of file diff --git a/20. Extensions.playground/section-5.swift b/20. Extensions.playground/section-5.swift new file mode 100644 index 0000000..607b2f6 --- /dev/null +++ b/20. Extensions.playground/section-5.swift @@ -0,0 +1 @@ +let oneInchInMeters = 1.inchToMeters \ No newline at end of file diff --git a/20. Extensions.playground/section-7.swift b/20. Extensions.playground/section-7.swift new file mode 100644 index 0000000..ae4afc8 --- /dev/null +++ b/20. Extensions.playground/section-7.swift @@ -0,0 +1 @@ +let threeFeetInMeters = 3.ftToMeters \ No newline at end of file diff --git a/20. Extensions.playground/section-9.swift b/20. Extensions.playground/section-9.swift new file mode 100644 index 0000000..8849e2e --- /dev/null +++ b/20. Extensions.playground/section-9.swift @@ -0,0 +1,15 @@ +struct Size +{ + var width = 0.0 + var height = 0.0 +} +struct Point +{ + var x = 0.0 + var y = 0.0 +} +struct Rect +{ + var origin = Point() + var size = Size() +} \ No newline at end of file diff --git a/21. Protocols.playground/Documentation/section-0.html b/21. Protocols.playground/Documentation/section-0.html new file mode 100644 index 0000000..300b5fc --- /dev/null +++ b/21. Protocols.playground/Documentation/section-0.html @@ -0,0 +1,49 @@ + + + + + Section 1 + + + + + + +
+
+

Protocols

+

Things to know:

+
    +
  • Protocols define a required set of functionality (including methods and properties) for a class, structure or enumeration.
  • +
  • Protocols are "adopted" by a class, structure or enumeration to provide the implementation. This is called "conforming" to a protocol.
  • +
  • As you should already be aware, protocols are often used for delegation.
  • +
  • Protocol Syntax looks like this:

    +

    protocol FirstProtocol + {

    +
      // protocol definition goes here
    +

    }

    +
  • +
  • Protocol adoption looks like this:

    +

    struct SomeStructure: FirstProtocol, AnotherProtocol + {

    +
      // structure definition goes here
    +

    }

    +

    class SomeClass: SomeSuperclass, FirstProtocol, AnotherProtocol + {

    +
      // class definition goes here
    +

    }

    +
  • +
+
+

Property requirements

+
    +
  • A protocol can require the conforming type to provide an instance of a property or type property.
  • +
  • The protocol can also specify if the property must be gettable or gettable and settable. If a protocol only requires a gettable property, the conforming class can use a stored property or a computed property. Also, the conforming class is allowed to add a setter if it needs.
  • +
  • Property requirements are always declared as variable types with the 'var' introducer.
  • +
+

Let's take a look at a simple protocol. As you'll see, we only need to define the properties in terms of 'get' and 'set' and do not provide the actual functionality.

+ +
+
+ + diff --git a/21. Protocols.playground/Documentation/section-10.html b/21. Protocols.playground/Documentation/section-10.html new file mode 100644 index 0000000..a310cbe --- /dev/null +++ b/21. Protocols.playground/Documentation/section-10.html @@ -0,0 +1,24 @@ + + + + + Section 11 + + + + + + +
+
+

Method Requirements

+

Similar to property requirements, the protocol only defines the method type, not the implementation.

+

Variadic parameters are allowed, but default values are not.

+

Method requirements can be mutating - simply use the 'mutating' keyword before the function definition. Note that when conforming to the protocol, only classes are required to use the 'mutating' keyword when conforming to the protocol, following the normal procedure.

+

As with property requirements, type methods are always prefixed with the 'class' keyword.

+

Here's a simple example that defines the requirements for a 'random' function which takes no parameters and returns a Double:

+ +
+
+ + diff --git a/21. Protocols.playground/Documentation/section-12.html b/21. Protocols.playground/Documentation/section-12.html new file mode 100644 index 0000000..3178671 --- /dev/null +++ b/21. Protocols.playground/Documentation/section-12.html @@ -0,0 +1,19 @@ + + + + + Section 13 + + + + + + +
+
+

Let's adopt this protocol:

+ +
+
+ + diff --git a/21. Protocols.playground/Documentation/section-14.html b/21. Protocols.playground/Documentation/section-14.html new file mode 100644 index 0000000..db64643 --- /dev/null +++ b/21. Protocols.playground/Documentation/section-14.html @@ -0,0 +1,26 @@ + + + + + Section 15 + + + + + + +
+
+

Protocols as Types

+

Protocols are types, which means they can be used where many other types are allowed, including:

+
    +
  • As a parameter to a function, method or initializer
  • +
  • As the type of a constant, variable or property
  • +
  • As the type of items in an Array, Dictionary or other container
  • +
+

Here's a protocol used as a type. We'll create a Dice class that stores a RandomNumberGenerator as a constant so that it can be used to customize the randomization of the dice roll:

+ +
+
+ + diff --git a/21. Protocols.playground/Documentation/section-16.html b/21. Protocols.playground/Documentation/section-16.html new file mode 100644 index 0000000..84f21f4 --- /dev/null +++ b/21. Protocols.playground/Documentation/section-16.html @@ -0,0 +1,19 @@ + + + + + Section 17 + + + + + + +
+
+

Just for fun, let's roll a few:

+ +
+
+ + diff --git a/21. Protocols.playground/Documentation/section-18.html b/21. Protocols.playground/Documentation/section-18.html new file mode 100644 index 0000000..04ba932 --- /dev/null +++ b/21. Protocols.playground/Documentation/section-18.html @@ -0,0 +1,21 @@ + + + + + Section 19 + + + + + + +
+
+

Adding Protocol Conformance with an Extension

+

Existing classes, structures and enumerations can be extended to conform to a given protocol.

+

Let's start with a simple protocol we'll use for expirimentation:

+ +
+
+ + diff --git a/21. Protocols.playground/Documentation/section-2.html b/21. Protocols.playground/Documentation/section-2.html new file mode 100644 index 0000000..1ce3a80 --- /dev/null +++ b/21. Protocols.playground/Documentation/section-2.html @@ -0,0 +1,19 @@ + + + + + Section 3 + + + + + + +
+
+

Let's create a more practical protocol that we can actually conform to:

+ +
+
+ + diff --git a/21. Protocols.playground/Documentation/section-20.html b/21. Protocols.playground/Documentation/section-20.html new file mode 100644 index 0000000..8b6c75a --- /dev/null +++ b/21. Protocols.playground/Documentation/section-20.html @@ -0,0 +1,19 @@ + + + + + Section 21 + + + + + + +
+
+

We'll extend our Dice class:

+ +
+
+ + diff --git a/21. Protocols.playground/Documentation/section-22.html b/21. Protocols.playground/Documentation/section-22.html new file mode 100644 index 0000000..5785ed2 --- /dev/null +++ b/21. Protocols.playground/Documentation/section-22.html @@ -0,0 +1,19 @@ + + + + + Section 23 + + + + + + +
+
+

Existing instances (such as 'd6' will automatically adopt and conform to the new protocol, even though it was declared prior to the extension.

+ +
+
+ + diff --git a/21. Protocols.playground/Documentation/section-24.html b/21. Protocols.playground/Documentation/section-24.html new file mode 100644 index 0000000..00b0f46 --- /dev/null +++ b/21. Protocols.playground/Documentation/section-24.html @@ -0,0 +1,22 @@ + + + + + Section 25 + + + + + + +
+
+

Declaring Protocol Adoption with an Extension

+

Some types may already adopt a protocol, but do not state so in their definition. Types do not automatically adopt to protocols whose requirements they satisfy - protocol adoption must always be stated explicitly.

+

This can be resolved with by using an empty extension.

+

To showcase this, let's create a structure that conforms to the TextRepresentable protocol, but doesn't include this as part of the definition:

+ +
+
+ + diff --git a/21. Protocols.playground/Documentation/section-26.html b/21. Protocols.playground/Documentation/section-26.html new file mode 100644 index 0000000..65ba9a6 --- /dev/null +++ b/21. Protocols.playground/Documentation/section-26.html @@ -0,0 +1,19 @@ + + + + + Section 27 + + + + + + +
+
+

Let's verify our work:

+ +
+
+ + diff --git a/21. Protocols.playground/Documentation/section-28.html b/21. Protocols.playground/Documentation/section-28.html new file mode 100644 index 0000000..02209ad --- /dev/null +++ b/21. Protocols.playground/Documentation/section-28.html @@ -0,0 +1,20 @@ + + + + + Section 29 + + + + + + +
+
+

We can add TextRepresentable conformance to Hamster by using an empty extension.

+

This works because the requirements are already met, so we don't need to include any additional functionality within the extension definition.

+ +
+
+ + diff --git a/21. Protocols.playground/Documentation/section-30.html b/21. Protocols.playground/Documentation/section-30.html new file mode 100644 index 0000000..3b790db --- /dev/null +++ b/21. Protocols.playground/Documentation/section-30.html @@ -0,0 +1,20 @@ + + + + + Section 31 + + + + + + +
+
+

Collections of Protocol Types

+

Hamsters and Dice don't have much in common, but in our sample code above, they both conform to the TextRepresentable protocol. Because of this, we can create an array of things that are TextRepresentable which includes each:

+ +
+
+ + diff --git a/21. Protocols.playground/Documentation/section-32.html b/21. Protocols.playground/Documentation/section-32.html new file mode 100644 index 0000000..72065e7 --- /dev/null +++ b/21. Protocols.playground/Documentation/section-32.html @@ -0,0 +1,19 @@ + + + + + Section 33 + + + + + + +
+
+

We can now loop through each and produce its text representation:

+ +
+
+ + diff --git a/21. Protocols.playground/Documentation/section-34.html b/21. Protocols.playground/Documentation/section-34.html new file mode 100644 index 0000000..d0c5d0e --- /dev/null +++ b/21. Protocols.playground/Documentation/section-34.html @@ -0,0 +1,21 @@ + + + + + Section 35 + + + + + + +
+
+

Protocol Inheritance

+

Protocols can inherit from other protocols in order to add further requirements. The syntax for this is similar to a class ineriting from its superclass.

+

Let's create a new text representable type, inherited from TextRepresentable:

+ +
+
+ + diff --git a/21. Protocols.playground/Documentation/section-36.html b/21. Protocols.playground/Documentation/section-36.html new file mode 100644 index 0000000..32166aa --- /dev/null +++ b/21. Protocols.playground/Documentation/section-36.html @@ -0,0 +1,20 @@ + + + + + Section 37 + + + + + + +
+
+

Let's make our Dice a little prettier.

+

Note that the Dice already knows that it conforms to the TextRepresentable, which means we can call asText() inside our asPrettyText() method.

+ +
+
+ + diff --git a/21. Protocols.playground/Documentation/section-38.html b/21. Protocols.playground/Documentation/section-38.html new file mode 100644 index 0000000..0dda5d8 --- /dev/null +++ b/21. Protocols.playground/Documentation/section-38.html @@ -0,0 +1,19 @@ + + + + + Section 39 + + + + + + +
+
+

We can test our work:

+ +
+
+ + diff --git a/21. Protocols.playground/Documentation/section-4.html b/21. Protocols.playground/Documentation/section-4.html new file mode 100644 index 0000000..6d31f62 --- /dev/null +++ b/21. Protocols.playground/Documentation/section-4.html @@ -0,0 +1,19 @@ + + + + + Section 5 + + + + + + +
+
+

A structure that conforms to FullyNamed. We won't bother creating an explicit getter or setter for the 'fullName' property because Swift's defaults will suffice.

+ +
+
+ + diff --git a/21. Protocols.playground/Documentation/section-40.html b/21. Protocols.playground/Documentation/section-40.html new file mode 100644 index 0000000..3c5cdd0 --- /dev/null +++ b/21. Protocols.playground/Documentation/section-40.html @@ -0,0 +1,22 @@ + + + + + Section 41 + + + + + + +
+
+

Protocol Composition

+

Protocols can be combined such that a type conforms to each of them. For example, a person can be an aged person as well as a named person.

+

Protocol Composition is a syntactic method of declaring that an instance conforms to a set of protocols. It takes the form "protocol". There can be as many protocols between the angle brackets as you need.

+

Let's start by creating a couple of protocols for expirimentation:

+ +
+
+ + diff --git a/21. Protocols.playground/Documentation/section-42.html b/21. Protocols.playground/Documentation/section-42.html new file mode 100644 index 0000000..6e757ee --- /dev/null +++ b/21. Protocols.playground/Documentation/section-42.html @@ -0,0 +1,19 @@ + + + + + Section 43 + + + + + + +
+
+

Here, we declare an Individual that conforms to both Name and Age protocols:

+ +
+
+ + diff --git a/21. Protocols.playground/Documentation/section-44.html b/21. Protocols.playground/Documentation/section-44.html new file mode 100644 index 0000000..3bf6349 --- /dev/null +++ b/21. Protocols.playground/Documentation/section-44.html @@ -0,0 +1,19 @@ + + + + + Section 45 + + + + + + +
+
+

Here, we can see the protocol composition at work as the parameter into the wishHappyBirthday() function:

+ +
+
+ + diff --git a/21. Protocols.playground/Documentation/section-46.html b/21. Protocols.playground/Documentation/section-46.html new file mode 100644 index 0000000..8ef330f --- /dev/null +++ b/21. Protocols.playground/Documentation/section-46.html @@ -0,0 +1,19 @@ + + + + + Section 47 + + + + + + +
+
+

If we call the member, we can see the celebratory wish for this individual:

+ +
+
+ + diff --git a/21. Protocols.playground/Documentation/section-48.html b/21. Protocols.playground/Documentation/section-48.html new file mode 100644 index 0000000..d4f5b52 --- /dev/null +++ b/21. Protocols.playground/Documentation/section-48.html @@ -0,0 +1,22 @@ + + + + + Section 49 + + + + + + +
+
+

Checking for Protocol Conformance

+

We can use 'is' and 'as' for testing for protocol conformance, just as we've seen in the section on Type Casting.

+

In order for this to work with protocols, they must be marked with an "@objc" attribute. See further down in this playground for a special note about the @objc attribute.

+

Let's create a new protocol with the proper prefix so that we can investigate:

+ +
+
+ + diff --git a/21. Protocols.playground/Documentation/section-50.html b/21. Protocols.playground/Documentation/section-50.html new file mode 100644 index 0000000..9792d12 --- /dev/null +++ b/21. Protocols.playground/Documentation/section-50.html @@ -0,0 +1,19 @@ + + + + + Section 51 + + + + + + +
+
+

We can store our objects into an array of type AnyObject[]

+ +
+
+ + diff --git a/21. Protocols.playground/Documentation/section-52.html b/21. Protocols.playground/Documentation/section-52.html new file mode 100644 index 0000000..802a116 --- /dev/null +++ b/21. Protocols.playground/Documentation/section-52.html @@ -0,0 +1,19 @@ + + + + + Section 53 + + + + + + +
+
+

Then we can test each for conformance to HasArea:

+ +
+
+ + diff --git a/21. Protocols.playground/Documentation/section-54.html b/21. Protocols.playground/Documentation/section-54.html new file mode 100644 index 0000000..24e0a98 --- /dev/null +++ b/21. Protocols.playground/Documentation/section-54.html @@ -0,0 +1,29 @@ + + + + + Section 55 + + + + + + +
+
+

Optional Protocol Requirements

+

Sometimes it's convenient to declare protocols that have one or more requirements that are optional. This is done by prefixing those requirements with the 'optional' keyword.

+

The term "optional protocol" refers to protocols that are optional in a very similar since to optionals we've seen in the past. However, rather than stored values that can be nil, they can still use optional chaining and optional binding for determining if an optional requirement has been satisfied and if so, using that requirement.

+

As with Protocol Conformance, a protocol that uses optional requirements must also be prefixed with the '@objc' attribute.

+

A special note about @objc attribute:

+
    +
  • In order to check if an instance of a class conforms to a given protocol, that protocol must be declared with the @objc attribute.
  • +
  • This is also the case with optional requirements in protocols. In order to use the optional declaration modifier, the protocol must be declared with the @objc attribute.
  • +
  • Additionally, any class, structure or enumeration that owns an instance that conforms to a protocol declared with the @objc attribute must also be declared with the @objc attribute.
  • +
+

Here's another simple protocol that uses optional requrements:

+ +
+
+ + diff --git a/21. Protocols.playground/Documentation/section-56.html b/21. Protocols.playground/Documentation/section-56.html new file mode 100644 index 0000000..03a467d --- /dev/null +++ b/21. Protocols.playground/Documentation/section-56.html @@ -0,0 +1,19 @@ + + + + + Section 57 + + + + + + +
+
+

In the class below, we'll see that checking to see if an instance conforms to a specific requirement is similar to checking for (and accessing) optionals. We'll use optional chaining for these optional reqirements:

+ +
+
+ + diff --git a/21. Protocols.playground/Documentation/section-6.html b/21. Protocols.playground/Documentation/section-6.html new file mode 100644 index 0000000..6902d26 --- /dev/null +++ b/21. Protocols.playground/Documentation/section-6.html @@ -0,0 +1,19 @@ + + + + + Section 7 + + + + + + +
+
+

Let's try a more complex class

+ +
+
+ + diff --git a/21. Protocols.playground/Documentation/section-8.html b/21. Protocols.playground/Documentation/section-8.html new file mode 100644 index 0000000..3ae7674 --- /dev/null +++ b/21. Protocols.playground/Documentation/section-8.html @@ -0,0 +1,20 @@ + + + + + Section 9 + + + + + + +
+
+

In the class above, we use a 'name' and an optional 'prefix' to represent the full name, then provide a computed value to fulfill our 'fullName' requirement.

+

Here it is in action:

+ +
+
+ + diff --git a/21. Protocols.playground/Documentation/stylesheet.css b/21. Protocols.playground/Documentation/stylesheet.css new file mode 100644 index 0000000..43b0230 --- /dev/null +++ b/21. Protocols.playground/Documentation/stylesheet.css @@ -0,0 +1,65 @@ +html { + font-size: 1em; +} + +body { + background-color: #fff; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: normal; + font-size: 1.0em; + line-height: 140%; + padding: 0 2em; +} + +code { + font-family: "Menlo", monospace; + font-size: 0.95em; +} + +section { + padding: 0.5em 0; + -webkit-overflow-scrolling: touch; +} + +a:link { + color: #00f; + text-decoration: none; + border-bottom: 1px solid #00f; +} + +h1 { + font-size: 1.5em; + padding: 0.7em 0 0 0; +} + +h2 { + font-size: 1.2em; + padding: 0.7em 0 0 0; +} + +h3 { + font-size: 1.0em; + padding: 0.7em 0 0 0; +} + +blockquote { + font-size: 0.9em; + margin-left: 0; + padding-left: 2em; + border-left: 8px solid #eee; +} + +.footnotes { + color: #666666; + margin-top: 2em; + font-size: 0.9em; + letter-spacing: -3px; +} + +.footnotes:before { + content: "――――"; +} + +.footnotes ol { + letter-spacing: normal; +} diff --git a/21. Protocols.playground/contents.xcplayground b/21. Protocols.playground/contents.xcplayground index 4937636..8cc069e 100644 --- a/21. Protocols.playground/contents.xcplayground +++ b/21. Protocols.playground/contents.xcplayground @@ -1,7 +1,121 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file diff --git a/21. Protocols.playground/section-1.swift b/21. Protocols.playground/section-1.swift index 6f1abc6..f7480f8 100644 --- a/21. Protocols.playground/section-1.swift +++ b/21. Protocols.playground/section-1.swift @@ -1,49 +1,3 @@ -// ------------------------------------------------------------------------------------------------ -// Things to know: -// -// * Protocols define a required set of functionality (including methods and properties) for a -// class, structure or enumeration. -// -// * Protocols are "adopted" by a class, structure or enumeration to provide the implementation. -// This is called "conforming" to a protocol. -// -// * As you should already be aware, protocols are often used for delegation. -// -// * Protcol Syntax looks like this: -// -// protocol SomeProtocol -// { -// // protocol definition goes here -// } -// -// * Protocol adoption looks like this: -// -// struct SomeStructure: FirstProtocol, AnotherProtocol -// { -// // structure definition goes here -// } -// -// class SomeClass: SomeSuperclass, FirstProtocol, AnotherProtocol -// { -// // class definition goes here -// } -// ------------------------------------------------------------------------------------------------ - -// ------------------------------------------------------------------------------------------------ -// Property requirements -// -// * A protocol can require the conforming type to provide an instance of a property or type -// property. -// -// * The protocol can also specify if the property must be gettable or gettable and -// settable. If a protocol only requires a gettable property, the conforming class can use -// a stored property or a computed property. Also, the conforming class is allowed to add -// a setter if it needs. -// -// * Property requirements are always declared as variable types with the 'var' introducer. -// -// Let's take a look at a simple protocol. As you'll see, we only need to define the properties -// in terms of 'get' and 'set' and do not provide the actual functionality. protocol someProtocolForProperties { // A read/write property @@ -55,354 +9,4 @@ protocol someProtocolForProperties // A type property always uses 'class'. This is the case even if adopted by a structure or // enumeration which will use 'static' when conforming to the protocol's property. class var someTypeProperty: Int { get set } -} - -// Let's create a more practical protocol that we can actually conform to: -protocol FullyNamed -{ - var fullName: String { get } -} - -// A structure that conforms to FullyNamed. We won't bother creating an explicit getter or setter -// for the 'fullName' property because Swift's defaults will suffice. -struct Person: FullyNamed -{ - var fullName: String -} - -let john = Person(fullName: "John Smith") - -// Let's try a more complex class -class Starship: FullyNamed -{ - var prefix: String? - var name: String - init(name: String, prefix: String? = nil) - { - self.name = name - self.prefix = prefix - } - - var fullName: String - { - return (prefix != .None ? prefix! + " " : "") + name - } -} - -// In the class above, we use a 'name' and an optional 'prefix' to represent the full name, then -// provide a computed value to fulfill our 'fullName' requirement. -// -// Here it is in action: -var ncc1701 = Starship(name: "Enterprise", prefix: "USS") -ncc1701.fullName - -// ------------------------------------------------------------------------------------------------ -// Method Requirements -// -// Similar to property requirements, the protocol only defines the method type, not the -// implementation. -// -// Variadic parameters are allowed, but default values are not. -// -// Method requirements can be mutating - simply use the 'mutating' keyword before the function -// definition. Note that when conforming to the protocol, only classes are required to use the -// 'mutating' keyword when conforming to the protocol, following the normal procedure. -// -// As with property requirements, type methods are always prefixed with the 'class' keyword. -// -// Here's a simple example that defines the requirements for a 'random' function which takes -// no parameters and returns a Double: -protocol RandomNumberGenerator -{ - func random() -> Double -} - -// Let's adopt this protocol: -class LinearCongruentialGenerator: RandomNumberGenerator -{ - var lastRandom = 42.0 - var m = 139956.0 - var a = 3877.0 - var c = 29573.0 - func random() -> Double - { - lastRandom = ((lastRandom * a + c) % m) - return lastRandom / m - } -} -let generator = LinearCongruentialGenerator() -generator.random() -generator.random() -generator.random() - -// ------------------------------------------------------------------------------------------------ -// Protocols as Types -// -// Protocols are types, which means they can be used where many other types are allowed, -// including: -// -// * As a parameter to a function, method or initializer -// * As the type of a constant, variable or property -// * As the type of items in an Array, Dictionary or other container -// -// Here's a protocol used as a type. We'll create a Dice class that stores a RandomNumberGenerator -// as a constant so that it can be used to customize the randomization of the dice roll: -class Dice -{ - let sides: Int - let generator: RandomNumberGenerator - init(sides: Int, generator: RandomNumberGenerator) - { - self.sides = sides - self.generator = generator - } - - func roll() -> Int - { - return Int(generator.random() * Double(sides)) + 1 - } -} - -// Just for fun, let's roll a few: -let d6 = Dice(sides: 6, generator: generator) -d6.roll() -d6.roll() -d6.roll() -d6.roll() -d6.roll() -d6.roll() - -// ------------------------------------------------------------------------------------------------ -// Adding Protocol Conformance with an Extension -// -// Existing classes, structures and enumerations can be extended to conform to a given protocol. -// -// Let's start with a simple protocol we'll use for expirimentation: -protocol TextRepresentable -{ - func asText() -> String -} - -// We'll extend our Dice class: -extension Dice: TextRepresentable -{ - func asText() -> String - { - return "A \(sides)-sided dice" - } -} - -// Existing instances (such as 'd6' will automatically adopt and conform to the new protocol, even -// though it was declared prior to the extension. -d6.asText() - -// ------------------------------------------------------------------------------------------------ -// Declaring Protocol Adoption with an Extension -// -// Some types may already adopt a protocol, but do not state so in their definition. Types do not -// automatically adopt to protocols whose requirements they satisfy - protocol adoption must -// always be stated explicitly. -// -// This can be resolved with by using an empty extension. -// -// To showcase this, let's create a structure that conforms to the TextRepresentable protocol, but -// doesn't include this as part of the definition: -struct Hamster -{ - var name: String - func asText() -> String - { - return "A hamster named \(name)" - } -} - -// Let's verify our work: -let tedTheHamster = Hamster(name: "Ted") -tedTheHamster.asText() - -// We can add TextRepresentable conformance to Hamster by using an empty extension. -// -// This works because the requirements are already met, so we don't need to include any additional -// functionality within the extension definition. -extension Hamster: TextRepresentable -{ - -} - -// ------------------------------------------------------------------------------------------------ -// Collections of Protocol Types -// -// Hamsters and Dice don't have much in common, but in our sample code above, they both conform -// to the TextRepresentable protocol. Because of this, we can create an array of things that are -// TextRepresentable which includes each: -let textRepresentableThigns: [TextRepresentable] = [d6, tedTheHamster] - -// We can now loop through each and produce its text representation: -for thing in textRepresentableThigns -{ - thing.asText() -} - -// ------------------------------------------------------------------------------------------------ -// Protocol Inheritance -// -// Protocols can inherit from other protocols in order to add further requirements. The syntax -// for this is similar to a class ineriting from its superclass. -// -// Let's create a new text representable type, inherited from TextRepresentable: -protocol PrettyTextRepresentable: TextRepresentable -{ - func asPrettyText() -> String -} - -// Let's make our Dice a little prettier. -// -// Note that the Dice already knows that it conforms to the TextRepresentable, which means we can -// call asText() inside our asPrettyText() method. -extension Dice: PrettyTextRepresentable -{ - func asPrettyText() -> String - { - return "The pretty version of " + asText() - } -} - -// We can test our work: -d6.asPrettyText() - -// ------------------------------------------------------------------------------------------------ -// Protocol Composition -// -// Protocols can be combined such that a type conforms to each of them. For example, a person can -// be an aged person as well as a named person. -// -// Protocol Composition is a syntactic method of declaring that an instance conforms to a set -// of protocols. It takes the form "protocol". There can be as -// many protocols between the angle brackets as you need. -// -// Let's start by creating a couple of protocols for expirimentation: -protocol Named -{ - var name: String { get } -} -protocol Aged -{ - var age: Int { get } -} - -// Here, we declare an Individual that conforms to both Name and Age protocols: -struct Individual: Named, Aged -{ - var name: String - var age: Int -} - -// Here, we can see the protocol composition at work as the parameter into the wishHappyBirthday() -// function: -func wishHappyBirthday(celebrator: protocol) -> String -{ - return "Happy Birthday \(celebrator.name) - you're \(celebrator.age)!" -} - -// If we call the member, we can see the celebratory wish for this individual: -wishHappyBirthday(Individual(name: "Bill", age: 31)) - -// ------------------------------------------------------------------------------------------------ -// Checking for Protocol Conformance -// -// We can use 'is' and 'as' for testing for protocol conformance, just as we've seen in the -// section on Type Casting. -// -// In order for this to work with protocols, they must be marked with an "@objc" attribute. See -// further down in this playground for a special note about the @objc attribute. -// -// Let's create a new protocol with the proper prefix so that we can investigate: -@objc protocol HasArea -{ - var area: Double { get } -} - -class Circle: HasArea -{ - let pi = 3.14159 - var radius: Double - var area: Double { return pi * radius * radius } - init(radius: Double) { self.radius = radius } -} -class Country: HasArea -{ - var area: Double - init(area: Double) { self.area = area } -} -class Animal -{ - var legs: Int - init(legs: Int) { self.legs = legs } -} - -// We can store our objects into an array of type AnyObject[] -let objects: [AnyObject] = -[ - Circle(radius: 3.0), - Country(area: 4356947.0), - Animal(legs: 4) -] - -// Then we can test each for conformance to HasArea: -objects[0] is HasArea -objects[1] is HasArea -objects[2] is HasArea - -// ------------------------------------------------------------------------------------------------ -// Optional Protocol Requirements -// -// Sometimes it's convenient to declare protocols that have one or more requirements that are -// optional. This is done by prefixing those requirements with the 'optional' keyword. -// -// The term "optional protocol" refers to protocols that are optional in a very similar since to -// optionals we've seen in the past. However, rather than stored values that can be nil, they -// can still use optional chaining and optional binding for determining if an optional requirement -// has been satisfied and if so, using that requirement. -// -// As with Protocol Conformance, a protocol that uses optional requirements must also be prefixed -// with the '@objc' attribute. -// -// A special note about @objc attribute: -// -// * In order to check if an instance of a class conforms to a given protocol, that protocol must -// be declared with the @objc attribute. -// -// * This is also the case with optional requirements in protocols. In order to use the optional -// declaration modifier, the protocol must be declared with the @objc attribute. -// -// * Additionally, any class, structure or enumeration that owns an instance that conforms to a -// protocol declared with the @objc attribute must also be declared with the @objc attribute. -// -// Here's another simple protocol that uses optional requrements: -@objc protocol CounterDataSource -{ - optional func incrementForCount(count: Int) -> Int - optional var fixedIncrement: Int { get } -} - -// In the class below, we'll see that checking to see if an instance conforms to a specific -// requirement is similar to checking for (and accessing) optionals. We'll use optional chaining -// for these optional reqirements: -@objc class Counter -{ - var count = 0 - var dataSource: CounterDataSource? - func increment() - { - // Does the dataSource conform to the incrementForCount method? - if let amount = dataSource?.incrementForCount?(count) - { - count += amount - } - // If not, does it conform to the fixedIncrement variable requirement? - else if let amount = dataSource?.fixedIncrement? - { - count += amount - } - } -} +} \ No newline at end of file diff --git a/21. Protocols.playground/section-11.swift b/21. Protocols.playground/section-11.swift new file mode 100644 index 0000000..b42752c --- /dev/null +++ b/21. Protocols.playground/section-11.swift @@ -0,0 +1,4 @@ +protocol RandomNumberGenerator +{ + func random() -> Double +} \ No newline at end of file diff --git a/21. Protocols.playground/section-13.swift b/21. Protocols.playground/section-13.swift new file mode 100644 index 0000000..b70a043 --- /dev/null +++ b/21. Protocols.playground/section-13.swift @@ -0,0 +1,16 @@ +class LinearCongruentialGenerator: RandomNumberGenerator +{ + var lastRandom = 42.0 + var m = 139956.0 + var a = 3877.0 + var c = 29573.0 + func random() -> Double + { + lastRandom = ((lastRandom * a + c) % m) + return lastRandom / m + } +} +let generator = LinearCongruentialGenerator() +generator.random() +generator.random() +generator.random() \ No newline at end of file diff --git a/21. Protocols.playground/section-15.swift b/21. Protocols.playground/section-15.swift new file mode 100644 index 0000000..909e87a --- /dev/null +++ b/21. Protocols.playground/section-15.swift @@ -0,0 +1,15 @@ +class Dice +{ + let sides: Int + let generator: RandomNumberGenerator + init(sides: Int, generator: RandomNumberGenerator) + { + self.sides = sides + self.generator = generator + } + + func roll() -> Int + { + return Int(generator.random() * Double(sides)) + 1 + } +} \ No newline at end of file diff --git a/21. Protocols.playground/section-17.swift b/21. Protocols.playground/section-17.swift new file mode 100644 index 0000000..3b3ae44 --- /dev/null +++ b/21. Protocols.playground/section-17.swift @@ -0,0 +1,7 @@ +let d6 = Dice(sides: 6, generator: generator) +d6.roll() +d6.roll() +d6.roll() +d6.roll() +d6.roll() +d6.roll() \ No newline at end of file diff --git a/21. Protocols.playground/section-19.swift b/21. Protocols.playground/section-19.swift new file mode 100644 index 0000000..6cbf32a --- /dev/null +++ b/21. Protocols.playground/section-19.swift @@ -0,0 +1,4 @@ +protocol TextRepresentable +{ + func asText() -> String +} \ No newline at end of file diff --git a/21. Protocols.playground/section-21.swift b/21. Protocols.playground/section-21.swift new file mode 100644 index 0000000..a0807f7 --- /dev/null +++ b/21. Protocols.playground/section-21.swift @@ -0,0 +1,7 @@ +extension Dice: TextRepresentable +{ + func asText() -> String + { + return "A \(sides)-sided dice" + } +} \ No newline at end of file diff --git a/21. Protocols.playground/section-23.swift b/21. Protocols.playground/section-23.swift new file mode 100644 index 0000000..4d148a2 --- /dev/null +++ b/21. Protocols.playground/section-23.swift @@ -0,0 +1 @@ +d6.asText() \ No newline at end of file diff --git a/21. Protocols.playground/section-25.swift b/21. Protocols.playground/section-25.swift new file mode 100644 index 0000000..69d1be2 --- /dev/null +++ b/21. Protocols.playground/section-25.swift @@ -0,0 +1,8 @@ +struct Hamster +{ + var name: String + func asText() -> String + { + return "A hamster named \(name)" + } +} \ No newline at end of file diff --git a/21. Protocols.playground/section-27.swift b/21. Protocols.playground/section-27.swift new file mode 100644 index 0000000..899410c --- /dev/null +++ b/21. Protocols.playground/section-27.swift @@ -0,0 +1,2 @@ +let tedTheHamster = Hamster(name: "Ted") +tedTheHamster.asText() \ No newline at end of file diff --git a/21. Protocols.playground/section-29.swift b/21. Protocols.playground/section-29.swift new file mode 100644 index 0000000..cd8e75e --- /dev/null +++ b/21. Protocols.playground/section-29.swift @@ -0,0 +1,4 @@ +extension Hamster: TextRepresentable +{ + +} \ No newline at end of file diff --git a/21. Protocols.playground/section-3.swift b/21. Protocols.playground/section-3.swift new file mode 100644 index 0000000..e2de58f --- /dev/null +++ b/21. Protocols.playground/section-3.swift @@ -0,0 +1,4 @@ +protocol FullyNamed +{ + var fullName: String { get } +} \ No newline at end of file diff --git a/21. Protocols.playground/section-31.swift b/21. Protocols.playground/section-31.swift new file mode 100644 index 0000000..0ebbe8d --- /dev/null +++ b/21. Protocols.playground/section-31.swift @@ -0,0 +1 @@ +let textRepresentableThigns: [TextRepresentable] = [d6, tedTheHamster] \ No newline at end of file diff --git a/21. Protocols.playground/section-33.swift b/21. Protocols.playground/section-33.swift new file mode 100644 index 0000000..6f5f41d --- /dev/null +++ b/21. Protocols.playground/section-33.swift @@ -0,0 +1,4 @@ +for thing in textRepresentableThigns +{ + thing.asText() +} \ No newline at end of file diff --git a/21. Protocols.playground/section-35.swift b/21. Protocols.playground/section-35.swift new file mode 100644 index 0000000..27d1449 --- /dev/null +++ b/21. Protocols.playground/section-35.swift @@ -0,0 +1,4 @@ +protocol PrettyTextRepresentable: TextRepresentable +{ + func asPrettyText() -> String +} \ No newline at end of file diff --git a/21. Protocols.playground/section-37.swift b/21. Protocols.playground/section-37.swift new file mode 100644 index 0000000..927bd50 --- /dev/null +++ b/21. Protocols.playground/section-37.swift @@ -0,0 +1,7 @@ +extension Dice: PrettyTextRepresentable +{ + func asPrettyText() -> String + { + return "The pretty version of " + asText() + } +} \ No newline at end of file diff --git a/21. Protocols.playground/section-39.swift b/21. Protocols.playground/section-39.swift new file mode 100644 index 0000000..dfde562 --- /dev/null +++ b/21. Protocols.playground/section-39.swift @@ -0,0 +1 @@ +d6.asPrettyText() \ No newline at end of file diff --git a/21. Protocols.playground/section-41.swift b/21. Protocols.playground/section-41.swift new file mode 100644 index 0000000..b42659d --- /dev/null +++ b/21. Protocols.playground/section-41.swift @@ -0,0 +1,8 @@ +protocol Named +{ + var name: String { get } +} +protocol Aged +{ + var age: Int { get } +} \ No newline at end of file diff --git a/21. Protocols.playground/section-43.swift b/21. Protocols.playground/section-43.swift new file mode 100644 index 0000000..cf2cf06 --- /dev/null +++ b/21. Protocols.playground/section-43.swift @@ -0,0 +1,5 @@ +struct Individual: Named, Aged +{ + var name: String + var age: Int +} \ No newline at end of file diff --git a/21. Protocols.playground/section-45.swift b/21. Protocols.playground/section-45.swift new file mode 100644 index 0000000..5e43aad --- /dev/null +++ b/21. Protocols.playground/section-45.swift @@ -0,0 +1,4 @@ +func wishHappyBirthday(celebrator: protocol) -> String +{ + return "Happy Birthday \(celebrator.name) - you're \(celebrator.age)!" +} \ No newline at end of file diff --git a/21. Protocols.playground/section-47.swift b/21. Protocols.playground/section-47.swift new file mode 100644 index 0000000..5e3a88e --- /dev/null +++ b/21. Protocols.playground/section-47.swift @@ -0,0 +1 @@ +wishHappyBirthday(Individual(name: "Bill", age: 31)) \ No newline at end of file diff --git a/21. Protocols.playground/section-49.swift b/21. Protocols.playground/section-49.swift new file mode 100644 index 0000000..182b5df --- /dev/null +++ b/21. Protocols.playground/section-49.swift @@ -0,0 +1,22 @@ +@objc protocol HasArea +{ + var area: Double { get } +} + +class Circle: HasArea +{ + let pi = 3.14159 + var radius: Double + var area: Double { return pi * radius * radius } + init(radius: Double) { self.radius = radius } +} +class Country: HasArea +{ + var area: Double + init(area: Double) { self.area = area } +} +class Animal +{ + var legs: Int + init(legs: Int) { self.legs = legs } +} \ No newline at end of file diff --git a/21. Protocols.playground/section-5.swift b/21. Protocols.playground/section-5.swift new file mode 100644 index 0000000..a2fa2a0 --- /dev/null +++ b/21. Protocols.playground/section-5.swift @@ -0,0 +1,6 @@ +struct Person: FullyNamed +{ + var fullName: String +} + +let john = Person(fullName: "John Smith") \ No newline at end of file diff --git a/21. Protocols.playground/section-51.swift b/21. Protocols.playground/section-51.swift new file mode 100644 index 0000000..e44c4fa --- /dev/null +++ b/21. Protocols.playground/section-51.swift @@ -0,0 +1,6 @@ +let objects: [AnyObject] = +[ + Circle(radius: 3.0), + Country(area: 4356947.0), + Animal(legs: 4) +] \ No newline at end of file diff --git a/21. Protocols.playground/section-53.swift b/21. Protocols.playground/section-53.swift new file mode 100644 index 0000000..c1248b4 --- /dev/null +++ b/21. Protocols.playground/section-53.swift @@ -0,0 +1,3 @@ +objects[0] is HasArea +objects[1] is HasArea +objects[2] is HasArea \ No newline at end of file diff --git a/21. Protocols.playground/section-55.swift b/21. Protocols.playground/section-55.swift new file mode 100644 index 0000000..ff3dddb --- /dev/null +++ b/21. Protocols.playground/section-55.swift @@ -0,0 +1,5 @@ +@objc protocol CounterDataSource +{ + optional func incrementForCount(count: Int) -> Int + optional var fixedIncrement: Int { get } +} \ No newline at end of file diff --git a/21. Protocols.playground/section-57.swift b/21. Protocols.playground/section-57.swift new file mode 100644 index 0000000..df31a62 --- /dev/null +++ b/21. Protocols.playground/section-57.swift @@ -0,0 +1,18 @@ +@objc class Counter +{ + var count = 0 + var dataSource: CounterDataSource? + func increment() + { + // Does the dataSource conform to the incrementForCount method? + if let amount = dataSource?.incrementForCount?(count) + { + count += amount + } + // If not, does it conform to the fixedIncrement variable requirement? + else if let amount = dataSource?.fixedIncrement? + { + count += amount + } + } +} \ No newline at end of file diff --git a/21. Protocols.playground/section-7.swift b/21. Protocols.playground/section-7.swift new file mode 100644 index 0000000..e468ab2 --- /dev/null +++ b/21. Protocols.playground/section-7.swift @@ -0,0 +1,15 @@ +class Starship: FullyNamed +{ + var prefix: String? + var name: String + init(name: String, prefix: String? = nil) + { + self.name = name + self.prefix = prefix + } + + var fullName: String + { + return (prefix != .None ? prefix! + " " : "") + name + } +} \ No newline at end of file diff --git a/21. Protocols.playground/section-9.swift b/21. Protocols.playground/section-9.swift new file mode 100644 index 0000000..da92f6b --- /dev/null +++ b/21. Protocols.playground/section-9.swift @@ -0,0 +1,2 @@ +var ncc1701 = Starship(name: "Enterprise", prefix: "USS") +ncc1701.fullName \ No newline at end of file diff --git a/21. Protocols.playground/timeline.xctimeline b/21. Protocols.playground/timeline.xctimeline deleted file mode 100644 index e56777b..0000000 --- a/21. Protocols.playground/timeline.xctimeline +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - diff --git a/22. Generics.playground/Documentation/section-0.html b/22. Generics.playground/Documentation/section-0.html new file mode 100644 index 0000000..ddd4fd2 --- /dev/null +++ b/22. Generics.playground/Documentation/section-0.html @@ -0,0 +1,28 @@ + + + + + Section 1 + + + + + + +
+
+

Generics

+

Things to know:

+
    +
  • Generics allow flexible, reusable functions and types that can work with any type, subject to restrictions that you define.
  • +
  • Swift's Array and Dictionary are both Generics.
  • +
  • Generics can be applied to Functions, Structures, Classes and Enumerations.
  • +
+
+

The problem that Generics solve

+

Consider the following function which can swap two Ints.

+ +
+
+ + diff --git a/22. Generics.playground/Documentation/section-10.html b/22. Generics.playground/Documentation/section-10.html new file mode 100644 index 0000000..3e2065a --- /dev/null +++ b/22. Generics.playground/Documentation/section-10.html @@ -0,0 +1,21 @@ + + + + + Section 11 + + + + + + +
+
+

Type constraints

+

So far, our type parameters are completely Generic - they can represent any given type. Sometimes we may want to apply constraints to those types. For example, the Swift Dictionary type uses generics and places a constraint on the key's type that it must be hashable (i.e., it must conform to the Hashable protocol, defined in the Swift standard library.)

+

Constraints are defined with the following syntax:

+ +
+
+ + diff --git a/22. Generics.playground/Documentation/section-12.html b/22. Generics.playground/Documentation/section-12.html new file mode 100644 index 0000000..00e3218 --- /dev/null +++ b/22. Generics.playground/Documentation/section-12.html @@ -0,0 +1,20 @@ + + + + + Section 13 + + + + + + +
+
+

Let's see type constraints in action. We'll create a function that finds a given value within an array and returns an optional index into the array where the first element was found.

+

Take notice the constraint "Equatable" on the type T, which is key to making this function compile. Without it, we would get an error on the conditional statement used to compare each element from the array with the value being searched for. By including the Equatable, we tell the generic function that it is guaranteed to receive only values that meet that specific criteria.

+ +
+
+ + diff --git a/22. Generics.playground/Documentation/section-14.html b/22. Generics.playground/Documentation/section-14.html new file mode 100644 index 0000000..99b527b --- /dev/null +++ b/22. Generics.playground/Documentation/section-14.html @@ -0,0 +1,19 @@ + + + + + Section 15 + + + + + + +
+
+

Let's try a few different inputs

+ +
+
+ + diff --git a/22. Generics.playground/Documentation/section-16.html b/22. Generics.playground/Documentation/section-16.html new file mode 100644 index 0000000..f5aa040 --- /dev/null +++ b/22. Generics.playground/Documentation/section-16.html @@ -0,0 +1,21 @@ + + + + + Section 17 + + + + + + +
+
+

Associated types

+

Protocols use a different method of defining generic types, called Associated Types, which use type inference combined with Type Aliases.

+

Let's jump right into some code:

+ +
+
+ + diff --git a/22. Generics.playground/Documentation/section-18.html b/22. Generics.playground/Documentation/section-18.html new file mode 100644 index 0000000..403264b --- /dev/null +++ b/22. Generics.playground/Documentation/section-18.html @@ -0,0 +1,21 @@ + + + + + Section 19 + + + + + + +
+
+

In the example above, we declare a Type Alias called ItemType, but because we're declaring a protocol, we're only declaring the requirement for the conforming target to provide the actual type alias.

+

With Generics, the type of ItemType can actually be inferred, such that it provides the correct types for the append() and the subscript implementations.

+

Let's see this in action as we turn our Stack into a container:

+ +
+
+ + diff --git a/22. Generics.playground/Documentation/section-2.html b/22. Generics.playground/Documentation/section-2.html new file mode 100644 index 0000000..7d8ebba --- /dev/null +++ b/22. Generics.playground/Documentation/section-2.html @@ -0,0 +1,19 @@ + + + + + Section 3 + + + + + + +
+
+

What if we wanted to swap Strings? Or any other type? We would need to write a lot of different swap functions. Instead, let's use Generics. Consider the following generic function:

+ +
+
+ + diff --git a/22. Generics.playground/Documentation/section-20.html b/22. Generics.playground/Documentation/section-20.html new file mode 100644 index 0000000..dc8af43 --- /dev/null +++ b/22. Generics.playground/Documentation/section-20.html @@ -0,0 +1,20 @@ + + + + + Section 21 + + + + + + +
+
+

The new StackContainer is now ready to go. You may notice that it does not include the typealias that was required as part of the Container protocol. This is because the all of the places where an ItemType would be used are using T. This allows Swift to perform a backwards inferrence that ItemType must be a type T, and it allows this requirement to be met.

+

Let's verify our work:

+ +
+
+ + diff --git a/22. Generics.playground/Documentation/section-22.html b/22. Generics.playground/Documentation/section-22.html new file mode 100644 index 0000000..a6a5ab9 --- /dev/null +++ b/22. Generics.playground/Documentation/section-22.html @@ -0,0 +1,20 @@ + + + + + Section 23 + + + + + + +
+
+

We can also extend an existing types to conform to our new generic protocol. As it turns out Swift's built-in Array class already supports the requirements of our Container.

+

Also, since the protocol's type inferrence method of implementing Generics, we can extend String without the need to modify String other than to extend it to conform to the protocol:

+ +
+
+ + diff --git a/22. Generics.playground/Documentation/section-24.html b/22. Generics.playground/Documentation/section-24.html new file mode 100644 index 0000000..15930e1 --- /dev/null +++ b/22. Generics.playground/Documentation/section-24.html @@ -0,0 +1,21 @@ + + + + + Section 25 + + + + + + +
+
+

Where Clauses

+

We can further extend our constraints on a type by including where clauses as part of a type parameter list. Where clauses provide a means for more constraints on associated types and/or one or more equality relationships between types and associated types.

+

Let's take a look at a where clause in action. We'll define a function that works on two different containers that that must contain the same type of item.

+ +
+
+ + diff --git a/22. Generics.playground/Documentation/section-26.html b/22. Generics.playground/Documentation/section-26.html new file mode 100644 index 0000000..b11550b --- /dev/null +++ b/22. Generics.playground/Documentation/section-26.html @@ -0,0 +1,27 @@ + + + + + Section 27 + + + + + + +
+
+

The function's type parameter list places the following restrictions on the types allowed:

+
    +
  • C1 must conform to the Container protocol (C1: Container)
  • +
  • C2 must also conform to the Container protocol (C1: Container)
  • +
  • The ItemType for C1 must be the same as the ItemType for C2 (C1.ItemType == C2.ItemType)
  • +
  • The ItemType for C1 must conform to the Equatable protocol (C1.ItemType: Equatable)
  • +
+

Note that we only need to specify that C1.ItemType conforms to Equatable because the code only calls the != operator (part of the Equatable protocol) on someContainer, which is the type C1.

+

Let's test this out by passing the same value for each parameter which should definitely return true:

+ +
+
+ + diff --git a/22. Generics.playground/Documentation/section-28.html b/22. Generics.playground/Documentation/section-28.html new file mode 100644 index 0000000..0e9623f --- /dev/null +++ b/22. Generics.playground/Documentation/section-28.html @@ -0,0 +1,19 @@ + + + + + Section 29 + + + + + + +
+
+

We can compare stringStack against an array of type String[] because we've extended Swift's Array type to conform to our Container protocol:

+ +
+
+ + diff --git a/22. Generics.playground/Documentation/section-30.html b/22. Generics.playground/Documentation/section-30.html new file mode 100644 index 0000000..29d7fe3 --- /dev/null +++ b/22. Generics.playground/Documentation/section-30.html @@ -0,0 +1,20 @@ + + + + + Section 31 + + + + + + +
+
+

Finally, if we attempt to call allItemsMatch with a stringStack and a doubleStack, we would get a compiler error because they do not store the same ItemType as defined in the function's where clause.

+

The following line of code does not compile:

+ +
+
+ + diff --git a/22. Generics.playground/Documentation/section-4.html b/22. Generics.playground/Documentation/section-4.html new file mode 100644 index 0000000..42ff5f4 --- /dev/null +++ b/22. Generics.playground/Documentation/section-4.html @@ -0,0 +1,25 @@ + + + + + Section 5 + + + + + + +
+
+

The 'swapTwoValues()' function is a generic function in a sense that some or all of the types that it works on are generic (i.e., specific only to the calls placed on that function.)

+

Study the first line of the function and notice the use of and the type for 'a' and 'b' as type T. In this case, T is just a placeholder for a type and by studying this function, we can see that both 'a' and 'b' are the same type.

+

If we call this function with two Integers, it will treat the function as one declared to accept two Ints, but if we pass in two Strings, it will work on Strings.

+

If we study the body of the function, we'll see that it is coded in such a way as to work with any type passed in: The 'tmp' parameter's type is inferred by the value 'a' and 'a' and 'b' must be assignable. If any of this criteria are not met, a compilation error will appear for the function call the tries to call swapTwoValues() with a type that doesn't meet this criteria.

+

Although we're using T as the name for our type placeholder, we can use any name we wish, though T is a common placeholder for single type lists. If we were to create a new implementation of the Dictionary class, we would want to use two type parameters and name them effectively, such as .

+

A type placholder can also be used to define the return type.

+

Let's call it a few times to see it in action:

+ +
+
+ + diff --git a/22. Generics.playground/Documentation/section-6.html b/22. Generics.playground/Documentation/section-6.html new file mode 100644 index 0000000..1126da5 --- /dev/null +++ b/22. Generics.playground/Documentation/section-6.html @@ -0,0 +1,21 @@ + + + + + Section 7 + + + + + + +
+
+

Generic Types

+

So far we've seen how to apply Generics to a function, let's see how they can be applied to a struct. We'll define a standard 'stack' implementation which works like an array that can "push" an element to the end of the array, or "pop" an element off of the end of the array.

+

As you can see, the type placeholder, once defined for a struct, can be used anywhere in that struct to represent the given type. In the code below, the the type placeholder is used as the type for a property, the input parameter for a method and the return value for a method.

+ +
+
+ + diff --git a/22. Generics.playground/Documentation/section-8.html b/22. Generics.playground/Documentation/section-8.html new file mode 100644 index 0000000..ff3459b --- /dev/null +++ b/22. Generics.playground/Documentation/section-8.html @@ -0,0 +1,19 @@ + + + + + Section 9 + + + + + + +
+
+

Let's use our new Stack:

+ +
+
+ + diff --git a/22. Generics.playground/Documentation/stylesheet.css b/22. Generics.playground/Documentation/stylesheet.css new file mode 100644 index 0000000..43b0230 --- /dev/null +++ b/22. Generics.playground/Documentation/stylesheet.css @@ -0,0 +1,65 @@ +html { + font-size: 1em; +} + +body { + background-color: #fff; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: normal; + font-size: 1.0em; + line-height: 140%; + padding: 0 2em; +} + +code { + font-family: "Menlo", monospace; + font-size: 0.95em; +} + +section { + padding: 0.5em 0; + -webkit-overflow-scrolling: touch; +} + +a:link { + color: #00f; + text-decoration: none; + border-bottom: 1px solid #00f; +} + +h1 { + font-size: 1.5em; + padding: 0.7em 0 0 0; +} + +h2 { + font-size: 1.2em; + padding: 0.7em 0 0 0; +} + +h3 { + font-size: 1.0em; + padding: 0.7em 0 0 0; +} + +blockquote { + font-size: 0.9em; + margin-left: 0; + padding-left: 2em; + border-left: 8px solid #eee; +} + +.footnotes { + color: #666666; + margin-top: 2em; + font-size: 0.9em; + letter-spacing: -3px; +} + +.footnotes:before { + content: "――――"; +} + +.footnotes ol { + letter-spacing: normal; +} diff --git a/22. Generics.playground/contents.xcplayground b/22. Generics.playground/contents.xcplayground index 4937636..0e6428a 100644 --- a/22. Generics.playground/contents.xcplayground +++ b/22. Generics.playground/contents.xcplayground @@ -1,7 +1,69 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file diff --git a/22. Generics.playground/section-1.swift b/22. Generics.playground/section-1.swift index 3bd203c..c8fc0ed 100644 --- a/22. Generics.playground/section-1.swift +++ b/22. Generics.playground/section-1.swift @@ -1,291 +1,6 @@ -// ------------------------------------------------------------------------------------------------ -// Things to know: -// -// * Generics allow flexible, reusable functions and types that can work with any type, subject -// to restrictions that you define. -// -// * Swift's Array and Dictionary are both Generics. -// -// * Generics can be applied to Functions, Structures, Classes and Enumerations. -// ------------------------------------------------------------------------------------------------ - -// The problem that Generics solve -// -// Consider the following function which can swap two Ints. func swapTwoInts(inout a: Int, inout b: Int) { let tmp = a a = b b = tmp -} - -// What if we wanted to swap Strings? Or any other type? We would need to write a lot of different -// swap functions. Instead, let's use Generics. Consider the following generic function: -func swapTwoValues(inout a: T, inout b: T) -{ - let tmp = a - a = b - b = tmp -} - -// The 'swapTwoValues()' function is a generic function in a sense that some or all of the types -// that it works on are generic (i.e., specific only to the calls placed on that function.) -// -// Study the first line of the function and notice the use of and the type for 'a' and 'b' as -// type T. In this case, T is just a placeholder for a type and by studying this function, we can -// see that both 'a' and 'b' are the same type. -// -// If we call this function with two Integers, it will treat the function as one declared to accept -// two Ints, but if we pass in two Strings, it will work on Strings. -// -// If we study the body of the function, we'll see that it is coded in such a way as to work with -// any type passed in: The 'tmp' parameter's type is inferred by the value 'a' and 'a' and 'b' must -// be assignable. If any of this criteria are not met, a compilation error will appear for the -// function call the tries to call swapTwoValues() with a type that doesn't meet this criteria. -// -// Although we're using T as the name for our type placeholder, we can use any name we wish, though -// T is a common placeholder for single type lists. If we were to create a new implementation of -// the Dictionary class, we would want to use two type parameters and name them effectively, such -// as . -// -// A type placholder can also be used to define the return type. -// -// Let's call it a few times to see it in action: -var aInt = 3 -var bInt = 4 -swapTwoValues(&aInt, &bInt) -aInt -bInt - -var aDouble = 3.3 -var bDouble = 4.4 -swapTwoValues(&aDouble, &bDouble) -aDouble -bDouble - -var aString = "three" -var bString = "four" -swapTwoValues(&aString, &bString) -aString -bString - -// ------------------------------------------------------------------------------------------------ -// Generic Types -// -// So far we've seen how to apply Generics to a function, let's see how they can be applied to -// a struct. We'll define a standard 'stack' implementation which works like an array that can -// "push" an element to the end of the array, or "pop" an element off of the end of the array. -// -// As you can see, the type placeholder, once defined for a struct, can be used anywhere in that -// struct to represent the given type. In the code below, the the type placeholder is used as the -// type for a property, the input parameter for a method and the return value for a method. -struct Stack -{ - var items = [T]() - mutating func push(item: T) - { - items.append(item) - } - mutating func pop() -> T - { - return items.removeLast() - } -} - -// Let's use our new Stack: -var stackOfStrings = Stack() - -stackOfStrings.push("uno") -stackOfStrings.push("dos") -stackOfStrings.push("tres") -stackOfStrings.push("cuatro") - -stackOfStrings.pop() -stackOfStrings.pop() -stackOfStrings.pop() -stackOfStrings.pop() - -// ------------------------------------------------------------------------------------------------ -// Type constraints -// -// So far, our type parameters are completely Generic - they can represent any given type. -// Sometimes we may want to apply constraints to those types. For example, the Swift Dictionary -// type uses generics and places a constraint on the key's type that it must be hashable (i.e., it -// must conform to the Hashable protocol, defined in the Swift standard library.) -// -// Constraints are defined with the following syntax: -func doSomethingWithKeyValue(someKey: KeyType, someValue: ValueType) -{ - // Our keyType is known to be a Hashable, so we can use the hashValue defined by that protocol - // shown here: - someKey.hashValue - - // 'someValue' is an unknown type to us, we'll just drop it here in case it's ever used so we - // can see the value - someValue -} - -// Let's see type constraints in action. We'll create a function that finds a given value within -// an array and returns an optional index into the array where the first element was found. -// -// Take notice the constraint "Equatable" on the type T, which is key to making this function -// compile. Without it, we would get an error on the conditional statement used to compare each -// element from the array with the value being searched for. By including the Equatable, we tell -// the generic function that it is guaranteed to receive only values that meet that specific -// criteria. -func findIndex(array: [T], valueToFind: T) -> Int? -{ - for (index, value) in enumerate(array) - { - if value == valueToFind - { - return index - } - } - return nil -} - -// Let's try a few different inputs -let doubleIndex = findIndex([3.14159, 0.1, 0.25], 9.3) -let stringIndex = findIndex(["Mike", "Malcolm", "Andrea"], "Andrea") - -// ------------------------------------------------------------------------------------------------ -// Associated types -// -// Protocols use a different method of defining generic types, called Associated Types, which use -// type inference combined with Type Aliases. -// -// Let's jump right into some code: -protocol Container -{ - typealias ItemType - mutating func append(item: ItemType) - var count: Int { get } - subscript(i: Int) -> ItemType { get } -} - -// In the example above, we declare a Type Alias called ItemType, but because we're declaring -// a protocol, we're only declaring the requirement for the conforming target to provide the -// actual type alias. -// -// With Generics, the type of ItemType can actually be inferred, such that it provides the correct -// types for the append() and the subscript implementations. -// -// Let's see this in action as we turn our Stack into a container: - -struct StackContainer : Container -{ - // Here we find our original stack implementation, unmodified - - var items = [T]() - mutating func push(item: T) - { - items.append(item) - } - mutating func pop() -> T - { - return items.removeLast() - } - - // Below, we conform to the protocol - - mutating func append(item: T) - { - self.push(item) - } - var count: Int - { - return items.count - } - subscript(i: Int) -> T - { - return items[i] - } -} - -// The new StackContainer is now ready to go. You may notice that it does not include the -// typealias that was required as part of the Container protocol. This is because the all of the -// places where an ItemType would be used are using T. This allows Swift to perform a backwards -// inferrence that ItemType must be a type T, and it allows this requirement to be met. -// -// Let's verify our work: -var stringStack = StackContainer() -stringStack.push("Albert") -stringStack.push("Andrew") -stringStack.push("Betty") -stringStack.push("Jacob") -stringStack.pop() -stringStack.count - -var doubleStack = StackContainer() -doubleStack.push(3.14159) -doubleStack.push(42.0) -doubleStack.push(1_000_000) -doubleStack.pop() -doubleStack.count - -// We can also extend an existing types to conform to our new generic protocol. As it turns out -// Swift's built-in Array class already supports the requirements of our Container. -// -// Also, since the protocol's type inferrence method of implementing Generics, we can extend -// String without the need to modify String other than to extend it to conform to the protocol: -extension Array: Container {} - -// ------------------------------------------------------------------------------------------------ -// Where Clauses -// -// We can further extend our constraints on a type by including where clauses as part of a type -// parameter list. Where clauses provide a means for more constraints on associated types and/or -// one or more equality relationships between types and associated types. -// -// Let's take a look at a where clause in action. We'll define a function that works on two -// different containers that that must contain the same type of item. -func allItemsMatch - - (someContainer: C1, anotherContainer: C2) -> Bool -{ - // Check that both containers contain the same number of items - if someContainer.count != anotherContainer.count - { - return false - } - - // Check each pair of items to see if they are equivalent - for i in 0..(someKey: KeyType, someValue: ValueType) +{ + // Our keyType is known to be a Hashable, so we can use the hashValue defined by that protocol + // shown here: + someKey.hashValue + + // 'someValue' is an unknown type to us, we'll just drop it here in case it's ever used so we + // can see the value + someValue +} \ No newline at end of file diff --git a/22. Generics.playground/section-13.swift b/22. Generics.playground/section-13.swift new file mode 100644 index 0000000..a167348 --- /dev/null +++ b/22. Generics.playground/section-13.swift @@ -0,0 +1,11 @@ +func findIndex(array: [T], valueToFind: T) -> Int? +{ + for (index, value) in enumerate(array) + { + if value == valueToFind + { + return index + } + } + return nil +} \ No newline at end of file diff --git a/22. Generics.playground/section-15.swift b/22. Generics.playground/section-15.swift new file mode 100644 index 0000000..3b0d794 --- /dev/null +++ b/22. Generics.playground/section-15.swift @@ -0,0 +1,2 @@ +let doubleIndex = findIndex([3.14159, 0.1, 0.25], 9.3) +let stringIndex = findIndex(["Mike", "Malcolm", "Andrea"], "Andrea") \ No newline at end of file diff --git a/22. Generics.playground/section-17.swift b/22. Generics.playground/section-17.swift new file mode 100644 index 0000000..59cd8dd --- /dev/null +++ b/22. Generics.playground/section-17.swift @@ -0,0 +1,7 @@ +protocol Container +{ + typealias ItemType + mutating func append(item: ItemType) + var count: Int { get } + subscript(i: Int) -> ItemType { get } +} \ No newline at end of file diff --git a/22. Generics.playground/section-19.swift b/22. Generics.playground/section-19.swift new file mode 100644 index 0000000..97674e0 --- /dev/null +++ b/22. Generics.playground/section-19.swift @@ -0,0 +1,29 @@ +struct StackContainer : Container +{ + // Here we find our original stack implementation, unmodified + + var items = [T]() + mutating func push(item: T) + { + items.append(item) + } + mutating func pop() -> T + { + return items.removeLast() + } + + // Below, we conform to the protocol + + mutating func append(item: T) + { + self.push(item) + } + var count: Int + { + return items.count + } + subscript(i: Int) -> T + { + return items[i] + } +} \ No newline at end of file diff --git a/22. Generics.playground/section-21.swift b/22. Generics.playground/section-21.swift new file mode 100644 index 0000000..859638d --- /dev/null +++ b/22. Generics.playground/section-21.swift @@ -0,0 +1,14 @@ +var stringStack = StackContainer() +stringStack.push("Albert") +stringStack.push("Andrew") +stringStack.push("Betty") +stringStack.push("Jacob") +stringStack.pop() +stringStack.count + +var doubleStack = StackContainer() +doubleStack.push(3.14159) +doubleStack.push(42.0) +doubleStack.push(1_000_000) +doubleStack.pop() +doubleStack.count \ No newline at end of file diff --git a/22. Generics.playground/section-23.swift b/22. Generics.playground/section-23.swift new file mode 100644 index 0000000..d3b97fe --- /dev/null +++ b/22. Generics.playground/section-23.swift @@ -0,0 +1 @@ +extension Array: Container {} \ No newline at end of file diff --git a/22. Generics.playground/section-25.swift b/22. Generics.playground/section-25.swift new file mode 100644 index 0000000..e0754f5 --- /dev/null +++ b/22. Generics.playground/section-25.swift @@ -0,0 +1,22 @@ +func allItemsMatch + + (someContainer: C1, anotherContainer: C2) -> Bool +{ + // Check that both containers contain the same number of items + if someContainer.count != anotherContainer.count + { + return false + } + + // Check each pair of items to see if they are equivalent + for i in 0..(inout a: T, inout b: T) +{ + let tmp = a + a = b + b = tmp +} \ No newline at end of file diff --git a/22. Generics.playground/section-31.swift b/22. Generics.playground/section-31.swift new file mode 100644 index 0000000..555c12f --- /dev/null +++ b/22. Generics.playground/section-31.swift @@ -0,0 +1 @@ +// allItemsMatch(stringStack, doubleStack) \ No newline at end of file diff --git a/22. Generics.playground/section-5.swift b/22. Generics.playground/section-5.swift new file mode 100644 index 0000000..b40b6ea --- /dev/null +++ b/22. Generics.playground/section-5.swift @@ -0,0 +1,17 @@ +var aInt = 3 +var bInt = 4 +swapTwoValues(&aInt, &bInt) +aInt +bInt + +var aDouble = 3.3 +var bDouble = 4.4 +swapTwoValues(&aDouble, &bDouble) +aDouble +bDouble + +var aString = "three" +var bString = "four" +swapTwoValues(&aString, &bString) +aString +bString \ No newline at end of file diff --git a/22. Generics.playground/section-7.swift b/22. Generics.playground/section-7.swift new file mode 100644 index 0000000..dfda3de --- /dev/null +++ b/22. Generics.playground/section-7.swift @@ -0,0 +1,12 @@ +struct Stack +{ + var items = [T]() + mutating func push(item: T) + { + items.append(item) + } + mutating func pop() -> T + { + return items.removeLast() + } +} \ No newline at end of file diff --git a/22. Generics.playground/section-9.swift b/22. Generics.playground/section-9.swift new file mode 100644 index 0000000..7fd4e16 --- /dev/null +++ b/22. Generics.playground/section-9.swift @@ -0,0 +1,11 @@ +var stackOfStrings = Stack() + +stackOfStrings.push("uno") +stackOfStrings.push("dos") +stackOfStrings.push("tres") +stackOfStrings.push("cuatro") + +stackOfStrings.pop() +stackOfStrings.pop() +stackOfStrings.pop() +stackOfStrings.pop() \ No newline at end of file diff --git a/23. Advanced Operators.playground/Documentation/section-0.html b/23. Advanced Operators.playground/Documentation/section-0.html new file mode 100644 index 0000000..14e5275 --- /dev/null +++ b/23. Advanced Operators.playground/Documentation/section-0.html @@ -0,0 +1,28 @@ + + + + + Section 1 + + + + + + +
+
+

Advanced Operators

+

Things to know:

+
    +
  • Arithmetic operators in Swift do not automatically overflow. Adding two values that overflow their type (for example, storing 300 in a UInt8) will cause an error. There are special operators that allow overflow, including dividing by zero.
  • +
  • Swift allows developers to define their own operators, including those that Swift doesn't currently define. You can even specify the associativity and precedence for operators.
  • +
+
+

Bitwise Operators

+

The Bitwise operators (AND, OR, XOR, etc.) in Swift effeectively mirror the functionality that you're used to with C++ and Objective-C.

+

We'll cover them briefly. The odd formatting is intended to help show the results:

+ +
+
+ + diff --git a/23. Advanced Operators.playground/Documentation/section-10.html b/23. Advanced Operators.playground/Documentation/section-10.html new file mode 100644 index 0000000..96cf452 --- /dev/null +++ b/23. Advanced Operators.playground/Documentation/section-10.html @@ -0,0 +1,21 @@ + + + + + Section 11 + + + + + + +
+
+

Next, we'll define a simple vector addition (adding the individual x & y components to create a new vector.)

+

Since we're adding two Vector2D instances, we'll use operator "+". We want our addition to take the form "vectorA + vectorB", which means we'll be defining an infix operator.

+

Here's what that looks like:

+ +
+
+ + diff --git a/23. Advanced Operators.playground/Documentation/section-12.html b/23. Advanced Operators.playground/Documentation/section-12.html new file mode 100644 index 0000000..0bd4f59 --- /dev/null +++ b/23. Advanced Operators.playground/Documentation/section-12.html @@ -0,0 +1,19 @@ + + + + + Section 13 + + + + + + +
+
+

Let's verify our work:

+ +
+
+ + diff --git a/23. Advanced Operators.playground/Documentation/section-14.html b/23. Advanced Operators.playground/Documentation/section-14.html new file mode 100644 index 0000000..f228e81 --- /dev/null +++ b/23. Advanced Operators.playground/Documentation/section-14.html @@ -0,0 +1,19 @@ + + + + + Section 15 + + + + + + +
+
+

We've seen the infix operator at work so let's move on to the prefix and postfix operators. We'll define a prefix operator that negates the vector taking the form (result = -value):

+ +
+
+ + diff --git a/23. Advanced Operators.playground/Documentation/section-16.html b/23. Advanced Operators.playground/Documentation/section-16.html new file mode 100644 index 0000000..e65f0b4 --- /dev/null +++ b/23. Advanced Operators.playground/Documentation/section-16.html @@ -0,0 +1,19 @@ + + + + + Section 17 + + + + + + +
+
+

Check our work:

+ +
+
+ + diff --git a/23. Advanced Operators.playground/Documentation/section-18.html b/23. Advanced Operators.playground/Documentation/section-18.html new file mode 100644 index 0000000..887842f --- /dev/null +++ b/23. Advanced Operators.playground/Documentation/section-18.html @@ -0,0 +1,21 @@ + + + + + Section 19 + + + + + + +
+
+

Next, let's consider the common prefix increment operator (++a) and postfix increment (a++) operations. Each of these performs the operation on a single value whle also returning the appropriate result (either the original value before the increment or the value after the increment.)

+

Each will either use the @prefix or @postfix attribute, but since they also modify the value they are also @assigmnent operators (and make use of inout for the parameter.)

+

Let's take a look:

+ +
+
+ + diff --git a/23. Advanced Operators.playground/Documentation/section-2.html b/23. Advanced Operators.playground/Documentation/section-2.html new file mode 100644 index 0000000..a903473 --- /dev/null +++ b/23. Advanced Operators.playground/Documentation/section-2.html @@ -0,0 +1,21 @@ + + + + + Section 3 + + + + + + +
+
+

Shifting in Swift is slightly different than in C++.

+

A lesser-known fact about C++ is that the signed right-shift of a signed value is implementation specific. Most compilers do what most programmers expect, which is an arithmetic shift (which maintains the sign bit and shifts 1's into the word from the right.)

+

Swift defines this behavior to be what you expect, shifting signed values to the right will perform an arithmetic shift using two's compilment.

+ +
+
+ + diff --git a/23. Advanced Operators.playground/Documentation/section-20.html b/23. Advanced Operators.playground/Documentation/section-20.html new file mode 100644 index 0000000..9c6405a --- /dev/null +++ b/23. Advanced Operators.playground/Documentation/section-20.html @@ -0,0 +1,19 @@ + + + + + Section 21 + + + + + + +
+
+

And we can check our work:

+ +
+
+ + diff --git a/23. Advanced Operators.playground/Documentation/section-22.html b/23. Advanced Operators.playground/Documentation/section-22.html new file mode 100644 index 0000000..10635b1 --- /dev/null +++ b/23. Advanced Operators.playground/Documentation/section-22.html @@ -0,0 +1,21 @@ + + + + + Section 23 + + + + + + +
+
+

Equivalence Operators allow us to define a means for checking if two values are the same or equivalent. They can be "equal to" (==) or "not equal to" (!=). These are simple infix opertors that return a Bool result.

+

Let's also take a moment to make sure we do this right. When comparing floating point values you can either check for exact bit-wise equality (a == b) or you can compare values that are "very close" by using an epsilon. It's important to recognize the difference, as there are cases when IEEE floating point values should be equal, but are actually represented differently in their bit-wise format because they were calculated differently. In these cases, a simple equality comparison will not suffice.

+

So here are our more robust equivalence operators:

+ +
+
+ + diff --git a/23. Advanced Operators.playground/Documentation/section-24.html b/23. Advanced Operators.playground/Documentation/section-24.html new file mode 100644 index 0000000..a42202e --- /dev/null +++ b/23. Advanced Operators.playground/Documentation/section-24.html @@ -0,0 +1,24 @@ + + + + + Section 25 + + + + + + +
+
+

Custom Operators

+

So far, we've been defining operator functions for operators that Swift understands and for which Swift provides defined behaviors. We can also define our own custom operators for doing other interestig things.

+

For example, Swift doesn't support the concept of a "vector normalization" or "cross product" because this functionality doesn't apply to any of the types Swift offers.

+

Let's keep it simple, though. How about an operator that adds a vector to itself. Let's make this a prefix operator that takes the form "+++value"

+

First, we we must introduce Swift to our new operator. The syntax takes the form of the 'operator' keyword, folowed by either 'prefix', 'postfix' or 'infix':

+

Swift meet operator, operator meet swift:

+ +
+
+ + diff --git a/23. Advanced Operators.playground/Documentation/section-26.html b/23. Advanced Operators.playground/Documentation/section-26.html new file mode 100644 index 0000000..36bcfcd --- /dev/null +++ b/23. Advanced Operators.playground/Documentation/section-26.html @@ -0,0 +1,19 @@ + + + + + Section 27 + + + + + + +
+
+

Now we can declare our new operator:

+ +
+
+ + diff --git a/23. Advanced Operators.playground/Documentation/section-28.html b/23. Advanced Operators.playground/Documentation/section-28.html new file mode 100644 index 0000000..0975c47 --- /dev/null +++ b/23. Advanced Operators.playground/Documentation/section-28.html @@ -0,0 +1,19 @@ + + + + + Section 29 + + + + + + +
+
+

Let's check our work:

+ +
+
+ + diff --git a/23. Advanced Operators.playground/Documentation/section-30.html b/23. Advanced Operators.playground/Documentation/section-30.html new file mode 100644 index 0000000..6e17907 --- /dev/null +++ b/23. Advanced Operators.playground/Documentation/section-30.html @@ -0,0 +1,34 @@ + + + + + Section 31 + + + + + + +
+
+

Precedence and Associativity for Custom Infix Operators

+

Custom infix operators can define their own associativity (left-to-right or right-to-left or none) as well as a precedence for determining the order of operations.

+

Associativity values are 'left' or 'right' or 'none'.

+

Precedence values are ranked with a numeric value. If not specified, the default value is 100. Operations are performed in order of precedence, with higher values being performed first. The precedence values are relative to all other precedence values for other operators. The following are the default values for operator precedence in the Swift standard library:

+
160 (none):  Operators <<  >>
+150 (left):  Operators *  /  %  &*  &/  &%  &
+140 (left):  Operators +  -  &+  &-  |  ^
+135 (none):  Operators ..  ...
+132 (none):  Operators is as
+130 (none):  Operators < <= > >= == != === !== ~=
+120 (left):  Operators &&
+110 (left):  Operators ||
+100 (right): Operators ?:
+ 90 (right): Operators = *= /= %= += -= <<= >>= &= ^= |= &&= ||=
+

Let's take a look at how we define a new custom infix operator with left associativity and a precedence of 140.

+

We'll define a function that adds the 'x' components of two vectors, but subtracts the 'y' components. We'll call this the "+-" operator:

+ +
+
+ + diff --git a/23. Advanced Operators.playground/Documentation/section-32.html b/23. Advanced Operators.playground/Documentation/section-32.html new file mode 100644 index 0000000..7f850b3 --- /dev/null +++ b/23. Advanced Operators.playground/Documentation/section-32.html @@ -0,0 +1,19 @@ + + + + + Section 33 + + + + + + +
+
+

Check our work. Let's setup a couple vectors that result in a value of (0, 0):

+ +
+
+ + diff --git a/23. Advanced Operators.playground/Documentation/section-4.html b/23. Advanced Operators.playground/Documentation/section-4.html new file mode 100644 index 0000000..b8635c5 --- /dev/null +++ b/23. Advanced Operators.playground/Documentation/section-4.html @@ -0,0 +1,21 @@ + + + + + Section 5 + + + + + + +
+
+

Overflow operators

+

If an arithmetic operation (specifically addition (+), subtraction (-) and multiplication (*)) results in a value too large or too small for the constant or variable that the result is intended for, Swift will produce an overflow/underflow error.

+

The last two lines of this code block will trigger an overflow/underflow:

+ +
+
+ + diff --git a/23. Advanced Operators.playground/Documentation/section-6.html b/23. Advanced Operators.playground/Documentation/section-6.html new file mode 100644 index 0000000..16a7d46 --- /dev/null +++ b/23. Advanced Operators.playground/Documentation/section-6.html @@ -0,0 +1,22 @@ + + + + + Section 7 + + + + + + +
+
+

This is also true for division by zero, which can be caused with the division (/) or remainder (%) operators.

+

Sometimes, however, overflow and underflow behavior is exactly what the programmer may intend, so Swift provides specific overflow/underflow operators which will not trigger an error and allow the overflow/underflow to perform as we see in C++/Objective-C.

+

Special operators for division by zero are also provided, which return 0 in the case of a division by zero.

+

Here they are, in all their glory:

+ +
+
+ + diff --git a/23. Advanced Operators.playground/Documentation/section-8.html b/23. Advanced Operators.playground/Documentation/section-8.html new file mode 100644 index 0000000..aefb13e --- /dev/null +++ b/23. Advanced Operators.playground/Documentation/section-8.html @@ -0,0 +1,29 @@ + + + + + Section 9 + + + + + + +
+
+

Operator Functions (a.k.a., Operator Overloading)

+

Most C++ programmers should be familiar with the concept of operator overloading. Swift offers the same kind of functionality as well as additional functionality of specifying the operator precednce and associativity.

+

The most common operators will usually take one of the following forms:

+
    +
  • prefix: the operator appears before a single identifier as in "-a" or "++i"
  • +
  • postfix: the operator appears after a single identifier as in "i++"
  • +
  • infix: the operator appears between two identifiers as in "a + b" or "c / d"
  • +
+

These are specified with the three attributes, @prefix, @postfix and @infix.

+

There are more types of operators (which use different attributes, which we'll cover shortly.

+

Let's define a Vector2D class to work with:

+ +
+
+ + diff --git a/23. Advanced Operators.playground/Documentation/stylesheet.css b/23. Advanced Operators.playground/Documentation/stylesheet.css new file mode 100644 index 0000000..43b0230 --- /dev/null +++ b/23. Advanced Operators.playground/Documentation/stylesheet.css @@ -0,0 +1,65 @@ +html { + font-size: 1em; +} + +body { + background-color: #fff; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: normal; + font-size: 1.0em; + line-height: 140%; + padding: 0 2em; +} + +code { + font-family: "Menlo", monospace; + font-size: 0.95em; +} + +section { + padding: 0.5em 0; + -webkit-overflow-scrolling: touch; +} + +a:link { + color: #00f; + text-decoration: none; + border-bottom: 1px solid #00f; +} + +h1 { + font-size: 1.5em; + padding: 0.7em 0 0 0; +} + +h2 { + font-size: 1.2em; + padding: 0.7em 0 0 0; +} + +h3 { + font-size: 1.0em; + padding: 0.7em 0 0 0; +} + +blockquote { + font-size: 0.9em; + margin-left: 0; + padding-left: 2em; + border-left: 8px solid #eee; +} + +.footnotes { + color: #666666; + margin-top: 2em; + font-size: 0.9em; + letter-spacing: -3px; +} + +.footnotes:before { + content: "――――"; +} + +.footnotes ol { + letter-spacing: normal; +} diff --git a/23. Advanced Operators.playground/contents.xcplayground b/23. Advanced Operators.playground/contents.xcplayground index 4937636..37181bb 100644 --- a/23. Advanced Operators.playground/contents.xcplayground +++ b/23. Advanced Operators.playground/contents.xcplayground @@ -1,7 +1,73 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file diff --git a/23. Advanced Operators.playground/section-1.swift b/23. Advanced Operators.playground/section-1.swift index 30d53d8..99be4f7 100644 --- a/23. Advanced Operators.playground/section-1.swift +++ b/23. Advanced Operators.playground/section-1.swift @@ -1,21 +1,3 @@ -// ------------------------------------------------------------------------------------------------ -// Things to know: -// -// * Arithmetic operators in Swift do not automatically overflow. Adding two values that overflow -// their type (for example, storing 300 in a UInt8) will cause an error. There are special -// operators that allow overflow, including dividing by zero. -// -// * Swift allows developers to define their own operators, including those that Swift doesn't -// currently define. You can even specify the associativity and precedence for operators. -// ------------------------------------------------------------------------------------------------ - -// ------------------------------------------------------------------------------------------------ -// Bitwise Operators -// -// The Bitwise operators (AND, OR, XOR, etc.) in Swift effeectively mirror the functionality that -// you're used to with C++ and Objective-C. -// -// We'll cover them briefly. The odd formatting is intended to help show the results: var andResult: UInt8 = 0b00101111 & 0b11110100 @@ -33,227 +15,4 @@ var orResult: UInt8 = var xorResult: UInt8 = 0b01010101 ^ 0b11110000 -// 0b10100101 <- result - -// Shifting in Swift is slightly different than in C++. -// -// A lesser-known fact about C++ is that the signed right-shift of a signed value is -// implementation specific. Most compilers do what most programmers expect, which is an arithmetic -// shift (which maintains the sign bit and shifts 1's into the word from the right.) -// -// Swift defines this behavior to be what you expect, shifting signed values to the right will -// perform an arithmetic shift using two's compilment. -var leftShiftUnsignedResult: UInt8 = 32 << 1 -var leftShiftSignedResult: Int8 = 32 << 1 -var leftShiftSignedNegativeResult: Int8 = -32 << 1 - -var rightShiftUnsignedResult: UInt8 = 32 >> 1 -var rightShiftSignedResult: Int8 = 32 >> 1 -var rightShiftSignedNegativeResult: Int8 = -32 >> 1 - -// ------------------------------------------------------------------------------------------------ -// Overflow operators -// -// If an arithmetic operation (specifically addition (+), subtraction (-) and multiplication (*)) -// results in a value too large or too small for the constant or variable that the result is -// intended for, Swift will produce an overflow/underflow error. -// -// The last two lines of this code block will trigger an overflow/underflow: -// -// var positive: Int8 = 120 -// var negative: Int8 = -120 -// var overflow: Int8 = positive + positive -// var underflow: Int8 = negative + negative -// -// This is also true for division by zero, which can be caused with the division (/) or remainder -// (%) operators. -// -// Sometimes, however, overflow and underflow behavior is exactly what the programmer may intend, -// so Swift provides specific overflow/underflow operators which will not trigger an error and -// allow the overflow/underflow to perform as we see in C++/Objective-C. -// -// Special operators for division by zero are also provided, which return 0 in the case of a -// division by zero. -// -// Here they are, in all their glory: -var someValue: Int8 = 120 -var aZero: Int8 = someValue - someValue -var overflowAdd: Int8 = someValue &+ someValue -var underflowSub: Int8 = -someValue &- someValue -var overflowMul: Int8 = someValue &* someValue -var divByZero: Int8 = 100 &/ aZero -var remainderDivByZero: Int8 = 100 &% aZero - -// ------------------------------------------------------------------------------------------------ -// Operator Functions (a.k.a., Operator Overloading) -// -// Most C++ programmers should be familiar with the concept of operator overloading. Swift offers -// the same kind of functionality as well as additional functionality of specifying the operator -// precednce and associativity. -// -// The most common operators will usually take one of the following forms: -// -// * prefix: the operator appears before a single identifier as in "-a" or "++i" -// * postfix: the operator appears after a single identifier as in "i++" -// * infix: the operator appears between two identifiers as in "a + b" or "c / d" -// -// These are specified with the three attributes, @prefix, @postfix and @infix. -// -// There are more types of operators (which use different attributes, which we'll cover shortly. -// -// Let's define a Vector2D class to work with: -struct Vector2D -{ - var x = 0.0 - var y = 0.0 -} - -// Next, we'll define a simple vector addition (adding the individual x & y components to create -// a new vector.) -// -// Since we're adding two Vector2D instances, we'll use operator "+". We want our addition to take -// the form "vectorA + vectorB", which means we'll be defining an infix operator. -// -// Here's what that looks like: -func + (left: Vector2D, right: Vector2D) -> Vector2D -{ - return Vector2D(x: left.x + right.x, y: left.y + right.y) -} - -// Let's verify our work: -var a = Vector2D(x: 1.0, y: 2.0) -var b = Vector2D(x: 3.0, y: 4.0) -var c = a + b - -// We've seen the infix operator at work so let's move on to the prefix and postfix operators. -// We'll define a prefix operator that negates the vector taking the form (result = -value): -prefix func - (vector: Vector2D) -> Vector2D -{ - return Vector2D(x: -vector.x, y: -vector.y) -} - -// Check our work: -c = -a - -// Next, let's consider the common prefix increment operator (++a) and postfix increment (a++) -// operations. Each of these performs the operation on a single value whle also returning the -// appropriate result (either the original value before the increment or the value after the -// increment.) -// -// Each will either use the @prefix or @postfix attribute, but since they also modify the value -// they are also @assigmnent operators (and make use of inout for the parameter.) -// -// Let's take a look: -prefix func ++ (inout vector: Vector2D) -> Vector2D -{ - vector = vector + Vector2D(x: 1.0, y: 1.0) - return vector -} - -postfix func ++ (inout vector: Vector2D) -> Vector2D -{ - var previous = vector; - vector = vector + Vector2D(x: 1.0, y: 1.0) - return previous -} - -// And we can check our work: -++c -c++ -c - -// Equivalence Operators allow us to define a means for checking if two values are the same -// or equivalent. They can be "equal to" (==) or "not equal to" (!=). These are simple infix -// opertors that return a Bool result. -// -// Let's also take a moment to make sure we do this right. When comparing floating point values -// you can either check for exact bit-wise equality (a == b) or you can compare values that are -// "very close" by using an epsilon. It's important to recognize the difference, as there are -// cases when IEEE floating point values should be equal, but are actually represented differently -// in their bit-wise format because they were calculated differently. In these cases, a simple -// equality comparison will not suffice. -// -// So here are our more robust equivalence operators: -let Epsilon = 0.1e-7 - -func == (left: Vector2D, right: Vector2D) -> Bool -{ - if abs(left.x - right.x) > Epsilon { return false } - if abs(left.y - right.y) > Epsilon { return false } - return true -} -func != (left: Vector2D, right: Vector2D) -> Bool -{ - // Here, we'll use the inverted result of the "==" operator: - return !(left == right) -} - -// ------------------------------------------------------------------------------------------------ -// Custom Operators -// -// So far, we've been defining operator functions for operators that Swift understands and -// for which Swift provides defined behaviors. We can also define our own custom operators for -// doing other interestig things. -// -// For example, Swift doesn't support the concept of a "vector normalization" or "cross product" -// because this functionality doesn't apply to any of the types Swift offers. -// -// Let's keep it simple, though. How about an operator that adds a vector to itself. Let's make -// this a prefix operator that takes the form "+++value" -// -// First, we we must introduce Swift to our new operator. The syntax takes the form of the -// 'operator' keyword, folowed by either 'prefix', 'postfix' or 'infix': -// -// Swift meet operator, operator meet swift: -prefix operator +++ {} - -// Now we can declare our new operator: -prefix func +++ (inout vector: Vector2D) -> Vector2D -{ - vector = vector + vector - return vector -} - -// Let's check our work: -var someVector = Vector2D(x: 5.0, y: 9.0) -+++someVector - -// ------------------------------------------------------------------------------------------------ -// Precedence and Associativity for Custom Infix Operators -// -// Custom infix operators can define their own associativity (left-to-right or right-to-left or -// none) as well as a precedence for determining the order of operations. -// -// Associativity values are 'left' or 'right' or 'none'. -// -// Precedence values are ranked with a numeric value. If not specified, the default value is 100. -// Operations are performed in order of precedence, with higher values being performed first. The -// precedence values are relative to all other precedence values for other operators. The following -// are the default values for operator precedence in the Swift standard library: -// -// 160 (none): Operators << >> -// 150 (left): Operators * / % &* &/ &% & -// 140 (left): Operators + - &+ &- | ^ -// 135 (none): Operators .. ... -// 132 (none): Operators is as -// 130 (none): Operators < <= > >= == != === !== ~= -// 120 (left): Operators && -// 110 (left): Operators || -// 100 (right): Operators ?: -// 90 (right): Operators = *= /= %= += -= <<= >>= &= ^= |= &&= ||= -// -// Let's take a look at how we define a new custom infix operator with left associativity and a -// precedence of 140. -// -// We'll define a function that adds the 'x' components of two vectors, but subtracts the 'y' -// components. We'll call this the "+-" operator: -infix operator +- { associativity left precedence 140 } -func +- (left: Vector2D, right: Vector2D) -> Vector2D -{ - return Vector2D(x: left.x + right.x, y: left.y - right.y) -} - -// Check our work. Let's setup a couple vectors that result in a value of (0, 0): -var first = Vector2D(x: 5.0, y: 5.0) -var second = Vector2D(x: -5.0, y: 5.0) -first +- second +// 0b10100101 <- result \ No newline at end of file diff --git a/23. Advanced Operators.playground/section-11.swift b/23. Advanced Operators.playground/section-11.swift new file mode 100644 index 0000000..dbcee4e --- /dev/null +++ b/23. Advanced Operators.playground/section-11.swift @@ -0,0 +1,4 @@ +func + (left: Vector2D, right: Vector2D) -> Vector2D +{ + return Vector2D(x: left.x + right.x, y: left.y + right.y) +} \ No newline at end of file diff --git a/23. Advanced Operators.playground/section-13.swift b/23. Advanced Operators.playground/section-13.swift new file mode 100644 index 0000000..f6d27ed --- /dev/null +++ b/23. Advanced Operators.playground/section-13.swift @@ -0,0 +1,3 @@ +var a = Vector2D(x: 1.0, y: 2.0) +var b = Vector2D(x: 3.0, y: 4.0) +var c = a + b \ No newline at end of file diff --git a/23. Advanced Operators.playground/section-15.swift b/23. Advanced Operators.playground/section-15.swift new file mode 100644 index 0000000..c12232f --- /dev/null +++ b/23. Advanced Operators.playground/section-15.swift @@ -0,0 +1,4 @@ +prefix func - (vector: Vector2D) -> Vector2D +{ + return Vector2D(x: -vector.x, y: -vector.y) +} \ No newline at end of file diff --git a/23. Advanced Operators.playground/section-17.swift b/23. Advanced Operators.playground/section-17.swift new file mode 100644 index 0000000..d354c7f --- /dev/null +++ b/23. Advanced Operators.playground/section-17.swift @@ -0,0 +1 @@ +c = -a \ No newline at end of file diff --git a/23. Advanced Operators.playground/section-19.swift b/23. Advanced Operators.playground/section-19.swift new file mode 100644 index 0000000..770c04a --- /dev/null +++ b/23. Advanced Operators.playground/section-19.swift @@ -0,0 +1,12 @@ +prefix func ++ (inout vector: Vector2D) -> Vector2D +{ + vector = vector + Vector2D(x: 1.0, y: 1.0) + return vector +} + +postfix func ++ (inout vector: Vector2D) -> Vector2D +{ + var previous = vector; + vector = vector + Vector2D(x: 1.0, y: 1.0) + return previous +} \ No newline at end of file diff --git a/23. Advanced Operators.playground/section-21.swift b/23. Advanced Operators.playground/section-21.swift new file mode 100644 index 0000000..29ee565 --- /dev/null +++ b/23. Advanced Operators.playground/section-21.swift @@ -0,0 +1,3 @@ +++c +c++ +c \ No newline at end of file diff --git a/23. Advanced Operators.playground/section-23.swift b/23. Advanced Operators.playground/section-23.swift new file mode 100644 index 0000000..868b497 --- /dev/null +++ b/23. Advanced Operators.playground/section-23.swift @@ -0,0 +1,13 @@ +let Epsilon = 0.1e-7 + +func == (left: Vector2D, right: Vector2D) -> Bool +{ + if abs(left.x - right.x) > Epsilon { return false } + if abs(left.y - right.y) > Epsilon { return false } + return true +} +func != (left: Vector2D, right: Vector2D) -> Bool +{ + // Here, we'll use the inverted result of the "==" operator: + return !(left == right) +} \ No newline at end of file diff --git a/23. Advanced Operators.playground/section-25.swift b/23. Advanced Operators.playground/section-25.swift new file mode 100644 index 0000000..1ea7eef --- /dev/null +++ b/23. Advanced Operators.playground/section-25.swift @@ -0,0 +1 @@ +prefix operator +++ {} \ No newline at end of file diff --git a/23. Advanced Operators.playground/section-27.swift b/23. Advanced Operators.playground/section-27.swift new file mode 100644 index 0000000..029cc3b --- /dev/null +++ b/23. Advanced Operators.playground/section-27.swift @@ -0,0 +1,5 @@ +prefix func +++ (inout vector: Vector2D) -> Vector2D +{ + vector = vector + vector + return vector +} \ No newline at end of file diff --git a/23. Advanced Operators.playground/section-29.swift b/23. Advanced Operators.playground/section-29.swift new file mode 100644 index 0000000..004c85f --- /dev/null +++ b/23. Advanced Operators.playground/section-29.swift @@ -0,0 +1,2 @@ +var someVector = Vector2D(x: 5.0, y: 9.0) ++++someVector \ No newline at end of file diff --git a/23. Advanced Operators.playground/section-3.swift b/23. Advanced Operators.playground/section-3.swift new file mode 100644 index 0000000..dab97ba --- /dev/null +++ b/23. Advanced Operators.playground/section-3.swift @@ -0,0 +1,7 @@ +var leftShiftUnsignedResult: UInt8 = 32 << 1 +var leftShiftSignedResult: Int8 = 32 << 1 +var leftShiftSignedNegativeResult: Int8 = -32 << 1 + +var rightShiftUnsignedResult: UInt8 = 32 >> 1 +var rightShiftSignedResult: Int8 = 32 >> 1 +var rightShiftSignedNegativeResult: Int8 = -32 >> 1 \ No newline at end of file diff --git a/23. Advanced Operators.playground/section-31.swift b/23. Advanced Operators.playground/section-31.swift new file mode 100644 index 0000000..1eeae13 --- /dev/null +++ b/23. Advanced Operators.playground/section-31.swift @@ -0,0 +1,5 @@ +infix operator +- { associativity left precedence 140 } +func +- (left: Vector2D, right: Vector2D) -> Vector2D +{ + return Vector2D(x: left.x + right.x, y: left.y - right.y) +} \ No newline at end of file diff --git a/23. Advanced Operators.playground/section-33.swift b/23. Advanced Operators.playground/section-33.swift new file mode 100644 index 0000000..451299a --- /dev/null +++ b/23. Advanced Operators.playground/section-33.swift @@ -0,0 +1,3 @@ +var first = Vector2D(x: 5.0, y: 5.0) +var second = Vector2D(x: -5.0, y: 5.0) +first +- second \ No newline at end of file diff --git a/23. Advanced Operators.playground/section-5.swift b/23. Advanced Operators.playground/section-5.swift new file mode 100644 index 0000000..12fbee4 --- /dev/null +++ b/23. Advanced Operators.playground/section-5.swift @@ -0,0 +1,4 @@ +// var positive: Int8 = 120 +// var negative: Int8 = -120 +// var overflow: Int8 = positive + positive +// var underflow: Int8 = negative + negative \ No newline at end of file diff --git a/23. Advanced Operators.playground/section-7.swift b/23. Advanced Operators.playground/section-7.swift new file mode 100644 index 0000000..9949d44 --- /dev/null +++ b/23. Advanced Operators.playground/section-7.swift @@ -0,0 +1,7 @@ +var someValue: Int8 = 120 +var aZero: Int8 = someValue - someValue +var overflowAdd: Int8 = someValue &+ someValue +var underflowSub: Int8 = -someValue &- someValue +var overflowMul: Int8 = someValue &* someValue +var divByZero: Int8 = 100 &/ aZero +var remainderDivByZero: Int8 = 100 &% aZero \ No newline at end of file diff --git a/23. Advanced Operators.playground/section-9.swift b/23. Advanced Operators.playground/section-9.swift new file mode 100644 index 0000000..56a72c5 --- /dev/null +++ b/23. Advanced Operators.playground/section-9.swift @@ -0,0 +1,5 @@ +struct Vector2D +{ + var x = 0.0 + var y = 0.0 +} \ No newline at end of file diff --git a/3. Strings and Characters.playground/Documentation/section-0.html b/3. Strings and Characters.playground/Documentation/section-0.html new file mode 100644 index 0000000..6c2985e --- /dev/null +++ b/3. Strings and Characters.playground/Documentation/section-0.html @@ -0,0 +1,26 @@ + + + + + Section 1 + + + + + + +
+
+

Strings and Characters

+

Things to know:

+
    +
  • Strings are bridged perfectly with NSString class
  • +
  • All Strings are Unicode compliant
  • +
+
+

Here's a string

+ +
+
+ + diff --git a/3. Strings and Characters.playground/Documentation/section-10.html b/3. Strings and Characters.playground/Documentation/section-10.html new file mode 100644 index 0000000..cca4546 --- /dev/null +++ b/3. Strings and Characters.playground/Documentation/section-10.html @@ -0,0 +1,19 @@ + + + + + Section 11 + + + + + + +
+
+

You can iterate over a string like this:

+ +
+
+ + diff --git a/3. Strings and Characters.playground/Documentation/section-12.html b/3. Strings and Characters.playground/Documentation/section-12.html new file mode 100644 index 0000000..a78c726 --- /dev/null +++ b/3. Strings and Characters.playground/Documentation/section-12.html @@ -0,0 +1,19 @@ + + + + + Section 13 + + + + + + +
+
+

Characters use double-quotes to specify them, so you must be explicit if you want a Character instead of a String:

+ +
+
+ + diff --git a/3. Strings and Characters.playground/Documentation/section-14.html b/3. Strings and Characters.playground/Documentation/section-14.html new file mode 100644 index 0000000..bd084c6 --- /dev/null +++ b/3. Strings and Characters.playground/Documentation/section-14.html @@ -0,0 +1,21 @@ + + + + + Section 15 + + + + + + +
+
+

There is no length or count member of string, you have to use the global function, countElements()

+

This is much like calling strlen in which it iterates over the Unicode string and counts characters. Note that Unicode chars are different lenghts, so this is a non-trivial process.

+

“Note also that the character count returned by countElements is not always the same as the length property of an NSString that contains the same characters. The length of an NSString is based on the number of 16-bit code units within the string’s UTF-16 representation and not the number of Unicode characters within the string. To reflect this fact, the length property from NSString is called utf16count when it is accessed on a Swift String value.”

+ +
+
+ + diff --git a/3. Strings and Characters.playground/Documentation/section-16.html b/3. Strings and Characters.playground/Documentation/section-16.html new file mode 100644 index 0000000..c158c60 --- /dev/null +++ b/3. Strings and Characters.playground/Documentation/section-16.html @@ -0,0 +1,19 @@ + + + + + Section 17 + + + + + + +
+
+

Strings can be concatenated with strings and characters

+ +
+
+ + diff --git a/3. Strings and Characters.playground/Documentation/section-18.html b/3. Strings and Characters.playground/Documentation/section-18.html new file mode 100644 index 0000000..211a7aa --- /dev/null +++ b/3. Strings and Characters.playground/Documentation/section-18.html @@ -0,0 +1,19 @@ + + + + + Section 19 + + + + + + +
+
+

Add a character

+ +
+
+ + diff --git a/3. Strings and Characters.playground/Documentation/section-2.html b/3. Strings and Characters.playground/Documentation/section-2.html new file mode 100644 index 0000000..3f53d3d --- /dev/null +++ b/3. Strings and Characters.playground/Documentation/section-2.html @@ -0,0 +1,19 @@ + + + + + Section 3 + + + + + + +
+
+

Strings have some special character constants. They are:

+ +
+
+ + diff --git a/3. Strings and Characters.playground/Documentation/section-20.html b/3. Strings and Characters.playground/Documentation/section-20.html new file mode 100644 index 0000000..8c6a256 --- /dev/null +++ b/3. Strings and Characters.playground/Documentation/section-20.html @@ -0,0 +1,19 @@ + + + + + Section 21 + + + + + + +
+
+

Concatenate a character onto the end of the string

+ +
+
+ + diff --git a/3. Strings and Characters.playground/Documentation/section-22.html b/3. Strings and Characters.playground/Documentation/section-22.html new file mode 100644 index 0000000..4e7e342 --- /dev/null +++ b/3. Strings and Characters.playground/Documentation/section-22.html @@ -0,0 +1,20 @@ + + + + + Section 23 + + + + + + +
+
+

String interpolation

+

String interpolation refers to referencing values inside of a String. This works different from printf-style functions, in that String interpolation allows you to insert the values directly in-line with the string, rather than as additional parameters to a formatting function.

+ +
+
+ + diff --git a/3. Strings and Characters.playground/Documentation/section-24.html b/3. Strings and Characters.playground/Documentation/section-24.html new file mode 100644 index 0000000..0abc04a --- /dev/null +++ b/3. Strings and Characters.playground/Documentation/section-24.html @@ -0,0 +1,20 @@ + + + + + Section 25 + + + + + + +
+
+

String comparison

+

String comparison is case-sensitive and can be compared for equality

+ +
+
+ + diff --git a/3. Strings and Characters.playground/Documentation/section-26.html b/3. Strings and Characters.playground/Documentation/section-26.html new file mode 100644 index 0000000..5c0ef0c --- /dev/null +++ b/3. Strings and Characters.playground/Documentation/section-26.html @@ -0,0 +1,19 @@ + + + + + Section 27 + + + + + + +
+
+

You can also compare prefix and suffix equality:

+ +
+
+ + diff --git a/3. Strings and Characters.playground/Documentation/section-28.html b/3. Strings and Characters.playground/Documentation/section-28.html new file mode 100644 index 0000000..b158f79 --- /dev/null +++ b/3. Strings and Characters.playground/Documentation/section-28.html @@ -0,0 +1,18 @@ + + + + + Section 29 + + + + + + +
+
+ +
+
+ + diff --git a/3. Strings and Characters.playground/Documentation/section-4.html b/3. Strings and Characters.playground/Documentation/section-4.html new file mode 100644 index 0000000..e498a33 --- /dev/null +++ b/3. Strings and Characters.playground/Documentation/section-4.html @@ -0,0 +1,19 @@ + + + + + Section 5 + + + + + + +
+
+

Initializing an empty string - these are equivalent to each other

+ +
+
+ + diff --git a/3. Strings and Characters.playground/Documentation/section-6.html b/3. Strings and Characters.playground/Documentation/section-6.html new file mode 100644 index 0000000..d02e142 --- /dev/null +++ b/3. Strings and Characters.playground/Documentation/section-6.html @@ -0,0 +1,19 @@ + + + + + Section 7 + + + + + + +
+
+

Use 'isEmpty' to check for empty String

+ +
+
+ + diff --git a/3. Strings and Characters.playground/Documentation/section-8.html b/3. Strings and Characters.playground/Documentation/section-8.html new file mode 100644 index 0000000..e118c72 --- /dev/null +++ b/3. Strings and Characters.playground/Documentation/section-8.html @@ -0,0 +1,20 @@ + + + + + Section 9 + + + + + + +
+
+

Strings are VALUE TYPES, but they're referenced for performance so they are only copied on +modification.

+ +
+
+ + diff --git a/3. Strings and Characters.playground/Documentation/stylesheet.css b/3. Strings and Characters.playground/Documentation/stylesheet.css new file mode 100644 index 0000000..43b0230 --- /dev/null +++ b/3. Strings and Characters.playground/Documentation/stylesheet.css @@ -0,0 +1,65 @@ +html { + font-size: 1em; +} + +body { + background-color: #fff; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: normal; + font-size: 1.0em; + line-height: 140%; + padding: 0 2em; +} + +code { + font-family: "Menlo", monospace; + font-size: 0.95em; +} + +section { + padding: 0.5em 0; + -webkit-overflow-scrolling: touch; +} + +a:link { + color: #00f; + text-decoration: none; + border-bottom: 1px solid #00f; +} + +h1 { + font-size: 1.5em; + padding: 0.7em 0 0 0; +} + +h2 { + font-size: 1.2em; + padding: 0.7em 0 0 0; +} + +h3 { + font-size: 1.0em; + padding: 0.7em 0 0 0; +} + +blockquote { + font-size: 0.9em; + margin-left: 0; + padding-left: 2em; + border-left: 8px solid #eee; +} + +.footnotes { + color: #666666; + margin-top: 2em; + font-size: 0.9em; + letter-spacing: -3px; +} + +.footnotes:before { + content: "――――"; +} + +.footnotes ol { + letter-spacing: normal; +} diff --git a/3. Strings and Characters.playground/contents.xcplayground b/3. Strings and Characters.playground/contents.xcplayground index 4937636..aec924a 100644 --- a/3. Strings and Characters.playground/contents.xcplayground +++ b/3. Strings and Characters.playground/contents.xcplayground @@ -1,7 +1,63 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file diff --git a/3. Strings and Characters.playground/section-1.swift b/3. Strings and Characters.playground/section-1.swift index 54dade8..b1e507a 100644 --- a/3. Strings and Characters.playground/section-1.swift +++ b/3. Strings and Characters.playground/section-1.swift @@ -1,101 +1 @@ -// ------------------------------------------------------------------------------------------------ -// Things to know: -// -// * Strings are bridged perfectly with NSString class -// -// * All Strings are Unicode compliant -// ------------------------------------------------------------------------------------------------ - -// Here's a string -var str: String = "Albatross! Get your albatross here!" - -// Strings have some special character constants. They are: -"\0" // Null character -"\\" // Backslash -"\t" // Tab -"\n" // Newline -"\r" // Carriage return -"\"" // Double quote -"\'" // Single quote -"\u{24}" // Single-byte Unicode -"\u{2665}" // Double-byte unicode -"\u{0001F49c}" // Four-byte unicode - -// Initializing an empty string - these are equivalent to each other -var emptyString = "" -var anotherEmptyString = String() - -// Use 'isEmpty' to check for empty String -if emptyString.isEmpty -{ - "Yep, it's empty" -} - -// Strings are VALUE TYPES, but they're referenced for performance so they are only copied on -// modification. -func somefunc(a: String) -{ - var b = a - b = "Changed!" -} - -var originalString = "Original" -somefunc(originalString) -originalString // not modified - -// You can iterate over a string like this: -for character in originalString -{ - character -} - -// Characters use double-quotes to specify them, so you must be explicit if you want a Character -// instead of a String: -var notAString: Character = "t" - -// There is no length or count member of string, you have to use the global function, -// countElements() -// -// This is much like calling strlen in which it iterates over the Unicode string and counts -// characters. Note that Unicode chars are different lenghts, so this is a non-trivial process. -// -// “Note also that the character count returned by countElements is not always the same as the -// length property of an NSString that contains the same characters. The length of an NSString is -// based on the number of 16-bit code units within the string’s UTF-16 representation and not the -// number of Unicode characters within the string. To reflect this fact, the length property from -// NSString is called utf16count when it is accessed on a Swift String value.” -countElements(originalString) - -// Strings can be concatenated with strings and characters -var helloworld = "hello, " + "world" - -// Add a character -var bang: Character = "!" -helloworld + bang - -// Concatenate a character onto the end of the string -helloworld += bang -helloworld - -// ------------------------------------------------------------------------------------------------ -// String interpolation -// -// String interpolation refers to referencing values inside of a String. This works different from -// printf-style functions, in that String interpolation allows you to insert the values directly -// in-line with the string, rather than as additional parameters to a formatting function. -let multiplier = 3 -let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)" - -// ------------------------------------------------------------------------------------------------ -// String comparison -// -// String comparison is case-sensitive and can be compared for equality -var str1 = "We're a lot alike, you and I." -var str2 = "We're a lot alike, you and I." -str1 == str2 - -// You can also compare prefix and suffix equality: -str1.hasPrefix("We're") -str2.hasSuffix("I.") -str1.hasPrefix("I.") - +var str: String = "Albatross! Get your albatross here!" \ No newline at end of file diff --git a/3. Strings and Characters.playground/section-11.swift b/3. Strings and Characters.playground/section-11.swift new file mode 100644 index 0000000..3812308 --- /dev/null +++ b/3. Strings and Characters.playground/section-11.swift @@ -0,0 +1,4 @@ +for character in originalString +{ + character +} \ No newline at end of file diff --git a/3. Strings and Characters.playground/section-13.swift b/3. Strings and Characters.playground/section-13.swift new file mode 100644 index 0000000..4ed2fea --- /dev/null +++ b/3. Strings and Characters.playground/section-13.swift @@ -0,0 +1 @@ +var notAString: Character = "t" \ No newline at end of file diff --git a/3. Strings and Characters.playground/section-15.swift b/3. Strings and Characters.playground/section-15.swift new file mode 100644 index 0000000..0d8619d --- /dev/null +++ b/3. Strings and Characters.playground/section-15.swift @@ -0,0 +1 @@ +countElements(originalString) \ No newline at end of file diff --git a/3. Strings and Characters.playground/section-17.swift b/3. Strings and Characters.playground/section-17.swift new file mode 100644 index 0000000..2336fe6 --- /dev/null +++ b/3. Strings and Characters.playground/section-17.swift @@ -0,0 +1 @@ +var helloworld = "hello, " + "world" \ No newline at end of file diff --git a/3. Strings and Characters.playground/section-19.swift b/3. Strings and Characters.playground/section-19.swift new file mode 100644 index 0000000..fa6a3b8 --- /dev/null +++ b/3. Strings and Characters.playground/section-19.swift @@ -0,0 +1,2 @@ +var bang: Character = "!" +helloworld + String(bang) \ No newline at end of file diff --git a/3. Strings and Characters.playground/section-21.swift b/3. Strings and Characters.playground/section-21.swift new file mode 100644 index 0000000..5047841 --- /dev/null +++ b/3. Strings and Characters.playground/section-21.swift @@ -0,0 +1,2 @@ +helloworld += String(bang) +helloworld \ No newline at end of file diff --git a/3. Strings and Characters.playground/section-23.swift b/3. Strings and Characters.playground/section-23.swift new file mode 100644 index 0000000..a2d07f6 --- /dev/null +++ b/3. Strings and Characters.playground/section-23.swift @@ -0,0 +1,2 @@ +let multiplier = 3 +let message = "\(multiplier) times 2.5 is \(Double(multiplier) * 2.5)" \ No newline at end of file diff --git a/3. Strings and Characters.playground/section-25.swift b/3. Strings and Characters.playground/section-25.swift new file mode 100644 index 0000000..a551ab5 --- /dev/null +++ b/3. Strings and Characters.playground/section-25.swift @@ -0,0 +1,3 @@ +var str1 = "We're a lot alike, you and I." +var str2 = "We're a lot alike, you and I." +str1 == str2 \ No newline at end of file diff --git a/3. Strings and Characters.playground/section-27.swift b/3. Strings and Characters.playground/section-27.swift new file mode 100644 index 0000000..9b9bb4f --- /dev/null +++ b/3. Strings and Characters.playground/section-27.swift @@ -0,0 +1,3 @@ +str1.hasPrefix("We're") +str2.hasSuffix("I.") +str1.hasPrefix("I.") \ No newline at end of file diff --git a/3. Strings and Characters.playground/section-3.swift b/3. Strings and Characters.playground/section-3.swift new file mode 100644 index 0000000..4a62743 --- /dev/null +++ b/3. Strings and Characters.playground/section-3.swift @@ -0,0 +1,10 @@ +"\0" // Null character +"\\" // Backslash +"\t" // Tab +"\n" // Newline +"\r" // Carriage return +"\"" // Double quote +"\'" // Single quote +"\u{24}" // Single-byte Unicode +"\u{2665}" // Double-byte unicode +"\u{0001F49c}" // Four-byte unicode \ No newline at end of file diff --git a/3. Strings and Characters.playground/section-5.swift b/3. Strings and Characters.playground/section-5.swift new file mode 100644 index 0000000..d47311b --- /dev/null +++ b/3. Strings and Characters.playground/section-5.swift @@ -0,0 +1,2 @@ +var emptyString = "" +var anotherEmptyString = String() \ No newline at end of file diff --git a/3. Strings and Characters.playground/section-7.swift b/3. Strings and Characters.playground/section-7.swift new file mode 100644 index 0000000..77e8e2b --- /dev/null +++ b/3. Strings and Characters.playground/section-7.swift @@ -0,0 +1,4 @@ +if emptyString.isEmpty +{ + "Yep, it's empty" +} \ No newline at end of file diff --git a/3. Strings and Characters.playground/section-9.swift b/3. Strings and Characters.playground/section-9.swift new file mode 100644 index 0000000..19aefb8 --- /dev/null +++ b/3. Strings and Characters.playground/section-9.swift @@ -0,0 +1,9 @@ +func somefunc(a: String) +{ + var b = a + b = "Changed!" +} + +var originalString = "Original" +somefunc(originalString) +originalString // not modified \ No newline at end of file diff --git a/4a. Arrays.playground/Documentation/section-0.html b/4a. Arrays.playground/Documentation/section-0.html new file mode 100644 index 0000000..e9b538b --- /dev/null +++ b/4a. Arrays.playground/Documentation/section-0.html @@ -0,0 +1,29 @@ + + + + + Section 1 + + + + + + +
+
+

Arrays

+

Things to know:

+
    +
  • Arrays are ordered lists of elements
  • +
  • The types of values that can be stored in an array must always be made clear either through explicit type annotation or through type inference and does not have to be a base class type.
  • +
  • Arrays are type-safe and always clear about what they contain.
  • +
  • Arrays are value types, but Swift is smart about only copying when necessary to improve performance.
  • +
  • Immutable arrays are immutable in terms of the array itself and the contents of the array. This means you can't add/remove an element nor can you modify an element of an immutable array.
  • +
+
+

Create an array of Strings

+ +
+
+ + diff --git a/4a. Arrays.playground/Documentation/section-10.html b/4a. Arrays.playground/Documentation/section-10.html new file mode 100644 index 0000000..9577053 --- /dev/null +++ b/4a. Arrays.playground/Documentation/section-10.html @@ -0,0 +1,20 @@ + + + + + Section 11 + + + + + + +
+
+

Accessing and modifying an Array

+

We can get the number of elements

+ +
+
+ + diff --git a/4a. Arrays.playground/Documentation/section-12.html b/4a. Arrays.playground/Documentation/section-12.html new file mode 100644 index 0000000..9ddb5a8 --- /dev/null +++ b/4a. Arrays.playground/Documentation/section-12.html @@ -0,0 +1,19 @@ + + + + + Section 13 + + + + + + +
+
+

We can check to see if it's empty

+ +
+
+ + diff --git a/4a. Arrays.playground/Documentation/section-14.html b/4a. Arrays.playground/Documentation/section-14.html new file mode 100644 index 0000000..509e060 --- /dev/null +++ b/4a. Arrays.playground/Documentation/section-14.html @@ -0,0 +1,19 @@ + + + + + Section 15 + + + + + + +
+
+

We can append to the end

+ +
+
+ + diff --git a/4a. Arrays.playground/Documentation/section-16.html b/4a. Arrays.playground/Documentation/section-16.html new file mode 100644 index 0000000..7b5fad0 --- /dev/null +++ b/4a. Arrays.playground/Documentation/section-16.html @@ -0,0 +1,19 @@ + + + + + Section 17 + + + + + + +
+
+

We can append another array of same type

+ +
+
+ + diff --git a/4a. Arrays.playground/Documentation/section-18.html b/4a. Arrays.playground/Documentation/section-18.html new file mode 100644 index 0000000..a910ec6 --- /dev/null +++ b/4a. Arrays.playground/Documentation/section-18.html @@ -0,0 +1,19 @@ + + + + + Section 19 + + + + + + +
+
+

We can get elements from the array by indexing them

+ +
+
+ + diff --git a/4a. Arrays.playground/Documentation/section-2.html b/4a. Arrays.playground/Documentation/section-2.html new file mode 100644 index 0000000..9494004 --- /dev/null +++ b/4a. Arrays.playground/Documentation/section-2.html @@ -0,0 +1,19 @@ + + + + + Section 3 + + + + + + +
+
+

Shorter, more common way to define an array of Strings

+ +
+
+ + diff --git a/4a. Arrays.playground/Documentation/section-20.html b/4a. Arrays.playground/Documentation/section-20.html new file mode 100644 index 0000000..bd5135f --- /dev/null +++ b/4a. Arrays.playground/Documentation/section-20.html @@ -0,0 +1,19 @@ + + + + + Section 21 + + + + + + +
+
+

We can modify an existing item

+ +
+
+ + diff --git a/4a. Arrays.playground/Documentation/section-22.html b/4a. Arrays.playground/Documentation/section-22.html new file mode 100644 index 0000000..d739a4a --- /dev/null +++ b/4a. Arrays.playground/Documentation/section-22.html @@ -0,0 +1,20 @@ + + + + + Section 23 + + + + + + +
+
+

We can use a range operator to modify existing items. This operation modifies a range with a target range. If the target range has more or fewer elements in it, the size of the array will be adjusted.

+

Here, we replace 3 items with only two, removing an item:

+ +
+
+ + diff --git a/4a. Arrays.playground/Documentation/section-24.html b/4a. Arrays.playground/Documentation/section-24.html new file mode 100644 index 0000000..3116ee7 --- /dev/null +++ b/4a. Arrays.playground/Documentation/section-24.html @@ -0,0 +1,19 @@ + + + + + Section 25 + + + + + + +
+
+

Or we can replace two items with three, inserting a new item:

+ +
+
+ + diff --git a/4a. Arrays.playground/Documentation/section-26.html b/4a. Arrays.playground/Documentation/section-26.html new file mode 100644 index 0000000..52bf5c4 --- /dev/null +++ b/4a. Arrays.playground/Documentation/section-26.html @@ -0,0 +1,19 @@ + + + + + Section 27 + + + + + + +
+
+

We can insert an item at a given index

+ +
+
+ + diff --git a/4a. Arrays.playground/Documentation/section-28.html b/4a. Arrays.playground/Documentation/section-28.html new file mode 100644 index 0000000..f6796a3 --- /dev/null +++ b/4a. Arrays.playground/Documentation/section-28.html @@ -0,0 +1,19 @@ + + + + + Section 29 + + + + + + +
+
+

We can remove the last element. During this, we can preserve the value of what was removed into a stored value

+ +
+
+ + diff --git a/4a. Arrays.playground/Documentation/section-30.html b/4a. Arrays.playground/Documentation/section-30.html new file mode 100644 index 0000000..70dbe9d --- /dev/null +++ b/4a. Arrays.playground/Documentation/section-30.html @@ -0,0 +1,20 @@ + + + + + Section 31 + + + + + + +
+
+

Enumeration

+

We can iterate over the the array using a for-in loop

+ +
+
+ + diff --git a/4a. Arrays.playground/Documentation/section-32.html b/4a. Arrays.playground/Documentation/section-32.html new file mode 100644 index 0000000..bc25369 --- /dev/null +++ b/4a. Arrays.playground/Documentation/section-32.html @@ -0,0 +1,19 @@ + + + + + Section 33 + + + + + + +
+
+

We can also use the the enumerate() method to return a tuple containing the index and value for each element:

+ +
+
+ + diff --git a/4a. Arrays.playground/Documentation/section-34.html b/4a. Arrays.playground/Documentation/section-34.html new file mode 100644 index 0000000..0a502d0 --- /dev/null +++ b/4a. Arrays.playground/Documentation/section-34.html @@ -0,0 +1,21 @@ + + + + + Section 35 + + + + + + +
+
+

Creating and initializing an array

+

Earlier, we saw how to declare an array of a given type. Here, we see how to declare an array +type and then assign it to a stored value, which gets its type by inference:

+ +
+
+ + diff --git a/4a. Arrays.playground/Documentation/section-36.html b/4a. Arrays.playground/Documentation/section-36.html new file mode 100644 index 0000000..413240b --- /dev/null +++ b/4a. Arrays.playground/Documentation/section-36.html @@ -0,0 +1,19 @@ + + + + + Section 37 + + + + + + +
+
+

Add the number '3' to the array

+ +
+
+ + diff --git a/4a. Arrays.playground/Documentation/section-38.html b/4a. Arrays.playground/Documentation/section-38.html new file mode 100644 index 0000000..9ab8824 --- /dev/null +++ b/4a. Arrays.playground/Documentation/section-38.html @@ -0,0 +1,19 @@ + + + + + Section 39 + + + + + + +
+
+

We can assign it to an empty array, but we don't modify the type, since someInts is already an Int[] type.

+ +
+
+ + diff --git a/4a. Arrays.playground/Documentation/section-4.html b/4a. Arrays.playground/Documentation/section-4.html new file mode 100644 index 0000000..e96c723 --- /dev/null +++ b/4a. Arrays.playground/Documentation/section-4.html @@ -0,0 +1,20 @@ + + + + + Section 5 + + + + + + +
+
+

This is an array literal. Since all members are of type String, this will create a String array.

+

If all members are not the same type (or cannot be inferred to a homogenized type) then you would get a compiler error.

+ +
+
+ + diff --git a/4a. Arrays.playground/Documentation/section-40.html b/4a. Arrays.playground/Documentation/section-40.html new file mode 100644 index 0000000..c28dfa5 --- /dev/null +++ b/4a. Arrays.playground/Documentation/section-40.html @@ -0,0 +1,19 @@ + + + + + Section 41 + + + + + + +
+
+

We can initialize an array and and fill it with default values

+ +
+
+ + diff --git a/4a. Arrays.playground/Documentation/section-42.html b/4a. Arrays.playground/Documentation/section-42.html new file mode 100644 index 0000000..09fab6a --- /dev/null +++ b/4a. Arrays.playground/Documentation/section-42.html @@ -0,0 +1,19 @@ + + + + + Section 43 + + + + + + +
+
+

We can also use the Array initializer to fill it with default values. Note that we don't need to specify type since it is inferred:

+ +
+
+ + diff --git a/4a. Arrays.playground/Documentation/section-44.html b/4a. Arrays.playground/Documentation/section-44.html new file mode 100644 index 0000000..8c74b31 --- /dev/null +++ b/4a. Arrays.playground/Documentation/section-44.html @@ -0,0 +1,19 @@ + + + + + Section 45 + + + + + + +
+
+

If you store an array in a constant, it is considered "Immutable"

+ +
+
+ + diff --git a/4a. Arrays.playground/Documentation/section-46.html b/4a. Arrays.playground/Documentation/section-46.html new file mode 100644 index 0000000..ee20b26 --- /dev/null +++ b/4a. Arrays.playground/Documentation/section-46.html @@ -0,0 +1,20 @@ + + + + + Section 47 + + + + + + +
+
+

In terms of immutability, it's important to consider that the array and its contents are treated separately. Therefore, you can change the contents of an immutable array, but you can't change the array itself.

+

We can't change the contents of an immutable array:

+ +
+
+ + diff --git a/4a. Arrays.playground/Documentation/section-48.html b/4a. Arrays.playground/Documentation/section-48.html new file mode 100644 index 0000000..ca14866 --- /dev/null +++ b/4a. Arrays.playground/Documentation/section-48.html @@ -0,0 +1,19 @@ + + + + + Section 49 + + + + + + +
+
+

Nor can we change the size or add an element, you will get a compiler error:

+ +
+
+ + diff --git a/4a. Arrays.playground/Documentation/section-50.html b/4a. Arrays.playground/Documentation/section-50.html new file mode 100644 index 0000000..a8feabb --- /dev/null +++ b/4a. Arrays.playground/Documentation/section-50.html @@ -0,0 +1,21 @@ + + + + + Section 51 + + + + + + +
+
+

Arrays are Value Types

+

Arrays are value types that only copy when necessary, which is only when the array itself changes (not the contents.)

+

Here are three copies of an array:

+ +
+
+ + diff --git a/4a. Arrays.playground/Documentation/section-52.html b/4a. Arrays.playground/Documentation/section-52.html new file mode 100644 index 0000000..b64d178 --- /dev/null +++ b/4a. Arrays.playground/Documentation/section-52.html @@ -0,0 +1,19 @@ + + + + + Section 53 + + + + + + +
+
+

However, if we change the contents of one array (mutating it), then it is copied and becomes its own unique entity:

+ +
+
+ + diff --git a/4a. Arrays.playground/Documentation/section-54.html b/4a. Arrays.playground/Documentation/section-54.html new file mode 100644 index 0000000..ac9248c --- /dev/null +++ b/4a. Arrays.playground/Documentation/section-54.html @@ -0,0 +1,19 @@ + + + + + Section 55 + + + + + + +
+
+

Now that we've changed a, it should have been copied to its own instance. Let's double-check that only b & c are the same:

+ +
+
+ + diff --git a/4a. Arrays.playground/Documentation/section-56.html b/4a. Arrays.playground/Documentation/section-56.html new file mode 100644 index 0000000..c72155e --- /dev/null +++ b/4a. Arrays.playground/Documentation/section-56.html @@ -0,0 +1,19 @@ + + + + + Section 57 + + + + + + +
+
+

The same is true if we mutate the array in other ways (mofify the array's size)...

+ +
+
+ + diff --git a/4a. Arrays.playground/Documentation/section-58.html b/4a. Arrays.playground/Documentation/section-58.html new file mode 100644 index 0000000..a385870 --- /dev/null +++ b/4a. Arrays.playground/Documentation/section-58.html @@ -0,0 +1,19 @@ + + + + + Section 59 + + + + + + +
+
+

Now, we have three different arrays...

+ +
+
+ + diff --git a/4a. Arrays.playground/Documentation/section-6.html b/4a. Arrays.playground/Documentation/section-6.html new file mode 100644 index 0000000..ba05d6f --- /dev/null +++ b/4a. Arrays.playground/Documentation/section-6.html @@ -0,0 +1,19 @@ + + + + + Section 7 + + + + + + +
+
+

Let's create an array with some stuff in it. We'll use an explicit String type:

+ +
+
+ + diff --git a/4a. Arrays.playground/Documentation/section-60.html b/4a. Arrays.playground/Documentation/section-60.html new file mode 100644 index 0000000..1795daf --- /dev/null +++ b/4a. Arrays.playground/Documentation/section-60.html @@ -0,0 +1,18 @@ + + + + + Section 61 + + + + + + +
+
+ +
+
+ + diff --git a/4a. Arrays.playground/Documentation/section-8.html b/4a. Arrays.playground/Documentation/section-8.html new file mode 100644 index 0000000..5562f9d --- /dev/null +++ b/4a. Arrays.playground/Documentation/section-8.html @@ -0,0 +1,20 @@ + + + + + Section 9 + + + + + + +
+
+

We can also let Swift infer the type of the Array based on the type of the initializer members.

+

The folowing is an array of Strings

+ +
+
+ + diff --git a/4a. Arrays.playground/Documentation/stylesheet.css b/4a. Arrays.playground/Documentation/stylesheet.css new file mode 100644 index 0000000..43b0230 --- /dev/null +++ b/4a. Arrays.playground/Documentation/stylesheet.css @@ -0,0 +1,65 @@ +html { + font-size: 1em; +} + +body { + background-color: #fff; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: normal; + font-size: 1.0em; + line-height: 140%; + padding: 0 2em; +} + +code { + font-family: "Menlo", monospace; + font-size: 0.95em; +} + +section { + padding: 0.5em 0; + -webkit-overflow-scrolling: touch; +} + +a:link { + color: #00f; + text-decoration: none; + border-bottom: 1px solid #00f; +} + +h1 { + font-size: 1.5em; + padding: 0.7em 0 0 0; +} + +h2 { + font-size: 1.2em; + padding: 0.7em 0 0 0; +} + +h3 { + font-size: 1.0em; + padding: 0.7em 0 0 0; +} + +blockquote { + font-size: 0.9em; + margin-left: 0; + padding-left: 2em; + border-left: 8px solid #eee; +} + +.footnotes { + color: #666666; + margin-top: 2em; + font-size: 0.9em; + letter-spacing: -3px; +} + +.footnotes:before { + content: "――――"; +} + +.footnotes ol { + letter-spacing: normal; +} diff --git a/4a. Arrays.playground/contents.xcplayground b/4a. Arrays.playground/contents.xcplayground index 4937636..2586517 100644 --- a/4a. Arrays.playground/contents.xcplayground +++ b/4a. Arrays.playground/contents.xcplayground @@ -1,7 +1,125 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file diff --git a/4a. Arrays.playground/section-1.swift b/4a. Arrays.playground/section-1.swift index 716b695..0afdbdc 100644 --- a/4a. Arrays.playground/section-1.swift +++ b/4a. Arrays.playground/section-1.swift @@ -1,165 +1 @@ -// ------------------------------------------------------------------------------------------------ -// Things to know: -// -// * Arrays are ordered lists of elements -// -// * The types of values that can be stored in an array must always be made clear either through -// explicit type annotation or through type inference and does not have to be a base class type. -// -// * Arrays are type-safe and always clear about what they contain. -// -// * Arrays are value types, but Swift is smart about only copying when necessary to improve -// performance. -// -// * Immutable arrays are immutable in terms of the array itself and the contents of the array. -// This means you can't add/remove an element nor can you modify an element of an immutable -// array. -// ------------------------------------------------------------------------------------------------ - -// Create an array of Strings -var someArray = Array() - -// Shorter, more common way to define an array of Strings -var shorter: [String] - -// This is an array literal. Since all members are of type String, this will create a String array. -// -// If all members are not the same type (or cannot be inferred to a homogenized type) then you -// would get a compiler error. -["Eggs", "Milk"] - -// Let's create an array with some stuff in it. We'll use an explicit String type: -var commonPets: [String] = ["Cats", "Dogs"] - -// We can also let Swift infer the type of the Array based on the type of the initializer members. -// -// The folowing is an array of Strings -var shoppingList = ["Eggs", "Milk"] - -// ------------------------------------------------------------------------------------------------ -// Accessing and modifying an Array -// -// We can get the number of elements -shoppingList.count - -// We can check to see if it's empty -if !shoppingList.isEmpty { "it's not empty" } - -// We can append to the end -shoppingList.append("Flour") -shoppingList.append("Baking Powder") -shoppingList.count - -// We can append another array of same type -shoppingList += ["Chocolate Spread", "Cheese", "Butter"] -shoppingList.count - -// We can get elements from the array by indexing them -shoppingList[0] -shoppingList[1] - -// We can modify an existing item -shoppingList[0] = "Six Eggs" - -// We can use a range operator to modify existing items. This operation modifies a range with -// a target range. If the target range has more or fewer elements in it, the size of the array -// will be adjusted. -// -// Here, we replace 3 items with only two, removing an item: -shoppingList[4...6] = ["Banannas", "Apples"] - -// Or we can replace two items with three, inserting a new item: -shoppingList[4..<6] = ["Limes", "Mint leaves", "Sugar"] - -// We can insert an item at a given index -shoppingList.insert("Maple Syrup", atIndex: 3) - -// We can remove the last element. During this, we can preserve the value of what was removed -// into a stored value -let apples = shoppingList.removeLast() - -// ------------------------------------------------------------------------------------------------ -// Enumeration -// -// We can iterate over the the array using a for-in loop -for item in shoppingList -{ - item -} - -// We can also use the the enumerate() method to return a tuple containing the index and value -// for each element: -for (index, value) in enumerate(shoppingList) -{ - index - value -} - -// ------------------------------------------------------------------------------------------------ -// Creating and initializing an array -// -// Earlier, we saw how to declare an array of a given type. Here, we see how to declare an array -// type and then assign it to a stored value, which gets its type by inference: -var someInts = [Int]() - -// Add the number '3' to the array -someInts.append(3) -someInts - -// We can assign it to an empty array, but we don't modify the type, since someInts is already -// an Int[] type. -someInts = [] - -// We can initialize an array and and fill it with default values -var threeDoubles = [Double](count: 3, repeatedValue: 3.3) - -// We can also use the Array initializer to fill it with default values. Note that we don't need to -// specify type since it is inferred: -var anotherThreeDoubles = Array(count: 3, repeatedValue: 2.5) - -// If you store an array in a constant, it is considered "Immutable" -let immutableArray = ["a", "b"] - -// In terms of immutability, it's important to consider that the array and its contents are treated -// separately. Therefore, you can change the contents of an immutable array, but you can't change -// the array itself. -// -// We can't change the contents of an immutable array: -// -// immutableArray[0] = "b" -// -// Nor can we change the size or add an element, you will get a compiler error: -// -// immutableArray += "c" - -// ------------------------------------------------------------------------------------------------ -// Arrays are Value Types -// -// Arrays are value types that only copy when necessary, which is only when the array itself -// changes (not the contents.) -// -// Here are three copies of an array: -var a = [1, 2, 3] -var b = a -var c = a - -// However, if we change the contents of one array (mutating it), then it is copied and becomes its -// own unique entity: -a[0] = 42 -b[0] -c[0] - -// Now that we've changed a, it should have been copied to its own instance. Let's double-check -// that only b & c are the same: -a -b -c - -// The same is true if we mutate the array in other ways (mofify the array's size)... -b.append(4) - -// Now, we have three different arrays... -a -b -c - +var someArray = Array() \ No newline at end of file diff --git a/4a. Arrays.playground/section-11.swift b/4a. Arrays.playground/section-11.swift new file mode 100644 index 0000000..a5187b8 --- /dev/null +++ b/4a. Arrays.playground/section-11.swift @@ -0,0 +1 @@ +shoppingList.count \ No newline at end of file diff --git a/4a. Arrays.playground/section-13.swift b/4a. Arrays.playground/section-13.swift new file mode 100644 index 0000000..e928b12 --- /dev/null +++ b/4a. Arrays.playground/section-13.swift @@ -0,0 +1 @@ +if !shoppingList.isEmpty { "it's not empty" } \ No newline at end of file diff --git a/4a. Arrays.playground/section-15.swift b/4a. Arrays.playground/section-15.swift new file mode 100644 index 0000000..0aac449 --- /dev/null +++ b/4a. Arrays.playground/section-15.swift @@ -0,0 +1,3 @@ +shoppingList.append("Flour") +shoppingList.append("Baking Powder") +shoppingList.count \ No newline at end of file diff --git a/4a. Arrays.playground/section-17.swift b/4a. Arrays.playground/section-17.swift new file mode 100644 index 0000000..3a3499b --- /dev/null +++ b/4a. Arrays.playground/section-17.swift @@ -0,0 +1,2 @@ +shoppingList += ["Chocolate Spread", "Cheese", "Butter"] +shoppingList.count \ No newline at end of file diff --git a/4a. Arrays.playground/section-19.swift b/4a. Arrays.playground/section-19.swift new file mode 100644 index 0000000..810734c --- /dev/null +++ b/4a. Arrays.playground/section-19.swift @@ -0,0 +1,2 @@ +shoppingList[0] +shoppingList[1] \ No newline at end of file diff --git a/4a. Arrays.playground/section-21.swift b/4a. Arrays.playground/section-21.swift new file mode 100644 index 0000000..7471de9 --- /dev/null +++ b/4a. Arrays.playground/section-21.swift @@ -0,0 +1 @@ +shoppingList[0] = "Six Eggs" \ No newline at end of file diff --git a/4a. Arrays.playground/section-23.swift b/4a. Arrays.playground/section-23.swift new file mode 100644 index 0000000..142392f --- /dev/null +++ b/4a. Arrays.playground/section-23.swift @@ -0,0 +1 @@ +shoppingList[4...6] = ["Banannas", "Apples"] \ No newline at end of file diff --git a/4a. Arrays.playground/section-25.swift b/4a. Arrays.playground/section-25.swift new file mode 100644 index 0000000..a783e8f --- /dev/null +++ b/4a. Arrays.playground/section-25.swift @@ -0,0 +1 @@ +shoppingList[4..<6] = ["Limes", "Mint leaves", "Sugar"] \ No newline at end of file diff --git a/4a. Arrays.playground/section-27.swift b/4a. Arrays.playground/section-27.swift new file mode 100644 index 0000000..a268d7a --- /dev/null +++ b/4a. Arrays.playground/section-27.swift @@ -0,0 +1 @@ +shoppingList.insert("Maple Syrup", atIndex: 3) \ No newline at end of file diff --git a/4a. Arrays.playground/section-29.swift b/4a. Arrays.playground/section-29.swift new file mode 100644 index 0000000..6c4038b --- /dev/null +++ b/4a. Arrays.playground/section-29.swift @@ -0,0 +1 @@ +let apples = shoppingList.removeLast() \ No newline at end of file diff --git a/4a. Arrays.playground/section-3.swift b/4a. Arrays.playground/section-3.swift new file mode 100644 index 0000000..494b11b --- /dev/null +++ b/4a. Arrays.playground/section-3.swift @@ -0,0 +1 @@ +var shorter: [String] \ No newline at end of file diff --git a/4a. Arrays.playground/section-31.swift b/4a. Arrays.playground/section-31.swift new file mode 100644 index 0000000..533a4c1 --- /dev/null +++ b/4a. Arrays.playground/section-31.swift @@ -0,0 +1,4 @@ +for item in shoppingList +{ + item +} \ No newline at end of file diff --git a/4a. Arrays.playground/section-33.swift b/4a. Arrays.playground/section-33.swift new file mode 100644 index 0000000..473a73e --- /dev/null +++ b/4a. Arrays.playground/section-33.swift @@ -0,0 +1,5 @@ +for (index, value) in enumerate(shoppingList) +{ + index + value +} \ No newline at end of file diff --git a/4a. Arrays.playground/section-35.swift b/4a. Arrays.playground/section-35.swift new file mode 100644 index 0000000..e3527e9 --- /dev/null +++ b/4a. Arrays.playground/section-35.swift @@ -0,0 +1 @@ +var someInts = [Int]() \ No newline at end of file diff --git a/4a. Arrays.playground/section-37.swift b/4a. Arrays.playground/section-37.swift new file mode 100644 index 0000000..7c1bdba --- /dev/null +++ b/4a. Arrays.playground/section-37.swift @@ -0,0 +1,2 @@ +someInts.append(3) +someInts \ No newline at end of file diff --git a/4a. Arrays.playground/section-39.swift b/4a. Arrays.playground/section-39.swift new file mode 100644 index 0000000..79d9171 --- /dev/null +++ b/4a. Arrays.playground/section-39.swift @@ -0,0 +1 @@ +someInts = [] \ No newline at end of file diff --git a/4a. Arrays.playground/section-41.swift b/4a. Arrays.playground/section-41.swift new file mode 100644 index 0000000..462b7f1 --- /dev/null +++ b/4a. Arrays.playground/section-41.swift @@ -0,0 +1 @@ +var threeDoubles = [Double](count: 3, repeatedValue: 3.3) \ No newline at end of file diff --git a/4a. Arrays.playground/section-43.swift b/4a. Arrays.playground/section-43.swift new file mode 100644 index 0000000..c87254e --- /dev/null +++ b/4a. Arrays.playground/section-43.swift @@ -0,0 +1 @@ +var anotherThreeDoubles = Array(count: 3, repeatedValue: 2.5) \ No newline at end of file diff --git a/4a. Arrays.playground/section-45.swift b/4a. Arrays.playground/section-45.swift new file mode 100644 index 0000000..a944d56 --- /dev/null +++ b/4a. Arrays.playground/section-45.swift @@ -0,0 +1 @@ +let immutableArray = ["a", "b"] \ No newline at end of file diff --git a/4a. Arrays.playground/section-47.swift b/4a. Arrays.playground/section-47.swift new file mode 100644 index 0000000..1785696 --- /dev/null +++ b/4a. Arrays.playground/section-47.swift @@ -0,0 +1 @@ +// immutableArray[0] = "b" \ No newline at end of file diff --git a/4a. Arrays.playground/section-49.swift b/4a. Arrays.playground/section-49.swift new file mode 100644 index 0000000..944822c --- /dev/null +++ b/4a. Arrays.playground/section-49.swift @@ -0,0 +1 @@ +// immutableArray += "c" \ No newline at end of file diff --git a/4a. Arrays.playground/section-5.swift b/4a. Arrays.playground/section-5.swift new file mode 100644 index 0000000..dc494dc --- /dev/null +++ b/4a. Arrays.playground/section-5.swift @@ -0,0 +1 @@ +["Eggs", "Milk"] \ No newline at end of file diff --git a/4a. Arrays.playground/section-51.swift b/4a. Arrays.playground/section-51.swift new file mode 100644 index 0000000..a79c80d --- /dev/null +++ b/4a. Arrays.playground/section-51.swift @@ -0,0 +1,3 @@ +var a = [1, 2, 3] +var b = a +var c = a \ No newline at end of file diff --git a/4a. Arrays.playground/section-53.swift b/4a. Arrays.playground/section-53.swift new file mode 100644 index 0000000..fbe43ef --- /dev/null +++ b/4a. Arrays.playground/section-53.swift @@ -0,0 +1,3 @@ +a[0] = 42 +b[0] +c[0] \ No newline at end of file diff --git a/4a. Arrays.playground/section-55.swift b/4a. Arrays.playground/section-55.swift new file mode 100644 index 0000000..1c943a9 --- /dev/null +++ b/4a. Arrays.playground/section-55.swift @@ -0,0 +1,3 @@ +a +b +c \ No newline at end of file diff --git a/4a. Arrays.playground/section-57.swift b/4a. Arrays.playground/section-57.swift new file mode 100644 index 0000000..8b94ea1 --- /dev/null +++ b/4a. Arrays.playground/section-57.swift @@ -0,0 +1 @@ +b.append(4) \ No newline at end of file diff --git a/4a. Arrays.playground/section-59.swift b/4a. Arrays.playground/section-59.swift new file mode 100644 index 0000000..1c943a9 --- /dev/null +++ b/4a. Arrays.playground/section-59.swift @@ -0,0 +1,3 @@ +a +b +c \ No newline at end of file diff --git a/4a. Arrays.playground/section-7.swift b/4a. Arrays.playground/section-7.swift new file mode 100644 index 0000000..1657805 --- /dev/null +++ b/4a. Arrays.playground/section-7.swift @@ -0,0 +1 @@ +var commonPets: [String] = ["Cats", "Dogs"] \ No newline at end of file diff --git a/4a. Arrays.playground/section-9.swift b/4a. Arrays.playground/section-9.swift new file mode 100644 index 0000000..97e8ec4 --- /dev/null +++ b/4a. Arrays.playground/section-9.swift @@ -0,0 +1 @@ +var shoppingList = ["Eggs", "Milk"] \ No newline at end of file diff --git a/4b. Dictionaries.playground/Documentation/section-0.html b/4b. Dictionaries.playground/Documentation/section-0.html new file mode 100644 index 0000000..666c40e --- /dev/null +++ b/4b. Dictionaries.playground/Documentation/section-0.html @@ -0,0 +1,28 @@ + + + + + Section 1 + + + + + + +
+
+

Dictionaries

+

Things to know:

+
    +
  • Dictionaries store multiple values of the same type, each associated with a key which acts as an identifier for that value within the dictionary.
  • +
  • Dictionaries are type-safe and always clear about what they contain.
  • +
  • The types of values that can be stored in a dictionary must always be made clear either through explicit type annotation or through type inference.
  • +
+
+

Creating a dictionary

+

This is a Dictionary literal. They contain a comma-separated list of key:value pairs:

+ +
+
+ + diff --git a/4b. Dictionaries.playground/Documentation/section-10.html b/4b. Dictionaries.playground/Documentation/section-10.html new file mode 100644 index 0000000..c1395f2 --- /dev/null +++ b/4b. Dictionaries.playground/Documentation/section-10.html @@ -0,0 +1,20 @@ + + + + + Section 11 + + + + + + +
+
+

What happens if we try to get a value that doesn't exist?

+

Since this can happen, the Dictionary always retuns an optional, so in the next line of code, notFound will be nil. It will also be of type String?

+ +
+
+ + diff --git a/4b. Dictionaries.playground/Documentation/section-12.html b/4b. Dictionaries.playground/Documentation/section-12.html new file mode 100644 index 0000000..40f54c5 --- /dev/null +++ b/4b. Dictionaries.playground/Documentation/section-12.html @@ -0,0 +1,19 @@ + + + + + Section 13 + + + + + + +
+
+

We can get the number of elements in the dictionary:

+ +
+
+ + diff --git a/4b. Dictionaries.playground/Documentation/section-14.html b/4b. Dictionaries.playground/Documentation/section-14.html new file mode 100644 index 0000000..778579e --- /dev/null +++ b/4b. Dictionaries.playground/Documentation/section-14.html @@ -0,0 +1,19 @@ + + + + + Section 15 + + + + + + +
+
+

We can add an element by accessing a key that doesn't exist:

+ +
+
+ + diff --git a/4b. Dictionaries.playground/Documentation/section-16.html b/4b. Dictionaries.playground/Documentation/section-16.html new file mode 100644 index 0000000..930eda0 --- /dev/null +++ b/4b. Dictionaries.playground/Documentation/section-16.html @@ -0,0 +1,19 @@ + + + + + Section 17 + + + + + + +
+
+

Here's another way to set/update a value. This lets us get the previous value before we set the new one. The returned value is optional, in case the new value doesn't replace an existing one:

+ +
+
+ + diff --git a/4b. Dictionaries.playground/Documentation/section-18.html b/4b. Dictionaries.playground/Documentation/section-18.html new file mode 100644 index 0000000..bab4e93 --- /dev/null +++ b/4b. Dictionaries.playground/Documentation/section-18.html @@ -0,0 +1,19 @@ + + + + + Section 19 + + + + + + +
+
+

We can remove an entry by setting the value for a key to nil:

+ +
+
+ + diff --git a/4b. Dictionaries.playground/Documentation/section-2.html b/4b. Dictionaries.playground/Documentation/section-2.html new file mode 100644 index 0000000..12ffddd --- /dev/null +++ b/4b. Dictionaries.playground/Documentation/section-2.html @@ -0,0 +1,20 @@ + + + + + Section 3 + + + + + + +
+
+

Let's use that literal to define and initialize a Dictionary.

+

In this case, we use type annotation to explicitly declare a Dictionary containing String keys and String values. This uses the syntactic sugar "[ KeyType: ValueType ]" to declare the dictionary.

+ +
+
+ + diff --git a/4b. Dictionaries.playground/Documentation/section-20.html b/4b. Dictionaries.playground/Documentation/section-20.html new file mode 100644 index 0000000..ef43561 --- /dev/null +++ b/4b. Dictionaries.playground/Documentation/section-20.html @@ -0,0 +1,19 @@ + + + + + Section 21 + + + + + + +
+
+

Here's another way to remove a value. The returned value is set to the value that was removed. Again, this is optional in case there was no value to remove. In this case, the APL airport was already removed, so the return value will be a nil optional:

+ +
+
+ + diff --git a/4b. Dictionaries.playground/Documentation/section-22.html b/4b. Dictionaries.playground/Documentation/section-22.html new file mode 100644 index 0000000..0d904a2 --- /dev/null +++ b/4b. Dictionaries.playground/Documentation/section-22.html @@ -0,0 +1,20 @@ + + + + + Section 23 + + + + + + +
+
+

Iterating over a Dictionary

+

We can iterating over key/value pairs with a for-in loop, which uses a Tuple to hold the key/value pair for each entry in the Dictionary:

+ +
+
+ + diff --git a/4b. Dictionaries.playground/Documentation/section-24.html b/4b. Dictionaries.playground/Documentation/section-24.html new file mode 100644 index 0000000..fe31e91 --- /dev/null +++ b/4b. Dictionaries.playground/Documentation/section-24.html @@ -0,0 +1,19 @@ + + + + + Section 25 + + + + + + +
+
+

We can iterate over just the keys

+ +
+
+ + diff --git a/4b. Dictionaries.playground/Documentation/section-26.html b/4b. Dictionaries.playground/Documentation/section-26.html new file mode 100644 index 0000000..ff0ca88 --- /dev/null +++ b/4b. Dictionaries.playground/Documentation/section-26.html @@ -0,0 +1,19 @@ + + + + + Section 27 + + + + + + +
+
+

We can iterate over jsut the values

+ +
+
+ + diff --git a/4b. Dictionaries.playground/Documentation/section-28.html b/4b. Dictionaries.playground/Documentation/section-28.html new file mode 100644 index 0000000..62bb411 --- /dev/null +++ b/4b. Dictionaries.playground/Documentation/section-28.html @@ -0,0 +1,20 @@ + + + + + Section 29 + + + + + + +
+
+

We can create an array from the keys or values

+

Note that when doing this, the use of Array() is needed to convert the keys or values into an array.

+ +
+
+ + diff --git a/4b. Dictionaries.playground/Documentation/section-30.html b/4b. Dictionaries.playground/Documentation/section-30.html new file mode 100644 index 0000000..ce8fc51 --- /dev/null +++ b/4b. Dictionaries.playground/Documentation/section-30.html @@ -0,0 +1,20 @@ + + + + + Section 31 + + + + + + +
+
+

Creating an empty Dictionary

+

Here, we create an empty Dictionary of Int keys and String values:

+ +
+
+ + diff --git a/4b. Dictionaries.playground/Documentation/section-32.html b/4b. Dictionaries.playground/Documentation/section-32.html new file mode 100644 index 0000000..ec6ab31 --- /dev/null +++ b/4b. Dictionaries.playground/Documentation/section-32.html @@ -0,0 +1,19 @@ + + + + + Section 33 + + + + + + +
+
+

Let's set one of the values

+ +
+
+ + diff --git a/4b. Dictionaries.playground/Documentation/section-34.html b/4b. Dictionaries.playground/Documentation/section-34.html new file mode 100644 index 0000000..fc0a18c --- /dev/null +++ b/4b. Dictionaries.playground/Documentation/section-34.html @@ -0,0 +1,19 @@ + + + + + Section 35 + + + + + + +
+
+

We can empty a dictionary using an empty dictionary literal:

+ +
+
+ + diff --git a/4b. Dictionaries.playground/Documentation/section-36.html b/4b. Dictionaries.playground/Documentation/section-36.html new file mode 100644 index 0000000..3ac8f04 --- /dev/null +++ b/4b. Dictionaries.playground/Documentation/section-36.html @@ -0,0 +1,19 @@ + + + + + Section 37 + + + + + + +
+
+

An immutable dictionary is a constant.

+ +
+
+ + diff --git a/4b. Dictionaries.playground/Documentation/section-38.html b/4b. Dictionaries.playground/Documentation/section-38.html new file mode 100644 index 0000000..e42adf1 --- /dev/null +++ b/4b. Dictionaries.playground/Documentation/section-38.html @@ -0,0 +1,19 @@ + + + + + Section 39 + + + + + + +
+
+

Similar to arrays, we cannot modify the contents of an immutable dictionary. The following lines will not compile:

+ +
+
+ + diff --git a/4b. Dictionaries.playground/Documentation/section-4.html b/4b. Dictionaries.playground/Documentation/section-4.html new file mode 100644 index 0000000..1d9fae3 --- /dev/null +++ b/4b. Dictionaries.playground/Documentation/section-4.html @@ -0,0 +1,19 @@ + + + + + Section 5 + + + + + + +
+
+

The declaration for airports above could also have been declared in this way:

+ +
+
+ + diff --git a/4b. Dictionaries.playground/Documentation/section-40.html b/4b. Dictionaries.playground/Documentation/section-40.html new file mode 100644 index 0000000..d0e559b --- /dev/null +++ b/4b. Dictionaries.playground/Documentation/section-40.html @@ -0,0 +1,20 @@ + + + + + Section 41 + + + + + + +
+
+

Dictionaries are value types, which means they are copied on assignment.

+

Let's create a Dictionary and copy it:

+ +
+
+ + diff --git a/4b. Dictionaries.playground/Documentation/section-42.html b/4b. Dictionaries.playground/Documentation/section-42.html new file mode 100644 index 0000000..7966010 --- /dev/null +++ b/4b. Dictionaries.playground/Documentation/section-42.html @@ -0,0 +1,19 @@ + + + + + Section 43 + + + + + + +
+
+

Next, we'll modify the copy:

+ +
+
+ + diff --git a/4b. Dictionaries.playground/Documentation/section-44.html b/4b. Dictionaries.playground/Documentation/section-44.html new file mode 100644 index 0000000..028cbab --- /dev/null +++ b/4b. Dictionaries.playground/Documentation/section-44.html @@ -0,0 +1,19 @@ + + + + + Section 45 + + + + + + +
+
+

And we can see that the original is not changed:

+ +
+
+ + diff --git a/4b. Dictionaries.playground/Documentation/section-46.html b/4b. Dictionaries.playground/Documentation/section-46.html new file mode 100644 index 0000000..ebc1f53 --- /dev/null +++ b/4b. Dictionaries.playground/Documentation/section-46.html @@ -0,0 +1,18 @@ + + + + + Section 47 + + + + + + +
+
+ +
+
+ + diff --git a/4b. Dictionaries.playground/Documentation/section-6.html b/4b. Dictionaries.playground/Documentation/section-6.html new file mode 100644 index 0000000..d4d5949 --- /dev/null +++ b/4b. Dictionaries.playground/Documentation/section-6.html @@ -0,0 +1,19 @@ + + + + + Section 7 + + + + + + +
+
+

In the case below, the literal contains only Strings for all keys and only Strings for all values, so type inference works in our favor allowing us to avoid the type annotation:

+ +
+
+ + diff --git a/4b. Dictionaries.playground/Documentation/section-8.html b/4b. Dictionaries.playground/Documentation/section-8.html new file mode 100644 index 0000000..402fff5 --- /dev/null +++ b/4b. Dictionaries.playground/Documentation/section-8.html @@ -0,0 +1,20 @@ + + + + + Section 9 + + + + + + +
+
+

Accessing and modifying a Dictionary

+

Let's get a value from the dictionary for the TYO airport:

+ +
+
+ + diff --git a/4b. Dictionaries.playground/Documentation/stylesheet.css b/4b. Dictionaries.playground/Documentation/stylesheet.css new file mode 100644 index 0000000..43b0230 --- /dev/null +++ b/4b. Dictionaries.playground/Documentation/stylesheet.css @@ -0,0 +1,65 @@ +html { + font-size: 1em; +} + +body { + background-color: #fff; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: normal; + font-size: 1.0em; + line-height: 140%; + padding: 0 2em; +} + +code { + font-family: "Menlo", monospace; + font-size: 0.95em; +} + +section { + padding: 0.5em 0; + -webkit-overflow-scrolling: touch; +} + +a:link { + color: #00f; + text-decoration: none; + border-bottom: 1px solid #00f; +} + +h1 { + font-size: 1.5em; + padding: 0.7em 0 0 0; +} + +h2 { + font-size: 1.2em; + padding: 0.7em 0 0 0; +} + +h3 { + font-size: 1.0em; + padding: 0.7em 0 0 0; +} + +blockquote { + font-size: 0.9em; + margin-left: 0; + padding-left: 2em; + border-left: 8px solid #eee; +} + +.footnotes { + color: #666666; + margin-top: 2em; + font-size: 0.9em; + letter-spacing: -3px; +} + +.footnotes:before { + content: "――――"; +} + +.footnotes ol { + letter-spacing: normal; +} diff --git a/4b. Dictionaries.playground/contents.xcplayground b/4b. Dictionaries.playground/contents.xcplayground index 4937636..df8f4ca 100644 --- a/4b. Dictionaries.playground/contents.xcplayground +++ b/4b. Dictionaries.playground/contents.xcplayground @@ -1,7 +1,99 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file diff --git a/4b. Dictionaries.playground/section-1.swift b/4b. Dictionaries.playground/section-1.swift index c901f6c..27dd5e3 100644 --- a/4b. Dictionaries.playground/section-1.swift +++ b/4b. Dictionaries.playground/section-1.swift @@ -1,126 +1 @@ -// ------------------------------------------------------------------------------------------------ -// Things to know: -// -// * Dictionaries store multiple values of the same type, each associated with a key which acts as -// an identifier for that value within the dictionary. -// -// * Dictionaries are type-safe and always clear about what they contain. -// -// * The types of values that can be stored in a dictionary must always be made clear either -// through explicit type annotation or through type inference. -// ------------------------------------------------------------------------------------------------ - -// ------------------------------------------------------------------------------------------------ -// Creating a dictionary -// -// This is a Dictionary literal. They contain a comma-separated list of key:value pairs: -["TYO": "Tokyo", "DUB": "Dublin"] - -// Let's use that literal to define and initialize a Dictionary. -// -// In this case, we use type annotation to explicitly declare a Dictionary containing String keys -// and String values. This uses the syntactic sugar "[ KeyType: ValueType ]" to declare the -// dictionary. -var airports: [String : String] = ["TYO": "Tokyo", "DUB": "Dublin", "APL": "Apple Intl"] - -// The declaration for airports above could also have been declared in this way: -var players: Dictionary = ["Who" : "First", "What" : "Second"] - -// In the case below, the literal contains only Strings for all keys and only Strings for all -// values, so type inference works in our favor allowing us to avoid the type annotation: -let inferredDictionary = ["TYO": "Tokyo", "DUB": "Dublin"] - -// ------------------------------------------------------------------------------------------------ -// Accessing and modifying a Dictionary -// -// Let's get a value from the dictionary for the TYO airport: -airports["TYO"] - -// What happens if we try to get a value that doesn't exist? -// -// Since this can happen, the Dictionary always retuns an optional, so in the next line of code, -// notFound will be nil. It will also be of type String? -var notFound = airports["FOO"] - -// We can get the number of elements in the dictionary: -airports.count - -// We can add an element by accessing a key that doesn't exist: -airports["LHR"] = "London" - -// Here's another way to set/update a value. This lets us get the previous value before we set -// the new one. The returned value is optional, in case the new value doesn't replace an existing -// one: -var previousValue = airports.updateValue("Dublin International", forKey: "DUB") - -// We can remove an entry by setting the value for a key to nil: -airports["APL"] = nil - -// Here's another way to remove a value. The returned value is set to the value that was removed. -// Again, this is optional in case there was no value to remove. In this case, the APL airport -// was already removed, so the return value will be a nil optional: -var removedValue = airports.removeValueForKey("APL") - -// ------------------------------------------------------------------------------------------------ -// Iterating over a Dictionary -// -// We can iterating over key/value pairs with a for-in loop, which uses a Tuple to hold the -// key/value pair for each entry in the Dictionary: -for (airportCode, airportName) in airports -{ - airportCode - airportName -} - -// We can iterate over just the keys -for airportCode in airports.keys -{ - airportCode -} - -// We can iterate over jsut the values -for airportName in airports.values -{ - airportName -} - -// We can create an array from the keys or values -// -// Note that when doing this, the use of Array() is needed to convert the keys or values into -// an array. -var airportCodes = Array(airports.keys) -var airportNames = Array(airports.values) - -// ------------------------------------------------------------------------------------------------ -// Creating an empty Dictionary -// -// Here, we create an empty Dictionary of Int keys and String values: -var namesOfIntegers = Dictionary() - -// Let's set one of the values -namesOfIntegers[16] = "Sixteen" - -// We can empty a dictionary using an empty dictionary literal: -namesOfIntegers = [:] - -// An immutable dictionary is a constant. -let immutableDict = ["a": "one", "b": "two"] - -// Similar to arrays, we cannot modify the contents of an immutable dictionary. The following lines -// will not compile: -// -// immutableDict["a"] = "b" // You cannot modify an element -// immutableDict["c"] = "three" // You cannot add a new entry or change the size - -// Dictionaries are value types, which means they are copied on assignment. -// -// Let's create a Dictionary and copy it: -var ages = ["Peter": 23, "Wei": 35, "Anish": 65, "Katya": 19] -var copiedAges = ages - -// Next, we'll modify the copy: -copiedAges["Peter"] = 24 - -// And we can see that the original is not changed: -ages["Peter"] - +["TYO": "Tokyo", "DUB": "Dublin"] \ No newline at end of file diff --git a/4b. Dictionaries.playground/section-11.swift b/4b. Dictionaries.playground/section-11.swift new file mode 100644 index 0000000..a0ea158 --- /dev/null +++ b/4b. Dictionaries.playground/section-11.swift @@ -0,0 +1 @@ +var notFound = airports["FOO"] \ No newline at end of file diff --git a/4b. Dictionaries.playground/section-13.swift b/4b. Dictionaries.playground/section-13.swift new file mode 100644 index 0000000..256b9a9 --- /dev/null +++ b/4b. Dictionaries.playground/section-13.swift @@ -0,0 +1 @@ +airports.count \ No newline at end of file diff --git a/4b. Dictionaries.playground/section-15.swift b/4b. Dictionaries.playground/section-15.swift new file mode 100644 index 0000000..a3263a4 --- /dev/null +++ b/4b. Dictionaries.playground/section-15.swift @@ -0,0 +1 @@ +airports["LHR"] = "London" \ No newline at end of file diff --git a/4b. Dictionaries.playground/section-17.swift b/4b. Dictionaries.playground/section-17.swift new file mode 100644 index 0000000..4e72761 --- /dev/null +++ b/4b. Dictionaries.playground/section-17.swift @@ -0,0 +1 @@ +var previousValue = airports.updateValue("Dublin International", forKey: "DUB") \ No newline at end of file diff --git a/4b. Dictionaries.playground/section-19.swift b/4b. Dictionaries.playground/section-19.swift new file mode 100644 index 0000000..7c31e46 --- /dev/null +++ b/4b. Dictionaries.playground/section-19.swift @@ -0,0 +1 @@ +airports["APL"] = nil \ No newline at end of file diff --git a/4b. Dictionaries.playground/section-21.swift b/4b. Dictionaries.playground/section-21.swift new file mode 100644 index 0000000..cef574b --- /dev/null +++ b/4b. Dictionaries.playground/section-21.swift @@ -0,0 +1 @@ +var removedValue = airports.removeValueForKey("APL") \ No newline at end of file diff --git a/4b. Dictionaries.playground/section-23.swift b/4b. Dictionaries.playground/section-23.swift new file mode 100644 index 0000000..fdbc348 --- /dev/null +++ b/4b. Dictionaries.playground/section-23.swift @@ -0,0 +1,5 @@ +for (airportCode, airportName) in airports +{ + airportCode + airportName +} \ No newline at end of file diff --git a/4b. Dictionaries.playground/section-25.swift b/4b. Dictionaries.playground/section-25.swift new file mode 100644 index 0000000..3db7cf3 --- /dev/null +++ b/4b. Dictionaries.playground/section-25.swift @@ -0,0 +1,4 @@ +for airportCode in airports.keys +{ + airportCode +} \ No newline at end of file diff --git a/4b. Dictionaries.playground/section-27.swift b/4b. Dictionaries.playground/section-27.swift new file mode 100644 index 0000000..f22fb5c --- /dev/null +++ b/4b. Dictionaries.playground/section-27.swift @@ -0,0 +1,4 @@ +for airportName in airports.values +{ + airportName +} \ No newline at end of file diff --git a/4b. Dictionaries.playground/section-29.swift b/4b. Dictionaries.playground/section-29.swift new file mode 100644 index 0000000..0573bd4 --- /dev/null +++ b/4b. Dictionaries.playground/section-29.swift @@ -0,0 +1,2 @@ +var airportCodes = Array(airports.keys) +var airportNames = Array(airports.values) \ No newline at end of file diff --git a/4b. Dictionaries.playground/section-3.swift b/4b. Dictionaries.playground/section-3.swift new file mode 100644 index 0000000..0ca4019 --- /dev/null +++ b/4b. Dictionaries.playground/section-3.swift @@ -0,0 +1 @@ +var airports: [String : String] = ["TYO": "Tokyo", "DUB": "Dublin", "APL": "Apple Intl"] \ No newline at end of file diff --git a/4b. Dictionaries.playground/section-31.swift b/4b. Dictionaries.playground/section-31.swift new file mode 100644 index 0000000..a002e74 --- /dev/null +++ b/4b. Dictionaries.playground/section-31.swift @@ -0,0 +1 @@ +var namesOfIntegers = Dictionary() \ No newline at end of file diff --git a/4b. Dictionaries.playground/section-33.swift b/4b. Dictionaries.playground/section-33.swift new file mode 100644 index 0000000..6f55831 --- /dev/null +++ b/4b. Dictionaries.playground/section-33.swift @@ -0,0 +1 @@ +namesOfIntegers[16] = "Sixteen" \ No newline at end of file diff --git a/4b. Dictionaries.playground/section-35.swift b/4b. Dictionaries.playground/section-35.swift new file mode 100644 index 0000000..a4784c8 --- /dev/null +++ b/4b. Dictionaries.playground/section-35.swift @@ -0,0 +1 @@ +namesOfIntegers = [:] \ No newline at end of file diff --git a/4b. Dictionaries.playground/section-37.swift b/4b. Dictionaries.playground/section-37.swift new file mode 100644 index 0000000..f2f79ee --- /dev/null +++ b/4b. Dictionaries.playground/section-37.swift @@ -0,0 +1 @@ +let immutableDict = ["a": "one", "b": "two"] \ No newline at end of file diff --git a/4b. Dictionaries.playground/section-39.swift b/4b. Dictionaries.playground/section-39.swift new file mode 100644 index 0000000..8b25fde --- /dev/null +++ b/4b. Dictionaries.playground/section-39.swift @@ -0,0 +1,2 @@ +// immutableDict["a"] = "b" // You cannot modify an element +// immutableDict["c"] = "three" // You cannot add a new entry or change the size \ No newline at end of file diff --git a/4b. Dictionaries.playground/section-41.swift b/4b. Dictionaries.playground/section-41.swift new file mode 100644 index 0000000..deab7fc --- /dev/null +++ b/4b. Dictionaries.playground/section-41.swift @@ -0,0 +1,2 @@ +var ages = ["Peter": 23, "Wei": 35, "Anish": 65, "Katya": 19] +var copiedAges = ages \ No newline at end of file diff --git a/4b. Dictionaries.playground/section-43.swift b/4b. Dictionaries.playground/section-43.swift new file mode 100644 index 0000000..fef0ad2 --- /dev/null +++ b/4b. Dictionaries.playground/section-43.swift @@ -0,0 +1 @@ +copiedAges["Peter"] = 24 \ No newline at end of file diff --git a/4b. Dictionaries.playground/section-45.swift b/4b. Dictionaries.playground/section-45.swift new file mode 100644 index 0000000..74df65c --- /dev/null +++ b/4b. Dictionaries.playground/section-45.swift @@ -0,0 +1 @@ +ages["Peter"] \ No newline at end of file diff --git a/4b. Dictionaries.playground/section-5.swift b/4b. Dictionaries.playground/section-5.swift new file mode 100644 index 0000000..c1f1711 --- /dev/null +++ b/4b. Dictionaries.playground/section-5.swift @@ -0,0 +1 @@ +var players: Dictionary = ["Who" : "First", "What" : "Second"] \ No newline at end of file diff --git a/4b. Dictionaries.playground/section-7.swift b/4b. Dictionaries.playground/section-7.swift new file mode 100644 index 0000000..37bbba2 --- /dev/null +++ b/4b. Dictionaries.playground/section-7.swift @@ -0,0 +1 @@ +let inferredDictionary = ["TYO": "Tokyo", "DUB": "Dublin"] \ No newline at end of file diff --git a/4b. Dictionaries.playground/section-9.swift b/4b. Dictionaries.playground/section-9.swift new file mode 100644 index 0000000..b9c18c9 --- /dev/null +++ b/4b. Dictionaries.playground/section-9.swift @@ -0,0 +1 @@ +airports["TYO"] \ No newline at end of file diff --git a/5. Control Flow.playground/Documentation/section-0.html b/5. Control Flow.playground/Documentation/section-0.html new file mode 100644 index 0000000..c3e6d80 --- /dev/null +++ b/5. Control Flow.playground/Documentation/section-0.html @@ -0,0 +1,27 @@ + + + + + Section 1 + + + + + + +
+
+

Control Flow

+

Things to know:

+
    +
  • Much of the control flow in Swift is similar to C-like languages, but there are some key differences. For example, switch-case constructs are much more flexible and powerful as well as extensions to break and continue statements.
  • +
+
+

For loops

+

We can loop through ranges using the closed-range operator ("...").

+

In the loop below, 'index' is a constant that is automatically declared.

+ +
+
+ + diff --git a/5. Control Flow.playground/Documentation/section-10.html b/5. Control Flow.playground/Documentation/section-10.html new file mode 100644 index 0000000..289ead6 --- /dev/null +++ b/5. Control Flow.playground/Documentation/section-10.html @@ -0,0 +1,19 @@ + + + + + Section 11 + + + + + + +
+
+

We can use an underscore if you don't need access to the loop constant:

+ +
+
+ + diff --git a/5. Control Flow.playground/Documentation/section-12.html b/5. Control Flow.playground/Documentation/section-12.html new file mode 100644 index 0000000..3f75cba --- /dev/null +++ b/5. Control Flow.playground/Documentation/section-12.html @@ -0,0 +1,19 @@ + + + + + Section 13 + + + + + + +
+
+

We can iterate over arrays

+ +
+
+ + diff --git a/5. Control Flow.playground/Documentation/section-14.html b/5. Control Flow.playground/Documentation/section-14.html new file mode 100644 index 0000000..ebca704 --- /dev/null +++ b/5. Control Flow.playground/Documentation/section-14.html @@ -0,0 +1,19 @@ + + + + + Section 15 + + + + + + +
+
+

We can iterate over a Dictionary's key/value pairs

+ +
+
+ + diff --git a/5. Control Flow.playground/Documentation/section-16.html b/5. Control Flow.playground/Documentation/section-16.html new file mode 100644 index 0000000..bf2a983 --- /dev/null +++ b/5. Control Flow.playground/Documentation/section-16.html @@ -0,0 +1,19 @@ + + + + + Section 17 + + + + + + +
+
+

We can iterate over characters in a String

+ +
+
+ + diff --git a/5. Control Flow.playground/Documentation/section-18.html b/5. Control Flow.playground/Documentation/section-18.html new file mode 100644 index 0000000..040ee50 --- /dev/null +++ b/5. Control Flow.playground/Documentation/section-18.html @@ -0,0 +1,20 @@ + + + + + Section 19 + + + + + + +
+
+

We can use the For-Condition-Increment loop construct, which resembles the C-like variant

+

Note that the loop value is a variable, not a constant. In fact, they cannot be constant because of the increment statement (++index)

+ +
+
+ + diff --git a/5. Control Flow.playground/Documentation/section-2.html b/5. Control Flow.playground/Documentation/section-2.html new file mode 100644 index 0000000..2cf573f --- /dev/null +++ b/5. Control Flow.playground/Documentation/section-2.html @@ -0,0 +1,19 @@ + + + + + Section 3 + + + + + + +
+
+

The constant 'index' from the previous loop is scoped only to the loop. As a result, you cannot access it beyond the loop. The following line will not compile:

+ +
+
+ + diff --git a/5. Control Flow.playground/Documentation/section-20.html b/5. Control Flow.playground/Documentation/section-20.html new file mode 100644 index 0000000..8fb85ce --- /dev/null +++ b/5. Control Flow.playground/Documentation/section-20.html @@ -0,0 +1,19 @@ + + + + + Section 21 + + + + + + +
+
+

The parenthesis are optional for the For-Condition-Increment loop:

+ +
+
+ + diff --git a/5. Control Flow.playground/Documentation/section-22.html b/5. Control Flow.playground/Documentation/section-22.html new file mode 100644 index 0000000..ac5a104 --- /dev/null +++ b/5. Control Flow.playground/Documentation/section-22.html @@ -0,0 +1,19 @@ + + + + + Section 23 + + + + + + +
+
+

Variables are scoped to the For-Condition-Increment construct. To alter this, pre-declare index

+ +
+
+ + diff --git a/5. Control Flow.playground/Documentation/section-24.html b/5. Control Flow.playground/Documentation/section-24.html new file mode 100644 index 0000000..3704788 --- /dev/null +++ b/5. Control Flow.playground/Documentation/section-24.html @@ -0,0 +1,21 @@ + + + + + Section 25 + + + + + + +
+
+

While loops

+

While loops resemble other C-like languages. They perform the condition before each iteration +through the loop:

+ +
+
+ + diff --git a/5. Control Flow.playground/Documentation/section-26.html b/5. Control Flow.playground/Documentation/section-26.html new file mode 100644 index 0000000..ab39ecf --- /dev/null +++ b/5. Control Flow.playground/Documentation/section-26.html @@ -0,0 +1,19 @@ + + + + + Section 27 + + + + + + +
+
+

Do-While loops also resemble their C-like language counterparts. They perform the condition after each iteration through the loop. As a result, they always execute the code inside the loop at least once:

+ +
+
+ + diff --git a/5. Control Flow.playground/Documentation/section-28.html b/5. Control Flow.playground/Documentation/section-28.html new file mode 100644 index 0000000..fbe1da5 --- /dev/null +++ b/5. Control Flow.playground/Documentation/section-28.html @@ -0,0 +1,20 @@ + + + + + Section 29 + + + + + + +
+
+

Conditional Statements

+

The if statement is very similar to C-like languages, except that the parenthesis are optional. You can also chain multiple conditions with 'else' and 'else if' statements:

+ +
+
+ + diff --git a/5. Control Flow.playground/Documentation/section-30.html b/5. Control Flow.playground/Documentation/section-30.html new file mode 100644 index 0000000..587d916 --- /dev/null +++ b/5. Control Flow.playground/Documentation/section-30.html @@ -0,0 +1,23 @@ + + + + + Section 31 + + + + + + +
+
+

Switch statements are more powerful than their C-like counterparts. Here are a few of those differences to get us started:

+

Unlike C-like languages, switch statements do not require a "break" statement to prevent falling through to the next case.

+

Additionally, multiple conditions can be separated by a comma for a single case to match multiple conditions.

+

Switch statements must also be exhaustive and include all possible values, or the compiler will generate an error.

+

There are many more differences, but let's start with a simple switch statement to get our feet wet:

+ +
+
+ + diff --git a/5. Control Flow.playground/Documentation/section-32.html b/5. Control Flow.playground/Documentation/section-32.html new file mode 100644 index 0000000..d3aa2cf --- /dev/null +++ b/5. Control Flow.playground/Documentation/section-32.html @@ -0,0 +1,20 @@ + + + + + Section 33 + + + + + + +
+
+

Each case clause must have a statement of some kind. A comment will not suffice.

+

Otherwise you will get a compilation error. The following won't compile because there is an empty case statement:

+ +
+
+ + diff --git a/5. Control Flow.playground/Documentation/section-34.html b/5. Control Flow.playground/Documentation/section-34.html new file mode 100644 index 0000000..8622b65 --- /dev/null +++ b/5. Control Flow.playground/Documentation/section-34.html @@ -0,0 +1,19 @@ + + + + + Section 35 + + + + + + +
+
+

We can perform range matching for cases:

+ +
+
+ + diff --git a/5. Control Flow.playground/Documentation/section-36.html b/5. Control Flow.playground/Documentation/section-36.html new file mode 100644 index 0000000..061a619 --- /dev/null +++ b/5. Control Flow.playground/Documentation/section-36.html @@ -0,0 +1,20 @@ + + + + + Section 37 + + + + + + +
+
+

Matching against tuples

+

In addition to matching Tuples, we can also use ranges inside Tuple values and even match against partial Tuple values by using an "_" to ignore matches against a specific value within the Tuple.

+ +
+
+ + diff --git a/5. Control Flow.playground/Documentation/section-38.html b/5. Control Flow.playground/Documentation/section-38.html new file mode 100644 index 0000000..774422d --- /dev/null +++ b/5. Control Flow.playground/Documentation/section-38.html @@ -0,0 +1,19 @@ + + + + + Section 39 + + + + + + +
+
+

Value bindings in switch statements

+ +
+
+ + diff --git a/5. Control Flow.playground/Documentation/section-4.html b/5. Control Flow.playground/Documentation/section-4.html new file mode 100644 index 0000000..a6dc70a --- /dev/null +++ b/5. Control Flow.playground/Documentation/section-4.html @@ -0,0 +1,20 @@ + + + + + Section 5 + + + + + + +
+
+

We can loop through ranges using the half-closed range operator ("..<")

+

We can also reuse the name 'index' because of the scoping noted previously.

+ +
+
+ + diff --git a/5. Control Flow.playground/Documentation/section-40.html b/5. Control Flow.playground/Documentation/section-40.html new file mode 100644 index 0000000..edf459d --- /dev/null +++ b/5. Control Flow.playground/Documentation/section-40.html @@ -0,0 +1,19 @@ + + + + + Section 41 + + + + + + +
+
+

We can also mix let/var for case statements. The following code block is the same as the previous except that the final case statement, which mixes variable and constants for the x and y components of the Tuple.

+ +
+
+ + diff --git a/5. Control Flow.playground/Documentation/section-42.html b/5. Control Flow.playground/Documentation/section-42.html new file mode 100644 index 0000000..c1d01e9 --- /dev/null +++ b/5. Control Flow.playground/Documentation/section-42.html @@ -0,0 +1,19 @@ + + + + + Section 43 + + + + + + +
+
+

Where clauses allow us to perform more detailed conditions on case conditions. The where clauses work on the values declared on the case line:

+ +
+
+ + diff --git a/5. Control Flow.playground/Documentation/section-44.html b/5. Control Flow.playground/Documentation/section-44.html new file mode 100644 index 0000000..8712d03 --- /dev/null +++ b/5. Control Flow.playground/Documentation/section-44.html @@ -0,0 +1,21 @@ + + + + + Section 45 + + + + + + +
+
+

Control transfer statements

+

Swift supports extended versions of continue and break as well as an additional 'fallthrough' statement for switch-case constructs.

+

Since swift doesn't require a break statement to avoid falling through to the next case, we can still use them to early-out of the current case without continuing work. The first statement after the 'break' will be the next statement following the entire switch construct.

+ +
+
+ + diff --git a/5. Control Flow.playground/Documentation/section-46.html b/5. Control Flow.playground/Documentation/section-46.html new file mode 100644 index 0000000..fdb1f69 --- /dev/null +++ b/5. Control Flow.playground/Documentation/section-46.html @@ -0,0 +1,19 @@ + + + + + Section 47 + + + + + + +
+
+

Since each case must have a statement and since we must have an exhaustive switch, we can use the break statement to effectively nullify the use of a case:

+ +
+
+ + diff --git a/5. Control Flow.playground/Documentation/section-48.html b/5. Control Flow.playground/Documentation/section-48.html new file mode 100644 index 0000000..237da11 --- /dev/null +++ b/5. Control Flow.playground/Documentation/section-48.html @@ -0,0 +1,19 @@ + + + + + Section 49 + + + + + + +
+
+

Since we don't need to break out of cases to avoid falling through automatically, we must specifically express our intention to fall through using the 'fallthrough' keyword

+ +
+
+ + diff --git a/5. Control Flow.playground/Documentation/section-50.html b/5. Control Flow.playground/Documentation/section-50.html new file mode 100644 index 0000000..7fb8ffd --- /dev/null +++ b/5. Control Flow.playground/Documentation/section-50.html @@ -0,0 +1,21 @@ + + + + + Section 51 + + + + + + +
+
+

Continue and Break statements have been extended in Swift to allow each to specify which switch or loop construct to break out of, or continue to.

+

To enable this, labels are used, similar to labels used by C's goto statement.

+

The following will print each name until it reaches the letter 'a' then skip to the next name

+ +
+
+ + diff --git a/5. Control Flow.playground/Documentation/section-52.html b/5. Control Flow.playground/Documentation/section-52.html new file mode 100644 index 0000000..ca65182 --- /dev/null +++ b/5. Control Flow.playground/Documentation/section-52.html @@ -0,0 +1,19 @@ + + + + + Section 53 + + + + + + +
+
+

Similarly, this prints all names without the letter 'a' in them:

+ +
+
+ + diff --git a/5. Control Flow.playground/Documentation/section-54.html b/5. Control Flow.playground/Documentation/section-54.html new file mode 100644 index 0000000..37a73f3 --- /dev/null +++ b/5. Control Flow.playground/Documentation/section-54.html @@ -0,0 +1,19 @@ + + + + + Section 55 + + + + + + +
+
+

Similarly, this prints all names until the letter 'x' is found, then aborts all processing by breaking out of the outer loop:

+ +
+
+ + diff --git a/5. Control Flow.playground/Documentation/section-6.html b/5. Control Flow.playground/Documentation/section-6.html new file mode 100644 index 0000000..9e3b933 --- /dev/null +++ b/5. Control Flow.playground/Documentation/section-6.html @@ -0,0 +1,21 @@ + + + + + Section 7 + + + + + + +
+
+

Apple's "Swift Programming Language" book states the following, which I find in practice to be incorrect:

+

“The index constant exists only within the scope of the loop. If you want to check the value of index after the loop completes, or if you want to work with its value as a variable rather than a constant, you must declare it yourself before its use in the loop.”

+

In practice, I find that the loop constant overrides any local variable/constant and maintains its scope to the loop and does not alter the locally defined value:

+ +
+
+ + diff --git a/5. Control Flow.playground/Documentation/section-8.html b/5. Control Flow.playground/Documentation/section-8.html new file mode 100644 index 0000000..2e2008e --- /dev/null +++ b/5. Control Flow.playground/Documentation/section-8.html @@ -0,0 +1,19 @@ + + + + + Section 9 + + + + + + +
+
+

After the loop, we find that 'indx' still contains the original value of 3999

+ +
+
+ + diff --git a/5. Control Flow.playground/Documentation/stylesheet.css b/5. Control Flow.playground/Documentation/stylesheet.css new file mode 100644 index 0000000..43b0230 --- /dev/null +++ b/5. Control Flow.playground/Documentation/stylesheet.css @@ -0,0 +1,65 @@ +html { + font-size: 1em; +} + +body { + background-color: #fff; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: normal; + font-size: 1.0em; + line-height: 140%; + padding: 0 2em; +} + +code { + font-family: "Menlo", monospace; + font-size: 0.95em; +} + +section { + padding: 0.5em 0; + -webkit-overflow-scrolling: touch; +} + +a:link { + color: #00f; + text-decoration: none; + border-bottom: 1px solid #00f; +} + +h1 { + font-size: 1.5em; + padding: 0.7em 0 0 0; +} + +h2 { + font-size: 1.2em; + padding: 0.7em 0 0 0; +} + +h3 { + font-size: 1.0em; + padding: 0.7em 0 0 0; +} + +blockquote { + font-size: 0.9em; + margin-left: 0; + padding-left: 2em; + border-left: 8px solid #eee; +} + +.footnotes { + color: #666666; + margin-top: 2em; + font-size: 0.9em; + letter-spacing: -3px; +} + +.footnotes:before { + content: "――――"; +} + +.footnotes ol { + letter-spacing: normal; +} diff --git a/5. Control Flow.playground/contents.xcplayground b/5. Control Flow.playground/contents.xcplayground index 4937636..8a284a3 100644 --- a/5. Control Flow.playground/contents.xcplayground +++ b/5. Control Flow.playground/contents.xcplayground @@ -1,7 +1,117 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file diff --git a/5. Control Flow.playground/section-1.swift b/5. Control Flow.playground/section-1.swift index 8780f44..4d9886e 100644 --- a/5. Control Flow.playground/section-1.swift +++ b/5. Control Flow.playground/section-1.swift @@ -1,17 +1,3 @@ -// ------------------------------------------------------------------------------------------------ -// Things to know: -// -// * Much of the control flow in Swift is similar to C-like languages, but there are some key -// differences. For example, switch-case constructs are much more flexible and powerful as well -// as extensions to break and continue statements. -// ------------------------------------------------------------------------------------------------ - -// ------------------------------------------------------------------------------------------------ -// For loops -// -// We can loop through ranges using the closed-range operator ("..."). -// -// In the loop below, 'index' is a constant that is automatically declared. for index in 1...5 { "This will print 5 times" @@ -19,395 +5,4 @@ for index in 1...5 // Being a constant, the following line won't compile: // // index = 99 -} - -// The constant 'index' from the previous loop is scoped only to the loop. As a result, you cannot -// access it beyond the loop. The following line will not compile: -// -// index = 0 - -// We can loop through ranges using the half-closed range operator ("..<") -// -// We can also reuse the name 'index' because of the scoping noted previously. -for index in 1 ..< 5 -{ - "This will print 4 times" -} - -// Apple's "Swift Programming Language" book states the following, which I find in practice to be -// incorrect: -// -// “The index constant exists only within the scope of the loop. If you want to check the value of -// index after the loop completes, or if you want to work with its value as a variable rather than -// a constant, you must declare it yourself before its use in the loop.” -// -// In practice, I find that the loop constant overrides any local variable/constant and maintains -// its scope to the loop and does not alter the locally defined value: -var indx = 3999 -for indx in 1...5 -{ - indx // This ranges from 1 to 5, inclusive - - // 'indx' is still acting like a constant, so this line won't compile: - // - // indx++ -} - -// After the loop, we find that 'indx' still contains the original value of 3999 -indx - -// We can use an underscore if you don't need access to the loop constant: -for _ in 1...10 -{ - println("do something") -} - -// We can iterate over arrays -let names = ["Anna", "Alex", "Brian", "Jack"] -for name in names -{ - name -} - -// We can iterate over a Dictionary's key/value pairs -let numberOfLegs = ["Spider":8, "Ant":6, "Cat":4] -for (animalName, legs) in numberOfLegs -{ - animalName - legs -} - -// We can iterate over characters in a String -for character in "Hello" -{ - character -} - -// We can use the For-Condition-Increment loop construct, which resembles the C-like variant -// -// Note that the loop value is a variable, not a constant. In fact, they cannot be constant -// because of the increment statement (++index) -for (var index = 0; index < 3; ++index) -{ - index -} - -// The parenthesis are optional for the For-Condition-Increment loop: -for var index = 0; index < 3; ++index -{ - index -} - -// Variables are scoped to the For-Condition-Increment construct. To alter this, pre-declare index -var index = 3000 -for index = 0; index < 3; ++index -{ - index -} -index // Index holds 3 after running through the loop - -// ------------------------------------------------------------------------------------------------ -// While loops -// -// While loops resemble other C-like languages. They perform the condition before each iteration -// through the loop: -while index > 0 -{ - --index -} - -// Do-While loops also resemble their C-like language counterparts. They perform the condition -// after each iteration through the loop. As a result, they always execute the code inside the -// loop at least once: -do -{ - ++index -} while (index < 3) - -// ------------------------------------------------------------------------------------------------ -// Conditional Statements -// -// The if statement is very similar to C-like languages, except that the parenthesis are optional. -// You can also chain multiple conditions with 'else' and 'else if' statements: -if (index > 0) -{ - "Index is positive" -} -else if index == 0 -{ - "index is zero" -} -else -{ - "index is negative" -} - -// Switch statements are more powerful than their C-like counterparts. Here are a few of those -// differences to get us started: -// -// Unlike C-like languages, switch statements do not require a "break" statement to prevent falling -// through to the next case. -// -// Additionally, multiple conditions can be separated by a comma for a single case to match -// multiple conditions. -// -// Switch statements must also be exhaustive and include all possible values, or the compiler will -// generate an error. -// -// There are many more differences, but let's start with a simple switch statement to get our feet -// wet: -let someCharacter: Character = "e" -switch someCharacter -{ - case "a", "e", "i", "o", "u": - "a vowel" - - case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t", "v", "w", "x", "u", "z": - "a consonant" - - // Necessary because switch statements must be exhaustive in order to capture all Characters - default: - "not a vowel or consonant" -} - -// Each case clause must have a statement of some kind. A comment will not suffice. -// -// Otherwise you will get a compilation error. The following won't compile because there is an -// empty case statement: -// -// let anotherCharacter: Character = "a" -// switch anotherCharacter -// { -// case "a": -// case "A": -// "the letter a" -// default: -// "not the letter a" -// } - -// We can perform range matching for cases: -let count = 3_000_000_000_000 -switch count -{ - case 0: - "no" - case 1...3: - "a few" - case 4...9: - "several" - case 10...99: - "tens of" - case 100...999: - "hundreds of" - case 1000...999999: - "thousands of" - default: - "millions and millions of" -} - -// Matching against tuples -// -// In addition to matching Tuples, we can also use ranges inside Tuple values and even match -// against partial Tuple values by using an "_" to ignore matches against a specific value within -// the Tuple. -let somePoint = (1,1) -switch somePoint -{ - case (0,0): - "origin" - - // Match only against y=0 - case (_, 0): - "On the X axis" - - // Match only against x=0 - case (0, _): - "On the y axis" - - // Match x and y from -2 to +2 (inclusive) - case (-2...2, -2...2): - "On or inside the 2x2 box" - - // Everything else - default: - "Outisde the 2x2 box" -} - -// Value bindings in switch statements -// -var anotherPoint = (2, 8) -switch anotherPoint -{ - // Bind 'x' to the first value (matching any x) of the tuple and match on y=0 - case (let x, 0): - "On the x axis with an x value of \(x)" - - // Bind 'y' to the second value (matching any y) of the tuple and match against x=0 - case (0, let y): - "On the y axis with an y value of \(y)" - - // Bind both values of the tuple, matching any x or y. Note the shorthand of the 'let' - // outside of the parenthesis. This works with 'var' as well. - // - // Also notice that since this matches any x or y, we fulfill the requirement for an exhaustive - // switch. - case let (x, y): - "Somewhere else on \(x), \(y)" -} - -// We can also mix let/var for case statements. The following code block is the same as the -// previous except that the final case statement, which mixes variable and constants for the x and -// y components of the Tuple. -switch anotherPoint -{ - case (let x, 0): - "On the x axis with an x value of \(x)" - - case (0, let y): - "On the y axis with an y value of \(y)" - - case (var x, let y): - ++x // We can modify the variable 'x', but not the constant 'y' - "Somewhere else on \(x), \(y)" -} - -// Where clauses allow us to perform more detailed conditions on case conditions. The where clauses -// work on the values declared on the case line: -let yetAnotherPoint = (1, -1) -switch yetAnotherPoint -{ - case let (x, y) where x == y: - "On the line of x == y" - - case let (x, y) where x == -y: - "On the line of x == -y" - - case let (x, y): - "Just some arbitrary point" -} - -// ------------------------------------------------------------------------------------------------ -// Control transfer statements -// -// Swift supports extended versions of continue and break as well as an additional 'fallthrough' -// statement for switch-case constructs. -// -// Since swift doesn't require a break statement to avoid falling through to the next case, we can -// still use them to early-out of the current case without continuing work. The first statement -// after the 'break' will be the next statement following the entire switch construct. -let someValue = 9000 -switch someValue -{ - case let x where (x & 1) == 1: - if someValue < 100 - { - "Odd number less than 100" - break - } - "Odd number greater or equal to 100" - - case let x where (x & 1) == 0: - if someValue < 100 - { - "Even number less than 100" - break - } - "Even number greater or equal to 100" - - default: - "Unknown value" -} - -// Since each case must have a statement and since we must have an exhaustive switch, we can use -// the break statement to effectively nullify the use of a case: -switch someValue -{ - case Int.min...100: - "Small number" - - case 101...1000: - break // We don't care about medium numbers - - case 1001...100_00: - "Big number" - - default: - break // We don't care about the rest, either -} - -// Since we don't need to break out of cases to avoid falling through automatically, we must -// specifically express our intention to fall through using the 'fallthrough' keyword -let integerToDescribe = 5 -var integerDescription = "\(integerToDescribe) is" -switch integerToDescribe -{ - case 2, 3, 5, 7, 11, 13, 17, 19: - integerDescription += " a prime number, and also" - fallthrough - - default: - integerDescription += " an integer." -} - -// Continue and Break statements have been extended in Swift to allow each to specify which -// switch or loop construct to break out of, or continue to. -// -// To enable this, labels are used, similar to labels used by C's goto statement. -// -// The following will print each name until it reaches the letter 'a' then skip to the next name -var result = "" -nameLoop: for name in names -{ - characterLoop: for character in name - { - theSwitch: switch character - { - case "a": - // Break out of the theSwitch and characterLoop - break characterLoop - - default: - result += character - } - } -} -result - -// Similarly, this prints all names without the letter 'a' in them: -result = "" -nameLoop: for name in names -{ - characterLoop: for character in name - { - theSwitch: switch character - { - case "a": - // Continue directly to the character loop, bypassing this character in this name - continue characterLoop - - default: - result += character - } - } -} -result - -// Similarly, this prints all names until the letter 'x' is found, then aborts all processing by -// breaking out of the outer loop: -result = "" -nameLoop: for name in names -{ - characterLoop: for character in name - { - theSwitch: switch character - { - case "x": - // Break completely out of the outer name loop - break nameLoop - - default: - result += character - } - } -} -result +} \ No newline at end of file diff --git a/5. Control Flow.playground/section-11.swift b/5. Control Flow.playground/section-11.swift new file mode 100644 index 0000000..f1cf891 --- /dev/null +++ b/5. Control Flow.playground/section-11.swift @@ -0,0 +1,4 @@ +for _ in 1...10 +{ + println("do something") +} \ No newline at end of file diff --git a/5. Control Flow.playground/section-13.swift b/5. Control Flow.playground/section-13.swift new file mode 100644 index 0000000..38d573a --- /dev/null +++ b/5. Control Flow.playground/section-13.swift @@ -0,0 +1,5 @@ +let names = ["Anna", "Alex", "Brian", "Jack"] +for name in names +{ + name +} \ No newline at end of file diff --git a/5. Control Flow.playground/section-15.swift b/5. Control Flow.playground/section-15.swift new file mode 100644 index 0000000..989606f --- /dev/null +++ b/5. Control Flow.playground/section-15.swift @@ -0,0 +1,6 @@ +let numberOfLegs = ["Spider":8, "Ant":6, "Cat":4] +for (animalName, legs) in numberOfLegs +{ + animalName + legs +} \ No newline at end of file diff --git a/5. Control Flow.playground/section-17.swift b/5. Control Flow.playground/section-17.swift new file mode 100644 index 0000000..ee127b0 --- /dev/null +++ b/5. Control Flow.playground/section-17.swift @@ -0,0 +1,4 @@ +for character in "Hello" +{ + character +} \ No newline at end of file diff --git a/5. Control Flow.playground/section-19.swift b/5. Control Flow.playground/section-19.swift new file mode 100644 index 0000000..1f61169 --- /dev/null +++ b/5. Control Flow.playground/section-19.swift @@ -0,0 +1,4 @@ +for (var index = 0; index < 3; ++index) +{ + index +} \ No newline at end of file diff --git a/5. Control Flow.playground/section-21.swift b/5. Control Flow.playground/section-21.swift new file mode 100644 index 0000000..a6868aa --- /dev/null +++ b/5. Control Flow.playground/section-21.swift @@ -0,0 +1,4 @@ +for var index = 0; index < 3; ++index +{ + index +} \ No newline at end of file diff --git a/5. Control Flow.playground/section-23.swift b/5. Control Flow.playground/section-23.swift new file mode 100644 index 0000000..48f6bf1 --- /dev/null +++ b/5. Control Flow.playground/section-23.swift @@ -0,0 +1,6 @@ +var index = 3000 +for index = 0; index < 3; ++index +{ + index +} +index // Index holds 3 after running through the loop \ No newline at end of file diff --git a/5. Control Flow.playground/section-25.swift b/5. Control Flow.playground/section-25.swift new file mode 100644 index 0000000..3dfe84d --- /dev/null +++ b/5. Control Flow.playground/section-25.swift @@ -0,0 +1,4 @@ +while index > 0 +{ + --index +} \ No newline at end of file diff --git a/5. Control Flow.playground/section-27.swift b/5. Control Flow.playground/section-27.swift new file mode 100644 index 0000000..d146781 --- /dev/null +++ b/5. Control Flow.playground/section-27.swift @@ -0,0 +1,4 @@ +do +{ + ++index +} while (index < 3) \ No newline at end of file diff --git a/5. Control Flow.playground/section-29.swift b/5. Control Flow.playground/section-29.swift new file mode 100644 index 0000000..a1dd901 --- /dev/null +++ b/5. Control Flow.playground/section-29.swift @@ -0,0 +1,12 @@ +if (index > 0) +{ + "Index is positive" +} +else if index == 0 +{ + "index is zero" +} +else +{ + "index is negative" +} \ No newline at end of file diff --git a/5. Control Flow.playground/section-3.swift b/5. Control Flow.playground/section-3.swift new file mode 100644 index 0000000..d101b98 --- /dev/null +++ b/5. Control Flow.playground/section-3.swift @@ -0,0 +1 @@ +// index = 0 \ No newline at end of file diff --git a/5. Control Flow.playground/section-31.swift b/5. Control Flow.playground/section-31.swift new file mode 100644 index 0000000..d3f636f --- /dev/null +++ b/5. Control Flow.playground/section-31.swift @@ -0,0 +1,13 @@ +let someCharacter: Character = "e" +switch someCharacter +{ + case "a", "e", "i", "o", "u": + "a vowel" + + case "b", "c", "d", "f", "g", "h", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t", "v", "w", "x", "u", "z": + "a consonant" + + // Necessary because switch statements must be exhaustive in order to capture all Characters + default: + "not a vowel or consonant" +} \ No newline at end of file diff --git a/5. Control Flow.playground/section-33.swift b/5. Control Flow.playground/section-33.swift new file mode 100644 index 0000000..b01dae1 --- /dev/null +++ b/5. Control Flow.playground/section-33.swift @@ -0,0 +1,9 @@ +/* let anotherCharacter: Character = "a" +switch anotherCharacter +{ + case "a": + case "A": + "the letter a" + default: + "not the letter a" +} */ \ No newline at end of file diff --git a/5. Control Flow.playground/section-35.swift b/5. Control Flow.playground/section-35.swift new file mode 100644 index 0000000..2315a38 --- /dev/null +++ b/5. Control Flow.playground/section-35.swift @@ -0,0 +1,18 @@ +let count = 3_000_000_000_000 +switch count +{ + case 0: + "no" + case 1...3: + "a few" + case 4...9: + "several" + case 10...99: + "tens of" + case 100...999: + "hundreds of" + case 1000...999999: + "thousands of" + default: + "millions and millions of" +} \ No newline at end of file diff --git a/5. Control Flow.playground/section-37.swift b/5. Control Flow.playground/section-37.swift new file mode 100644 index 0000000..9987760 --- /dev/null +++ b/5. Control Flow.playground/section-37.swift @@ -0,0 +1,22 @@ +let somePoint = (1,1) +switch somePoint +{ + case (0,0): + "origin" + + // Match only against y=0 + case (_, 0): + "On the X axis" + + // Match only against x=0 + case (0, _): + "On the y axis" + + // Match x and y from -2 to +2 (inclusive) + case (-2...2, -2...2): + "On or inside the 2x2 box" + + // Everything else + default: + "Outisde the 2x2 box" +} \ No newline at end of file diff --git a/5. Control Flow.playground/section-39.swift b/5. Control Flow.playground/section-39.swift new file mode 100644 index 0000000..d0b3d58 --- /dev/null +++ b/5. Control Flow.playground/section-39.swift @@ -0,0 +1,19 @@ +var anotherPoint = (2, 8) +switch anotherPoint +{ + // Bind 'x' to the first value (matching any x) of the tuple and match on y=0 + case (let x, 0): + "On the x axis with an x value of \(x)" + + // Bind 'y' to the second value (matching any y) of the tuple and match against x=0 + case (0, let y): + "On the y axis with an y value of \(y)" + + // Bind both values of the tuple, matching any x or y. Note the shorthand of the 'let' + // outside of the parenthesis. This works with 'var' as well. + // + // Also notice that since this matches any x or y, we fulfill the requirement for an exhaustive + // switch. + case let (x, y): + "Somewhere else on \(x), \(y)" +} \ No newline at end of file diff --git a/5. Control Flow.playground/section-41.swift b/5. Control Flow.playground/section-41.swift new file mode 100644 index 0000000..1f6135e --- /dev/null +++ b/5. Control Flow.playground/section-41.swift @@ -0,0 +1,12 @@ +switch anotherPoint +{ + case (let x, 0): + "On the x axis with an x value of \(x)" + + case (0, let y): + "On the y axis with an y value of \(y)" + + case (var x, let y): + ++x // We can modify the variable 'x', but not the constant 'y' + "Somewhere else on \(x), \(y)" +} \ No newline at end of file diff --git a/5. Control Flow.playground/section-43.swift b/5. Control Flow.playground/section-43.swift new file mode 100644 index 0000000..4067ee2 --- /dev/null +++ b/5. Control Flow.playground/section-43.swift @@ -0,0 +1,12 @@ +let yetAnotherPoint = (1, -1) +switch yetAnotherPoint +{ + case let (x, y) where x == y: + "On the line of x == y" + + case let (x, y) where x == -y: + "On the line of x == -y" + + case let (x, y): + "Just some arbitrary point" +} \ No newline at end of file diff --git a/5. Control Flow.playground/section-45.swift b/5. Control Flow.playground/section-45.swift new file mode 100644 index 0000000..b4a83d3 --- /dev/null +++ b/5. Control Flow.playground/section-45.swift @@ -0,0 +1,22 @@ +let someValue = 9000 +switch someValue +{ + case let x where (x & 1) == 1: + if someValue < 100 + { + "Odd number less than 100" + break + } + "Odd number greater or equal to 100" + + case let x where (x & 1) == 0: + if someValue < 100 + { + "Even number less than 100" + break + } + "Even number greater or equal to 100" + + default: + "Unknown value" +} \ No newline at end of file diff --git a/5. Control Flow.playground/section-47.swift b/5. Control Flow.playground/section-47.swift new file mode 100644 index 0000000..d8db8e5 --- /dev/null +++ b/5. Control Flow.playground/section-47.swift @@ -0,0 +1,14 @@ +switch someValue +{ + case Int.min...100: + "Small number" + + case 101...1000: + break // We don't care about medium numbers + + case 1001...100_00: + "Big number" + + default: + break // We don't care about the rest, either +} \ No newline at end of file diff --git a/5. Control Flow.playground/section-49.swift b/5. Control Flow.playground/section-49.swift new file mode 100644 index 0000000..227b5b0 --- /dev/null +++ b/5. Control Flow.playground/section-49.swift @@ -0,0 +1,11 @@ +let integerToDescribe = 5 +var integerDescription = "\(integerToDescribe) is" +switch integerToDescribe +{ + case 2, 3, 5, 7, 11, 13, 17, 19: + integerDescription += " a prime number, and also" + fallthrough + + default: + integerDescription += " an integer." +} \ No newline at end of file diff --git a/5. Control Flow.playground/section-5.swift b/5. Control Flow.playground/section-5.swift new file mode 100644 index 0000000..3e130bf --- /dev/null +++ b/5. Control Flow.playground/section-5.swift @@ -0,0 +1,4 @@ +for index in 1 ..< 5 +{ + "This will print 4 times" +} \ No newline at end of file diff --git a/5. Control Flow.playground/section-51.swift b/5. Control Flow.playground/section-51.swift new file mode 100644 index 0000000..42af275 --- /dev/null +++ b/5. Control Flow.playground/section-51.swift @@ -0,0 +1,17 @@ +var result = "" +nameLoop: for name in names +{ + characterLoop: for character in name + { + theSwitch: switch character + { + case "a": + // Break out of the theSwitch and characterLoop + break characterLoop + + default: + result += String(character) + } + } +} +result \ No newline at end of file diff --git a/5. Control Flow.playground/section-53.swift b/5. Control Flow.playground/section-53.swift new file mode 100644 index 0000000..56ad969 --- /dev/null +++ b/5. Control Flow.playground/section-53.swift @@ -0,0 +1,17 @@ +result = "" +nameLoop: for name in names +{ + characterLoop: for character in name + { + theSwitch: switch character + { + case "a": + // Continue directly to the character loop, bypassing this character in this name + continue characterLoop + + default: + result += String(character) + } + } +} +result \ No newline at end of file diff --git a/5. Control Flow.playground/section-55.swift b/5. Control Flow.playground/section-55.swift new file mode 100644 index 0000000..1d2a9e9 --- /dev/null +++ b/5. Control Flow.playground/section-55.swift @@ -0,0 +1,17 @@ +result = "" +nameLoop: for name in names +{ + characterLoop: for character in name + { + theSwitch: switch character + { + case "x": + // Break completely out of the outer name loop + break nameLoop + + default: + result += String(character) + } + } +} +result \ No newline at end of file diff --git a/5. Control Flow.playground/section-7.swift b/5. Control Flow.playground/section-7.swift new file mode 100644 index 0000000..9ede3c9 --- /dev/null +++ b/5. Control Flow.playground/section-7.swift @@ -0,0 +1,9 @@ +var indx = 3999 +for indx in 1...5 +{ + indx // This ranges from 1 to 5, inclusive + + // 'indx' is still acting like a constant, so this line won't compile: + // + // indx++ +} \ No newline at end of file diff --git a/5. Control Flow.playground/section-9.swift b/5. Control Flow.playground/section-9.swift new file mode 100644 index 0000000..87a0bbc --- /dev/null +++ b/5. Control Flow.playground/section-9.swift @@ -0,0 +1 @@ +indx \ No newline at end of file diff --git a/6. Functions.playground/Documentation/section-0.html b/6. Functions.playground/Documentation/section-0.html new file mode 100644 index 0000000..c9f4062 --- /dev/null +++ b/6. Functions.playground/Documentation/section-0.html @@ -0,0 +1,28 @@ + + + + + Section 1 + + + + + + +
+
+

Functions

+

Things to know:

+
    +
  • Like most other languages, functions contain units of code to perform a task, but there are a few key featurs of functions in Swift.
  • +
  • Functions are types in Swift. This means that a function can receive another function as an input parameter or return a function as a result. You can even declare variables that are functions.
  • +
  • Functions can be nested, and the inner function can be returned by the outer function. To enable this, the current state of any local variables (local to the outer function) that are used by the inner function are "captured" for use by the inner function. Any time that inner function is called, it uses this captured state. This is the case even when a nested function is returned by the parent function.
  • +
+
+

Here's a simple function that receives a Single string and returns a String

+

Note that each parameter has a local name (for use within the function) and a standard type annotation. The return value's type is at the end of the function, following the ->.

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-10.html b/6. Functions.playground/Documentation/section-10.html new file mode 100644 index 0000000..9db51ee --- /dev/null +++ b/6. Functions.playground/Documentation/section-10.html @@ -0,0 +1,19 @@ + + + + + Section 11 + + + + + + +
+
+

We can also remove the return type (and the -> delimiter) alltogether:

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-12.html b/6. Functions.playground/Documentation/section-12.html new file mode 100644 index 0000000..d95abd3 --- /dev/null +++ b/6. Functions.playground/Documentation/section-12.html @@ -0,0 +1,20 @@ + + + + + Section 13 + + + + + + +
+
+

Functions can return Tuples, which enable them to return multiple values.

+

The following function simply returns two hard-coded strings.

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-14.html b/6. Functions.playground/Documentation/section-14.html new file mode 100644 index 0000000..3945207 --- /dev/null +++ b/6. Functions.playground/Documentation/section-14.html @@ -0,0 +1,19 @@ + + + + + Section 15 + + + + + + +
+
+

Since the return value is a Tuple, we can use the Tuple's naming feature to name the values being returned:

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-16.html b/6. Functions.playground/Documentation/section-16.html new file mode 100644 index 0000000..6be8f04 --- /dev/null +++ b/6. Functions.playground/Documentation/section-16.html @@ -0,0 +1,20 @@ + + + + + Section 17 + + + + + + +
+
+

External Parameter Names

+

We can use Objective-C-like external parameter names so the caller must name the external parameters when they call the function. The extenal name appears before the local name.

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-18.html b/6. Functions.playground/Documentation/section-18.html new file mode 100644 index 0000000..5fd8ccf --- /dev/null +++ b/6. Functions.playground/Documentation/section-18.html @@ -0,0 +1,20 @@ + + + + + Section 19 + + + + + + +
+
+

If your internal and external names are the same, you can use a shorthand #name syntax to create both names at once.

+

The following declaration creates an internal parameter named "action" as well as an external parameter named "action":

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-2.html b/6. Functions.playground/Documentation/section-2.html new file mode 100644 index 0000000..f4729d8 --- /dev/null +++ b/6. Functions.playground/Documentation/section-2.html @@ -0,0 +1,19 @@ + + + + + Section 3 + + + + + + +
+
+

If we call the function, we'll receive the greeting

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-20.html b/6. Functions.playground/Documentation/section-20.html new file mode 100644 index 0000000..32a0313 --- /dev/null +++ b/6. Functions.playground/Documentation/section-20.html @@ -0,0 +1,19 @@ + + + + + Section 21 + + + + + + +
+
+

We can now use the external name ("action") to make the call:

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-22.html b/6. Functions.playground/Documentation/section-22.html new file mode 100644 index 0000000..1517a43 --- /dev/null +++ b/6. Functions.playground/Documentation/section-22.html @@ -0,0 +1,20 @@ + + + + + Section 23 + + + + + + +
+
+

We can also have default parameter values. Default parameter values must be placed at the end of the parameter list.

+

In the addMul routine, we'll add two numbers and multiply the result by an optional multiplier value. We will default the multiplier to 1 so that if the parameter is not specified, the multiplication won't affect the result.

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-24.html b/6. Functions.playground/Documentation/section-24.html new file mode 100644 index 0000000..f8b19ed --- /dev/null +++ b/6. Functions.playground/Documentation/section-24.html @@ -0,0 +1,19 @@ + + + + + Section 25 + + + + + + +
+
+

We can call with just two parameters to add them together

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-26.html b/6. Functions.playground/Documentation/section-26.html new file mode 100644 index 0000000..3621758 --- /dev/null +++ b/6. Functions.playground/Documentation/section-26.html @@ -0,0 +1,21 @@ + + + + + Section 27 + + + + + + +
+
+

Default parameter values and external names

+

Swift automatically creates external parameter names for those parameters that have default values. Since our declaration of addMul did not specify an external name (either explicitly or using the shorthand method), Swift created one for us based on the name of the internal parameter name. This acts as if we had defined the third parameter using the "#" shorthand.

+

Therefore, when calling the function and specifying a value for the defaulted parameter, we must provide the default parameter's external name:

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-28.html b/6. Functions.playground/Documentation/section-28.html new file mode 100644 index 0000000..934a54b --- /dev/null +++ b/6. Functions.playground/Documentation/section-28.html @@ -0,0 +1,19 @@ + + + + + Section 29 + + + + + + +
+
+

We can opt out of the automatic external name for default parameter values by specify an external name of "_" like so:

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-30.html b/6. Functions.playground/Documentation/section-30.html new file mode 100644 index 0000000..c345d58 --- /dev/null +++ b/6. Functions.playground/Documentation/section-30.html @@ -0,0 +1,19 @@ + + + + + Section 31 + + + + + + +
+
+

Here, we call without the third parameter as before:

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-32.html b/6. Functions.playground/Documentation/section-32.html new file mode 100644 index 0000000..9f1876f --- /dev/null +++ b/6. Functions.playground/Documentation/section-32.html @@ -0,0 +1,19 @@ + + + + + Section 33 + + + + + + +
+
+

And now we can call with an un-named third parameter:

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-34.html b/6. Functions.playground/Documentation/section-34.html new file mode 100644 index 0000000..a4cc6a8 --- /dev/null +++ b/6. Functions.playground/Documentation/section-34.html @@ -0,0 +1,22 @@ + + + + + Section 35 + + + + + + +
+
+

Variadic Parameters

+

Variadic parameters allow you to call functions with zero or more values of a specified type.

+

Variadic parameters appear within the receiving function as an array of the given type.

+

A function may only have at most one variadic and it must appear as the last parameter.

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-36.html b/6. Functions.playground/Documentation/section-36.html new file mode 100644 index 0000000..33a7948 --- /dev/null +++ b/6. Functions.playground/Documentation/section-36.html @@ -0,0 +1,19 @@ + + + + + Section 37 + + + + + + +
+
+

Let's call it with a few parameter lengths. Note that we can call with no parameters, since that meets the criteria of a variadic parameter (zero or more).

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-38.html b/6. Functions.playground/Documentation/section-38.html new file mode 100644 index 0000000..0c8eed8 --- /dev/null +++ b/6. Functions.playground/Documentation/section-38.html @@ -0,0 +1,19 @@ + + + + + Section 39 + + + + + + +
+
+

If we want to use variadic parameters and default parameter values, we can do so by making sure that the default parameters come before the variadic, at the end of the parameter list:

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-4.html b/6. Functions.playground/Documentation/section-4.html new file mode 100644 index 0000000..df77503 --- /dev/null +++ b/6. Functions.playground/Documentation/section-4.html @@ -0,0 +1,19 @@ + + + + + Section 5 + + + + + + +
+
+

Multiple input parameters are separated by a comma

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-40.html b/6. Functions.playground/Documentation/section-40.html new file mode 100644 index 0000000..363a7c8 --- /dev/null +++ b/6. Functions.playground/Documentation/section-40.html @@ -0,0 +1,19 @@ + + + + + Section 41 + + + + + + +
+
+

Here, we can still call with no parameters because of the default parameter

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-42.html b/6. Functions.playground/Documentation/section-42.html new file mode 100644 index 0000000..746dc74 --- /dev/null +++ b/6. Functions.playground/Documentation/section-42.html @@ -0,0 +1,19 @@ + + + + + Section 43 + + + + + + +
+
+

Going forward, we must specify the default parameter's external name (because we didn't opt-out of it.) Also, you can see why Swift attempts to enforce the use of external parameter names for default parameter values. In this case, it helps us recognize where the defalt parameters leave off and the variadic parameters begin:

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-44.html b/6. Functions.playground/Documentation/section-44.html new file mode 100644 index 0000000..a999e39 --- /dev/null +++ b/6. Functions.playground/Documentation/section-44.html @@ -0,0 +1,19 @@ + + + + + Section 45 + + + + + + +
+
+

Variadic parameters with external parameter names only apply their external name to the first variadic parameter specified in the function call (if present.)

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-46.html b/6. Functions.playground/Documentation/section-46.html new file mode 100644 index 0000000..3122bac --- /dev/null +++ b/6. Functions.playground/Documentation/section-46.html @@ -0,0 +1,19 @@ + + + + + Section 47 + + + + + + +
+
+

And here we can see the impact on the function call of adding the external name "values" to the variadic parameter:

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-48.html b/6. Functions.playground/Documentation/section-48.html new file mode 100644 index 0000000..72caed0 --- /dev/null +++ b/6. Functions.playground/Documentation/section-48.html @@ -0,0 +1,20 @@ + + + + + Section 49 + + + + + + +
+
+

Constant and variable parameters

+

All function parameters are constant by default. To make them variable, add the var introducer:

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-50.html b/6. Functions.playground/Documentation/section-50.html new file mode 100644 index 0000000..66a7225 --- /dev/null +++ b/6. Functions.playground/Documentation/section-50.html @@ -0,0 +1,19 @@ + + + + + Section 51 + + + + + + +
+
+

Note that the function does not modify the caller's copy of the string that was passed in because the value is still passed by value:

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-52.html b/6. Functions.playground/Documentation/section-52.html new file mode 100644 index 0000000..d8ae9ec --- /dev/null +++ b/6. Functions.playground/Documentation/section-52.html @@ -0,0 +1,22 @@ + + + + + Section 53 + + + + + + +
+
+

In-Out Parameters

+

In-Out parameters allow us to force a parameter to be passed by reference so those changes can persist after the function call.

+

Note that inout parameters cannot be variadic or have default parameter values.

+

We'll write a standard swap function to exercise this:

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-54.html b/6. Functions.playground/Documentation/section-54.html new file mode 100644 index 0000000..77b2205 --- /dev/null +++ b/6. Functions.playground/Documentation/section-54.html @@ -0,0 +1,21 @@ + + + + + Section 55 + + + + + + +
+
+

Let's call the swap function to see what happens.

+

To ensure that the caller is aware that the parameter is an inout (and hence, can modify their variable), we must prefix the parameter name with "&".

+

Note that the variables that will be passed as references may not be defined as constants, because it is expected that their values will change:

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-56.html b/6. Functions.playground/Documentation/section-56.html new file mode 100644 index 0000000..9ebefca --- /dev/null +++ b/6. Functions.playground/Documentation/section-56.html @@ -0,0 +1,19 @@ + + + + + Section 57 + + + + + + +
+
+

And we can see that 'one' contains a 2 and 'two' contains a 1:

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-58.html b/6. Functions.playground/Documentation/section-58.html new file mode 100644 index 0000000..6152dc8 --- /dev/null +++ b/6. Functions.playground/Documentation/section-58.html @@ -0,0 +1,22 @@ + + + + + Section 59 + + + + + + +
+
+

Function types

+

The type of a function is comprised of the specific parameter list (the number of parameters as well as their type) and the return value's type.

+

The following two functions have the same type.

+

It's important to note that their type is described as: (Int, Int) -> Int

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-6.html b/6. Functions.playground/Documentation/section-6.html new file mode 100644 index 0000000..770cd9f --- /dev/null +++ b/6. Functions.playground/Documentation/section-6.html @@ -0,0 +1,19 @@ + + + + + Section 7 + + + + + + +
+
+

A function with no parameters simply has an empty set of parenthesis following the function name:

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-60.html b/6. Functions.playground/Documentation/section-60.html new file mode 100644 index 0000000..4c23b02 --- /dev/null +++ b/6. Functions.playground/Documentation/section-60.html @@ -0,0 +1,19 @@ + + + + + Section 61 + + + + + + +
+
+

A function that has no parameters or return value would have the type: () -> ()

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-62.html b/6. Functions.playground/Documentation/section-62.html new file mode 100644 index 0000000..456786c --- /dev/null +++ b/6. Functions.playground/Documentation/section-62.html @@ -0,0 +1,20 @@ + + + + + Section 63 + + + + + + +
+
+

The type of the function below is the same as the one above: () -> ()

+

It is written in a shorter form, eliminating the return type entirely, but this syntactic simplification doesn't alter the way that the function's type is notated:

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-64.html b/6. Functions.playground/Documentation/section-64.html new file mode 100644 index 0000000..ad4c478 --- /dev/null +++ b/6. Functions.playground/Documentation/section-64.html @@ -0,0 +1,19 @@ + + + + + Section 65 + + + + + + +
+
+

Using what we know about funciton types, we can define variables that are the type of a function

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-66.html b/6. Functions.playground/Documentation/section-66.html new file mode 100644 index 0000000..e47577a --- /dev/null +++ b/6. Functions.playground/Documentation/section-66.html @@ -0,0 +1,19 @@ + + + + + Section 67 + + + + + + +
+
+

We can now use the variable to perform the function:

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-68.html b/6. Functions.playground/Documentation/section-68.html new file mode 100644 index 0000000..45dffb8 --- /dev/null +++ b/6. Functions.playground/Documentation/section-68.html @@ -0,0 +1,20 @@ + + + + + Section 69 + + + + + + +
+
+

We can also name our parameters when assigning them to a variable, as well as taking advantage of un-named parameters ("_").

+

This additional syntactic decoration has a purpose, but it doesn't affect the underlying function type, which remains: (Int, Int) -> Int

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-70.html b/6. Functions.playground/Documentation/section-70.html new file mode 100644 index 0000000..3503b74 --- /dev/null +++ b/6. Functions.playground/Documentation/section-70.html @@ -0,0 +1,19 @@ + + + + + Section 71 + + + + + + +
+
+

Calling the function now requires external names for the first two parameters

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-72.html b/6. Functions.playground/Documentation/section-72.html new file mode 100644 index 0000000..2dda53a --- /dev/null +++ b/6. Functions.playground/Documentation/section-72.html @@ -0,0 +1,20 @@ + + + + + Section 73 + + + + + + +
+
+

We can pass function types as parameters to funcions, too.

+

Here, our first parameter named 'doMulFunc' has the type "(Int, Int) -> Int" followed by two parameters, 'a' and 'b' that are used as parameters to the 'doMulFunc' function:

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-74.html b/6. Functions.playground/Documentation/section-74.html new file mode 100644 index 0000000..c4d3f4f --- /dev/null +++ b/6. Functions.playground/Documentation/section-74.html @@ -0,0 +1,19 @@ + + + + + Section 75 + + + + + + +
+
+

We can now pass the function (along with a couple parameters to call it with) to another function:

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-76.html b/6. Functions.playground/Documentation/section-76.html new file mode 100644 index 0000000..db12bea --- /dev/null +++ b/6. Functions.playground/Documentation/section-76.html @@ -0,0 +1,20 @@ + + + + + Section 77 + + + + + + +
+
+

We can also return function types.

+

The syntax looks a little wird because of the two sets of arrows. To read this, understand that the first arrow specifies the return type, and the second arrow is simply part of the function type being returned:

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-78.html b/6. Functions.playground/Documentation/section-78.html new file mode 100644 index 0000000..f1cf14e --- /dev/null +++ b/6. Functions.playground/Documentation/section-78.html @@ -0,0 +1,19 @@ + + + + + Section 79 + + + + + + +
+
+

Or, an even shorter version that avoids the additional stored value:

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-8.html b/6. Functions.playground/Documentation/section-8.html new file mode 100644 index 0000000..b8beea2 --- /dev/null +++ b/6. Functions.playground/Documentation/section-8.html @@ -0,0 +1,19 @@ + + + + + Section 9 + + + + + + +
+
+

A funciton with no return value can be expressed in two different ways. The first is to replace the return type with a set of empty parenthesis, which can be thought of as an empty Tuple.

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-80.html b/6. Functions.playground/Documentation/section-80.html new file mode 100644 index 0000000..5cb7b81 --- /dev/null +++ b/6. Functions.playground/Documentation/section-80.html @@ -0,0 +1,20 @@ + + + + + Section 81 + + + + + + +
+
+

Earlier we discussed how functions that do not return values still have a return type that includes: -> ()

+

Here, we'll see this in action by returning a function that doesn't return a value:

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-82.html b/6. Functions.playground/Documentation/section-82.html new file mode 100644 index 0000000..3f391e8 --- /dev/null +++ b/6. Functions.playground/Documentation/section-82.html @@ -0,0 +1,19 @@ + + + + + Section 83 + + + + + + +
+
+

And we'll call nop (note the second set of parenthesis to make the actual call to nop())

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-84.html b/6. Functions.playground/Documentation/section-84.html new file mode 100644 index 0000000..53bb202 --- /dev/null +++ b/6. Functions.playground/Documentation/section-84.html @@ -0,0 +1,19 @@ + + + + + Section 85 + + + + + + +
+
+

We can nest functions inside of other functions:

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-86.html b/6. Functions.playground/Documentation/section-86.html new file mode 100644 index 0000000..4e370db --- /dev/null +++ b/6. Functions.playground/Documentation/section-86.html @@ -0,0 +1,19 @@ + + + + + Section 87 + + + + + + +
+
+

Calling getFive will return the Int value 5:

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-88.html b/6. Functions.playground/Documentation/section-88.html new file mode 100644 index 0000000..4c55a07 --- /dev/null +++ b/6. Functions.playground/Documentation/section-88.html @@ -0,0 +1,19 @@ + + + + + Section 89 + + + + + + +
+
+

You can return nested functions, too:

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-90.html b/6. Functions.playground/Documentation/section-90.html new file mode 100644 index 0000000..3cf2e8f --- /dev/null +++ b/6. Functions.playground/Documentation/section-90.html @@ -0,0 +1,19 @@ + + + + + Section 91 + + + + + + +
+
+

Calling outerFunc2 will return a function capable of returning the Int value 5:

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/section-92.html b/6. Functions.playground/Documentation/section-92.html new file mode 100644 index 0000000..1d9cfa4 --- /dev/null +++ b/6. Functions.playground/Documentation/section-92.html @@ -0,0 +1,19 @@ + + + + + Section 93 + + + + + + +
+
+

Here we call the nested function:

+ +
+
+ + diff --git a/6. Functions.playground/Documentation/stylesheet.css b/6. Functions.playground/Documentation/stylesheet.css new file mode 100644 index 0000000..43b0230 --- /dev/null +++ b/6. Functions.playground/Documentation/stylesheet.css @@ -0,0 +1,65 @@ +html { + font-size: 1em; +} + +body { + background-color: #fff; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: normal; + font-size: 1.0em; + line-height: 140%; + padding: 0 2em; +} + +code { + font-family: "Menlo", monospace; + font-size: 0.95em; +} + +section { + padding: 0.5em 0; + -webkit-overflow-scrolling: touch; +} + +a:link { + color: #00f; + text-decoration: none; + border-bottom: 1px solid #00f; +} + +h1 { + font-size: 1.5em; + padding: 0.7em 0 0 0; +} + +h2 { + font-size: 1.2em; + padding: 0.7em 0 0 0; +} + +h3 { + font-size: 1.0em; + padding: 0.7em 0 0 0; +} + +blockquote { + font-size: 0.9em; + margin-left: 0; + padding-left: 2em; + border-left: 8px solid #eee; +} + +.footnotes { + color: #666666; + margin-top: 2em; + font-size: 0.9em; + letter-spacing: -3px; +} + +.footnotes:before { + content: "――――"; +} + +.footnotes ol { + letter-spacing: normal; +} diff --git a/6. Functions.playground/contents.xcplayground b/6. Functions.playground/contents.xcplayground index 4937636..ee9a8e7 100644 --- a/6. Functions.playground/contents.xcplayground +++ b/6. Functions.playground/contents.xcplayground @@ -1,7 +1,193 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file diff --git a/6. Functions.playground/section-1.swift b/6. Functions.playground/section-1.swift index b281d2d..d4e56f9 100644 --- a/6. Functions.playground/section-1.swift +++ b/6. Functions.playground/section-1.swift @@ -1,375 +1,4 @@ -// ------------------------------------------------------------------------------------------------ -// Things to know: -// -// * Like most other languages, functions contain units of code to perform a task, but there are -// a few key featurs of functions in Swift. -// -// * Functions are types in Swift. This means that a function can receive another function as an -// input parameter or return a function as a result. You can even declare variables that are -// functions. -// -// * Functions can be nested, and the inner function can be returned by the outer function. To -// enable this, the current state of any local variables (local to the outer function) that are -// used by the inner function are "captured" for use by the inner function. Any time that inner -// function is called, it uses this captured state. This is the case even when a nested function -// is returned by the parent function. -// ------------------------------------------------------------------------------------------------ - -// Here's a simple function that receives a Single string and returns a String -// -// Note that each parameter has a local name (for use within the function) and a standard type -// annotation. The return value's type is at the end of the function, following the ->. func sayHello(personName: String) -> String { return "Hello, \(personName)" -} - -// If we call the function, we'll receive the greeting -sayHello("Peter Parker") - -// Multiple input parameters are separated by a comma -func halfOpenRangeLength(start: Int, end: Int) -> Int -{ - return end - start -} - -// A function with no parameters simply has an empty set of parenthesis following the function -// name: -func sayHelloWorld() -> String -{ - return "Hello, world" -} - -// A funciton with no return value can be expressed in two different ways. The first is to replace -// the return type with a set of empty parenthesis, which can be thought of as an empty Tuple. -func sayGoodbye(name: String) -> () -{ - "Goodbye, \(name)" -} - -// We can also remove the return type (and the -> delimiter) alltogether: -func sayGoodbyeToMyLittleFriend(name: String) -{ - "Goodbye, \(name)" -} - -// Functions can return Tuples, which enable them to return multiple values. -// -// The following function simply returns two hard-coded strings. -func getApplicationNameAndVersion() -> (String, String) -{ - return ("Modaferator", "v1.0") -} - -// Since the return value is a Tuple, we can use the Tuple's naming feature to name the values -// being returned: -func getApplicationInfo() -> (name: String, version: String) -{ - return ("Modaferator", "v1.0") -} -var appInfo = getApplicationInfo() -appInfo.name -appInfo.version - -// ------------------------------------------------------------------------------------------------ -// External Parameter Names -// -// We can use Objective-C-like external parameter names so the caller must name the external -// parameters when they call the function. The extenal name appears before the local name. -func addSeventeen(toNumber value: Int) -> Int -{ - return value + 17 -} -addSeventeen(toNumber: 42) - -// If your internal and external names are the same, you can use a shorthand #name syntax to create -// both names at once. -// -// The following declaration creates an internal parameter named "action" as well as an external -// parameter named "action": -func kangaroosCan(#action: String) -> String -{ - return "A Kangaroo can \(action)" -} - -// We can now use the external name ("action") to make the call: -kangaroosCan(action: "jump") -kangaroosCan(action: "carry children in their pouches") - -// We can also have default parameter values. Default parameter values must be placed at the end -// of the parameter list. -// -// In the addMul routine, we'll add two numbers and multiply the result by an optional multiplier -// value. We will default the multiplier to 1 so that if the parameter is not specified, the -// multiplication won't affect the result. -func addMul(firstAdder: Int, secondAdder: Int, multiplier: Int = 1) -> Int -{ - return (firstAdder + secondAdder) * multiplier -} - -// We can call with just two parameters to add them together -addMul(1, 2) - -// Default parameter values and external names -// -// Swift automatically creates external parameter names for those parameters that have default -// values. Since our declaration of addMul did not specify an external name (either explicitly or -// using the shorthand method), Swift created one for us based on the name of the internal -// parameter name. This acts as if we had defined the third parameter using the "#" shorthand. -// -// Therefore, when calling the function and specifying a value for the defaulted parameter, we -// must provide the default parameter's external name: -addMul(1, 2, multiplier: 9) - -// We can opt out of the automatic external name for default parameter values by specify an -// external name of "_" like so: -func anotherAddMul(firstAdder: Int, secondAdder: Int, _ multiplier: Int = 1) -> Int -{ - return (firstAdder + secondAdder) * multiplier -} - -// Here, we call without the third parameter as before: -anotherAddMul(1, 2) - -// And now we can call with an un-named third parameter: -anotherAddMul(1, 2, 9) - -// ------------------------------------------------------------------------------------------------ -// Variadic Parameters -// -// Variadic parameters allow you to call functions with zero or more values of a specified type. -// -// Variadic parameters appear within the receiving function as an array of the given type. -// -// A function may only have at most one variadic and it must appear as the last parameter. -func arithmeticMean(numbers: Double...) -> Double -{ - var total = 0.0 - - // The variadic, numbers, looks like an array to the internal function so we can just loop - // through them - for number in numbers - { - total += number - } - return numbers.count == 0 ? total : total / Double(numbers.count) -} - -// Let's call it with a few parameter lengths. Note that we can call with no parameters, since that -// meets the criteria of a variadic parameter (zero or more). -arithmeticMean() -arithmeticMean(1) -arithmeticMean(1, 2) -arithmeticMean(1, 2, 3) -arithmeticMean(1, 2, 3, 4) -arithmeticMean(1, 2, 3, 4, 5) -arithmeticMean(1, 2, 3, 4, 5, 6) - -// If we want to use variadic parameters and default parameter values, we can do so by making sure -// that the default parameters come before the variadic, at the end of the parameter list: -func anotherArithmeticMean(initialTotal: Double = 0, numbers: Double...) -> Double -{ - var total = initialTotal - for number in numbers - { - total += number - } - return numbers.count == 0 ? total : total / Double(numbers.count) -} - -// Here, we can still call with no parameters because of the default parameter -anotherArithmeticMean() - -// Going forward, we must specify the default parameter's external name (because we didn't opt-out -// of it.) Also, you can see why Swift attempts to enforce the use of external parameter names for -// default parameter values. In this case, it helps us recognize where the defalt parameters leave -// off and the variadic parameters begin: -anotherArithmeticMean(initialTotal: 1) -anotherArithmeticMean(initialTotal: 1, 2) -anotherArithmeticMean(initialTotal: 1, 2, 3) -anotherArithmeticMean(initialTotal: 1, 2, 3, 4) -anotherArithmeticMean(initialTotal: 1, 2, 3, 4, 5) -anotherArithmeticMean(initialTotal: 1, 2, 3, 4, 5, 6) - -// Variadic parameters with external parameter names only apply their external name to the first -// variadic parameter specified in the function call (if present.) -func yetAnotherArithmeticMean(initialTotal: Double = 0, values numbers: Double...) -> Double -{ - var total = initialTotal - for number in numbers - { - total += number - } - return numbers.count == 0 ? total : total / Double(numbers.count) -} - -// And here we can see the impact on the function call of adding the external name "values" to the -// variadic parameter: -yetAnotherArithmeticMean() -yetAnotherArithmeticMean(initialTotal: 1) -yetAnotherArithmeticMean(initialTotal: 1, values: 2) -yetAnotherArithmeticMean(initialTotal: 1, values: 2, 3) -yetAnotherArithmeticMean(initialTotal: 1, values: 2, 3, 4) -yetAnotherArithmeticMean(initialTotal: 1, values: 2, 3, 4, 5) -yetAnotherArithmeticMean(initialTotal: 1, values: 2, 3, 4, 5, 6) - -// ------------------------------------------------------------------------------------------------ -// Constant and variable parameters -// -// All function parameters are constant by default. To make them variable, add the var introducer: -func padString(var str: String, pad: Character, count: Int) -> String -{ - str = Array(count: count, repeatedValue: pad) + str - return str -} - -var paddedString = "padded with dots" -padString(paddedString, ".", 10) - -// Note that the function does not modify the caller's copy of the string that was passed in -// because the value is still passed by value: -paddedString - -// ------------------------------------------------------------------------------------------------ -// In-Out Parameters -// -// In-Out parameters allow us to force a parameter to be passed by reference so those changes can -// persist after the function call. -// -// Note that inout parameters cannot be variadic or have default parameter values. -// -// We'll write a standard swap function to exercise this: -func swap(inout a: Int, inout b: Int) -{ - let tmp = a - a = b - b = tmp -} - -// Let's call the swap function to see what happens. -// -// To ensure that the caller is aware that the parameter is an inout (and hence, can modify their -// variable), we must prefix the parameter name with "&". -// -// Note that the variables that will be passed as references may not be defined as constants, -// because it is expected that their values will change: -var one = 1, two = 2 -swap(&one, &two) - -// And we can see that 'one' contains a 2 and 'two' contains a 1: -one -two - -// ------------------------------------------------------------------------------------------------ -// Function types -// -// The type of a function is comprised of the specific parameter list (the number of parameters as -// well as their type) and the return value's type. -// -// The following two functions have the same type. -// -// It's important to note that their type is described as: (Int, Int) -> Int -func add(a: Int, b: Int) -> Int {return a+b} -func mul(a: Int, b: Int) -> Int {return a*b} - -// A function that has no parameters or return value would have the type: () -> () -func nop() -> () -{ - return -} - -// The type of the function below is the same as the one above: () -> () -// -// It is written in a shorter form, eliminating the return type entirely, but this syntactic -// simplification doesn't alter the way that the function's type is notated: -func doNothing() -{ - return -} - -// Using what we know about funciton types, we can define variables that are the type of a function -let doMul: (Int, Int) -> Int = mul - -// We can now use the variable to perform the function: -doMul(4, 5) - -// We can also name our parameters when assigning them to a variable, as well as taking advantage -// of un-named parameters ("_"). -// -// This additional syntactic decoration has a purpose, but it doesn't affect the underlying -// function type, which remains: (Int, Int) -> Int -let doAddMul: (a: Int, b: Int, Int) -> Int = addMul - -// Calling the function now requires external names for the first two parameters -doAddMul(a: 4, b: 2, 55) - -// We can pass function types as parameters to funcions, too. -// -// Here, our first parameter named 'doMulFunc' has the type "(Int, Int) -> Int" followed by two -// parameters, 'a' and 'b' that are used as parameters to the 'doMulFunc' function: -func doDoMul(doMulFunc: (Int, Int) -> Int, a: Int, b: Int) -> Int -{ - return doMulFunc(a, b) -} - -// We can now pass the function (along with a couple parameters to call it with) to another -// function: -doDoMul(doMul, 5, 5) - -// We can also return function types. -// -// The syntax looks a little wird because of the two sets of arrows. To read this, understand that -// the first arrow specifies the return type, and the second arrow is simply part of the function -// type being returned: -func getDoMul() -> (Int, Int) -> Int -{ - return doMul -} -let newDoMul = getDoMul() -newDoMul(9, 5) - -// Or, an even shorter version that avoids the additional stored value: -getDoMul()(5, 5) - -// Earlier we discussed how functions that do not return values still have a return type that -// includes: -> () -// -// Here, we'll see this in action by returning a function that doesn't return a value: -func getNop() -> () -> () -{ - return nop -} - -// And we'll call nop (note the second set of parenthesis to make the actual call to nop()) -getNop()() - -// We can nest functions inside of other functions: -func getFive() -> Int -{ - func returnFive() -> Int - { - return 5 - } - - // Call returnFive() and return the result of that function - return returnFive() -} - -// Calling getFive will return the Int value 5: -getFive() - -// You can return nested functions, too: -func getReturnFive() -> () -> Int -{ - func returnFive() -> Int - { - return 5 - } - return returnFive -} - -// Calling outerFunc2 will return a function capable of returning the Int value 5: -let returnFive = getReturnFive() - -// Here we call the nested function: -returnFive() +} \ No newline at end of file diff --git a/6. Functions.playground/section-11.swift b/6. Functions.playground/section-11.swift new file mode 100644 index 0000000..5e32be9 --- /dev/null +++ b/6. Functions.playground/section-11.swift @@ -0,0 +1,4 @@ +func sayGoodbyeToMyLittleFriend(name: String) +{ + "Goodbye, \(name)" +} \ No newline at end of file diff --git a/6. Functions.playground/section-13.swift b/6. Functions.playground/section-13.swift new file mode 100644 index 0000000..cbed447 --- /dev/null +++ b/6. Functions.playground/section-13.swift @@ -0,0 +1,4 @@ +func getApplicationNameAndVersion() -> (String, String) +{ + return ("Modaferator", "v1.0") +} \ No newline at end of file diff --git a/6. Functions.playground/section-15.swift b/6. Functions.playground/section-15.swift new file mode 100644 index 0000000..2548c92 --- /dev/null +++ b/6. Functions.playground/section-15.swift @@ -0,0 +1,7 @@ +func getApplicationInfo() -> (name: String, version: String) +{ + return ("Modaferator", "v1.0") +} +var appInfo = getApplicationInfo() +appInfo.name +appInfo.version \ No newline at end of file diff --git a/6. Functions.playground/section-17.swift b/6. Functions.playground/section-17.swift new file mode 100644 index 0000000..3095d75 --- /dev/null +++ b/6. Functions.playground/section-17.swift @@ -0,0 +1,5 @@ +func addSeventeen(toNumber value: Int) -> Int +{ + return value + 17 +} +addSeventeen(toNumber: 42) \ No newline at end of file diff --git a/6. Functions.playground/section-19.swift b/6. Functions.playground/section-19.swift new file mode 100644 index 0000000..2134de8 --- /dev/null +++ b/6. Functions.playground/section-19.swift @@ -0,0 +1,4 @@ +func kangaroosCan(#action: String) -> String +{ + return "A Kangaroo can \(action)" +} \ No newline at end of file diff --git a/6. Functions.playground/section-21.swift b/6. Functions.playground/section-21.swift new file mode 100644 index 0000000..1a82086 --- /dev/null +++ b/6. Functions.playground/section-21.swift @@ -0,0 +1,2 @@ +kangaroosCan(action: "jump") +kangaroosCan(action: "carry children in their pouches") \ No newline at end of file diff --git a/6. Functions.playground/section-23.swift b/6. Functions.playground/section-23.swift new file mode 100644 index 0000000..77a68b0 --- /dev/null +++ b/6. Functions.playground/section-23.swift @@ -0,0 +1,4 @@ +func addMul(firstAdder: Int, secondAdder: Int, multiplier: Int = 1) -> Int +{ + return (firstAdder + secondAdder) * multiplier +} \ No newline at end of file diff --git a/6. Functions.playground/section-25.swift b/6. Functions.playground/section-25.swift new file mode 100644 index 0000000..25a0d04 --- /dev/null +++ b/6. Functions.playground/section-25.swift @@ -0,0 +1 @@ +addMul(1, 2) \ No newline at end of file diff --git a/6. Functions.playground/section-27.swift b/6. Functions.playground/section-27.swift new file mode 100644 index 0000000..0170732 --- /dev/null +++ b/6. Functions.playground/section-27.swift @@ -0,0 +1 @@ +addMul(1, 2, multiplier: 9) \ No newline at end of file diff --git a/6. Functions.playground/section-29.swift b/6. Functions.playground/section-29.swift new file mode 100644 index 0000000..6f12638 --- /dev/null +++ b/6. Functions.playground/section-29.swift @@ -0,0 +1,4 @@ +func anotherAddMul(firstAdder: Int, secondAdder: Int, _ multiplier: Int = 1) -> Int +{ + return (firstAdder + secondAdder) * multiplier +} \ No newline at end of file diff --git a/6. Functions.playground/section-3.swift b/6. Functions.playground/section-3.swift new file mode 100644 index 0000000..38ac525 --- /dev/null +++ b/6. Functions.playground/section-3.swift @@ -0,0 +1 @@ +sayHello("Peter Parker") \ No newline at end of file diff --git a/6. Functions.playground/section-31.swift b/6. Functions.playground/section-31.swift new file mode 100644 index 0000000..a8cad20 --- /dev/null +++ b/6. Functions.playground/section-31.swift @@ -0,0 +1 @@ +anotherAddMul(1, 2) \ No newline at end of file diff --git a/6. Functions.playground/section-33.swift b/6. Functions.playground/section-33.swift new file mode 100644 index 0000000..7604c1b --- /dev/null +++ b/6. Functions.playground/section-33.swift @@ -0,0 +1 @@ +anotherAddMul(1, 2, 9) \ No newline at end of file diff --git a/6. Functions.playground/section-35.swift b/6. Functions.playground/section-35.swift new file mode 100644 index 0000000..ba5631a --- /dev/null +++ b/6. Functions.playground/section-35.swift @@ -0,0 +1,12 @@ +func arithmeticMean(numbers: Double...) -> Double +{ + var total = 0.0 + + // The variadic, numbers, looks like an array to the internal function so we can just loop + // through them + for number in numbers + { + total += number + } + return numbers.count == 0 ? total : total / Double(numbers.count) +} \ No newline at end of file diff --git a/6. Functions.playground/section-37.swift b/6. Functions.playground/section-37.swift new file mode 100644 index 0000000..dc3cbb4 --- /dev/null +++ b/6. Functions.playground/section-37.swift @@ -0,0 +1,7 @@ +arithmeticMean() +arithmeticMean(1) +arithmeticMean(1, 2) +arithmeticMean(1, 2, 3) +arithmeticMean(1, 2, 3, 4) +arithmeticMean(1, 2, 3, 4, 5) +arithmeticMean(1, 2, 3, 4, 5, 6) \ No newline at end of file diff --git a/6. Functions.playground/section-39.swift b/6. Functions.playground/section-39.swift new file mode 100644 index 0000000..bfe6ac8 --- /dev/null +++ b/6. Functions.playground/section-39.swift @@ -0,0 +1,9 @@ +func anotherArithmeticMean(initialTotal: Double = 0, numbers: Double...) -> Double +{ + var total = initialTotal + for number in numbers + { + total += number + } + return numbers.count == 0 ? total : total / Double(numbers.count) +} \ No newline at end of file diff --git a/6. Functions.playground/section-41.swift b/6. Functions.playground/section-41.swift new file mode 100644 index 0000000..6236d87 --- /dev/null +++ b/6. Functions.playground/section-41.swift @@ -0,0 +1 @@ +anotherArithmeticMean() \ No newline at end of file diff --git a/6. Functions.playground/section-43.swift b/6. Functions.playground/section-43.swift new file mode 100644 index 0000000..c32a1d3 --- /dev/null +++ b/6. Functions.playground/section-43.swift @@ -0,0 +1,6 @@ +anotherArithmeticMean(initialTotal: 1) +anotherArithmeticMean(initialTotal: 1, 2) +anotherArithmeticMean(initialTotal: 1, 2, 3) +anotherArithmeticMean(initialTotal: 1, 2, 3, 4) +anotherArithmeticMean(initialTotal: 1, 2, 3, 4, 5) +anotherArithmeticMean(initialTotal: 1, 2, 3, 4, 5, 6) \ No newline at end of file diff --git a/6. Functions.playground/section-45.swift b/6. Functions.playground/section-45.swift new file mode 100644 index 0000000..f5853ed --- /dev/null +++ b/6. Functions.playground/section-45.swift @@ -0,0 +1,9 @@ +func yetAnotherArithmeticMean(initialTotal: Double = 0, values numbers: Double...) -> Double +{ + var total = initialTotal + for number in numbers + { + total += number + } + return numbers.count == 0 ? total : total / Double(numbers.count) +} \ No newline at end of file diff --git a/6. Functions.playground/section-47.swift b/6. Functions.playground/section-47.swift new file mode 100644 index 0000000..bf8e993 --- /dev/null +++ b/6. Functions.playground/section-47.swift @@ -0,0 +1,7 @@ +yetAnotherArithmeticMean() +yetAnotherArithmeticMean(initialTotal: 1) +yetAnotherArithmeticMean(initialTotal: 1, values: 2) +yetAnotherArithmeticMean(initialTotal: 1, values: 2, 3) +yetAnotherArithmeticMean(initialTotal: 1, values: 2, 3, 4) +yetAnotherArithmeticMean(initialTotal: 1, values: 2, 3, 4, 5) +yetAnotherArithmeticMean(initialTotal: 1, values: 2, 3, 4, 5, 6) \ No newline at end of file diff --git a/6. Functions.playground/section-49.swift b/6. Functions.playground/section-49.swift new file mode 100644 index 0000000..d6677c3 --- /dev/null +++ b/6. Functions.playground/section-49.swift @@ -0,0 +1,8 @@ +func padString(var str: String, pad: Character, count: Int) -> String +{ + str = Array(count: count, repeatedValue: pad) + str + return str +} + +var paddedString = "padded with dots" +padString(paddedString, ".", 10) \ No newline at end of file diff --git a/6. Functions.playground/section-5.swift b/6. Functions.playground/section-5.swift new file mode 100644 index 0000000..733d60b --- /dev/null +++ b/6. Functions.playground/section-5.swift @@ -0,0 +1,4 @@ +func halfOpenRangeLength(start: Int, end: Int) -> Int +{ + return end - start +} \ No newline at end of file diff --git a/6. Functions.playground/section-51.swift b/6. Functions.playground/section-51.swift new file mode 100644 index 0000000..ee32a10 --- /dev/null +++ b/6. Functions.playground/section-51.swift @@ -0,0 +1 @@ +paddedString \ No newline at end of file diff --git a/6. Functions.playground/section-53.swift b/6. Functions.playground/section-53.swift new file mode 100644 index 0000000..0b47dbd --- /dev/null +++ b/6. Functions.playground/section-53.swift @@ -0,0 +1,6 @@ +func swap(inout a: Int, inout b: Int) +{ + let tmp = a + a = b + b = tmp +} \ No newline at end of file diff --git a/6. Functions.playground/section-55.swift b/6. Functions.playground/section-55.swift new file mode 100644 index 0000000..c5ec26e --- /dev/null +++ b/6. Functions.playground/section-55.swift @@ -0,0 +1,2 @@ +var one = 1, two = 2 +swap(&one, &two) \ No newline at end of file diff --git a/6. Functions.playground/section-57.swift b/6. Functions.playground/section-57.swift new file mode 100644 index 0000000..9ed40b4 --- /dev/null +++ b/6. Functions.playground/section-57.swift @@ -0,0 +1,2 @@ +one +two \ No newline at end of file diff --git a/6. Functions.playground/section-59.swift b/6. Functions.playground/section-59.swift new file mode 100644 index 0000000..1ce250e --- /dev/null +++ b/6. Functions.playground/section-59.swift @@ -0,0 +1,2 @@ +func add(a: Int, b: Int) -> Int {return a+b} +func mul(a: Int, b: Int) -> Int {return a*b} \ No newline at end of file diff --git a/6. Functions.playground/section-61.swift b/6. Functions.playground/section-61.swift new file mode 100644 index 0000000..20ad1e3 --- /dev/null +++ b/6. Functions.playground/section-61.swift @@ -0,0 +1,4 @@ +func nop() -> () +{ + return +} \ No newline at end of file diff --git a/6. Functions.playground/section-63.swift b/6. Functions.playground/section-63.swift new file mode 100644 index 0000000..3869b01 --- /dev/null +++ b/6. Functions.playground/section-63.swift @@ -0,0 +1,4 @@ +func doNothing() +{ + return +} \ No newline at end of file diff --git a/6. Functions.playground/section-65.swift b/6. Functions.playground/section-65.swift new file mode 100644 index 0000000..3fd198b --- /dev/null +++ b/6. Functions.playground/section-65.swift @@ -0,0 +1 @@ +let doMul: (Int, Int) -> Int = mul \ No newline at end of file diff --git a/6. Functions.playground/section-67.swift b/6. Functions.playground/section-67.swift new file mode 100644 index 0000000..ae15370 --- /dev/null +++ b/6. Functions.playground/section-67.swift @@ -0,0 +1 @@ +doMul(4, 5) \ No newline at end of file diff --git a/6. Functions.playground/section-69.swift b/6. Functions.playground/section-69.swift new file mode 100644 index 0000000..0b3f7d5 --- /dev/null +++ b/6. Functions.playground/section-69.swift @@ -0,0 +1 @@ +let doAddMul: (a: Int, b: Int, Int) -> Int = addMul \ No newline at end of file diff --git a/6. Functions.playground/section-7.swift b/6. Functions.playground/section-7.swift new file mode 100644 index 0000000..4dbbfb6 --- /dev/null +++ b/6. Functions.playground/section-7.swift @@ -0,0 +1,4 @@ +func sayHelloWorld() -> String +{ + return "Hello, world" +} \ No newline at end of file diff --git a/6. Functions.playground/section-71.swift b/6. Functions.playground/section-71.swift new file mode 100644 index 0000000..b2ece54 --- /dev/null +++ b/6. Functions.playground/section-71.swift @@ -0,0 +1 @@ +doAddMul(a: 4, b: 2, 55) \ No newline at end of file diff --git a/6. Functions.playground/section-73.swift b/6. Functions.playground/section-73.swift new file mode 100644 index 0000000..8ceb72e --- /dev/null +++ b/6. Functions.playground/section-73.swift @@ -0,0 +1,4 @@ +func doDoMul(doMulFunc: (Int, Int) -> Int, a: Int, b: Int) -> Int +{ + return doMulFunc(a, b) +} \ No newline at end of file diff --git a/6. Functions.playground/section-75.swift b/6. Functions.playground/section-75.swift new file mode 100644 index 0000000..179c1d2 --- /dev/null +++ b/6. Functions.playground/section-75.swift @@ -0,0 +1 @@ +doDoMul(doMul, 5, 5) \ No newline at end of file diff --git a/6. Functions.playground/section-77.swift b/6. Functions.playground/section-77.swift new file mode 100644 index 0000000..c1f0b59 --- /dev/null +++ b/6. Functions.playground/section-77.swift @@ -0,0 +1,6 @@ +func getDoMul() -> (Int, Int) -> Int +{ + return doMul +} +let newDoMul = getDoMul() +newDoMul(9, 5) \ No newline at end of file diff --git a/6. Functions.playground/section-79.swift b/6. Functions.playground/section-79.swift new file mode 100644 index 0000000..072105f --- /dev/null +++ b/6. Functions.playground/section-79.swift @@ -0,0 +1 @@ +getDoMul()(5, 5) \ No newline at end of file diff --git a/6. Functions.playground/section-81.swift b/6. Functions.playground/section-81.swift new file mode 100644 index 0000000..54bc544 --- /dev/null +++ b/6. Functions.playground/section-81.swift @@ -0,0 +1,4 @@ +func getNop() -> () -> () +{ + return nop +} \ No newline at end of file diff --git a/6. Functions.playground/section-83.swift b/6. Functions.playground/section-83.swift new file mode 100644 index 0000000..3ad8090 --- /dev/null +++ b/6. Functions.playground/section-83.swift @@ -0,0 +1 @@ +getNop()() \ No newline at end of file diff --git a/6. Functions.playground/section-85.swift b/6. Functions.playground/section-85.swift new file mode 100644 index 0000000..d546e46 --- /dev/null +++ b/6. Functions.playground/section-85.swift @@ -0,0 +1,10 @@ +func getFive() -> Int +{ + func returnFive() -> Int + { + return 5 + } + + // Call returnFive() and return the result of that function + return returnFive() +} \ No newline at end of file diff --git a/6. Functions.playground/section-87.swift b/6. Functions.playground/section-87.swift new file mode 100644 index 0000000..3bb6472 --- /dev/null +++ b/6. Functions.playground/section-87.swift @@ -0,0 +1 @@ +getFive() \ No newline at end of file diff --git a/6. Functions.playground/section-89.swift b/6. Functions.playground/section-89.swift new file mode 100644 index 0000000..00c9585 --- /dev/null +++ b/6. Functions.playground/section-89.swift @@ -0,0 +1,8 @@ +func getReturnFive() -> () -> Int +{ + func returnFive() -> Int + { + return 5 + } + return returnFive +} \ No newline at end of file diff --git a/6. Functions.playground/section-9.swift b/6. Functions.playground/section-9.swift new file mode 100644 index 0000000..bdc1252 --- /dev/null +++ b/6. Functions.playground/section-9.swift @@ -0,0 +1,4 @@ +func sayGoodbye(name: String) -> () +{ + "Goodbye, \(name)" +} \ No newline at end of file diff --git a/6. Functions.playground/section-91.swift b/6. Functions.playground/section-91.swift new file mode 100644 index 0000000..1bd68a7 --- /dev/null +++ b/6. Functions.playground/section-91.swift @@ -0,0 +1 @@ +let returnFive = getReturnFive() \ No newline at end of file diff --git a/6. Functions.playground/section-93.swift b/6. Functions.playground/section-93.swift new file mode 100644 index 0000000..bcab872 --- /dev/null +++ b/6. Functions.playground/section-93.swift @@ -0,0 +1 @@ +returnFive() \ No newline at end of file diff --git a/7. Closures.playground/Documentation/section-0.html b/7. Closures.playground/Documentation/section-0.html new file mode 100644 index 0000000..08c1de9 --- /dev/null +++ b/7. Closures.playground/Documentation/section-0.html @@ -0,0 +1,29 @@ + + + + + Section 1 + + + + + + +
+
+

Closures

+

Things to know:

+
    +
  • Closures are blocks of code.
  • +
  • The can be passed as parameters to functions much like Function Types. In fact, functions are a special case of closures.
  • +
  • Closures of all types (including nested functions) employ a method of capturing the surrounding context in which is is defined, allowing it to access constants and variables from that context.
  • +
+
+

Closures can use constant, variable, inout, variadics, tuples for their parameters. They can +return any value, including Tuples. They cannot, however, have default parameters.

+

The basic syntax is:

+ +
+
+ + diff --git a/7. Closures.playground/Documentation/section-10.html b/7. Closures.playground/Documentation/section-10.html new file mode 100644 index 0000000..163a90d --- /dev/null +++ b/7. Closures.playground/Documentation/section-10.html @@ -0,0 +1,19 @@ + + + + + Section 11 + + + + + + +
+
+

Since all types can be inferred and we're not using any type annotation on the parameters, we can simplify a bit further by removing the paranthesis around the parameters. We'll also put it all on a single line, since it's a bit more clear now:

+ +
+
+ + diff --git a/7. Closures.playground/Documentation/section-12.html b/7. Closures.playground/Documentation/section-12.html new file mode 100644 index 0000000..2490a47 --- /dev/null +++ b/7. Closures.playground/Documentation/section-12.html @@ -0,0 +1,19 @@ + + + + + Section 13 + + + + + + +
+
+

If the closuere has only a single expression, then the return statement is also inferred. When this is the case, the closure returns the value of the single expression:

+ +
+
+ + diff --git a/7. Closures.playground/Documentation/section-14.html b/7. Closures.playground/Documentation/section-14.html new file mode 100644 index 0000000..38605f6 --- /dev/null +++ b/7. Closures.playground/Documentation/section-14.html @@ -0,0 +1,20 @@ + + + + + Section 15 + + + + + + +
+
+

We're not done simplifying yet. It turns out we can get rid of the parameters as well. If we remove the parameters, we can still access them because Swift provides shorthand names to parameters passed to inline closures. To access the first parameter, use $0. The second parameter would be $1 and so on.

+

Here's what that would might like (this will not compile - yet):

+ +
+
+ + diff --git a/7. Closures.playground/Documentation/section-16.html b/7. Closures.playground/Documentation/section-16.html new file mode 100644 index 0000000..0ee97b9 --- /dev/null +++ b/7. Closures.playground/Documentation/section-16.html @@ -0,0 +1,19 @@ + + + + + Section 17 + + + + + + +
+
+

This won't compile because you're not allowed to use shorthand names if you specify the parameter list. Therefore, we need to remove those in order to get it to compile. This makes for a very short inline closure:

+ +
+
+ + diff --git a/7. Closures.playground/Documentation/section-18.html b/7. Closures.playground/Documentation/section-18.html new file mode 100644 index 0000000..d11e9fe --- /dev/null +++ b/7. Closures.playground/Documentation/section-18.html @@ -0,0 +1,21 @@ + + + + + Section 19 + + + + + + +
+
+

Interestingly enough, the operator < for String types is defined as: (String, String) -> Bool

+

Notice how this is the same as the closure's type for the sorted() routine? Wouldn't it be nice if we could just pass in this operator? It turns out that for inline closures, Swift allows exactly this.

+

Here's what that looks like:

+ +
+
+ + diff --git a/7. Closures.playground/Documentation/section-2.html b/7. Closures.playground/Documentation/section-2.html new file mode 100644 index 0000000..690b14a --- /dev/null +++ b/7. Closures.playground/Documentation/section-2.html @@ -0,0 +1,19 @@ + + + + + Section 3 + + + + + + +
+
+

Here's an example of a simple String comparison closure that might be used for sorting Strings:

+ +
+
+ + diff --git a/7. Closures.playground/Documentation/section-20.html b/7. Closures.playground/Documentation/section-20.html new file mode 100644 index 0000000..3bbc45d --- /dev/null +++ b/7. Closures.playground/Documentation/section-20.html @@ -0,0 +1,19 @@ + + + + + Section 21 + + + + + + +
+
+

If you want to just sort a mutable copy of an array (in place) you can use the sort() method

+ +
+
+ + diff --git a/7. Closures.playground/Documentation/section-22.html b/7. Closures.playground/Documentation/section-22.html new file mode 100644 index 0000000..7e256b3 --- /dev/null +++ b/7. Closures.playground/Documentation/section-22.html @@ -0,0 +1,21 @@ + + + + + Section 23 + + + + + + +
+
+

Trailing Closures

+

Trailing Closures refer to closures that are the last parameter to a function. This special-case syntax allows a few other syntactic simplifications. In essence, you can move trailing closures just outside of the parameter list. Swift's sorted() member function uses a trailing closure for just this reason.

+

Let's go back to our original call to sort with a fully-formed closure and move the closure outside of the parameter list. This resembles a function definition, but it's a function call.

+ +
+
+ + diff --git a/7. Closures.playground/Documentation/section-24.html b/7. Closures.playground/Documentation/section-24.html new file mode 100644 index 0000000..593a4e7 --- /dev/null +++ b/7. Closures.playground/Documentation/section-24.html @@ -0,0 +1,19 @@ + + + + + Section 25 + + + + + + +
+
+

Note that the opening brace for the closure must be on the same line as the function call's ending paranthesis. This is the same functinon call with the starting brace for the closure moved to the next line. This will not compile:

+ +
+
+ + diff --git a/7. Closures.playground/Documentation/section-26.html b/7. Closures.playground/Documentation/section-26.html new file mode 100644 index 0000000..12b7e0f --- /dev/null +++ b/7. Closures.playground/Documentation/section-26.html @@ -0,0 +1,19 @@ + + + + + Section 27 + + + + + + +
+
+

Let's jump back to our simplified closure ({$0 > $1}) and apply the trailing closure principle:

+ +
+
+ + diff --git a/7. Closures.playground/Documentation/section-28.html b/7. Closures.playground/Documentation/section-28.html new file mode 100644 index 0000000..e917f26 --- /dev/null +++ b/7. Closures.playground/Documentation/section-28.html @@ -0,0 +1,19 @@ + + + + + Section 29 + + + + + + +
+
+

Another simplification: if a function receives just one closure as the only parameter, you can remove the () from the function call. First, we'll need a function that receives just one parameter, a closure:

+ +
+
+ + diff --git a/7. Closures.playground/Documentation/section-30.html b/7. Closures.playground/Documentation/section-30.html new file mode 100644 index 0000000..84e3867 --- /dev/null +++ b/7. Closures.playground/Documentation/section-30.html @@ -0,0 +1,19 @@ + + + + + Section 31 + + + + + + +
+
+

Now let's call the function with the parenthesis removed and a trailing closure:

+ +
+
+ + diff --git a/7. Closures.playground/Documentation/section-32.html b/7. Closures.playground/Documentation/section-32.html new file mode 100644 index 0000000..031e694 --- /dev/null +++ b/7. Closures.playground/Documentation/section-32.html @@ -0,0 +1,19 @@ + + + + + Section 33 + + + + + + +
+
+

And if we apply the simplification described earlier that implies the return statement for single-expresssion closures, it simplifies to this oddly-looking line of code:

+ +
+
+ + diff --git a/7. Closures.playground/Documentation/section-34.html b/7. Closures.playground/Documentation/section-34.html new file mode 100644 index 0000000..31c114a --- /dev/null +++ b/7. Closures.playground/Documentation/section-34.html @@ -0,0 +1,22 @@ + + + + + Section 35 + + + + + + +
+
+

Capturing Values

+

The idea of capturing is to allow a closure to access the variables and constants in their surrounding context.

+

For example, a nested function can access contstans and variables from the function in which it is defined. If this nested function is returned, each time it is called, it will work within that "captured" context.

+

Here's an example that should help clear this up:

+ +
+
+ + diff --git a/7. Closures.playground/Documentation/section-36.html b/7. Closures.playground/Documentation/section-36.html new file mode 100644 index 0000000..25ae9b6 --- /dev/null +++ b/7. Closures.playground/Documentation/section-36.html @@ -0,0 +1,19 @@ + + + + + Section 37 + + + + + + +
+
+

Let's get a copy of the incrementor:

+ +
+
+ + diff --git a/7. Closures.playground/Documentation/section-38.html b/7. Closures.playground/Documentation/section-38.html new file mode 100644 index 0000000..4c18291 --- /dev/null +++ b/7. Closures.playground/Documentation/section-38.html @@ -0,0 +1,19 @@ + + + + + Section 39 + + + + + + +
+
+

Whenever we call this function, it will return a value incremented by 10:

+ +
+
+ + diff --git a/7. Closures.playground/Documentation/section-4.html b/7. Closures.playground/Documentation/section-4.html new file mode 100644 index 0000000..7a84b24 --- /dev/null +++ b/7. Closures.playground/Documentation/section-4.html @@ -0,0 +1,20 @@ + + + + + Section 5 + + + + + + +
+
+

Here's an example using Swift's 'sorted' member function. It's important to note that this function receives a single closure.

+

These can be a little tricky to read if you're not used to them. To understand the syntax, pay special attention to the curly braces that encapsulate the closure and the parenthesis just outside of those curly braces:

+ +
+
+ + diff --git a/7. Closures.playground/Documentation/section-40.html b/7. Closures.playground/Documentation/section-40.html new file mode 100644 index 0000000..f152981 --- /dev/null +++ b/7. Closures.playground/Documentation/section-40.html @@ -0,0 +1,19 @@ + + + + + Section 41 + + + + + + +
+
+

We can get another copy of incrementor that works on increments of 3.

+ +
+
+ + diff --git a/7. Closures.playground/Documentation/section-42.html b/7. Closures.playground/Documentation/section-42.html new file mode 100644 index 0000000..cb4df53 --- /dev/null +++ b/7. Closures.playground/Documentation/section-42.html @@ -0,0 +1,19 @@ + + + + + Section 43 + + + + + + +
+
+

'incrementBy10' and 'incrementBy3' each has its own captured context, so they work independently of each other.

+ +
+
+ + diff --git a/7. Closures.playground/Documentation/section-44.html b/7. Closures.playground/Documentation/section-44.html new file mode 100644 index 0000000..f984bec --- /dev/null +++ b/7. Closures.playground/Documentation/section-44.html @@ -0,0 +1,19 @@ + + + + + Section 45 + + + + + + +
+
+

Closures are reference types, which allows us to assign them to a variable. When this happens, the captured context comes along for the ride.

+ +
+
+ + diff --git a/7. Closures.playground/Documentation/section-46.html b/7. Closures.playground/Documentation/section-46.html new file mode 100644 index 0000000..483b7fe --- /dev/null +++ b/7. Closures.playground/Documentation/section-46.html @@ -0,0 +1,19 @@ + + + + + Section 47 + + + + + + +
+
+

If we request a new incremntor that increments by 10, it will have a separate and unique captured context:

+ +
+
+ + diff --git a/7. Closures.playground/Documentation/section-48.html b/7. Closures.playground/Documentation/section-48.html new file mode 100644 index 0000000..f089144 --- /dev/null +++ b/7. Closures.playground/Documentation/section-48.html @@ -0,0 +1,19 @@ + + + + + Section 49 + + + + + + +
+
+

Our first incrementor is still using its own context:

+ +
+
+ + diff --git a/7. Closures.playground/Documentation/section-6.html b/7. Closures.playground/Documentation/section-6.html new file mode 100644 index 0000000..12741b9 --- /dev/null +++ b/7. Closures.playground/Documentation/section-6.html @@ -0,0 +1,22 @@ + + + + + Section 7 + + + + + + +
+
+

Inferring Type from Context

+

Like functions, closures have a type.

+

If the type is known (as is always the case when passing a closure as a parameter to a function) then the return type of the closure can be inferred, allowing us to simplify the syntax of our call to sort.

+

The following call is identical to the one above with the exception that "-> Bool" was removed:

+ +
+
+ + diff --git a/7. Closures.playground/Documentation/section-8.html b/7. Closures.playground/Documentation/section-8.html new file mode 100644 index 0000000..dc17b37 --- /dev/null +++ b/7. Closures.playground/Documentation/section-8.html @@ -0,0 +1,20 @@ + + + + + Section 9 + + + + + + +
+
+

Just as the return type can be inferred, so can the parameter types. This allows us to simplify the syntax a bit further by removing the type annotations from the closure's parameters.

+

The following call is identical to the one above with the exception that the parameter type annotations (": String") have been removed:

+ +
+
+ + diff --git a/7. Closures.playground/Documentation/stylesheet.css b/7. Closures.playground/Documentation/stylesheet.css new file mode 100644 index 0000000..43b0230 --- /dev/null +++ b/7. Closures.playground/Documentation/stylesheet.css @@ -0,0 +1,65 @@ +html { + font-size: 1em; +} + +body { + background-color: #fff; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: normal; + font-size: 1.0em; + line-height: 140%; + padding: 0 2em; +} + +code { + font-family: "Menlo", monospace; + font-size: 0.95em; +} + +section { + padding: 0.5em 0; + -webkit-overflow-scrolling: touch; +} + +a:link { + color: #00f; + text-decoration: none; + border-bottom: 1px solid #00f; +} + +h1 { + font-size: 1.5em; + padding: 0.7em 0 0 0; +} + +h2 { + font-size: 1.2em; + padding: 0.7em 0 0 0; +} + +h3 { + font-size: 1.0em; + padding: 0.7em 0 0 0; +} + +blockquote { + font-size: 0.9em; + margin-left: 0; + padding-left: 2em; + border-left: 8px solid #eee; +} + +.footnotes { + color: #666666; + margin-top: 2em; + font-size: 0.9em; + letter-spacing: -3px; +} + +.footnotes:before { + content: "――――"; +} + +.footnotes ol { + letter-spacing: normal; +} diff --git a/7. Closures.playground/contents.xcplayground b/7. Closures.playground/contents.xcplayground index 4937636..8f232aa 100644 --- a/7. Closures.playground/contents.xcplayground +++ b/7. Closures.playground/contents.xcplayground @@ -1,7 +1,105 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file diff --git a/7. Closures.playground/section-1.swift b/7. Closures.playground/section-1.swift index b46fb84..49e847e 100644 --- a/7. Closures.playground/section-1.swift +++ b/7. Closures.playground/section-1.swift @@ -1,205 +1,3 @@ -// ------------------------------------------------------------------------------------------------ -// Things to know: -// -// * Closures are blocks of code. -// -// * The can be passed as parameters to functions much like Function Types. In fact, functions -// are a special case of closures. -// -// * Closures of all types (including nested functions) employ a method of capturing the surrounding -// context in which is is defined, allowing it to access constants and variables from that -// context. -// ------------------------------------------------------------------------------------------------ - -// Closures can use constant, variable, inout, variadics, tuples for their parameters. They can -// return any value, including Tuples. They cannot, however, have default parameters. -// -// The basic syntax is: -// // { (parameters) -> return_type in // ... statements ... -// } -// -// Here's an example of a simple String comparison closure that might be used for sorting Strings: -// -// { (s1: String, s2: String) -> Bool in -// return s1 < s2 -// } -// -// Here's an example using Swift's 'sorted' member function. It's important to note that this -// function receives a single closure. -// -// These can be a little tricky to read if you're not used to them. To understand the syntax, pay -// special attention to the curly braces that encapsulate the closure and the parenthesis just -// outside of those curly braces: -let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"] -var reversed = [String]() -reversed = names.sorted({ - (s1: String, s2: String) -> Bool in - return s1 > s2 -}) - -// ------------------------------------------------------------------------------------------------ -// Inferring Type from Context -// -// Like functions, closures have a type. -// -// If the type is known (as is always the case when passing a closure as a parameter to a function) -// then the return type of the closure can be inferred, allowing us to simplify the syntax of our -// call to sort. -// -// The following call is identical to the one above with the exception that "-> Bool" was removed: -reversed = names.sorted({ - (s1: String, s2: String) in - return s1 > s2 -}) - -// Just as the return type can be inferred, so can the parameter types. This allows us to simplify -// the syntax a bit further by removing the type annotations from the closure's parameters. -// -// The following call is identical to the one above with the exception that the parameter type -// annotations (": String") have been removed: -reversed = names.sorted({ - (s1, s2) in - return s1 > s2 -}) - -// Since all types can be inferred and we're not using any type annotation on the parameters, -// we can simplify a bit further by removing the paranthesis around the parameters. We'll also put -// it all on a single line, since it's a bit more clear now: -reversed = names.sorted({ s1, s2 in return s1 > s2 }) - -// If the closuere has only a single expression, then the return statement is also inferred. When -// this is the case, the closure returns the value of the single expression: -reversed = names.sorted({ s1, s2 in s1 > s2 }) - -// We're not done simplifying yet. It turns out we can get rid of the parameters as well. If we -// remove the parameters, we can still access them because Swift provides shorthand names to -// parameters passed to inline closures. To access the first parameter, use $0. The second -// parameter would be $1 and so on. -// -// Here's what that would might like (this will not compile - yet): -// -// reversed = names.sorted({ s1, s2 in $0 > $1 }) -// -// This won't compile because you're not allowed to use shorthand names if you specify the -// parameter list. Therefore, we need to remove those in order to get it to compile. This makes -// for a very short inline closure: -reversed = names.sorted({ $0 > $1 }) - -// Interestingly enough, the operator < for String types is defined as: -// -// (String, String) -> Bool -// -// Notice how this is the same as the closure's type for the sorted() routine? Wouldn't it be -// nice if we could just pass in this operator? It turns out that for inline closures, Swift allows -// exactly this. -// -// Here's what that looks like: -reversed = names.sorted(>) - -// If you want to just sort a mutable copy of an array (in place) you can use the sort() method -var mutableCopyOfNames = names - -mutableCopyOfNames.sort(>) - -mutableCopyOfNames - -// ------------------------------------------------------------------------------------------------ -// Trailing Closures -// -// Trailing Closures refer to closures that are the last parameter to a function. This special-case -// syntax allows a few other syntactic simplifications. In essence, you can move trailing closures -// just outside of the parameter list. Swift's sorted() member function uses a trailing closure for -// just this reason. -// -// Let's go back to our original call to sort with a fully-formed closure and move the closure -// outside of the parameter list. This resembles a function definition, but it's a function call. -reversed = names.sorted { - (s1: String, s2: String) -> Bool in - return s1 > s2 - } - -// Note that the opening brace for the closure must be on the same line as the function call's -// ending paranthesis. This is the same functinon call with the starting brace for the closure -// moved to the next line. This will not compile: -// -// reversed = sort(names) -// { -// (s1: String, s2: String) -> Bool in -// return s1 > s2 -// } - -// Let's jump back to our simplified closure ({$0 > $1}) and apply the trailing closure principle: -reversed = names.sorted {$0 > $1} - -// Another simplification: if a function receives just one closure as the only parameter, you can -// remove the () from the function call. First, we'll need a function that receives just one -// parameter, a closure: -func returnValue(f: () -> Int) -> Int -{ - // Simply return the value that the closure 'f' returns - return f() -} - -// Now let's call the function with the parenthesis removed and a trailing closure: -returnValue {return 6} - -// And if we apply the simplification described earlier that implies the return statement for -// single-expresssion closures, it simplifies to this oddly-looking line of code: -returnValue {6} - -// ------------------------------------------------------------------------------------------------ -// Capturing Values -// -// The idea of capturing is to allow a closure to access the variables and constants in their -// surrounding context. -// -// For example, a nested function can access contstans and variables from the function in which -// it is defined. If this nested function is returned, each time it is called, it will work within -// that "captured" context. -// -// Here's an example that should help clear this up: -func makeIncrementor(forIncrement amount: Int) -> () -> Int -{ - var runningTotal = 0 - - // runningTotal and amount are 'captured' for the nested function incrementor() - func incrementor() -> Int - { - runningTotal += amount - return runningTotal - } - - // We return the nested function, which has captured it's environment - return incrementor -} - -// Let's get a copy of the incrementor: -var incrementBy10 = makeIncrementor(forIncrement: 10) - -// Whenever we call this function, it will return a value incremented by 10: -incrementBy10() // returns 10 -incrementBy10() // returns 20 - -// We can get another copy of incrementor that works on increments of 3. -var incrementBy3 = makeIncrementor(forIncrement: 3) -incrementBy3() // returns 3 -incrementBy3() // returns 6 - -// 'incrementBy10' and 'incrementBy3' each has its own captured context, so they work independently -// of each other. -incrementBy10() // returns 30 - -// Closures are reference types, which allows us to assign them to a variable. When this happens, -// the captured context comes along for the ride. -var copyIncrementBy10 = incrementBy10 -copyIncrementBy10() // returns 40 - -// If we request a new incremntor that increments by 10, it will have a separate and unique captured -// context: -var anotherIncrementBy10 = makeIncrementor(forIncrement: 10) -anotherIncrementBy10() // returns 10 - -// Our first incrementor is still using its own context: -incrementBy10() // returns 50 +// } \ No newline at end of file diff --git a/7. Closures.playground/section-11.swift b/7. Closures.playground/section-11.swift new file mode 100644 index 0000000..b7e8673 --- /dev/null +++ b/7. Closures.playground/section-11.swift @@ -0,0 +1 @@ +reversed = names.sorted({ s1, s2 in return s1 > s2 }) \ No newline at end of file diff --git a/7. Closures.playground/section-13.swift b/7. Closures.playground/section-13.swift new file mode 100644 index 0000000..60ac44e --- /dev/null +++ b/7. Closures.playground/section-13.swift @@ -0,0 +1 @@ +reversed = names.sorted({ s1, s2 in s1 > s2 }) \ No newline at end of file diff --git a/7. Closures.playground/section-15.swift b/7. Closures.playground/section-15.swift new file mode 100644 index 0000000..2b22ef6 --- /dev/null +++ b/7. Closures.playground/section-15.swift @@ -0,0 +1 @@ +// reversed = names.sorted({ s1, s2 in $0 > $1 }) \ No newline at end of file diff --git a/7. Closures.playground/section-17.swift b/7. Closures.playground/section-17.swift new file mode 100644 index 0000000..6bee89a --- /dev/null +++ b/7. Closures.playground/section-17.swift @@ -0,0 +1 @@ +reversed = names.sorted({ $0 > $1 }) \ No newline at end of file diff --git a/7. Closures.playground/section-19.swift b/7. Closures.playground/section-19.swift new file mode 100644 index 0000000..6f917c8 --- /dev/null +++ b/7. Closures.playground/section-19.swift @@ -0,0 +1 @@ +reversed = names.sorted(>) \ No newline at end of file diff --git a/7. Closures.playground/section-21.swift b/7. Closures.playground/section-21.swift new file mode 100644 index 0000000..8a2285a --- /dev/null +++ b/7. Closures.playground/section-21.swift @@ -0,0 +1,5 @@ +var mutableCopyOfNames = names + +mutableCopyOfNames.sort(>) + +mutableCopyOfNames \ No newline at end of file diff --git a/7. Closures.playground/section-23.swift b/7. Closures.playground/section-23.swift new file mode 100644 index 0000000..1bd09b9 --- /dev/null +++ b/7. Closures.playground/section-23.swift @@ -0,0 +1,4 @@ +reversed = names.sorted { + (s1: String, s2: String) -> Bool in + return s1 > s2 + } \ No newline at end of file diff --git a/7. Closures.playground/section-25.swift b/7. Closures.playground/section-25.swift new file mode 100644 index 0000000..efe4348 --- /dev/null +++ b/7. Closures.playground/section-25.swift @@ -0,0 +1,5 @@ +// reversed = sort(names) +// { +// (s1: String, s2: String) -> Bool in +// return s1 > s2 +// } \ No newline at end of file diff --git a/7. Closures.playground/section-27.swift b/7. Closures.playground/section-27.swift new file mode 100644 index 0000000..f51ef64 --- /dev/null +++ b/7. Closures.playground/section-27.swift @@ -0,0 +1 @@ +reversed = names.sorted {$0 > $1} \ No newline at end of file diff --git a/7. Closures.playground/section-29.swift b/7. Closures.playground/section-29.swift new file mode 100644 index 0000000..6338af7 --- /dev/null +++ b/7. Closures.playground/section-29.swift @@ -0,0 +1,5 @@ +func returnValue(f: () -> Int) -> Int +{ + // Simply return the value that the closure 'f' returns + return f() +} \ No newline at end of file diff --git a/7. Closures.playground/section-3.swift b/7. Closures.playground/section-3.swift new file mode 100644 index 0000000..93764b0 --- /dev/null +++ b/7. Closures.playground/section-3.swift @@ -0,0 +1,3 @@ +// { (s1: String, s2: String) -> Bool in +// return s1 < s2 +// } \ No newline at end of file diff --git a/7. Closures.playground/section-31.swift b/7. Closures.playground/section-31.swift new file mode 100644 index 0000000..a679212 --- /dev/null +++ b/7. Closures.playground/section-31.swift @@ -0,0 +1 @@ +returnValue {return 6} \ No newline at end of file diff --git a/7. Closures.playground/section-33.swift b/7. Closures.playground/section-33.swift new file mode 100644 index 0000000..bdc2c88 --- /dev/null +++ b/7. Closures.playground/section-33.swift @@ -0,0 +1 @@ +returnValue {6} \ No newline at end of file diff --git a/7. Closures.playground/section-35.swift b/7. Closures.playground/section-35.swift new file mode 100644 index 0000000..6c47177 --- /dev/null +++ b/7. Closures.playground/section-35.swift @@ -0,0 +1,14 @@ +func makeIncrementor(forIncrement amount: Int) -> () -> Int +{ + var runningTotal = 0 + + // runningTotal and amount are 'captured' for the nested function incrementor() + func incrementor() -> Int + { + runningTotal += amount + return runningTotal + } + + // We return the nested function, which has captured it's environment + return incrementor +} \ No newline at end of file diff --git a/7. Closures.playground/section-37.swift b/7. Closures.playground/section-37.swift new file mode 100644 index 0000000..8dab886 --- /dev/null +++ b/7. Closures.playground/section-37.swift @@ -0,0 +1 @@ +var incrementBy10 = makeIncrementor(forIncrement: 10) \ No newline at end of file diff --git a/7. Closures.playground/section-39.swift b/7. Closures.playground/section-39.swift new file mode 100644 index 0000000..93745cb --- /dev/null +++ b/7. Closures.playground/section-39.swift @@ -0,0 +1,2 @@ +incrementBy10() // returns 10 +incrementBy10() // returns 20 \ No newline at end of file diff --git a/7. Closures.playground/section-41.swift b/7. Closures.playground/section-41.swift new file mode 100644 index 0000000..ea47657 --- /dev/null +++ b/7. Closures.playground/section-41.swift @@ -0,0 +1,3 @@ +var incrementBy3 = makeIncrementor(forIncrement: 3) +incrementBy3() // returns 3 +incrementBy3() // returns 6 \ No newline at end of file diff --git a/7. Closures.playground/section-43.swift b/7. Closures.playground/section-43.swift new file mode 100644 index 0000000..b168c2b --- /dev/null +++ b/7. Closures.playground/section-43.swift @@ -0,0 +1 @@ +incrementBy10() // returns 30 \ No newline at end of file diff --git a/7. Closures.playground/section-45.swift b/7. Closures.playground/section-45.swift new file mode 100644 index 0000000..55eddd7 --- /dev/null +++ b/7. Closures.playground/section-45.swift @@ -0,0 +1,2 @@ +var copyIncrementBy10 = incrementBy10 +copyIncrementBy10() // returns 40 \ No newline at end of file diff --git a/7. Closures.playground/section-47.swift b/7. Closures.playground/section-47.swift new file mode 100644 index 0000000..20f9c16 --- /dev/null +++ b/7. Closures.playground/section-47.swift @@ -0,0 +1,2 @@ +var anotherIncrementBy10 = makeIncrementor(forIncrement: 10) +anotherIncrementBy10() // returns 10 \ No newline at end of file diff --git a/7. Closures.playground/section-49.swift b/7. Closures.playground/section-49.swift new file mode 100644 index 0000000..4f5dc2e --- /dev/null +++ b/7. Closures.playground/section-49.swift @@ -0,0 +1 @@ +incrementBy10() // returns 50 \ No newline at end of file diff --git a/7. Closures.playground/section-5.swift b/7. Closures.playground/section-5.swift new file mode 100644 index 0000000..3f2f78d --- /dev/null +++ b/7. Closures.playground/section-5.swift @@ -0,0 +1,6 @@ +let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"] +var reversed = [String]() +reversed = names.sorted({ + (s1: String, s2: String) -> Bool in + return s1 > s2 +}) \ No newline at end of file diff --git a/7. Closures.playground/section-7.swift b/7. Closures.playground/section-7.swift new file mode 100644 index 0000000..067394a --- /dev/null +++ b/7. Closures.playground/section-7.swift @@ -0,0 +1,4 @@ +reversed = names.sorted({ + (s1: String, s2: String) in + return s1 > s2 +}) \ No newline at end of file diff --git a/7. Closures.playground/section-9.swift b/7. Closures.playground/section-9.swift new file mode 100644 index 0000000..7ddfbb7 --- /dev/null +++ b/7. Closures.playground/section-9.swift @@ -0,0 +1,4 @@ +reversed = names.sorted({ + (s1, s2) in + return s1 > s2 +}) \ No newline at end of file diff --git a/8. Enumerations.playground/Documentation/section-0.html b/8. Enumerations.playground/Documentation/section-0.html new file mode 100644 index 0000000..4456871 --- /dev/null +++ b/8. Enumerations.playground/Documentation/section-0.html @@ -0,0 +1,31 @@ + + + + + Section 1 + + + + + + +
+
+

Enumerations

+

Things to know:

+
    +
  • Enumerations in Swift are different from their popular counterparts in C-like languages. Rather that containing "one of a set of integer values" like most C-like languages, Swift's enumerations can be thought of as holding one named type of a given set of named types.

    +
  • +
  • To clarify: Rather than holding an integer value that has been pre-defined integer value (Error = -1, Success = 0) an enumeration in Swift only associates a name with a type (like Int, String, Tuple, etc.) These elements of the enumeration can then be assigned "Associated Values." For example, an enumeration can store an "Error" which is a Tuple with an Int value for the error code and a String containing the message. Each time a function or piece of code assignes or returns an Error(Int, String), it can set populate the Tuple with Int/String par for the specific error condition.

    +
  • +
  • Alternative to the enumeration storing named types, Swift enumerations can have a type. If that type is Int, then they will behave more like their C-style counterparts.

    +
  • +
+
+

Here is the simple enumeration.

+

Unlike their C counterparts, the members of the enumeration below are not integer values (0, 1, 2, etc.) Instead, each member is a fully-fledged value in its own right.

+ +
+
+ + diff --git a/8. Enumerations.playground/Documentation/section-10.html b/8. Enumerations.playground/Documentation/section-10.html new file mode 100644 index 0000000..ca8381e --- /dev/null +++ b/8. Enumerations.playground/Documentation/section-10.html @@ -0,0 +1,21 @@ + + + + + Section 11 + + + + + + +
+
+

Associated Values

+

Associated values allows us to store information with each member of the switch using a Tuple.

+

The following enumeration will store not only the type of a barcode (UPCA, QR Code) but also the data of the barcode (this is likely a foreign concept for most.)

+ +
+
+ + diff --git a/8. Enumerations.playground/Documentation/section-12.html b/8. Enumerations.playground/Documentation/section-12.html new file mode 100644 index 0000000..eae184c --- /dev/null +++ b/8. Enumerations.playground/Documentation/section-12.html @@ -0,0 +1,19 @@ + + + + + Section 13 + + + + + + +
+
+

Let's specify a UPCA code (letting the compiler infer the enum type of Barcode):

+ +
+
+ + diff --git a/8. Enumerations.playground/Documentation/section-14.html b/8. Enumerations.playground/Documentation/section-14.html new file mode 100644 index 0000000..c8f937e --- /dev/null +++ b/8. Enumerations.playground/Documentation/section-14.html @@ -0,0 +1,19 @@ + + + + + Section 15 + + + + + + +
+
+

Let's change that to a QR code (still of a Barcode type)

+ +
+
+ + diff --git a/8. Enumerations.playground/Documentation/section-16.html b/8. Enumerations.playground/Documentation/section-16.html new file mode 100644 index 0000000..79c0d9b --- /dev/null +++ b/8. Enumerations.playground/Documentation/section-16.html @@ -0,0 +1,19 @@ + + + + + Section 17 + + + + + + +
+
+

We use a switch to check the value and extract the associated value:

+ +
+
+ + diff --git a/8. Enumerations.playground/Documentation/section-18.html b/8. Enumerations.playground/Documentation/section-18.html new file mode 100644 index 0000000..7f9c0f5 --- /dev/null +++ b/8. Enumerations.playground/Documentation/section-18.html @@ -0,0 +1,19 @@ + + + + + Section 19 + + + + + + +
+
+

Using the switch statement simplification (see the Switch statement section) to reduce the number of occurrances of the 'let' introducer:

+ +
+
+ + diff --git a/8. Enumerations.playground/Documentation/section-2.html b/8. Enumerations.playground/Documentation/section-2.html new file mode 100644 index 0000000..c9b0d36 --- /dev/null +++ b/8. Enumerations.playground/Documentation/section-2.html @@ -0,0 +1,19 @@ + + + + + Section 3 + + + + + + +
+
+

You can also combine members onto a single line if you prefer, or mix them up. This has no effect on the enumeration itself.

+ +
+
+ + diff --git a/8. Enumerations.playground/Documentation/section-20.html b/8. Enumerations.playground/Documentation/section-20.html new file mode 100644 index 0000000..d035274 --- /dev/null +++ b/8. Enumerations.playground/Documentation/section-20.html @@ -0,0 +1,20 @@ + + + + + Section 21 + + + + + + +
+
+

Raw values

+

We can assign a type to an enumeration. If we use Int as the type, then we are effectively making an enumeration that functions like its C counterpart:

+ +
+
+ + diff --git a/8. Enumerations.playground/Documentation/section-22.html b/8. Enumerations.playground/Documentation/section-22.html new file mode 100644 index 0000000..c261326 --- /dev/null +++ b/8. Enumerations.playground/Documentation/section-22.html @@ -0,0 +1,19 @@ + + + + + Section 23 + + + + + + +
+
+

We can get the raw value of an enumeration value with the toRaw() method:

+ +
+
+ + diff --git a/8. Enumerations.playground/Documentation/section-24.html b/8. Enumerations.playground/Documentation/section-24.html new file mode 100644 index 0000000..0cf3833 --- /dev/null +++ b/8. Enumerations.playground/Documentation/section-24.html @@ -0,0 +1,19 @@ + + + + + Section 25 + + + + + + +
+
+

We can give enumerations many types. Here's one of type Character:

+ +
+
+ + diff --git a/8. Enumerations.playground/Documentation/section-26.html b/8. Enumerations.playground/Documentation/section-26.html new file mode 100644 index 0000000..a75b752 --- /dev/null +++ b/8. Enumerations.playground/Documentation/section-26.html @@ -0,0 +1,19 @@ + + + + + Section 27 + + + + + + +
+
+

Alternatively, we could also use Strings

+ +
+
+ + diff --git a/8. Enumerations.playground/Documentation/section-28.html b/8. Enumerations.playground/Documentation/section-28.html new file mode 100644 index 0000000..9d5a8ab --- /dev/null +++ b/8. Enumerations.playground/Documentation/section-28.html @@ -0,0 +1,19 @@ + + + + + Section 29 + + + + + + +
+
+

And we can get their raw value as well:

+ +
+
+ + diff --git a/8. Enumerations.playground/Documentation/section-30.html b/8. Enumerations.playground/Documentation/section-30.html new file mode 100644 index 0000000..53bca8c --- /dev/null +++ b/8. Enumerations.playground/Documentation/section-30.html @@ -0,0 +1,19 @@ + + + + + Section 31 + + + + + + +
+
+

We can also generate the enumeration value from the raw value. Note that this is an optional because not all raw values will have a matching enumeration:

+ +
+
+ + diff --git a/8. Enumerations.playground/Documentation/section-32.html b/8. Enumerations.playground/Documentation/section-32.html new file mode 100644 index 0000000..563df84 --- /dev/null +++ b/8. Enumerations.playground/Documentation/section-32.html @@ -0,0 +1,19 @@ + + + + + Section 33 + + + + + + +
+
+

Let's verify this:

+ +
+
+ + diff --git a/8. Enumerations.playground/Documentation/section-34.html b/8. Enumerations.playground/Documentation/section-34.html new file mode 100644 index 0000000..363d72e --- /dev/null +++ b/8. Enumerations.playground/Documentation/section-34.html @@ -0,0 +1,19 @@ + + + + + Section 35 + + + + + + +
+
+

An example of when a raw doesn't translate to an enum, leaving us with a nil optional:

+ +
+
+ + diff --git a/8. Enumerations.playground/Documentation/section-36.html b/8. Enumerations.playground/Documentation/section-36.html new file mode 100644 index 0000000..2b4a760 --- /dev/null +++ b/8. Enumerations.playground/Documentation/section-36.html @@ -0,0 +1,18 @@ + + + + + Section 37 + + + + + + +
+
+ +
+
+ + diff --git a/8. Enumerations.playground/Documentation/section-4.html b/8. Enumerations.playground/Documentation/section-4.html new file mode 100644 index 0000000..ae69995 --- /dev/null +++ b/8. Enumerations.playground/Documentation/section-4.html @@ -0,0 +1,19 @@ + + + + + Section 5 + + + + + + +
+
+

Let's store an enumeration value into a variable. We'll let the compiler infer the type:

+ +
+
+ + diff --git a/8. Enumerations.playground/Documentation/section-6.html b/8. Enumerations.playground/Documentation/section-6.html new file mode 100644 index 0000000..7ddb3b8 --- /dev/null +++ b/8. Enumerations.playground/Documentation/section-6.html @@ -0,0 +1,19 @@ + + + + + Section 7 + + + + + + +
+
+

Now that directionToHead has a CompassPoint type (which was inferred) we can set it to a different CompassPoint value using a shorter syntax:

+ +
+
+ + diff --git a/8. Enumerations.playground/Documentation/section-8.html b/8. Enumerations.playground/Documentation/section-8.html new file mode 100644 index 0000000..75aec07 --- /dev/null +++ b/8. Enumerations.playground/Documentation/section-8.html @@ -0,0 +1,20 @@ + + + + + Section 9 + + + + + + +
+
+

We can use a switch to match values from an enumeration.

+

Remember that switches have to be exhaustive. But in this case, Swift knows that the CompassType enumeration only has 4 values, so as long as we cover all 4, we don't need the default case.

+ +
+
+ + diff --git a/8. Enumerations.playground/Documentation/stylesheet.css b/8. Enumerations.playground/Documentation/stylesheet.css new file mode 100644 index 0000000..43b0230 --- /dev/null +++ b/8. Enumerations.playground/Documentation/stylesheet.css @@ -0,0 +1,65 @@ +html { + font-size: 1em; +} + +body { + background-color: #fff; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: normal; + font-size: 1.0em; + line-height: 140%; + padding: 0 2em; +} + +code { + font-family: "Menlo", monospace; + font-size: 0.95em; +} + +section { + padding: 0.5em 0; + -webkit-overflow-scrolling: touch; +} + +a:link { + color: #00f; + text-decoration: none; + border-bottom: 1px solid #00f; +} + +h1 { + font-size: 1.5em; + padding: 0.7em 0 0 0; +} + +h2 { + font-size: 1.2em; + padding: 0.7em 0 0 0; +} + +h3 { + font-size: 1.0em; + padding: 0.7em 0 0 0; +} + +blockquote { + font-size: 0.9em; + margin-left: 0; + padding-left: 2em; + border-left: 8px solid #eee; +} + +.footnotes { + color: #666666; + margin-top: 2em; + font-size: 0.9em; + letter-spacing: -3px; +} + +.footnotes:before { + content: "――――"; +} + +.footnotes ol { + letter-spacing: normal; +} diff --git a/8. Enumerations.playground/contents.xcplayground b/8. Enumerations.playground/contents.xcplayground index 4937636..2502554 100644 --- a/8. Enumerations.playground/contents.xcplayground +++ b/8. Enumerations.playground/contents.xcplayground @@ -1,7 +1,79 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file diff --git a/8. Enumerations.playground/section-1.swift b/8. Enumerations.playground/section-1.swift index d908dea..1ff0435 100644 --- a/8. Enumerations.playground/section-1.swift +++ b/8. Enumerations.playground/section-1.swift @@ -1,26 +1,3 @@ -// ------------------------------------------------------------------------------------------------ -// Things to know: -// -// * Enumerations in Swift are different from their popular counterparts in C-like languages. -// Rather that containing "one of a set of integer values" like most C-like languages, Swift's -// enumerations can be thought of as holding one named type of a given set of named types. -// -// To clarify: Rather than holding an integer value that has been pre-defined integer value -// (Error = -1, Success = 0) an enumeration in Swift only associates a name with a type (like -// Int, String, Tuple, etc.) These elements of the enumeration can then be assigned "Associated -// Values." For example, an enumeration can store an "Error" which is a Tuple with an Int value -// for the error code and a String containing the message. Each time a function or piece of code -// assignes or returns an Error(Int, String), it can set populate the Tuple with Int/String par -// for the specific error condition. -// -// * Alternative to the enumeration storing named types, Swift enumerations can have a type. If -// that type is Int, then they will behave more like their C-style counterparts. -// ------------------------------------------------------------------------------------------------ - -// Here is the simple enumeration. -// -// Unlike their C counterparts, the members of the enumeration below are not integer values (0, -// 1, 2, etc.) Instead, each member is a fully-fledged value in its own right. enum Planet { case Mercury @@ -31,130 +8,4 @@ enum Planet case Saturn case Uranus case Neptune -} - -// You can also combine members onto a single line if you prefer, or mix them up. This has no -// effect on the enumeration itself. -enum CompassPoint -{ - case North, South - case East, West -} - -// Let's store an enumeration value into a variable. We'll let the compiler infer the type: -var directionToHead = CompassPoint.West - -// Now that directionToHead has a CompassPoint type (which was inferred) we can set it to a -// different CompassPoint value using a shorter syntax: -directionToHead = .East - -// We can use a switch to match values from an enumeration. -// -// Remember that switches have to be exhaustive. But in this case, Swift knows that the CompassType -// enumeration only has 4 values, so as long as we cover all 4, we don't need the default case. -switch directionToHead -{ - case .North: - "North" - case .South: - "South" - case .East: - "East" - case .West: - "West" -} - -// ------------------------------------------------------------------------------------------------ -// Associated Values -// -// Associated values allows us to store information with each member of the switch using a Tuple. -// -// The following enumeration will store not only the type of a barcode (UPCA, QR Code) but also -// the data of the barcode (this is likely a foreign concept for most.) -enum Barcode -{ - case UPCA(Int, Int, Int) // UPCA with associated value type (Int, Int, Int) - case QRCode(String) // QRCode with associated value type of String -} - -// Let's specify a UPCA code (letting the compiler infer the enum type of Barcode): -var productBarcode = Barcode.UPCA(0, 8590951226, 3) - -// Let's change that to a QR code (still of a Barcode type) -productBarcode = .QRCode("ABCDEFGHIJKLMNOP") - -// We use a switch to check the value and extract the associated value: -switch productBarcode -{ - case .UPCA(let numberSystem, let identifier, let check): - "UPCA: \(numberSystem), \(identifier), \(check)" - case .QRCode(let productCode): - "QR: \(productCode)" -} - -// Using the switch statement simplification (see the Switch statement section) to reduce the -// number of occurrances of the 'let' introducer: -switch productBarcode -{ - // All constants - case let .UPCA(numberSystem, identifier, check): - "UPCA: \(numberSystem), \(identifier), \(check)" - - // All variables - case var .QRCode(productCode): - "QR: \(productCode)" -} - -// ------------------------------------------------------------------------------------------------ -// Raw values -// -// We can assign a type to an enumeration. If we use Int as the type, then we are effectively -// making an enumeration that functions like its C counterpart: -enum StatusCode: Int -{ - case Error = -1 - case Success = 9 - case OtherResult = 1 - case YetAnotherResult // Unspecified values are auto-incremented from the previous value -} - -// We can get the raw value of an enumeration value with the toRaw() method: -StatusCode.OtherResult.toRaw() - -// We can give enumerations many types. Here's one of type Character: -enum ASCIIControlCharacter: Character -{ - case Tab = "\t" - case LineFeed = "\n" - case CarriageReturn = "\r" - - // Note that only Int type enumerations can auto-increment. Since this is a Character type, - // the following line of code won't compile: - // - // case VerticalTab -} - -// Alternatively, we could also use Strings -enum FamilyPet: String -{ - case Cat = "Cat" - case Dog = "Dog" - case Ferret = "Ferret" -} - -// And we can get their raw value as well: -FamilyPet.Ferret.toRaw() - -// We can also generate the enumeration value from the raw value. Note that this is an optional -// because not all raw values will have a matching enumeration: -var pet = FamilyPet.fromRaw("Ferret") - -// Let's verify this: -if pet != .None { "We have a pet!" } -else { "No pet :(" } - -// An example of when a raw doesn't translate to an enum, leaving us with a nil optional: -pet = FamilyPet.fromRaw("Snake") -if pet != .None { "We have a pet" } -else { "No pet :(" } - +} \ No newline at end of file diff --git a/8. Enumerations.playground/section-11.swift b/8. Enumerations.playground/section-11.swift new file mode 100644 index 0000000..210dfbc --- /dev/null +++ b/8. Enumerations.playground/section-11.swift @@ -0,0 +1,5 @@ +enum Barcode +{ + case UPCA(Int, Int, Int) // UPCA with associated value type (Int, Int, Int) + case QRCode(String) // QRCode with associated value type of String +} \ No newline at end of file diff --git a/8. Enumerations.playground/section-13.swift b/8. Enumerations.playground/section-13.swift new file mode 100644 index 0000000..855c5d5 --- /dev/null +++ b/8. Enumerations.playground/section-13.swift @@ -0,0 +1 @@ +var productBarcode = Barcode.UPCA(0, 8590951226, 3) \ No newline at end of file diff --git a/8. Enumerations.playground/section-15.swift b/8. Enumerations.playground/section-15.swift new file mode 100644 index 0000000..8d6e83f --- /dev/null +++ b/8. Enumerations.playground/section-15.swift @@ -0,0 +1 @@ +productBarcode = .QRCode("ABCDEFGHIJKLMNOP") \ No newline at end of file diff --git a/8. Enumerations.playground/section-17.swift b/8. Enumerations.playground/section-17.swift new file mode 100644 index 0000000..86f48c6 --- /dev/null +++ b/8. Enumerations.playground/section-17.swift @@ -0,0 +1,7 @@ +switch productBarcode +{ + case .UPCA(let numberSystem, let identifier, let check): + "UPCA: \(numberSystem), \(identifier), \(check)" + case .QRCode(let productCode): + "QR: \(productCode)" +} \ No newline at end of file diff --git a/8. Enumerations.playground/section-19.swift b/8. Enumerations.playground/section-19.swift new file mode 100644 index 0000000..33f47a6 --- /dev/null +++ b/8. Enumerations.playground/section-19.swift @@ -0,0 +1,10 @@ +switch productBarcode +{ + // All constants + case let .UPCA(numberSystem, identifier, check): + "UPCA: \(numberSystem), \(identifier), \(check)" + + // All variables + case var .QRCode(productCode): + "QR: \(productCode)" +} \ No newline at end of file diff --git a/8. Enumerations.playground/section-21.swift b/8. Enumerations.playground/section-21.swift new file mode 100644 index 0000000..1bceb4f --- /dev/null +++ b/8. Enumerations.playground/section-21.swift @@ -0,0 +1,7 @@ +enum StatusCode: Int +{ + case Error = -1 + case Success = 9 + case OtherResult = 1 + case YetAnotherResult // Unspecified values are auto-incremented from the previous value +} \ No newline at end of file diff --git a/8. Enumerations.playground/section-23.swift b/8. Enumerations.playground/section-23.swift new file mode 100644 index 0000000..0e91ff2 --- /dev/null +++ b/8. Enumerations.playground/section-23.swift @@ -0,0 +1 @@ +StatusCode.OtherResult.toRaw() \ No newline at end of file diff --git a/8. Enumerations.playground/section-25.swift b/8. Enumerations.playground/section-25.swift new file mode 100644 index 0000000..1c32c5e --- /dev/null +++ b/8. Enumerations.playground/section-25.swift @@ -0,0 +1,11 @@ +enum ASCIIControlCharacter: Character +{ + case Tab = "\t" + case LineFeed = "\n" + case CarriageReturn = "\r" + + // Note that only Int type enumerations can auto-increment. Since this is a Character type, + // the following line of code won't compile: + // + // case VerticalTab +} \ No newline at end of file diff --git a/8. Enumerations.playground/section-27.swift b/8. Enumerations.playground/section-27.swift new file mode 100644 index 0000000..2a9c7c8 --- /dev/null +++ b/8. Enumerations.playground/section-27.swift @@ -0,0 +1,6 @@ +enum FamilyPet: String +{ + case Cat = "Cat" + case Dog = "Dog" + case Ferret = "Ferret" +} \ No newline at end of file diff --git a/8. Enumerations.playground/section-29.swift b/8. Enumerations.playground/section-29.swift new file mode 100644 index 0000000..f640402 --- /dev/null +++ b/8. Enumerations.playground/section-29.swift @@ -0,0 +1 @@ +FamilyPet.Ferret.toRaw() \ No newline at end of file diff --git a/8. Enumerations.playground/section-3.swift b/8. Enumerations.playground/section-3.swift new file mode 100644 index 0000000..e8e7a95 --- /dev/null +++ b/8. Enumerations.playground/section-3.swift @@ -0,0 +1,5 @@ +enum CompassPoint +{ + case North, South + case East, West +} \ No newline at end of file diff --git a/8. Enumerations.playground/section-31.swift b/8. Enumerations.playground/section-31.swift new file mode 100644 index 0000000..9a6f06c --- /dev/null +++ b/8. Enumerations.playground/section-31.swift @@ -0,0 +1 @@ +var pet = FamilyPet.fromRaw("Ferret") \ No newline at end of file diff --git a/8. Enumerations.playground/section-33.swift b/8. Enumerations.playground/section-33.swift new file mode 100644 index 0000000..ceb30e1 --- /dev/null +++ b/8. Enumerations.playground/section-33.swift @@ -0,0 +1,2 @@ +if pet != .None { "We have a pet!" } +else { "No pet :(" } \ No newline at end of file diff --git a/8. Enumerations.playground/section-35.swift b/8. Enumerations.playground/section-35.swift new file mode 100644 index 0000000..836ccd9 --- /dev/null +++ b/8. Enumerations.playground/section-35.swift @@ -0,0 +1,3 @@ +pet = FamilyPet.fromRaw("Snake") +if pet != .None { "We have a pet" } +else { "No pet :(" } \ No newline at end of file diff --git a/8. Enumerations.playground/section-5.swift b/8. Enumerations.playground/section-5.swift new file mode 100644 index 0000000..38cd039 --- /dev/null +++ b/8. Enumerations.playground/section-5.swift @@ -0,0 +1 @@ +var directionToHead = CompassPoint.West \ No newline at end of file diff --git a/8. Enumerations.playground/section-7.swift b/8. Enumerations.playground/section-7.swift new file mode 100644 index 0000000..5b80d5c --- /dev/null +++ b/8. Enumerations.playground/section-7.swift @@ -0,0 +1 @@ +directionToHead = .East \ No newline at end of file diff --git a/8. Enumerations.playground/section-9.swift b/8. Enumerations.playground/section-9.swift new file mode 100644 index 0000000..5e45896 --- /dev/null +++ b/8. Enumerations.playground/section-9.swift @@ -0,0 +1,11 @@ +switch directionToHead +{ + case .North: + "North" + case .South: + "South" + case .East: + "East" + case .West: + "West" +} \ No newline at end of file diff --git a/9. Classes and Structures.playground/Documentation/section-0.html b/9. Classes and Structures.playground/Documentation/section-0.html new file mode 100644 index 0000000..10ee6d5 --- /dev/null +++ b/9. Classes and Structures.playground/Documentation/section-0.html @@ -0,0 +1,39 @@ + + + + + Section 1 + + + + + + +
+
+

Classes and Structures

+

Things to know:

+

Classes and structures can both:

+
    +
  • Define properties to store values
  • +
  • Define methods to provide functionality
  • +
  • Define subscripts to provide access to their values using subscript syntax
  • +
  • Define initializers to set up their initial state
  • +
  • Be extended to expand their functionality beyond a default implementation
  • +
  • Conform to protocols to provide standard functionality of a certain kind
  • +
+

Only classes have:

+
    +
  • Inheritance enables one class to inherit the characteristics of another.
  • +
  • Type casting enables you to check and interpret the type of a class instance at runtime.
  • +
  • Deinitializers enable an instance of a class to free up any resources it has assigned.
  • +
  • Reference counting allows more than one reference to a class instance.
  • +
+
+

First, let's create a basic structure with a couple of simple properties.

+

Our structure must have all of its properties initialized, either with default values or through initialization (described later.) For now, we'll just ensure they're all initialized with default values.

+ +
+
+ + diff --git a/9. Classes and Structures.playground/Documentation/section-10.html b/9. Classes and Structures.playground/Documentation/section-10.html new file mode 100644 index 0000000..ac90f13 --- /dev/null +++ b/9. Classes and Structures.playground/Documentation/section-10.html @@ -0,0 +1,21 @@ + + + + + Section 11 + + + + + + +
+
+

Structures and Enumerations are Value Types

+

This means that when passing an instance of a structure or enumeration to a function (or assigning an instance of a structure or enumeration to another value), the structure or enumeration is copied.

+

Let's create two independent copies of a Resolution structure

+ +
+
+ + diff --git a/9. Classes and Structures.playground/Documentation/section-12.html b/9. Classes and Structures.playground/Documentation/section-12.html new file mode 100644 index 0000000..d544342 --- /dev/null +++ b/9. Classes and Structures.playground/Documentation/section-12.html @@ -0,0 +1,19 @@ + + + + + Section 13 + + + + + + +
+
+

We can modify the variable resolution:

+ +
+
+ + diff --git a/9. Classes and Structures.playground/Documentation/section-14.html b/9. Classes and Structures.playground/Documentation/section-14.html new file mode 100644 index 0000000..3a544ce --- /dev/null +++ b/9. Classes and Structures.playground/Documentation/section-14.html @@ -0,0 +1,19 @@ + + + + + Section 15 + + + + + + +
+
+

We can see that the original (from where the variable copy originated) is unchanged:

+ +
+
+ + diff --git a/9. Classes and Structures.playground/Documentation/section-16.html b/9. Classes and Structures.playground/Documentation/section-16.html new file mode 100644 index 0000000..b40deb1 --- /dev/null +++ b/9. Classes and Structures.playground/Documentation/section-16.html @@ -0,0 +1,20 @@ + + + + + Section 17 + + + + + + +
+
+

Note that since structures and enumerations are value types, we are unable to modify the contents of constant intances.

+

The following will not compile:

+ +
+
+ + diff --git a/9. Classes and Structures.playground/Documentation/section-18.html b/9. Classes and Structures.playground/Documentation/section-18.html new file mode 100644 index 0000000..b489024 --- /dev/null +++ b/9. Classes and Structures.playground/Documentation/section-18.html @@ -0,0 +1,21 @@ + + + + + Section 19 + + + + + + +
+
+

Classes are Reference Types:

+

This means that when passing an instance of an object to a function (or assigning an instance of an object to another value), the new value will hold a reference to the original object.

+

Let's create an object and assign it's instance to another variable:

+ +
+
+ + diff --git a/9. Classes and Structures.playground/Documentation/section-2.html b/9. Classes and Structures.playground/Documentation/section-2.html new file mode 100644 index 0000000..e0726c3 --- /dev/null +++ b/9. Classes and Structures.playground/Documentation/section-2.html @@ -0,0 +1,20 @@ + + + + + Section 3 + + + + + + +
+
+

Similarly, a basic class with a few properties, fully initialized. Notice that the first property is an instance of the Resolution structure.

+

Also, note that the final member, the 'name' property, does not need to be initialized because optionals are initalized to nil by default.

+ +
+
+ + diff --git a/9. Classes and Structures.playground/Documentation/section-20.html b/9. Classes and Structures.playground/Documentation/section-20.html new file mode 100644 index 0000000..db3d792 --- /dev/null +++ b/9. Classes and Structures.playground/Documentation/section-20.html @@ -0,0 +1,19 @@ + + + + + Section 21 + + + + + + +
+
+

If we modify the variable..

+ +
+
+ + diff --git a/9. Classes and Structures.playground/Documentation/section-22.html b/9. Classes and Structures.playground/Documentation/section-22.html new file mode 100644 index 0000000..ac83e96 --- /dev/null +++ b/9. Classes and Structures.playground/Documentation/section-22.html @@ -0,0 +1,19 @@ + + + + + Section 23 + + + + + + +
+
+

...we can see that the other instance is also modified:

+ +
+
+ + diff --git a/9. Classes and Structures.playground/Documentation/section-24.html b/9. Classes and Structures.playground/Documentation/section-24.html new file mode 100644 index 0000000..3cb72c7 --- /dev/null +++ b/9. Classes and Structures.playground/Documentation/section-24.html @@ -0,0 +1,19 @@ + + + + + Section 25 + + + + + + +
+
+

In addition to this, we can even modify the 'constantVideoMode' instance. This is the case because it is a reference type and modifing the contents do not modify the reference itself.

+ +
+
+ + diff --git a/9. Classes and Structures.playground/Documentation/section-26.html b/9. Classes and Structures.playground/Documentation/section-26.html new file mode 100644 index 0000000..7691964 --- /dev/null +++ b/9. Classes and Structures.playground/Documentation/section-26.html @@ -0,0 +1,20 @@ + + + + + Section 27 + + + + + + +
+
+

We cannot, however, modify the instance variable.

+

This line of code will not compile:

+ +
+
+ + diff --git a/9. Classes and Structures.playground/Documentation/section-28.html b/9. Classes and Structures.playground/Documentation/section-28.html new file mode 100644 index 0000000..057e49f --- /dev/null +++ b/9. Classes and Structures.playground/Documentation/section-28.html @@ -0,0 +1,21 @@ + + + + + Section 29 + + + + + + +
+
+

Memberwise Initializers for Structure Types

+

We can set the properties without the need to create a specialiized init routine. If a struct (not a class) does not have any initializers, then Swift will provide a "Memberwise Initializer" for us automatically.

+

Here's what tha memberwise initializer looks like. It's pretty self-explanatory in that it is an initializer that includes one externally named parameter for each property in the structure.

+ +
+
+ + diff --git a/9. Classes and Structures.playground/Documentation/section-30.html b/9. Classes and Structures.playground/Documentation/section-30.html new file mode 100644 index 0000000..3faef59 --- /dev/null +++ b/9. Classes and Structures.playground/Documentation/section-30.html @@ -0,0 +1,20 @@ + + + + + Section 31 + + + + + + +
+
+

Identity operators

+

Since classes are reference types, we can check to see if they are 'identical' with the Identity (===) operator:

+ +
+
+ + diff --git a/9. Classes and Structures.playground/Documentation/section-32.html b/9. Classes and Structures.playground/Documentation/section-32.html new file mode 100644 index 0000000..835d65e --- /dev/null +++ b/9. Classes and Structures.playground/Documentation/section-32.html @@ -0,0 +1,20 @@ + + + + + Section 33 + + + + + + +
+
+

Identical-to is not the same as equal to:

+

The following line will not compile as it uses the equality operator and VideoMode hasn't defined an equality operator:

+ +
+
+ + diff --git a/9. Classes and Structures.playground/Documentation/section-4.html b/9. Classes and Structures.playground/Documentation/section-4.html new file mode 100644 index 0000000..35ac328 --- /dev/null +++ b/9. Classes and Structures.playground/Documentation/section-4.html @@ -0,0 +1,19 @@ + + + + + Section 5 + + + + + + +
+
+

Here are some instances of our structure and class:

+ +
+
+ + diff --git a/9. Classes and Structures.playground/Documentation/section-6.html b/9. Classes and Structures.playground/Documentation/section-6.html new file mode 100644 index 0000000..e723081 --- /dev/null +++ b/9. Classes and Structures.playground/Documentation/section-6.html @@ -0,0 +1,20 @@ + + + + + Section 7 + + + + + + +
+
+

Accessing properties

+

We can access members of the class or structure using the dot operator:

+ +
+
+ + diff --git a/9. Classes and Structures.playground/Documentation/section-8.html b/9. Classes and Structures.playground/Documentation/section-8.html new file mode 100644 index 0000000..4818f51 --- /dev/null +++ b/9. Classes and Structures.playground/Documentation/section-8.html @@ -0,0 +1,19 @@ + + + + + Section 9 + + + + + + +
+
+

In Objective-C, if an object contained a structure, the sub-properties (the properties of the structure inside the object) could not be modified directly. Instead the entire structure would have to be replaced completely. This is not the case for Swift.

+ +
+
+ + diff --git a/9. Classes and Structures.playground/Documentation/stylesheet.css b/9. Classes and Structures.playground/Documentation/stylesheet.css new file mode 100644 index 0000000..43b0230 --- /dev/null +++ b/9. Classes and Structures.playground/Documentation/stylesheet.css @@ -0,0 +1,65 @@ +html { + font-size: 1em; +} + +body { + background-color: #fff; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-weight: normal; + font-size: 1.0em; + line-height: 140%; + padding: 0 2em; +} + +code { + font-family: "Menlo", monospace; + font-size: 0.95em; +} + +section { + padding: 0.5em 0; + -webkit-overflow-scrolling: touch; +} + +a:link { + color: #00f; + text-decoration: none; + border-bottom: 1px solid #00f; +} + +h1 { + font-size: 1.5em; + padding: 0.7em 0 0 0; +} + +h2 { + font-size: 1.2em; + padding: 0.7em 0 0 0; +} + +h3 { + font-size: 1.0em; + padding: 0.7em 0 0 0; +} + +blockquote { + font-size: 0.9em; + margin-left: 0; + padding-left: 2em; + border-left: 8px solid #eee; +} + +.footnotes { + color: #666666; + margin-top: 2em; + font-size: 0.9em; + letter-spacing: -3px; +} + +.footnotes:before { + content: "――――"; +} + +.footnotes ol { + letter-spacing: normal; +} diff --git a/9. Classes and Structures.playground/contents.xcplayground b/9. Classes and Structures.playground/contents.xcplayground index 4937636..37181bb 100644 --- a/9. Classes and Structures.playground/contents.xcplayground +++ b/9. Classes and Structures.playground/contents.xcplayground @@ -1,7 +1,73 @@ - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - \ No newline at end of file diff --git a/9. Classes and Structures.playground/section-1.swift b/9. Classes and Structures.playground/section-1.swift index 8add319..4104dce 100644 --- a/9. Classes and Structures.playground/section-1.swift +++ b/9. Classes and Structures.playground/section-1.swift @@ -1,137 +1,5 @@ -// ------------------------------------------------------------------------------------------------ -// Things to know: -// -// Classes and structures can both: -// -// * Define properties to store values -// * Define methods to provide functionality -// * Define subscripts to provide access to their values using subscript syntax -// * Define initializers to set up their initial state -// * Be extended to expand their functionality beyond a default implementation -// * Conform to protocols to provide standard functionality of a certain kind -// -// Only classes have: -// -// * Inheritance enables one class to inherit the characteristics of another. -// * Type casting enables you to check and interpret the type of a class instance at runtime. -// * Deinitializers enable an instance of a class to free up any resources it has assigned. -// * Reference counting allows more than one reference to a class instance. -// ------------------------------------------------------------------------------------------------ - -// First, let's create a basic structure with a couple of simple properties. -// -// Our structure must have all of its properties initialized, either with default values or through -// initialization (described later.) For now, we'll just ensure they're all initialized with -// default values. struct Resolution { var width = 1280 var height = 1024 -} - -// Similarly, a basic class with a few properties, fully initialized. Notice that the first -// property is an instance of the Resolution structure. -// -// Also, note that the final member, the 'name' property, does not need to be initialized because -// optionals are initalized to nil by default. -class VideoMode -{ - var resolution = Resolution() - var interlaced = false - var frameRate = 0.0 - var name: String? -} - -// Here are some instances of our structure and class: -var someResolution = Resolution() -var someVideoMode = VideoMode() - -// ------------------------------------------------------------------------------------------------ -// Accessing properties -// -// We can access members of the class or structure using the dot operator: -someResolution.width -someVideoMode.resolution.width - -// In Objective-C, if an object contained a structure, the sub-properties (the properties of the -// structure inside the object) could not be modified directly. Instead the entire structure would -// have to be replaced completely. This is not the case for Swift. -someVideoMode.resolution.width = 2880 -someVideoMode.resolution.height = 1800 - -// ------------------------------------------------------------------------------------------------ -// Structures and Enumerations are Value Types -// -// This means that when passing an instance of a structure or enumeration to a function (or -// assigning an instance of a structure or enumeration to another value), the structure or -// enumeration is copied. -// -// Let's create two independent copies of a Resolution structure -let constantResolution = Resolution() -var variableResolution = constantResolution - -// We can modify the variable resolution: -variableResolution.width = 320 -variableResolution.height = 200 - -// We can see that the original (from where the variable copy originated) is unchanged: -constantResolution - -// Note that since structures and enumerations are value types, we are unable to modify the -// contents of constant intances. -// -// The following will not compile: -// -// constantResolution.width = 320 - -// ------------------------------------------------------------------------------------------------ -// Classes are Reference Types: -// -// This means that when passing an instance of an object to a function (or assigning an instance -// of an object to another value), the new value will hold a reference to the original object. -// -// Let's create an object and assign it's instance to another variable: -let constantVideoMode = VideoMode() -var variableVideoMode = constantVideoMode - -// If we modify the variable.. -variableVideoMode.frameRate = 240 - -// ...we can see that the other instance is also modified: -constantVideoMode.frameRate - -// In addition to this, we can even modify the 'constantVideoMode' instance. This is the case -// because it is a reference type and modifing the contents do not modify the reference itself. -constantVideoMode.frameRate = 24 - -// We cannot, however, modify the instance variable. -// -// This line of code will not compile: -// -// constantVideoMode = VideoMode - -// ------------------------------------------------------------------------------------------------ -// Memberwise Initializers for Structure Types -// -// We can set the properties without the need to create a specialiized init routine. If a struct -// (not a class) does not have any initializers, then Swift will provide a "Memberwise Initializer" -// for us automatically. -// -// Here's what tha memberwise initializer looks like. It's pretty self-explanatory in that it is -// an initializer that includes one externally named parameter for each property in the structure. -let vga = Resolution(width: 640, height: 480) - -// ------------------------------------------------------------------------------------------------ -// Identity operators -// -// Since classes are reference types, we can check to see if they are 'identical' with the -// Identity (===) operator: -someVideoMode === variableVideoMode -constantVideoMode === variableVideoMode - -// Identical-to is not the same as equal to: -// -// The following line will not compile as it uses the equality operator and VideoMode hasn't -// defined an equality operator: -// -// constantVideoMode == variableVideoMode +} \ No newline at end of file diff --git a/9. Classes and Structures.playground/section-11.swift b/9. Classes and Structures.playground/section-11.swift new file mode 100644 index 0000000..2785aee --- /dev/null +++ b/9. Classes and Structures.playground/section-11.swift @@ -0,0 +1,2 @@ +let constantResolution = Resolution() +var variableResolution = constantResolution \ No newline at end of file diff --git a/9. Classes and Structures.playground/section-13.swift b/9. Classes and Structures.playground/section-13.swift new file mode 100644 index 0000000..cdee500 --- /dev/null +++ b/9. Classes and Structures.playground/section-13.swift @@ -0,0 +1,2 @@ +variableResolution.width = 320 +variableResolution.height = 200 \ No newline at end of file diff --git a/9. Classes and Structures.playground/section-15.swift b/9. Classes and Structures.playground/section-15.swift new file mode 100644 index 0000000..f5ea771 --- /dev/null +++ b/9. Classes and Structures.playground/section-15.swift @@ -0,0 +1 @@ +constantResolution \ No newline at end of file diff --git a/9. Classes and Structures.playground/section-17.swift b/9. Classes and Structures.playground/section-17.swift new file mode 100644 index 0000000..ec12b09 --- /dev/null +++ b/9. Classes and Structures.playground/section-17.swift @@ -0,0 +1 @@ +// constantResolution.width = 320 \ No newline at end of file diff --git a/9. Classes and Structures.playground/section-19.swift b/9. Classes and Structures.playground/section-19.swift new file mode 100644 index 0000000..1b326dc --- /dev/null +++ b/9. Classes and Structures.playground/section-19.swift @@ -0,0 +1,2 @@ +let constantVideoMode = VideoMode() +var variableVideoMode = constantVideoMode \ No newline at end of file diff --git a/9. Classes and Structures.playground/section-21.swift b/9. Classes and Structures.playground/section-21.swift new file mode 100644 index 0000000..73a03e5 --- /dev/null +++ b/9. Classes and Structures.playground/section-21.swift @@ -0,0 +1 @@ +variableVideoMode.frameRate = 240 \ No newline at end of file diff --git a/9. Classes and Structures.playground/section-23.swift b/9. Classes and Structures.playground/section-23.swift new file mode 100644 index 0000000..6dfd60f --- /dev/null +++ b/9. Classes and Structures.playground/section-23.swift @@ -0,0 +1 @@ +constantVideoMode.frameRate \ No newline at end of file diff --git a/9. Classes and Structures.playground/section-25.swift b/9. Classes and Structures.playground/section-25.swift new file mode 100644 index 0000000..b13ee0c --- /dev/null +++ b/9. Classes and Structures.playground/section-25.swift @@ -0,0 +1 @@ +constantVideoMode.frameRate = 24 \ No newline at end of file diff --git a/9. Classes and Structures.playground/section-27.swift b/9. Classes and Structures.playground/section-27.swift new file mode 100644 index 0000000..0091f23 --- /dev/null +++ b/9. Classes and Structures.playground/section-27.swift @@ -0,0 +1 @@ +// constantVideoMode = VideoMode \ No newline at end of file diff --git a/9. Classes and Structures.playground/section-29.swift b/9. Classes and Structures.playground/section-29.swift new file mode 100644 index 0000000..fc1b59e --- /dev/null +++ b/9. Classes and Structures.playground/section-29.swift @@ -0,0 +1 @@ +let vga = Resolution(width: 640, height: 480) \ No newline at end of file diff --git a/9. Classes and Structures.playground/section-3.swift b/9. Classes and Structures.playground/section-3.swift new file mode 100644 index 0000000..4fb4b1a --- /dev/null +++ b/9. Classes and Structures.playground/section-3.swift @@ -0,0 +1,7 @@ +class VideoMode +{ + var resolution = Resolution() + var interlaced = false + var frameRate = 0.0 + var name: String? +} \ No newline at end of file diff --git a/9. Classes and Structures.playground/section-31.swift b/9. Classes and Structures.playground/section-31.swift new file mode 100644 index 0000000..99e5c91 --- /dev/null +++ b/9. Classes and Structures.playground/section-31.swift @@ -0,0 +1,2 @@ +someVideoMode === variableVideoMode +constantVideoMode === variableVideoMode \ No newline at end of file diff --git a/9. Classes and Structures.playground/section-33.swift b/9. Classes and Structures.playground/section-33.swift new file mode 100644 index 0000000..1227b99 --- /dev/null +++ b/9. Classes and Structures.playground/section-33.swift @@ -0,0 +1 @@ +// constantVideoMode == variableVideoMode \ No newline at end of file diff --git a/9. Classes and Structures.playground/section-5.swift b/9. Classes and Structures.playground/section-5.swift new file mode 100644 index 0000000..258b1a1 --- /dev/null +++ b/9. Classes and Structures.playground/section-5.swift @@ -0,0 +1,2 @@ +var someResolution = Resolution() +var someVideoMode = VideoMode() \ No newline at end of file diff --git a/9. Classes and Structures.playground/section-7.swift b/9. Classes and Structures.playground/section-7.swift new file mode 100644 index 0000000..d417dc0 --- /dev/null +++ b/9. Classes and Structures.playground/section-7.swift @@ -0,0 +1,2 @@ +someResolution.width +someVideoMode.resolution.width \ No newline at end of file diff --git a/9. Classes and Structures.playground/section-9.swift b/9. Classes and Structures.playground/section-9.swift new file mode 100644 index 0000000..b7ef235 --- /dev/null +++ b/9. Classes and Structures.playground/section-9.swift @@ -0,0 +1,2 @@ +someVideoMode.resolution.width = 2880 +someVideoMode.resolution.height = 1800 \ No newline at end of file diff --git a/99. Not The End.playground/Documentation/section-0.html b/99. Not The End.playground/Documentation/section-0.html new file mode 100644 index 0000000..607caec --- /dev/null +++ b/99. Not The End.playground/Documentation/section-0.html @@ -0,0 +1,34 @@ + + + + + Section 1 + + + + + + +
+
+
------------------------------------------------------------------------------------------------
+   ____                            _       _
+  / ___|___  _ __   __ _ _ __ __ _| |_ ___| |
+    | |   / _ \| '_ \ / _` | '__/ _` | __/ __| |
+ | |__| (_) | | | | (_| | | | (_| | |_\__ \_|
+  \____\___/|_| |_|\__, |_|  \__,_|\__|___(_)
+                   |___/
+
+                                               You've made it to the end!
+
+------------------------------------------------------------------------------------------------
+

There's Still More To Learn

+

These playgrounds are taken directly from the Language Guide section of Apple's book titled "The Swift Programming Language".

+

The book also includes a Language Reference, which discusses the language in a more terse form with greater detail (including the grammar.) It's not as dry as reading the C++ Ansi Spec, but it's still rather detailed. In fact, some of the information from these playgrounds came from the Language Reference section.

+

The good news is that having managed to get through these playgrounds, you'll probably find the Language Reference to be rather quick reading, chock full of additional goodies that you never knew about (because the Language Guide and these Playgrounds don't touch on.)

+

For example, how would you code the assert function such that the first parameter is executed and evaluated to a Bool for use in determining an error condition?

+ +
+
+ + diff --git a/99. Not The End.playground/Documentation/section-10.html b/99. Not The End.playground/Documentation/section-10.html new file mode 100644 index 0000000..4a6c483 --- /dev/null +++ b/99. Not The End.playground/Documentation/section-10.html @@ -0,0 +1,21 @@ + + + + + Section 11 + + + + + + +
+
+

Most importantly, you'll solidify your understanding of the concepts that were presented in these playgrounds.

+

Happy coding!

+

Paul Nettle

+ +
+
+ + diff --git a/99. Not The End.playground/Documentation/section-2.html b/99. Not The End.playground/Documentation/section-2.html new file mode 100644 index 0000000..df6f5de --- /dev/null +++ b/99. Not The End.playground/Documentation/section-2.html @@ -0,0 +1,19 @@ + + + + + Section 3 + + + + + + +
+
+

Do you know why this compiles?

+ +
+
+ + diff --git a/99. Not The End.playground/Documentation/section-4.html b/99. Not The End.playground/Documentation/section-4.html new file mode 100644 index 0000000..77e417d --- /dev/null +++ b/99. Not The End.playground/Documentation/section-4.html @@ -0,0 +1,19 @@ + + + + + Section 5 + + + + + + +
+
+

...or why it can be executed like this?

+ +
+
+ + diff --git a/99. Not The End.playground/Documentation/section-6.html b/99. Not The End.playground/Documentation/section-6.html new file mode 100644 index 0000000..805388f --- /dev/null +++ b/99. Not The End.playground/Documentation/section-6.html @@ -0,0 +1,21 @@ + + + + + Section 7 + + + + + + +
+
+

You'll also learn about Metatypes and did you know that Swift's operator precedence is slightly different than C?

+

This is clearly stuff you should know before submitting your forehead to the wall.

+

You'll learn about these constants:

+ +
+
+ + diff --git a/99. Not The End.playground/Documentation/section-8.html b/99. Not The End.playground/Documentation/section-8.html new file mode 100644 index 0000000..62f4bc5 --- /dev/null +++ b/99. Not The End.playground/Documentation/section-8.html @@ -0,0 +1,19 @@ + + + + + Section 9 + + + + + + +
+
+

Furthermore, don't let somebody else's code confuse you when you see something like this in their code and realize that it actually compiles!

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