Mobile Application Development 2
IT0093
Module 3
Classes, Structure, and Enumeration
Sub-topic 1
Closure and Enumeration
• Familiarize with closure syntax
• Optimize closure
• Create and Instantiate enumeration
• Use advance method in application
Closures are self-contained blocks of functionality that can be passed
around and used in your code.
Closures can capture and store references to any constants and
variables from the context in which they are defined. This is known as
closing over those constants and variables, hence the name
“closures”. Swift handles all of the memory management of capturing
for you
Three Forms
Ø Global functions are closures that have a name and do not capture any values.
Ø Nested functions are closures that have a name and can capture values from their enclosing
function.
Ø Closure expressions are unnamed closures written in a lightweight syntax that can capture
values from their surrounding context.
Nested functions, as introduced in Nested Functions, are a convenient
means of naming and defining self-contained blocks of code as part of a
larger function. However, it’s sometimes useful to write shorter versions of
function-like constructs without a full declaration and name. This is
particularly true when you work with functions or methods that take functions
as one or more of their arguments.
Closure expressions are a way to write inline closures in a brief, focused
syntax. Closure expressions provide several syntax optimizations for writing
closures in a shortened form without loss of clarity or intent. The closure
expression examples below illustrate these optimizations by refining a single
example of the sorted(by:) method over several iterations, each of which
expresses the same functionality in a more succinct way.
• Inferring parameter and return value types from context
• Implicit returns from single-expression closures
• Shorthand argument names
• Trailing closure syntax
Closure Expression Syntax
Inferring Type From Context
Implicit Returns from Single-Expression Closures
Shorthand Argument Names
Operator Functions
Trailing Closures
If you need to pass a closure expression to a function as the function’s final
argument and the closure expression is long, it can be useful to write it as
a trailing closure instead. You write a trailing closure after the function call’s
parentheses, even though the trailing closure is still an argument to the
function.
When you use the trailing closure syntax, you don’t write the argument label
for the first closure as part of the function call. A function call can include
multiple trailing closures; however, the first few examples below use a single
trailing closure.
A closure can capture constants and variables from the surrounding context
in which it’s defined. The closure can then refer to and modify the values of
those constants and variables from within its body, even if the original scope
that defined the constants and variables no longer exists.
In Swift, the simplest form of a closure that can capture values is a nested
function, written within the body of another function. A nested function can
capture any of its outer function’s arguments and can also capture any
constants and variables defined within the outer function.
Closure are reference types.
A closure is said to escape a function when the closure is passed as an
argument to the function, but is called after the function returns. When you
declare a function that takes a closure as one of its parameters, you can
write @escaping before the parameter’s type to indicate that the closure is
allowed to escape.
One way that a closure can escape is by being stored in a variable that’s
defined outside the function. As an example, many functions that start an
asynchronous operation take a closure argument as a completion handler.
The function returns after it starts the operation, but the closure isn’t called
until the operation is completed—the closure needs to escape, to be called
later. For example:
Normally, a closure captures variables implicitly by using them in the body of
the closure, but in this case you need to be explicit. If you want to
capture self, write self explicitly when you use it, or include self in the
closure’s capture list. Writing self explicitly lets you express your intent, and
reminds you to confirm that there isn’t a reference cycle. For example, in the
code below, the closure passed
to someFunctionWithEscapingClosure(_:) refers to self explicitly. In
contrast, the closure passed
to someFunctionWithNonescapingClosure(_:) is a nonescaping closure,
which means it can refer to self implicitly.
An autoclosure is a closure that’s automatically created to wrap an
expression that’s being passed as an argument to a function. It doesn’t take
any arguments, and when it’s called, it returns the value of the expression
that’s wrapped inside of it. This syntactic convenience lets you omit braces
around a function’s parameter by writing a normal expression instead of an
explicit closure.
It’s common to call functions that take autoclosures, but it’s not common
to implement that kind of function. For example,
the assert(condition:message:file:line:) function takes an autoclosure for
its condition and message parameters; its condition parameter is evaluated
only in debug builds and its message parameter is evaluated only
if condition is false.
An autoclosure lets you delay evaluation, because the code inside isn’t run
until you call the closure. Delaying evaluation is useful for code that has side
effects or is computationally expensive, because it lets you control when that
code is evaluated. The code below shows how a closure delays evaluation.
You get the same behavior of delayed evaluation when you pass a closure
as an argument to a function.
NOTE
Overusing autoclosures can make your code hard to understand. The context and function
name should make it clear that evaluation is being deferred.
• An enumeration defines a common type for a group of related
values and enables you to work with those values in a type-safe way
within your code.
• more flexible, and do not have to provide a value for each case of
the enumeration
• enumeration cases can specify associated values of any type to be
stored along with each different case value, much as unions or
variants do in other languages.
• are first-class types in their own right. They adopt many features
traditionally supported only by classes, such as computed
properties to provide additional information about the
enumeration’s current value, and instance methods to provide
functionality related to the values the enumeration represents.
You introduce enumerations with the enum keyword and place their entire
definition within a pair of braces:
Here’s an example for the four main points of a compass:
Raw values can be strings, characters, or any of the integer or floating-point number
types. Each raw value must be unique within its enumeration declaration.
Implicitly Assigned Raw Values
A recursive enumeration is an enumeration that has another
instance of the enumeration as the associated value for one
or more of the enumeration cases. You indicate that an
enumeration case is recursive by writing indirect before it,
which tells the compiler to insert the necessary layer of
indirection.
Sub-topic 2
Structure and Classes
• Differentiate operators from Use closure values to
some predefined method that accepts closure as
parameters
• Create a custom defined closure and apply to a given
application
• Build some enumeration, structure and classes
Classes and structures are general-purpose, flexible constructs that
become the building blocks of your program’s code. You define
properties and methods to add functionality to your classes and
structures by using exactly the same syntax as for constants,
variables, and functions.
Unlike other programming languages, Swift does not require you to
create separate interface and implementation files for custom classes
and structures. In Swift, you define a class or a structure in a single
file, and the external interface to that class or structure is automatically
made available for other code to use.
Classes and structures in Swift have many things in common. Both
can:
• Define properties to store values
• Define methods to provide functionality
• Define subscripts to provide access to their values using subscript syntax
• Define initializers to set up their initial state
• Be extended to expand their functionality beyond a default implementation
• Conform to protocols to provide standard functionality of a certain kind
Classes have additional capabilities that structures do not:
• Inheritance enables one class to inherit the characteristics of another.
• Type casting enables you to check and interpret the type of a class instance at runtime.
• Deinitializers enable an instance of a class to free up any resources it has assigned.
• Reference counting allows more than one reference to a class instance.
Class and Structure Instances
Accessing Properties
Memberwise Initializers for Structure Types
Structures and Enumerations Are Value Types
A value type is a type whose value is copied when it is assigned to a
variable or constant, or when it is passed to a function.
all of the basic types in Swift—integers, floating-point numbers,
Booleans, strings, arrays and dictionaries—are value types, and are
implemented as structures behind the scenes.
Reference types are not copied when they are assigned to a variable or
constant, or when they are passed to a function. Rather than a copy, a
reference to the same existing instance is used instead.
Identity Operators
Identical to (===)
Not identical to (!==)
• “Identical to” means that two constants or variables of class type refer to exactly the same
class instance.
• “Equal to” means that two instances are considered “equal” or “equivalent” in value, for
some appropriate meaning of “equal”, as defined by the type’s designer.
Properties associate values with a particular class,
structure, or enumeration. Stored properties store
constant and variable values as part of an instance,
whereas computed properties calculate (rather than
store) a value. Computed properties are provided by
classes, structures, and enumerations. Stored
properties are provided only by classes and
structures.
Stored Properties
stored property is a constant or variable that is stored as part of an instance of a
particular class or structure. Stored properties can be either variable stored
properties (introduced by the var keyword) or constant stored properties
(introduced by the let keyword).
Stored Properties of Constant Structure Instances
If you create an instance of a structure and assign that instance to a constant,
you cannot modify the instance’s properties, even if they were declared as
variable properties:
A lazy stored property is a property whose initial value is not calculated
until the first time it is used. You indicate a lazy stored property by
writing the lazy modifier before its declaration.
Do not store a value but they provide a getter and an optional
setter to retrieve and set other properties and values indirectly.
Shorthand Setter Declaration
A computed property with a getter but no setter is known as a read-
only computed property. A read-only computed property always returns
a value, and can be accessed through dot syntax, but cannot be set to
a different value.
Property observers observe and respond to changes in a property’s value.
Property observers are called every time a property’s value is set, even if the
new value is the same as the property’s current value.
• willSet is called just before the value is stored.
• didSet is called immediately after the new value is stored.
If you implement a willSet observer, it is passed the new property value as a
constant parameter. You can specify a name for this parameter as part of
your willSet implementation. If you don’t write the parameter name and
parentheses within your implementation, the parameter is made available
with a default parameter name of newValue.
Instance properties are properties that belong to an instance of a particular
type. Every time you create a new instance of that type, it has its own set of
property values, separate from any other instance.
You can also define properties that belong to the type itself, not to any one
instance of that type. There will only ever be one copy of these properties,
no matter how many instances of that type you create. These kinds of
properties are called type properties.
Methods are functions that are associated with a particular type. Classes,
structures, and enumerations can all define instance methods, which
encapsulate specific tasks and functionality for working with an instance of a
given type. Classes, structures, and enumerations can also define type
methods, which are associated with the type itself.
Instance methods are functions that belong to instances of a particular class,
structure, or enumeration. They support the functionality of those instances,
either by providing ways to access and modify instance properties, or by
providing functionality related to the instance’s purpose.
Function parameters can have both a local name (for use within the
function’s body) and an external name (for use when calling the function)
Sometimes it’s useful to provide an external parameter name for a method’s
first parameter, even though this is not the default behavior. To do so, you
can add an explicit external name yourself.
Conversely, if you do not want to provide an external name for the second or
subsequent parameter of a method, override the default behavior by using
an underscore character (_) as an explicit external parameter name for that
parameter.
Every instance of a type has an implicit property called self, which is exactly
equivalent to the instance itself. You use the self property to refer to the
current instance within its own instance methods.
Structures and enumerations are value types. By default, the properties of a
value type cannot be modified from within its instance methods.
Methods:
Assigning to self Within a Mutating Method