Swift Language Reference
Day 123: Declarations (cont.)
- Special Kinds of Parameters: parameters can be ignored, take a variable number of values, and provide default values.
- An underscore (
_
) parameter is explicitly ignored and can’t be accessed within the body of the function. - A parameter with a base type name followed immediately by three dots (
...
) is understood as a variadic parameter. A function can have at most one variadic parameter. A variadic parameter is treated as an array that contains elements of the base type name. - A parameter with an equals sign (
=
) and an expression after its type is understood to have a default value of the given expression. The given expression is evaluated when the function is called. If the parameter is omitted when calling the function, the default value is used instead.
- An underscore (
- Special Kinds of Methods: methods on an enumeration or a structure that modify
self
must be marked with themutating
declaration modifier.- Methods that override a superclass method must be marked with the
override
declaration modifier. - Methods associated with a type rather than an instance of a type must be marked with the
static
declaration modifier for enumerations and structures, or with either thestatic
orclass
declaration modifier for classes.
- Methods that override a superclass method must be marked with the
- Throwing Functions and Methods: functions and methods that can throw an error must be marked with the
throws
keyword. These functions and methods are known as throwing functions and throwing methods.
Calls to a throwing function or method must be wrapped in atry
ortry!
expression. - Rethrowing Functions and Methods: a function or method can be declared with the
rethrows
keyword to indicate that itthrows
an error only if one of its function parameters throws an error. These functions and methods are known as rethrowing functions and rethrowing methods.
A rethrowing function or method can contain a throw statement only inside acatch
clause. - Functions that Never Return: Swift defines a
Never
type, which indicates that a function or method doesn’t return to its caller. Functions and methods with theNever
return type are called nonreturning. Nonreturning functions and methods either cause an irrecoverable error or begin a sequence of work that continues indefinitely.
Enumeration Declaration
An enumeration declaration introduces a named enumeration type into our program.Enumeration declarations have two basic forms and are declared using the enum
keyword. The body of an enumeration declared using either form contains zero or more values—called enumeration cases—and any number of declarations, including computed properties, instance methods, type methods, initializers, type aliases, and even other enumeration, structure, and class declarations. Enumeration declarations can’t contain deinitializer or protocol declarations.
Enumeration types can adopt any number of protocols, but can’t inherit from classes, structures, or other enumerations.
Like structures but unlike classes, enumerations are value types
- Enumerations with Cases of Any Type:
In this form, each case block consists of thecase
keyword followed by one or more enumeration cases, separated by commas. The name of each case must be unique. Each case can also specify that it stores values of a given type. These types are specified in the associated value types tuple, immediately following the name of the case.
Enumeration cases that store associated values can be used as functions that create instances of the enumeration with the specified associated values. - Enumerations with Indirection: enumerations can have a recursive structure, that is, they can have cases with associated values that are instances of the enumeration type itself. However, instances of enumeration types have value semantics, which means they have a fixed layout in memory. To support recursion, the compiler must insert a layer of indirection. To enable indirection for a particular enumeration case, we need to mark it with the
indirect
declaration modifier. An indirect case must have an associated value. - Enumerations with Cases of a Raw-Value Type:
In this form, each case block consists of the case keyword, followed by one or more enumeration cases, separated by commas. Unlike the cases in the first form, each case has an underlying value, called a raw value, of the same basic type. The type of these values is specified in the raw-value type and must represent an integer, floating-point number, string, or single character.
If the raw-value type is specified asInt
and we don’t assign a value to the cases explicitly, they are implicitly assigned the values 0, 1, 2, and so on. If the raw-value type is specified asString
and we don’t assign values to the cases explicitly, each unassigned case is implicitly assigned a string with the same text as the name of that case. - Accessing Enumeration Cases: to reference the case of an enumeration type, we must use dot (
.
) syntax, as inEnumerationType.enumerationCase
. When the enumeration type can be inferred from context, we can omit it (the dot is still required).
Structure Declaration
A structure declaration introduces a named structure type into our program. The body of a structure contains zero or more declarations. Structure types can adopt any number of protocols, but can’t inherit from classes, enumerations, or other structures. There are three ways to create an instance of a previously declared structure:
- Call one of the initialisers declared within the structure
- If no initialisers are declared, call the structure’s memberwise initialiser,
- If no initialisers are declared, and all properties of the structure declaration were given initial values, call the structure’s default initialiser.
Class Declaration
A class declaration introduces a named class type into our program. The body of a class contains zero or more declarations. A class type can inherit from only one parent class, its superclass, but can adopt any number of protocols. The superclass appears first after the class name and colon, followed by any adopted protocols. Generic classes can inherit from other generic and nongeneric classes, but a nongeneric class can inherit only from other nongeneric classes.
Classes can have designated and convenience initialisers. The designated initialiser of a class must initialise all of the class’s declared properties and it must do so before calling any of its superclass’s designated initialisers.
A class can override properties, methods, subscripts, and initialisers of its superclass. Overridden properties, methods, subscripts, and designated initialisers must be marked with the override
declaration modifier.
To require that subclasses implement a superclass’s initialiser, we need to mark the superclass’s initialiser with the required
declaration modifier. The subclass’s implementation of that initialiser must also be marked with the required
declaration modifier.
There are two ways to create an instance of a previously declared class:
- Call one of the initialisers declared within the class.
- If no initialisers are declared, and all properties of the class declaration were given initial values, call the class’s default initialiser.
Protocol Declaration
The body of a protocol contains zero or more protocol member declarations, which describe the conformance requirements that any type adopting the protocol must fulfil. In particular, a protocol can declare that conforming types must implement certain properties, methods, initialisers, and subscripts. Protocols can also declare special kinds of type aliases, called associated types, that can specify relationships among the various declarations of the protocol. Protocol declarations can’t contain class, structure, enumeration, or other protocol declarations.
Protocol types can inherit from any number of other protocols. When a protocol type inherits from other protocols, the set of requirements from those other protocols are aggregated, and any type that inherits from the current protocol must conform to all those requirements.
We can add protocol conformance to a previously declared type by adopting the protocol in an extension declaration of that type.
To restrict the adoption of a protocol to class types only, include the AnyObject
protocol in the inherited protocols list after the colon.
- Protocol Property Declaration: protocols declare that conforming types must implement a property by including a protocol property declaration in the body of the protocol declaration. Protocol property declarations have a special form of a variable declaration:
To declare a type property requirement in a protocol declaration, mark the property declaration with thestatic
keyword. - Protocol Method Declaration: protocols declare that conforming types must implement a method by including a protocol method declaration in the body of the protocol declaration. Protocol method declarations have the same form as function declarations, with two exceptions: they don’t include a function body, and you can’t provide any default parameter values as part of the function declaration. To declare a class or static method requirement in a protocol declaration, mark the method declaration with the
static
declaration modifier. - Protocol Initialiser Declaration: protocols declare that conforming types must implement an initialiser by including a protocol initialiser declaration in the body of the protocol declaration. Protocol initialiser declarations have the same form as initialiser declarations, except they don’t include the initialiser’s body. When a class implements an initialiser to satisfy a protocol’s initialiser requirement, the initialiser must be marked with the
required
declaration modifier if the class is not already marked with thefinal
declaration modifier. - Protocol Subscript Declaration: protocols declare that conforming types must implement a subscript by including a protocol subscript declaration in the body of the protocol declaration. Protocol subscript declarations have a special form of a subscript declaration:
- Protocol Associated Type Declaration: protocols declare associated types using the
associatedtype
keyword. An associated type provides an alias for a type that is used as part of a protocol’s declaration. Associated types are similar to type parameters in generic parameter clauses, but they’re associated withSelf
in the protocol in which they’re declared. In that context, Self refers to the eventual type that conforms to the protocol.
Initialiser Declaration
An initialiser declaration introduces an initialiser for a class, structure, or enumeration into our program. Initializer declarations are declared using the init
keyword and have two basic forms.
The following form declares initialisers for structures, enumerations, and designated initialisers of classes:
init(parameters) {
statements
}
To declare convenience initialisers for a class, we must mark the initialiser declaration with the convenience
declaration modifier. Convenience initialisers can delegate the initialisation process to another convenience initialiser or to one of the class’s designated initialisers. Convenience initialisers can’t call a superclass’s initialisers.
We can mark designated and convenience initialisers with the required
declaration modifier to require that every subclass implement the initialiser. A subclass’s implementation of that initialiser must also be marked with the required
declaration modifier.
Just like functions and methods, initialisers can throw or rethrow errors.
- Failable Initialisers: a failable initialiser is a type of initialiser that produces an optional instance or an implicitly unwrapped optional instance of the type the initialiser is declared on. As a result, a failable initialiser can return nil to indicate that initialisation failed. To declare a failable initialiser that produces an optional instance, we need to append a question mark to the
init
keyword in the initialiser declaration (init?
). To declare a failable initialiser that produces an implicitly unwrapped optional instance, append an exclamation mark instead (init!
).
Deinitialiser Declaration
A deinitialiser declaration declares a deinitialiser for a class type. Deinitialisers take no parameters and have the following form:
deinit {
statements
}
A deinitialiser is called automatically when there are no longer any references to a class object, just before the class object is deallocated. A deinitialiser can be declared only in the body of a class declaration—but not in an extension of a class—and each class can have at most one.
Extension Declaration
An extension declaration allows us to extend the behaviur of existing types. Extension declarations are declared using the extension
keyword and have the following form:
extension type name where requirements {
declarations
}
The body of an extension declaration contains zero or more declarations. Extension declarations can’t contain deinitialiser or protocol declarations, stored properties, property observers, or other extension declarations. Declarations in a protocol extension can’t be marked final
.
If the type name is a class, structure, or enumeration type, the extension extends that type. If the type name is a protocol type, the extension extends all types that conform to that protocol.
Extension declarations that extend a generic type or a protocol with associated types can include requirements. Extension declarations can contain initialiser declarations.
Properties, methods, and initialisers of an existing type can’t be overridden in an extension of that type. Extension declarations can’t add class inheritance to an existing class, and therefore we can specify only a list of protocols after the type name and colon.
- Conditional Conformance: we can extend a generic type to conditionally conform to a protocol, so that instances of the type conform to the protocol only when certain requirements are met. We add conditional conformance to a protocol by including requirements in an extension declaration.
- Overridden Requirements Aren’t Used in Some Generic Contexts: in some generic contexts, types that get behaviour from conditional conformance to a protocol don’t always use the specialised implementations of that protocol’s requirements.
- Protocol Conformance Must Not Be Redundant: a concrete type can conform to a particular protocol only once. Swift marks redundant protocol conformances as an error.
- Resolving Explicit Redundancy: multiple extensions on a concrete type can’t add conformance to the same protocol, even if the extensions’ requirements are mutually exclusive.
- Resolving Implicit Redundancy: when a concrete type conditionally conforms to a protocol, that type implicitly conforms to any parent protocols with the same requirements. If we need a type to conditionally conform to two protocols that inherit from a single parent, explicitly declare conformance to the parent protocol. This avoids implicitly conforming to the parent protocol twice with different requirements.
Subscript Declaration
A subscript declaration allows us to add subscripting support for objects of a particular type and are typically used to provide a convenient syntax for accessing the elements in a collection, list, or sequence. Subscript declarations are declared using the subscript
keyword and have the following form:
subscript (parameters) -> return type {
get {
statements
}
set(setter name) {
statements
}
}
Subscript declarations can appear only in the context of a class, structure, enumeration, extension, or protocol declaration.
The parameters specify one or more indexes used to access elements of the corresponding type in a subscript expression. The return type specifies the type of the element being accessed.
As with computed properties, subscript declarations support reading and writing the value of the accessed elements. The getter is used to read the value, and the setter is used to write the value.
- Type Subscript Declarations: to declare a subscript that’s exposed by the type, rather than by instances of the type, mark the subscript declaration with the
static
declaration modifier. Classes can mark type computed properties with theclass
declaration modifier instead to allow subclasses to override the superclass’s implementation.
Operator Declaration
An operator declaration introduces a new infix, prefix, or postfix operator into your program and is declared using the operator
keyword.
You can declare operators of three different fixities: infix, prefix, and postfix. The fixity of an operator specifies the relative position of an operator to its operands. The fixity of the operator is specified by marking the operator declaration with the infix
, prefix
, or postfix
declaration modifier before the operator
keyword.
- Infix Operators:
infix operator operatorName: precedenceGroup
. An infix operator is a binary operator that is written between its two operands. Infix operators can optionally specify a precedence group. - Prefix Operators:
prefix operator operatorName
. A prefix operator is a unary operator that is written immediately before its operand. Prefix operators declarations don’t specify a precedence level. Prefix operators are nonassociative. - Postfix Operators:
postfix operator operatorName
. A postfix operator is a unary operator that is written immediately after its operand. As with prefix operators, postfix operator declarations don’t specify a precedence level. Postfix operators are nonassociative.
After declaring a new operator, we must implement it by declaring a static method that has the same name as the operator.
Precedence Group Declaration
A precedence group declaration introduces a new grouping for infix operator precedence into your program. The precedence of an operator specifies how tightly the operator binds to its operands, in the absence of grouping parentheses.
precedencegroup precedenceGroupName {
higherThan: lowerGroupNames
lowerThan: higherGroupNames
associativity: associativity
assignment: assignment
}
The lower group names and higher group names lists specify the new precedence group’s relation to existing precedence groups. The lowerThan
precedence group attribute may only be used to refer to precedence groups declared outside of the current module.
The associativity of an operator specifies how a sequence of operators with the same precedence level are grouped together in the absence of grouping parentheses. We specify the associativity of an operator by writing one of the context-sensitive keywords left
, right
, or none
—if we omit the associativity, the default is none
.
Declaration Modifiers
Declaration modifiers are keywords or context-sensitive keywords that modify the behaviour or meaning of a declaration.
class
: we need to apply this modifier to a member of a class to indicate that the member is a member of the class itself, rather than a member or instances of the class. Members of a superclass that have this modifier and don’t have the final modifier can be overridden by subclasses.dynamic
: we need to apply this modifier to any member of a class that can be represented by Objective-C. Declarations must be marked with the@objc
attribute.final
: we need to apply this modifier to a class or to a property, method, or subscript member of a class. It’s applied to a class to indicate that the class can’t be subclassed, while it’s applied to a property, method, or subscript of a class to indicate that a class member can’t be overridden in any subclass.lazy
: we need to apply this modifier to a stored variable property of a class or structure to indicate that the property’s initial value is calculated and stored at most once, when the property is first accessed.optional
: we need to apply this modifier to a protocol’s property, method, or subscript members to indicate that a conforming type isn’t required to implement those members.required
: we need to apply this modifier to a designated or convenience initialiser of a class to indicate that every subclass must implement that initialiser. The subclass’s implementation of that initialiser must also be marked with therequired
modifier.static
: we need to apply this modifier to a member of a structure, class, enumeration, or protocol to indicate that the member is a member of the type, rather than a member of instances of that type.unowned
: we need to apply this modifier to a stored variable, constant, or stored property to indicate that the variable or property has an unowned reference to the object stored as its value.The type of the property or value must be a class type.unowned(safe)
: an explicit spelling ofunowned
.unowned(unsafe)
: exactly asunowned
above but if we try to access the variable or property after the object has been deallocated, we will access the memory at the location where the object used to be, which is a memory-unsafe operation.weak
: we need to apply this modifier to a stored variable or stored variable property to indicate that the variable or property has a weak reference to the object stored as its value. The type of the variable or property must be an optional class type. If we access the variable or property after the object has been deallocated, its value isnil
.
Access Control Levels
Swift provides five levels of access control: open, public, internal, file private, and private.
open
: apply this modifier to a declaration to indicate the declaration can be accessed and subclassed by code in the same module as the declaration. Declarations marked with the open access-level modifier can also be accessed and subclassed by code in a module that imports the module that contains that declaration.public
: same as open but declarations marked with the public access-level modifier can also be accessed (but not subclassed) by code in a module that imports the module that contains that declaration.internal
: apply this modifier to a declaration to indicate the declaration can be accessed only by code in the same module as the declaration. By default, most declarations are implicitly marked with the internal access-level modifier.fileprivate
: apply this modifier to a declaration to indicate the declaration can be accessed only by code in the same source file as the declaration.private
: apply this modifier to a declaration to indicate the declaration can be accessed only by code within the declaration’s immediate enclosing scope.
Day 124
Attributes
There are two kinds of attributes in Swift—those that apply to declarations and those that apply to types. An attribute provides additional information about the declaration or type.
We specify an attribute by writing the @
symbol followed by the attribute’s name and any arguments that the attribute accepts:
@attributeName
@attributeName(attribute arguments)
Declaration Attributes
We can apply a declaration attribute to declarations only.
- available: apply this attribute to indicate a declaration’s life cycle relative to certain Swift language versions or certain platforms and operating system versions. The remaining arguments can appear in any order and specify additional information about the declaration’s life cycle, including important milestones.
- The
unavailable
argument indicates that the declaration isn’t available on the specified platform. - The
introduced
argument indicates the first version of the specified platform or language in which the declaration was introduced. - The
deprecated
argument indicates the first version of the specified platform or language in which the declaration was deprecated. - The
obsoleted
argument indicates the first version of the specified platform or language in which the declaration was obsoleted. When a declaration is obsoleted, it’s removed from the specified platform or language and can no longer be used. - The
message
argument provides a textual message that the compiler displays when emitting a warning or error about the use of a deprecated or obsoleted declaration. - The
renamed
argument provides a textual message that indicates the new name for a declaration that’s been renamed.
a`vailable
attribute only specifies an introduced argument in addition to a platform or language name argument, we can use the following shorthand syntax instead: - The
- discardableResult: apply this attribute to a function or method declaration to suppress the compiler warning when the function or method that returns a value is called without using its result.
- dynamicCallable: apply this attribute to a class, structure, enumeration, or protocol to treat instances of the type as callable functions. The type must implement either a
dynamicallyCall(withArguments:)
method, adynamicallyCall(withKeywordArguments:)
method, or both. - dynamicMemberLookup: apply this attribute to a class, structure, enumeration, or protocol to enable members to be looked up by name at runtime. The type must implement a
subscript(dynamicMemberLookup:)
subscript.- Dynamic member lookup by member name can be used to create a wrapper type around data that can’t be type checked at compile time, such as when bridging data from other languages into Swift.
- GKInspectable: apply this attribute to expose a custom GameplayKit component property to the SpriteKit editor UI. Applying this attribute also implies the
objc
attribute. - inlinable: apply this attribute to a function, method, computed property, subscript, convenience initialiser, or deinitialiser declaration to expose that declaration’s implementation as part of the module’s public interface. The compiler is allowed to replace calls to an inlinable symbol with a copy of the symbol’s implementation at the call site.
- nonobjc: apply this attribute to a method, property, subscript, or initialiser declaration to suppress an implicit
objc
attribute. Thenonobjc
attribute tells the compiler to make the declaration unavailable in Objective-C code, even though it’s possible to represent it in Objective-C. - NSApplicationMain: apply this attribute to a class to indicate that it’s the application delegate. Using this attribute is equivalent to calling the
NSApplicationMain(_:_:)
function. - NSCopying: apply this attribute to a stored variable property of a class. This attribute causes the property’s setter to be synthesised with a copy of the property’s value—returned by the
copyWithZone(_:)
method—instead of the value of the property itself. The type of the property must conform to theNSCopying
protocol. - NSManaged: apply this attribute to an instance method or stored variable property of a class that inherits from
NSManagedObject
to indicate that Core Data dynamically provides its implementation at runtime, based on the associated entity description. For a property marked with theNSManaged
attribute, Core Data also provides the storage at runtime. Applying this attribute also implies theobjc
attribute. - objc: apply this attribute to any declaration that can be represented in Objective-C. The
objc
attribute tells the compiler that a declaration is available to use in Objective-C code.- Applying this attribute to an extension has the same effect as applying it to every member of that extension that isn’t explicitly marked with the
nonobjc
attribute. - If we apply the
objc
attribute to an enumeration, each enumeration case is exposed to Objective-C code as the concatenation of the enumeration name and the case name.
- Applying this attribute to an extension has the same effect as applying it to every member of that extension that isn’t explicitly marked with the
- objcMembers: apply this attribute to a class declaration, to implicitly apply the objc attribute to all Objective-C compatible members of the class, its extensions, its subclasses, and all of the extensions of its subclasses.
- requires_stored_property_inits: apply this attribute to a class declaration to require all stored properties within the class to provide default values as part of their definitions. This attribute is inferred for any class that inherits from
NSManagedObject
. - testable: apply this attribute to an import declaration to import that module with changes to its access control that simplify testing the module’s code.
- UIApplicationMain: apply this attribute to a class to indicate that it’s the application delegate. Using this attribute is equivalent to calling the
UIApplicationMain
function and passing this class’s name as the name of the delegate class. - usableFromInline: apply this attribute to a function, method, computed property, subscript, initialiser, or deinitialiser declaration to allow that symbol to be used in inlinable code that’s defined in the same module as the declaration. The declaration must have the
internal
access level modifier. - warn_unqualified_access: apply this attribute to a top-level function, instance method, or class or static method to trigger warnings when that function or method is used without a preceding qualifier, such as a module name, type name, or instance variable or constant. Use this attribute to help discourage ambiguity between functions with the same name that are accessible from the same scope.
- Declaration Attributes Used by Interface Builder: Interface Builder attributes are declaration attributes used by Interface Builder to synchronise with Xcode. Swift provides the following Interface Builder attributes:
IBAction
,IBSegueAction
,IBOutlet
,IBDesignable
, andIBInspectable
. These attributes are conceptually the same as their Objective-C counterparts.
Type Attributes
We can apply type attributes to types only.
- autoclosure: apply this attribute to delay the evaluation of an expression by automatically wrapping that expression in a closure with no arguments.
- convention: apply this attribute to the type of a function to indicate its calling conventions. The
convention
attribute always appears with one of the following arguments:- The
swift
argument indicates a Swift function reference. - The
block
argument indicates an Objective-C compatible block reference. - The
c
argument indicates a C function reference.
- The
- escaping: apply this attribute to a parameter’s type in a method or function declaration to indicate that the parameter’s value can be stored for later execution. This means that the value is allowed to outlive the lifetime of the call. Function type parameters with the escaping type attribute require explicit use of
self.
for properties or methods.
Switch Case Attributes
We can apply switch case attributes to switch cases only.
- unknown: apply this attribute to a switch case to indicate that it isn’t expected to be matched by any case of the enumeration that’s known at the time the code is compiled.
Patterns
A pattern represents the structure of a single value or a composite value. Because patterns represent the structure of a value rather than any one particular value, we can match them with a variety of values. In addition to matching a pattern with a value, we can extract part or all of a composite value and bind each part to a constant or variable name.
In Swift, there are two basic kinds of patterns: those that successfully match any kind of value, and those that may fail to match a specified value at runtime.
The first kind of pattern is used for destructuring values in simple variable, constant, and optional bindings. The second kind of pattern is used for full pattern matching, where the values we are trying to match against may not be there at runtime.
Wildcard Pattern
A wildcard pattern matches and ignores any value and consists of an underscore (_
). We can use a wildcard pattern when we don’t care about the values being matched against.
Identifier Pattern
An identifier pattern matches any value and binds the matched value to a variable or constant name.
Value-Binding Pattern
A value-binding pattern binds matched values to variable or constant names. Value-binding patterns that bind a matched value to the name of a constant begin with the let
keyword; those that bind to the name of variable begin with the var
keyword.
Tuple Pattern
A tuple pattern is a comma-separated list of zero or more patterns, enclosed in parentheses. Tuple patterns match values of corresponding tuple types.
Enumeration Case Pattern
An enumeration case pattern matches a case of an existing enumeration type. Enumeration case patterns appear in switch statement case labels and in the case conditions of if
, while
, guard
, and for-in
statements.
Optional Pattern
An optional pattern matches values wrapped in a some(Wrapped)
case of an Optional<Wrapped>
enumeration. Optional patterns consist of an identifier pattern followed immediately by a question mark and appear in the same places as enumeration case patterns.
Type-Casting Patterns
There are two type-casting patterns, the is
pattern and the as
pattern. The is
pattern appears only in switch
statement case labels.
The is
pattern matches a value if the type of that value at runtime is the same as the type specified in the right-hand side of the is
pattern—or a subclass of that type.
The as
pattern matches a value if the type of that value at runtime is the same as the type specified in the right-hand side of the as
pattern—or a subclass of that type. If the match succeeds, the type of the matched value is cast to the pattern specified in the right-hand side of the as pattern.
Expression Pattern
An expression pattern represents the value of an expression. Expression patterns appear only in switch statement case labels.
Generic Parameters and Arguments
Generic Parameter Clause
A generic parameter clause specifies the type parameters of a generic type or function, along with any associated constraints and requirements on those parameters. A generic parameter clause is enclosed in angle brackets (<>
).
The generic parameter list is a comma-separated list of generic parameters, each of which has the following form: typeParameter: constraint
.
A generic parameter consists of a type parameter followed by an optional constraint. A type parameter is simply the name of a placeholder type.
The constraint specifies that a type parameter inherits from a specific class or conforms to a protocol or protocol composition.
- Generic Where Clauses: we can specify additional requirements on type parameters and their associated types by including a generic
where
clause right before the opening curly brace of a type or function’s body. A genericwhere
clause consists of thewhere
keyword, followed by a comma-separated list of one or more requirements. The requirements in a genericwhere
clause specify that a type parameter inherits from a class or conforms to a protocol or protocol composition.
Generic Argument Clause
A generic argument clause specifies the type arguments of a generic type. A generic argument clause is enclosed in angle brackets (<>). The generic argument list is a comma-separated list of type arguments. A type argument is the name of an actual concrete type that replaces a corresponding type parameter in the generic parameter clause of a generic type. The result is a specialised version of that generic type. Each type argument must satisfy all the constraints of the generic parameter it replaces, including any additional requirements specified in a generic where
clause.
And that’s it!
Now back to app building!
If you like what I’m doing here please consider liking this article and sharing it with some of your peers. If you are feeling like being really awesome, please consider making a small donation to support my studies and my writing (please appreciate that I am not using advertisement on my articles).
If you are interested in my music engraving and my publications don’t forget visit my Facebook page and the pages where I publish my scores (Gumroad, SheetMusicPlus, ScoreExchange and on Apple Books).
You can also support me by buying Paul Hudson’s books from this Affiliate Link.
Anyways, thank you so much for reading!
Till the next one!