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 @@ + + +
+ +Here's a structure with a couple simple stored properties:
+ +Notice how we haven't used the importer yet, so it is nil:
+ +So now let's access it:
+ +And now we see the importer was created:
+ +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.
+ +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.
+ +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:
+ +Here, we'll create a square from our Rect structure
+ +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.
+ +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.
+ +We can see that the origin has been updated from (0, 0) to (10, 10):
+ +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':
+ +We can also have a read-only computed property by simply omitting the setter:
+ +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:
+ +Let's declare our structure and read the 'volume' property
+ +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:
+ +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.
+ +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:
+ +Let's create an instance of StepCounter so we can try out our observer
+ +The following will first call 'willSet' on the 'totalSteps' property, followed by a change to the value itself, followed by a call to 'didSet'
+ +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'
+ +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
+ +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
+ +Similarly, here's an enumeration with a couple of type properties
+ +Classes are a little different in that they cannot contain stored type properties, but may only contain computed type 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.
+ +Now let's instantiate the data manager and add some simple data to the class:
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 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:
+ +We'll create a constant Point2...
+ +Because 'fixedPoint' is constant, we are not allowed to call mutating memthods:
+The following line won't compile:
+ +If you're working with a structure or enumeration (not a class), uou can assign to 'self' directly
+ +Assigning to 'self' in an enumeration is used to change to a different member of the same enumeration:
+ +Type methods are like C++'s static methods.
+They can only access Type members.
+ +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:
+ +To call a type method, use the type name, not the instance name:
+ +If we attempt to use an instance to call a type method, we'll get an error
+ +The following line will not compile:
+ +For classes, type methods use the 'class' keyword rather than the 'static' keyword:
+ +We call class type methods with the type name just as we do for structures and enumerations:
+ +Now let's see how we call each of those functions
+ +The 'self' property refers to the current instance of a class, structure or enumeration. For C++ developers, think of 'self' as 'this'.
+ +Instance methods cannot by default modify properties of structures or enumerations. To enable this, mark them as 'mutating':
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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.
+ +We can now make use of our newly created subscripts
+ +Subscripts can take any parameter type and variadic parameters, but cannot use inout or default parameters.
+Here's a more complex example:
+ +We'll create a standard 4x4 identity matrix
+ +
+
+
+
+
+
+
+
+
Let's start with a simple base class:
+ +Here, we'll check our description to see that it does indeed give us something different from the superclass' default description:
+ +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.
+ +We can see our override in action
+ +We can also override property observers
+ +Here is our overridden observers in action
+ +Now let's subclass the Vehicle to create a two-wheeled vehicle called a Bicycle
+ +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:
+ +We can call a member from the superclass
+ +Subclasses can also be subclassed
+ +Here, we'll create a car that includes a new description by overriding the superclass' instance method
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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.
+ +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:
+ +Here, we can see our efforts pay off:
+ +Optional properties do not need to be initialized:
+ +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:
+ +Here, we initialize the class with a blank initializer (calling init()) to let text's default value initialize the stored value
+ +Since the class can fully initialize itself, we can safely instantiate it with no error:
+ +Here, we'll us an aalternat initializer to specify a different value for 'text'
+ +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.
+ +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.
+ +Here, we call the default memberwise initializer that Swift created for us
+ +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.
+ +Here, we call the three initializers:
+ +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:
+ +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:
+ +Now let's try our new initializers
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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:
+ +Let's initialize our new ShoppingListItem using the super's initializer
+ +Here, we can create an array of ShippingListItems
+ +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.
+ +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.
+ +We can now check our work
+ +Here we make use of our two initializers
+ +Two-phase initialization is a new concept enforced by Swift. Think of it like this:
+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.
+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:
+ +Now we can call our various initializers to see them in action:
+ +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.
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Let's create a couple classes to work with...
+ +Let's exercise the Player class a bit and create a new player with 100 coins in his purse (pulled from the Bank.)
+ +The Player now wins 2000 coins!
+ +When we cause playerOne to be deallocated, the deinitializer is called
+ +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.
+ +
+
+
+
+
+
+
+
+
+
+
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
+ +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:
+ +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):
+ +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.
+ +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):
+ +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.)
+ +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:
+ +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.
+ +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.
+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.
+ +Here is our new tenant and his new apartment.
+This will create a single strong reference to each:
+ +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.)
+ +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.
+ +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:
+ +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.
+ +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:
+ +We can define a Country with a capital city
+ +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.
+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.
+ +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:
+ +Let's use the HTMLElement. We'll make sure we declare it as optional so we can set it to 'nil' later.
+ +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:
+ +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:
+ +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:
+ +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"
+ +Playgrounds do not allow us to test/prove this, so feel free to plug this into a compiled application to see it in action.
+ +With a reference count of 2, we can set our original reference to nil. This will drop our reference count down to 1.
+ +The copyOfPerson still exists and holds a strong reference to our instance:
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 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:
+ +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:
+ +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.
+ +This line could be read as: optional person's second optional music preference's favorite song's optional artist's name.
+ +Here, we'll create a working chain:
+ +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.
+ +Let's break the chain, removing the user's music preferences:
+ +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:
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Let's start by creating a few types to work with:
+ +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.
+ +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:
+ +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:
+ +Alternatively, we can downcast the array itself rather than each item:
+ +Finally, we can avoid the additional local variable and performt he downcast right inside the loop structure:
+ +We'll create a library of Movies and Songs. Note that Swift will infer the type of the array to be MediaItem[].
+ +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:
+ +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.
+ +We can check the type of an object very simply with the 'is' operator, also known as the Type Check Operator.
+ +Let's see this in action. Lets loop through the library and count up the movies and songs:
+ +Our final Movie and Song counts:
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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:
+ +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.
+ +To access the nested type, we can drill down into the type using type names:
+ +
+
+
+
+
+
+
These are known as "Stored Values" in Swift
+Use the let
keyword to define a constant
You can combine them on a single line with a comma
+ +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.
+ +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:
+ +You can print a value using println (this doesn't do anything in a playground, though)
+ +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:
+ +Use the var
keyword to define a variable
Tip: only use variables when you need a stored value that changes. Otherwise, prefer constants.
+ +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:
+ +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:
+ +We can now use let
like any normal variable:
This works for any keyword:
+ +Additionally, it's important to know that this works on non-colliding identifier names:
+ +Also note that myConstant
and myConstant refer to the same constant:
You've probably already figured this out, but anything after the "//" is a comment. There's more +to comments, though:
+ +Semicolons on the end of a line are optional, but the preferred style for Swift is to not use +them to terminate lines.
+ +However, if you want to put two lines of code on one line, you'll need the semicolon to separate +them.
+ +There are multiple types of integers. Signed and unsigned with sizes of 8, 16, 32 and 64 bits. +Here are a couple samples:
+ +Constants cannot change. This line wouldn't compile:
+ +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.
+ +To find the bounds of any integer, try ".min" or ".max"
+ +Double is a 64-bit floating point numbers and Float is a 32-bit floating point number
+ +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
+ +Floating point literals are always inferred to be Double
+ +If you want a Float instead, you must use type annotation
+ +String literals are inferred to be String type
+ +Here's a bool
+ +These lines won't compile because we are specifying a type that doesn't match the given value
+ +You can specify numbers in a few interesting ways
+ +Variables can change:
+ +Floating point numbers can be specified in a few different ways as well. Here are a few raw +examples (not assigned to variables):
+ +We can pad our literals as well:
+ +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
+ +Since the default type for numeric values is Int, you need to specify a different type
+ +This will infer a UInt16 based on the types of both operands
+ +Conversions between integer and floating point types must be made explicit
+ +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
+ +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.
+ +You also can't redeclare a variable or constant once it has been declared. These lines +won't compile:
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Create an alias for UInt16 called "AudioSample"
+ +This actually calls UInt16.min
+ +We can also typealias custom types
+ +
+
+
+
+
+
+
Use parenthesis around the comma-delimited list of values
+This Tuple doesn't specify types, so it relies on inference
+ +Alternatively, you can name the elements of a Tuple
+ +When you name the elements you effectively assign names to their indices, so the dot operator works with names or integers:
+ +We can also specify the type in order to avoid inferrence
+ +Decomposing tuples looks like this
+ +We can also decompose into variables instead of constants, but you probably figured that out
+ +You can also access them with the dot operator followed by their index:
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 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)
+ +If we assign it to a constant, the type of that constant will be an Optional Int (Int?)
+ +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:
+ +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:
+ +One way to do this is to "force unwrap" the value using the "!" symbol, like this:
+ +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.
+ +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
+ +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'
+ +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.
+ +Setting an optional to 'nil' sets it to be contain "no value"
+ +Now if we check it, we see that it holds no value:
+ +We can also try optional binding on an empty optional
+ +Because of the introduction of optionals, you can no longer use nil for non-optional variables or constants.
+The following line will not compile
+ +The following line will compile, because the String is optional
+ +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:
+ +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!"
+ +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
+ +Notice how failedConversion is 'nil', even though it's an Int
+ +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:
+ +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.
+ +Like any other optional, we can still check if it holds a value:
+ +Let's carry on with a successful conversion
+ +This one worked
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Let's start with a value...
+ +You can assert with a message
+ +You can assert without the message
+ +
+
+
+
+
+
+
+ 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.
+ +Unary prefix operators appear before their taget. Here we increment a then negate it:
+ +You can also use the uniary + operator, though it doesn't do anything
+ +We have the compound assigment operator
+ +The logical NOT
+ +Unary postfix operators appear after their target: i++
+ +Assignment can also take multiple values (for Tuples):
+ +Binary operators are infix because they appear between to targets
+ +String concatenation uses the + operator:
+ +You can add characters, too, which form a string
+ +You can also add characters to a string
+ +Ternary operators work on three targets:
+ +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:
+ +String comparisons are case sensitive
+ +Comparisons use the logical operators with AND, OR and NOT
+ +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.
+ +The range operator with two dots means up to but NOT including the final value.
+This is called the "Half-Closed Range Operator"
+ +The range operator with three dots is inclusive with last value like
+This is called the "Closed Range Operator"
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Let's take a look at how extensions are declared. Note that, unlike Objective-C categories, extensions are not named:
+ +Since we didn't provide any initializers, we can use Swift's default memberwise initializer for the Rect.
+ +Let's extend Rect to add a new convenience initializer. Note that we're still responsible for ensuring that the instance is fully initialized.
+ +Let's try out our new initializer:
+ +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:
+ +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'.
+ +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.
+ +Let's call our new member using the shorthand syntax for trailing closures:
+ +Instance methods can mutate the instance itself.
+Note the use of the 'mutating' keyword.
+ +Let's add a subscript to Int:
+ +And we can call our subscript directly on an Int, including a literal Int type:
+ +We can also add nested types to an existing type:
+ +Let's test out our new extension with nested types:
+ +We can call upon Double's new computed property to convert inches to meters
+ +Similarly, we'll convert three feet to meters
+ +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:
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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
+
}
+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.
+ +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:
+ +Let's adopt this protocol:
+ +Protocols are types, which means they can be used where many other types are allowed, including:
+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:
+ +Just for fun, let's roll a few:
+ +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:
+ +Let's create a more practical protocol that we can actually conform to:
+ +We'll extend our Dice class:
+ +Existing instances (such as 'd6' will automatically adopt and conform to the new protocol, even though it was declared prior to the 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:
+ +Let's verify our work:
+ +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.
+ +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:
+ +We can now loop through each and produce its text representation:
+ +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:
+ +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.
+ +We can test our work:
+ +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.
+ +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
Let's start by creating a couple of protocols for expirimentation:
+ +Here, we declare an Individual that conforms to both Name and Age protocols:
+ +Here, we can see the protocol composition at work as the parameter into the wishHappyBirthday() function:
+ +If we call the member, we can see the celebratory wish for this individual:
+ +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:
+ +We can store our objects into an array of type AnyObject[]
+ +Then we can test each for conformance to HasArea:
+ +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:
+Here's another simple protocol that uses optional requrements:
+ +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:
+ +Let's try a more complex class
+ +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:
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
The problem that Generics solve
+Consider the following function which can swap two Ints.
+ +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:
+ +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.
+ +Let's try a few different inputs
+ +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:
+ +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:
+ +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:
+ +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:
+ +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:
+ +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.
+ +The function's type parameter list places the following restrictions on the types allowed:
+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:
+ +We can compare stringStack against an array of type String[] because we've extended Swift's Array type to conform to our Container protocol:
+ +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:
+ +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
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:
+ +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.
+ +Let's use our new Stack:
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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:
+ +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:
+ +Let's verify our work:
+ +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):
+ +Check our work:
+ +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:
+ +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.
+ +And we can check our work:
+ +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:
+ +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:
+ +Now we can declare our new operator:
+ +Let's check our work:
+ +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:
+ +Check our work. Let's setup a couple vectors that result in a value of (0, 0):
+ +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:
+ +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:
+ +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:
+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:
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Here's a string
+ +You can iterate over a string like this:
+ +Characters use double-quotes to specify them, so you must be explicit if you want a Character instead of a String:
+ +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.”
+ +Strings can be concatenated with strings and characters
+ +Add a character
+ +Strings have some special character constants. They are:
+ +Concatenate a character onto the end of the string
+ +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.
+ +String comparison is case-sensitive and can be compared for equality
+ +You can also compare prefix and suffix equality:
+ +Initializing an empty string - these are equivalent to each other
+ +Use 'isEmpty' to check for empty String
+ +Strings are VALUE TYPES, but they're referenced for performance so they are only copied on +modification.
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Create an array of Strings
+ +We can get the number of elements
+ +We can check to see if it's empty
+ +We can append to the end
+ +We can append another array of same type
+ +We can get elements from the array by indexing them
+ +Shorter, more common way to define an array of Strings
+ +We can modify an existing item
+ +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:
+ +Or we can replace two items with three, inserting a new item:
+ +We can insert an item at a given index
+ +We can remove the last element. During this, we can preserve the value of what was removed into a stored value
+ +We can iterate over the the array using a for-in loop
+ +We can also use the the enumerate() method to return a tuple containing the index and value for each element:
+ +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:
+ +Add the number '3' to the array
+ +We can assign it to an empty array, but we don't modify the type, since someInts is already an Int[] type.
+ +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.
+ +We can initialize an array and and fill it with default values
+ +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:
+ +If you store an array in a constant, it is considered "Immutable"
+ +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:
+ +Nor can we change the size or add an element, you will get a compiler error:
+ +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:
+ +However, if we change the contents of one array (mutating it), then it is copied and becomes its own unique entity:
+ +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:
+ +The same is true if we mutate the array in other ways (mofify the array's size)...
+ +Now, we have three different arrays...
+ +Let's create an array with some stuff in it. We'll use an explicit String type:
+ +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
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
This is a Dictionary literal. They contain a comma-separated list of key:value pairs:
+ +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?
+ +We can get the number of elements in the dictionary:
+ +We can add an element by accessing a key that doesn't exist:
+ +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:
+ +We can remove an entry by setting the value for a key to nil:
+ +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.
+ +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:
+ +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:
+ +We can iterate over just the keys
+ +We can iterate over jsut the values
+ +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.
+ +Here, we create an empty Dictionary of Int keys and String values:
+ +Let's set one of the values
+ +We can empty a dictionary using an empty dictionary literal:
+ +An immutable dictionary is a constant.
+ +Similar to arrays, we cannot modify the contents of an immutable dictionary. The following lines will not compile:
+ +The declaration for airports above could also have been declared in this way:
+ +Dictionaries are value types, which means they are copied on assignment.
+Let's create a Dictionary and copy it:
+ +Next, we'll modify the copy:
+ +And we can see that the original is not changed:
+ +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's get a value from the dictionary for the TYO airport:
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ We can loop through ranges using the closed-range operator ("...").
+In the loop below, 'index' is a constant that is automatically declared.
+ +We can use an underscore if you don't need access to the loop constant:
+ +We can iterate over arrays
+ +We can iterate over a Dictionary's key/value pairs
+ +We can iterate over characters in a String
+ +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)
+ +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:
+ +The parenthesis are optional for the For-Condition-Increment loop:
+ +Variables are scoped to the For-Condition-Increment construct. To alter this, pre-declare index
+ +While loops resemble other C-like languages. They perform the condition before each iteration +through the loop:
+ +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:
+ +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:
+ +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:
+ +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:
+ +We can perform range matching for cases:
+ +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.
+ +Value bindings in switch statements
+ +We can loop through ranges using the half-closed range operator ("..<")
+We can also reuse the name 'index' because of the scoping noted previously.
+ +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.
+ +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 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.
+ +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:
+ +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
+ +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
+ +Similarly, this prints all names without the letter 'a' in them:
+ +Similarly, this prints all names until the letter 'x' is found, then aborts all processing by breaking out of the outer loop:
+ +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:
+ +After the loop, we find that 'indx' still contains the original value of 3999
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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 ->.
+ +We can also remove the return type (and the -> delimiter) alltogether:
+ +Functions can return Tuples, which enable them to return multiple values.
+The following function simply returns two hard-coded strings.
+ +Since the return value is a Tuple, we can use the Tuple's naming feature to name the values being returned:
+ +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.
+ +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":
+ +If we call the function, we'll receive the greeting
+ +We can now use the external name ("action") to make the call:
+ +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.
+ +We can call with just two parameters to add them together
+ +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:
+ +We can opt out of the automatic external name for default parameter values by specify an external name of "_" like so:
+ +Here, we call without the third parameter as before:
+ +And now we can call with an un-named third parameter:
+ +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.
+ +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).
+ +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:
+ +Multiple input parameters are separated by a comma
+ +Here, we can still call with no parameters because of the default parameter
+ +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:
+ +Variadic parameters with external parameter names only apply their external name to the first variadic parameter specified in the function call (if present.)
+ +And here we can see the impact on the function call of adding the external name "values" to the variadic parameter:
+ +All function parameters are constant by default. To make them variable, add the var introducer:
+ +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:
+ +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:
+ +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:
+ +And we can see that 'one' contains a 2 and 'two' contains a 1:
+ +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
+ +A function with no parameters simply has an empty set of parenthesis following the function name:
+ +A function that has no parameters or return value would have the type: () -> ()
+ +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:
+ +Using what we know about funciton types, we can define variables that are the type of a function
+ +We can now use the variable to perform the function:
+ +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
+ +Calling the function now requires external names for the first two parameters
+ +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:
+ +We can now pass the function (along with a couple parameters to call it with) to another function:
+ +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:
+ +Or, an even shorter version that avoids the additional stored value:
+ +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.
+ +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:
+ +And we'll call nop (note the second set of parenthesis to make the actual call to nop())
+ +We can nest functions inside of other functions:
+ +Calling getFive will return the Int value 5:
+ +You can return nested functions, too:
+ +Calling outerFunc2 will return a function capable of returning the Int value 5:
+ +Here we call the nested function:
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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:
+ +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:
+ +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:
+ +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):
+ +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:
+ +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:
+ +Here's an example of a simple String comparison closure that might be used for sorting Strings:
+ +If you want to just sort a mutable copy of an array (in place) you can use the sort() method
+ +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.
+ +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:
+ +Let's jump back to our simplified closure ({$0 > $1}) and apply the trailing closure principle:
+ +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:
+ +Now let's call the function with the parenthesis removed and a trailing closure:
+ +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:
+ +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:
+ +Let's get a copy of the incrementor:
+ +Whenever we call this function, it will return a value incremented by 10:
+ +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:
+ +We can get another copy of incrementor that works on increments of 3.
+ +'incrementBy10' and 'incrementBy3' each has its own captured context, so they work independently of each other.
+ +Closures are reference types, which allows us to assign them to a variable. When this happens, the captured context comes along for the ride.
+ +If we request a new incremntor that increments by 10, it will have a separate and unique captured context:
+ +Our first incrementor is still using its own 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:
+ +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:
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
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.
+ +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.)
+ +Let's specify a UPCA code (letting the compiler infer the enum type of Barcode):
+ +Let's change that to a QR code (still of a Barcode type)
+ +We use a switch to check the value and extract the associated value:
+ +Using the switch statement simplification (see the Switch statement section) to reduce the number of occurrances of the 'let' introducer:
+ +You can also combine members onto a single line if you prefer, or mix them up. This has no effect on the enumeration itself.
+ +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:
+ +We can get the raw value of an enumeration value with the toRaw() method:
+ +We can give enumerations many types. Here's one of type Character:
+ +Alternatively, we could also use Strings
+ +And we can get their raw value as well:
+ +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:
+ +Let's verify this:
+ +An example of when a raw doesn't translate to an enum, leaving us with a nil optional:
+ +Let's store an enumeration value into a variable. We'll let the compiler infer the type:
+ +Now that directionToHead has a CompassPoint type (which was inferred) we can set it to a different CompassPoint value using a shorter syntax:
+ +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.
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Classes and structures can both:
+Only classes have:
+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.
+ +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
+ +We can modify the variable resolution:
+ +We can see that the original (from where the variable copy originated) is unchanged:
+ +Note that since structures and enumerations are value types, we are unable to modify the contents of constant intances.
+The following will not compile:
+ +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:
+ +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.
+ +If we modify the variable..
+ +...we can see that the other instance is also modified:
+ +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.
+ +We cannot, however, modify the instance variable.
+This line of code will not compile:
+ +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.
+ +Since classes are reference types, we can check to see if they are 'identical' with the Identity (===) operator:
+ +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:
+ +Here are some instances of our structure and class:
+ +We can access members of the class or structure using the dot operator:
+ +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.
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
------------------------------------------------------------------------------------------------
+ ____ _ _
+ / ___|___ _ __ __ _ _ __ __ _| |_ ___| |
+ | | / _ \| '_ \ / _` | '__/ _` | __/ __| |
+ | |__| (_) | | | | (_| | | | (_| | |_\__ \_|
+ \____\___/|_| |_|\__, |_| \__,_|\__|___(_)
+ |___/
+
+ You've made it to the end!
+
+------------------------------------------------------------------------------------------------
+
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?
+ +Most importantly, you'll solidify your understanding of the concepts that were presented in these playgrounds.
+Happy coding!
+Paul Nettle
+ +Do you know why this compiles?
+ +...or why it can be executed like this?
+ +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:
+ +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!
+ +
+
+
+
+
+
+
+
+
+
+
+