Protocols provide a blueprint for Methods, properties and other requirements functionality. It is just described as a methods or properties skeleton instead of implementation. Methods and properties implementation can further be done by defining classes, functions and enumerations. Conformance of a protocol is defined as the methods or properties satisfying the requirements of the protocol.
Syntax
Protocols also follow the similar syntax as that of classes, structures, and enumerations −
protocol SomeProtocol {
// protocol definition
}
Protocols are declared after the class, structure or enumeration type names. Single and Multiple protocol declarations are also possible. If multiple protocols are defined they have to be separated by commas.
When a protocol has to be defined for super class, the protocol name should follow the super class name with a comma.
class SomeClass: SomeSuperclass, Protocol1, Protocol2 {
// class definition
}
Property and Method Requirements
Protocol is used to specify particular class type property or instance property. It just specifies the type or instance property alone rather than specifying whether it is a stored or computed property. Also, it is used to specify whether the property is ‘gettable’ or ‘settable’.
Property requirements are declared by ‘var’ keyword as property variables. {get set} is used to declare gettable and settable properties after their type declaration. Gettable is mentioned by {get} property after their type declaration.
protocol classa {
var marks: Int { get set }
var result: Bool { get }
func attendance() -> String
func markssecured() -> String
}
protocol classb: classa {
var present: Bool { get set }
var subject: String { get set }
var stname: String { get set }
}
class classc: classb {
var marks = 96
let result = true
var present = false
var subject = "Swift 4 Protocols"
var stname = "Protocols"
func attendance() -> String {
return "The (stname) has secured 99% attendance"
}
func markssecured() -> String {
return "(stname) has scored (marks)"
}
}
let studdet = classc()
studdet.stname = "Swift 4"
studdet.marks = 98
studdet.markssecured()
print(studdet.marks)
print(studdet.result)
print(studdet.present)
print(studdet.subject)
print(studdet.stname)
When we run the above program using playground, we get the following result −
98
true
false
Swift 4 Protocols
Swift 4
Mutating Method Requirements
protocol daysofaweek {
mutating func print()
}
enum days: daysofaweek {
case sun, mon, tue, wed, thurs, fri, sat
mutating func print() {
switch self {
case sun:
self = sun
print("Sunday")
case mon:
self = mon
print("Monday")
case tue:
self = tue
print("Tuesday")
case wed:
self = wed
print("Wednesday")
case mon:
self = thurs
print("Thursday")
case tue:
self = fri
print("Friday")
case sat:
self = sat
print("Saturday")
default:
print("NO Such Day")
}
}
}
var res = days.wed
res.print()
When we run the above program using playground, we get the following result −
Wednesday
Initializer Requirements
Swing allows the user to initialize protocols to follow type conformance similar to that of normal initializers.
Protocol conformance is ensured on all subclasses for explicit or inherited implementation by ‘required’ modifier. (protocol의 initializer는 모든 subclasses에서 해당 initializer가 implemented되어야 하므로 protocol을 수행하는 첫 superclass에서는 required를 사용해야 한다는 내용으로 이해)
When a subclass overrides its super class initialization requirement it is specified by the ‘override’ modifier keyword.
In Swift initializers are not inherited for subclasses by default. If you want to provide the same initializer for a subclass that the parent class already has, you have to use the override keyword. )
protocol tcpprotocol {
init(no1: Int)
}
class mainClass {
var no1: Int // local storage
init(no1: Int) {
self.no1 = no1 // initialization
}
}
class subClass: mainClass, tcpprotocol {
var no2: Int
init(no1: Int, no2 : Int) {
self.no2 = no2
super.init(no1:no1)
}
// Requires only one parameter for convenient method
required override convenience init(no1: Int) {
self.init(no1:no1, no2:0)
}
}
let res = mainClass(no1: 20)
let print = subClass(no1: 30, no2: 50)
print("res is: (res.no1)")
print("res is: (print.no1)")
print("res is: (print.no2)")
When we run the above program using playground, we get the following result −
A protocol defines a blueprint of methods, properties, and other requirements that suit a particular task or piece of functionality. The protocol can then be adopted by a class, structure, or enumeration to provide an actual implementation of those requirements. Any type that satisfies the requirements of a protocol is said to conform to that protocol.
So, keeping it simple, a protocol says a struct , class or enum that if you want to be THAT, do THIS, this and THis. Example: if you want to be a human, you have to EAT, SLEEP, and Take REST.
Protocol Syntax
Classes , structs, enums can adopt these protocol by placing protocol’s name after the type’s name, separated by a colon, as part of their definition. Multiple protocols can be listed, and are separated by commas:
If a class has a superclass, list the superclass name before any protocols it adopts, followed by a comma:
You might have already seen UIViewControllers implementing UITableviewdatasource and delegate protocols.
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { }
Although, the best practice is to group this in a separate extension of ViewController and implement the protocols.
A protocol can have properties as well as methods that a class, enum or struct conforming to this protocol can implement.
A protocol declaration only specifies the required property name and type. It doesn’t say anything about whether the property should be a stored one or a computed one.
A protocol also specifies whether each property must be gettable or gettable and settable.
Property requirements are always declared as variable properties, prefixed with the var keyword.
Gettable and settable properties are indicated by writing { get set }after their type declaration, and gettable properties are indicated by writing { get }.
Note:
A { get set } property cannot be a constant stored property. It should be a computed property and both get and set should be implemented.
A { get } property can be any kind of property, and it is valid for the property to be also settable if required.
Stored and computed properties are usually associated with instances of a particular type. However, properties can also be associated with the type itself. Such properties are known as type properties.
Always prefix type property requirements with the static keyword when you define them in a protocol. This rule pertains even though type property requirements can be prefixed with the class or static keyword when implemented by a class:
What does confirming to a protocol means?
As explained earlier, a protocol says that you need to do few things in order to become something.
Example: if you want to become a bird, you should fly . Or you should confirm to Flyable protocol.
Consider the following example:
Here, we declared a protocol named FullyNamed . It has a gettable stored property called fullName of type string .
Now, we are defining a struct called Person and we are saying that the struct confirms to the FullyNamed protocol. This means that we should implement the fullName string variable inside the struct defined. Otherwise it will throw an error on us.
We can also define the fullName property as a computed property.
Method Requirements
As mentioned earlier, protocols can have methods as well.
A protocol can have type methods or instance methods.
Methods are declared in exactly the same way as for normal instance and type methods, but without curly braces or a method body.
Variadic parameters are allowed.
Default values are not allowed.
As with type property requirements, you always prefix type method requirements with the static keyword when they are defined in a protocol. This is true even though type method requirements are prefixed with the class or static keyword when implemented by a class:
Consider the following example. These are self explanatory I believe:
Note: In the above class implementation. someStaticMethod(variadicParam:)method is defined as a class method. In the protocol declaration, it is defined as static method.
(참고사항 tumblr #swift #mutating #enum: Structures and enumerations are value types. By default, the properties of a value type cannot be modified from within its instance methods.)
Mutating methods are methods that we use on value types like structs and enums. These methods are allowed to modify the instance it belongs to and any properties of that instance. A small example:
Consider a simple struct Rectangle:
Coming back to mutating method requirements:
If you define a protocol instance method requirement that is intended to mutate instances of any type that adopts the protocol, mark the method with the mutating keyword as part of the protocol’s definition.
If you mark a protocol instance method requirement as mutating, you do not need to write the mutatingkeyword when writing an implementation of that method for a class. The mutating keyword is only used by structures and enumerations.
Consider an enum and class implementing a protocol with mutating function:
(protocol정의부분에서 mutating으로 설정한경우 이 protocol을 따르는 structures, enumerations 에서는 반드시 mutating을 써주어야 하지만 class의 경우는 생략가능하다는 이야기)
Initializer Requirements
Protocols can have specific initializers like normal methods which the confirming types can implement.
Class Implementations of Protocol Initializer Requirements
You can implement a protocol initializer requirement on a conforming class as either a designated initializer or a convenience initializer. In both cases, you must mark the initializer implementation with the requiredmodifier:
If a subclass overrides a designated initializer from a superclass, and also implements a matching initializer requirement from a protocol, mark the initializer implementation with both the required and overridemodifiers:
protocol SomeProtocol { init() } class SomeSuperClass { init() { // initializer implementation goes here } } class SomeSubClass: SomeSuperClass, SomeProtocol { // “required” from SomeProtocol conformance; “override” from SomeSuperClass required override init() { // initializer implementation goes here } }
Failable Initializer Requirements
Protocols can have failable initializers. A failable initializer requirement can be satisfied by a failable or nonfailable initializer on a conforming type. A nonfailable initializer requirement can be satisfied by a nonfailable initializer or an implicitly unwrapped failable initializer.
(참고사항 init? 이런 형태의 initializer를 말하며 initialization하는 과정에서 에러발생하면 instance가 생기지 않게된다. )
Protocol is a type. You can use it in many places like:
As a parameter type or return type in a function, method, or initializer
As the type of a constant, variable, or property
As the type of items in an array, dictionary, or other container
Because protocols are types, begin their names with a capital letter to match the names of other types in Swift (such as Int, String, and Double).
Delegation
Delegation is a design pattern that enables a class or structure to hand off (or delegate) some of its responsibilities to an instance of another type. Delegation pattern can also be used as a callback kind of mechanism.
The following code is self explanatory for a swift developer with intermediate level of knowledge. If you have any doubt , please comment below.
Adding Protocol Conformance with an Extension
You can extend an existing type to adopt and conform to a new protocol, even if you do not have access to the source code for the existing type. Extensions can add new properties, methods, and subscripts to an existing type, and are therefore able to add any requirements that a protocol may demand.
Declaring Protocol Adoption with an Extension
If a type already conforms to all of the requirements of a protocol, but has not yet stated that it adopts that protocol, you can make it adopt the protocol with an empty extension:
Types do not automatically adopt a protocol just by satisfying its requirements. They must always explicitly declare their adoption of the protocol.
Consider the class Animal. It has an optional Int? computed property called age . This makes the class confirming to the protocol Growable . So inorder to use the class anywhere where the required type should confirm to the protocol, use a simple empty extension of Animal class confirming to the Growable protocol.
Collections of Protocol Types
Protocols can be used as a type to be stored in collection types like array or dictionary.
Since, Animal and Human confirms to the Growable protocol, they can be stored in an array of type Growable .
Protocol Inheritance
A protocol can inherit one or more other protocols. The syntax of protocol inheritance is similar to class inheritance.
Suppose there is a struct conforming to the Inheriting protocol, it should also confirm and satisfy all the other protocols that Inheriting protocol inherits from.
Class-Only Protocols
You can limit protocol adoption to class types (and not structures or enumerations) by adding the AnyObject or class protocol to a protocol’s inheritance list.
In the example above, SomeClassOnlyProtocol can only be adopted by class types. It is a compile-time error to write a structure or enumeration definition that tries to adopt SomeClassOnlyProtocol.
If a non — class type tries to confirm to this protocol, the compiler will show error like:
error: non-class type ‘XXXXXXXX’ cannot conform to class protocol “YYYYYY.”
Protocol Composition
(protocol은 type이며 composited protocol은 두개protocol이 합쳐진 type이다.)
Sometimes it is required for a type to confirm to multiple protocols. Image a function which accepts a parameter which should confirm to multiple protocols.
You can combine multiple protocols into a single requirement with a protocol composition. Protocol compositions behave like you defined a temporary local protocol that has the combined requirements of all protocols in the composition. Protocol compositions don’t define any new protocol types.
In swift 3 & 4, protocol compositions have the form SomeProtocol & AnotherProtocol. You can list as many protocols as you need to, separating them by ampersands (&). In addition to its list of protocols, in swift 4, a protocol composition can also contain one class type, which lets you specify a required superclass. This is not possible in swift 3.
Note: Swift 3 syntax for protocol composition is same. But it doesn’t allow us to add Class name along with the protocols using & sign. The playground is throwing error when I tried to add the Animal class in the composition list.
Checking for Protocol Conformance
You can use the is and as operators described in Type Casting to check for protocol conformance, and to cast to a specific protocol. Checking for and casting to a protocol follows exactly the same syntax as checking for and casting to a type:
The is operator returns true if an instance conforms to a protocol and returns false if it does not.
The as? version of the downcast operator returns an optional value of the protocol’s type, and this value is nil if the instance does not conform to that protocol.
The as! version of the downcast operator forces the downcast to the protocol type and triggers a runtime error if the downcast does not succeed.
Optional Protocol Requirements
Protocol can have optional methods and properties.These requirements do not have to be implemented by types that conform to the protocol.
Optional requirements are prefixed by the optional modifier as part of the protocol’s definition. Optional requirements are available so that you can write code that interoperates with Objective-C. Both the protocol and the optional requirement must be marked with the @objc attribute. Note that @objc protocols can be adopted only by classes that inherit from Objective-C classes or other @objcclasses. They can’t be adopted by structures or enumerations.
First, we add @objc keyword in front of the protocol keyword. Then we add the @objc optional keyword in front of our optional method/variable.
When you use a method or property in an optional requirement, its type automatically becomes an optional.
Protocol Extensions
(보통 protocol의 실행 부분은 실제 이를 implementation하는 하위 class,structure,enumeration에서 이루어지나 protocol extension을 이용하면 기본 implementation을 지정할수 있다. )
Protocols can be extended to provide method and property implementations to conforming types. This allows you to define behavior on protocols themselves, rather than in each type’s individual conformance or in a global function.
Providing Default Implementations using protocol Extensions
Protocol extension can be used for providing default implementation within the protocol itself as explained in the previous section.
Important: If a conforming type provides its own implementation of a required method or property, that implementation will be used instead of the one provided by the extension. (protocol extension에서 지정된 내용은 하부 class,structure,enumeration에서 지정된 내용으로 override된다는 내용)
Adding Constraints to Protocol Extensions
(protocol extension지정시 conforming types 즉 implementing하는 하위 class, structure,enumeration이 특정 protocol인경우에만 extension이 되게하는 방법)
When you define a protocol extension, you can specify constraints that conforming types must satisfy before the methods and properties of the extension are available. You write these constraints after the name of the protocol you’re extending using a generic where clause.
In the above example, it says that the Array collection can implement the Equatableprotocol only when its elements are themselves Equatable .
Extensions add new functionality to an existing class, structure, enumeration, or protocol type. This includes the ability to extend types for which you do not have access to the original source code (known as retroactive modeling).
Extensions in Swift can:
Add computed instance properties and computed type properties (stored property는 추가 할수 없다.)
Define instance methods and type methods
Provide new initializers
Define subscripts
Define and use new nested types
Make an existing type conform to a protocol
In Swift, you can even extend a protocol to provide implementations of its requirements or add additional functionality that conforming types can take advantage of. For more details, see Protocol Extensions.
NOTE
Extensions can add new functionality to a type, but they cannot override existing functionality.
Extension Syntax
Declare extensions with the extension keyword:
extension SomeType { // new functionality to add to SomeType goes here }
An extension can extend an existing type to make it adopt one or more protocols. To add protocol conformance, you write the protocol names the same way as you write them for a class or structure:
extension SomeType: SomeProtocol, AnotherProtocol { // implementation of protocol requirements goes here }
If you define an extension to add new functionality to an existing type, the new functionality will be available on all existing instances of that type, even if they were created before the extension was defined
Computed Properties
Extensions can add computed instance properties and computed type properties to existing types. This example adds five computed instance properties to Swift’s built-in Double type, to provide basic support for working with distance units:
extension Double { var km: Double { return self * 1_000.0 } var m: Double { return self } var cm: Double { return self / 100.0 } var mm: Double { return self / 1_000.0 } var ft: Double { return self / 3.28084 } } let oneInch = 25.4.mm print(“One inch is (oneInch) meters”) // Prints “One inch is 0.0254 meters” let threeFeet = 3.ft print(“Three feet is (threeFeet) meters”) // Prints “Three feet is 0.914399970739201 meters”
let aMarathon = 42.km + 195.m print(“A marathon is (aMarathon) meters long”) // Prints “A marathon is 42195.0 meters long”
NOTE
Extensions can add new computed properties, but they cannot add stored properties, or add property observers to existing properties.
Initializers
Extensions can add new initializers to existing types.
Extensions can add new convenience initializers to a class, but they cannot add new designated initializers or deinitializers to a class. Designated initializers and deinitializers must always be provided by the original class implementation.
NOTE
If you use an extension to add an initializer to a value type that provides default values for all of its stored properties and does not define any custom initializers, you can call the default initializer and memberwise initializer for that value type from within your extension’s initializer.
This would not be the case if you had written the initializer as part of the value type’s original implementation, as described in Initializer Delegation for Value Types.
struct Size { var width = 0.0, height = 0.0 } struct Point { var x = 0.0, y = 0.0 } struct Rect { var origin = Point() var size = Size() }
let defaultRect = Rect() let memberwiseRect = Rect(origin: Point(x: 2.0, y: 2.0), size: Size(width: 5.0, height: 5.0))
You can extend the Rect structure to provide an additional initializer that takes a specific center point and size:
extension Rect { init(center: Point, size: Size) { let originX = center.x – (size.width / 2) let originY = center.y – (size.height / 2) self.init(origin: Point(x: originX, y: originY), size: size) } }
This new initializer starts by calculating an appropriate origin point based on the provided center point and size value. The initializer then calls the structure’s automatic memberwise initializer init(origin:size:), which stores the new origin and size values in the appropriate properties:
let centerRect = Rect(center: Point(x: 4.0, y: 4.0), size: Size(width: 3.0, height: 3.0)) // centerRect’s origin is (2.5, 2.5) and its size is (3.0, 3.0)
Methods
Extensions can add new instance methods and type methods to existing types. The following example adds a new instance method called repetitions to the Int type:
extension Int { func repetitions(task: () -> Void) {
Structures and enumerations are value types. By default, the properties of a value type cannot be modified from within its instance methods.)
Instance methods added with an extension can also modify (or mutate) the instance itself. Structure and enumeration methods that modify self or its properties must mark the instance method as mutating, just like mutating methods from an original implementation.
extension Int { mutating func square() { self = self * self } } var someInt = 3 someInt.square() // someInt is now 9
Subscripts
Extensions can add new subscripts to an existing type.
123456789[0] returns 9
123456789[1] returns 8
extension Int { subscript(digitIndex: Int) -> Int { var decimalBase = 1 for _ in 0..<digitIndex { decimalBase *= 10 } return (self / decimalBase) % 10 } } 746381295[0] // returns 5 746381295[1] // returns 9 746381295[2] // returns 2 746381295[8] // returns 7
746381295[9] // returns 0, as if you had requested: 0746381295[9]
Nested Types
Extensions can add new nested types to existing classes, structures, and enumerations:
extension Int { enum Kind { case negative, zero, positive } var kind: Kind { switch self { case 0: return .zero case let x where x > 0: return .positive default: return .negative } } }
func printIntegerKinds(_ numbers: [Int]) { for number in numbers { switch number.kind { case .negative: print(“- ”, terminator: “”) case .zero: print(“0 ”, terminator: “”) case .positive: print(“+ ”, terminator: “”) } } print(“”) } printIntegerKinds([3, 19, -27, 0, -6, 0, 7]) // Prints “+ + – 0 – 0 + ”
NOTE
number.kind is already known to be of type Int.Kind. Because of this, all of the Int.Kind case values can be written in shorthand form inside the switch statement, such as .negative rather than Int.Kind.negative
To nest a type within another type, write its definition within the outer braces of the type it supports. Types can be nested to as many levels as are required.
// nested Rank enumeration enum Rank: Int { case two = 2, three, four, five, six, seven, eight, nine, ten case jack, queen, king, ace struct Values { let first: Int, second: Int? } var values: Values { switch self { case .ace: return Values(first: 1, second: 11) case .jack, .queen, .king: return Values(first: 10, second: nil) default: return Values(first: self.rawValue, second: nil) } } }
// BlackjackCard properties and methods let rank: Rank, suit: Suit var description: String { var output = “suit is (suit.rawValue),” output += “ value is (rank.values.first)” if let second = rank.values.second { output += “ or (second)” } return output } }
let theAceOfSpades = BlackjackCard(rank: .ace, suit: .spades) print(“theAceOfSpades: (theAceOfSpades.description)”) // Prints “theAceOfSpades: suit is ♠, value is 1 or 11”
let heartsSymbol = BlackjackCard.Suit.hearts.rawValue // heartsSymbol is “♡”
This is the Optional Pattern. It tests and unwraps an Optional, executing the conditional only if the Optional is non-nil.
The keyword case is needed because it follows from the original switch…case syntax. The case tests a pattern and if it matches then the following statement is executed. In your example the let next? is the pattern. If the value is unwrapped and assigned then the case matches and your code is executed.
From the documentation:
Optional Pattern
An optional pattern matches values wrapped in a Some(Wrapped) case of an Optional or ImplicitlyUnwrappedOptional enumeration. Optional patterns consist of an identifier pattern followed immediately by a question mark and appear in the same places as enumeration case patterns.
Because optional patterns are syntactic sugar for Optional and ImplicitlyUnwrappedOptional enumeration case patterns, the following are equivalent:
let someOptional: Int? = 42
// Match using an enumeration case pattern
if case .Some(let x) = someOptional {
print(x)
}
// Match using an optional pattern
if case let x? = someOptional {
print(x)
}
The optional pattern provides a convenient way to iterate over an array of optional values in a for-in statement, executing the body of the loop only for non-nil elements.
let arrayOfOptionalInts: [Int?] = [nil, 2, 3, nil, 5]
// Match only non-nil values
for case let number? in arrayOfOptionalInts {
print("Found a (number)")
}
// Found a 2
// Found a 3
// Found a 5
The operator is if case, so you can’t put parentheses. The syntax and behavior are based on those of the case statement in a Swift switch statement (see my online book if you need details). In a case statement, 20...30 is an interval, used as a pattern, which operates by using containsagainst the interval. The equals sign is indeed truly confusing, but that was their first attempt at a syntax for expressing what the case statement should be comparing with (i.e. the tag that comes after the switch keyword in a switch statement).
So, if you understand this:
switch age {
case 20...30:
// do stuff
default:break
}
… then you understand how it is morphed directly into this:
Type casting is a way to check the type of an instance, or to treat that instance as a different superclass or subclass from somewhere else in its own class hierarchy.
Type casting in Swift is implemented with the is and as operators. These two operators provide a simple and expressive way to check the type of a value or cast a value to a different type.
Defining a Class Hierarchy for Type Casting
class MediaItem { var name: String init(name: String) { self.name = name } }
class Movie: MediaItem { var director: String init(name: String, director: String) { self.director = director super.init(name: name) } } class Song: MediaItem { var artist: String init(name: String, artist: String) { self.artist = artist super.init(name: name) } }
The final snippet creates a constant array called library, which contains two Movie instances and three Song instances. The type of the library array is inferred by initializing it with the contents of an array literal. Swift’s type checker is able to deduce that Movie and Song have a common superclass of MediaItem, and so it infers a type of [MediaItem] for the library array:
let library = [ Movie(name: “Casablanca”, director: “Michael Curtiz”), Song(name: “Blue Suede Shoes”, artist: “Elvis Presley”), Movie(name: “Citizen Kane”, director: “Orson Welles”), Song(name: “The One And Only”, artist: “Chesney Hawkes”), Song(name: “Never Gonna Give You Up”, artist: “Rick Astley”) ] // the type of “library” is inferred to be [MediaItem]
The items stored in library are still Movie and Song instances behind the scenes. However, if you iterate over the contents of this array, the items you receive back are typed as MediaItem, and not as Movie or Song. In order to work with them as their native type, you need to check their type, or downcast them to a different type, as described below.
Checking Type
Use the type check operator (is) to check whether an instance is of a certain subclass type. The type check operator returns true if the instance is of that subclass type and false if it is not.
var movieCount = 0 var songCount = 0 for item in library { if item is Movie { movieCount += 1 } else if item is Song { songCount += 1 } } print(“Media library contains (movieCount) movies and (songCount) songs”) // Prints “Media library contains 2 movies and 3 songs”
Downcasting
A constant or variable of a certain class type may actually refer to an instance of a subclass behind the scenes. Where you believe this is the case, you can try to downcast to the subclass type with a type cast operator (as? or as!).
Because downcasting can fail, the type cast operator comes in two different forms. The conditional form, as?, returns an optional value of the type you are trying to downcast to. The forced form, as!, attempts the downcast and force-unwraps the result as a single compound action.
Use the conditional form of the type cast operator (as?) when you are not sure if the downcast will succeed. This form of the operator will always return an optional value, and the value will be nil if the downcast was not possible. This enables you to check for a successful downcast.
Use the forced form of the type cast operator (as!) only when you are sure that the downcast will always succeed. This form of the operator will trigger a runtime error if you try to downcast to an incorrect class type.
for item in library { if let movie = item as? Movie { print(“Movie: (movie.name), dir. (movie.director)”) } else if let song = item as? Song { print(“Song: (song.name), by (song.artist)”) } } // Movie: Casablanca, dir. Michael Curtiz // Song: Blue Suede Shoes, by Elvis Presley // Movie: Citizen Kane, dir. Orson Welles // Song: The One And Only, by Chesney Hawkes // Song: Never Gonna Give You Up, by Rick Astley
Type Casting for Any and AnyObject
Swift provides two special types for working with nonspecific types:
Any can represent an instance of any type at all, including function types.
AnyObject can represent an instance of any class type.
var things = [Any]() things.append(0) things.append(0.0) things.append(42) things.append(3.14159) things.append(“hello”) things.append((3.0, 5.0)) things.append(Movie(name: “Ghostbusters”, director: “Ivan Reitman”)) things.append({ (name: String) -> String in “Hello, (name)” })
for thing in things { switch thing { case 0 as Int: print(“zero as an Int”) case 0 as Double: print(“zero as a Double”) case let someInt as Int: print(“an integer value of (someInt)”) case let someDouble as Double where someDouble > 0: print(“a positive double value of (someDouble)”) case is Double: print(“some other double value that I don’t want to print”) case let someString as String: print(“a string value of “(someString)””) case let (x, y) as (Double, Double): print(“an (x, y) point at (x), (y)”) case let movie as Movie: print(“a movie called (movie.name), dir. (movie.director)”) case let stringConverter as (String) -> String: print(stringConverter(“Michael”)) default: print(“something else”) } } // zero as an Int // zero as a Double // an integer value of 42 // a positive double value of 3.14159 // a string value of “hello” // an (x, y) point at 3.0, 5.0 // a movie called Ghostbusters, dir. Ivan Reitman // Hello, Michael
NOTE
The Any type represents values of any type, including optional types. Swift gives you a warning if you use an optional value where a value of type Any is expected. If you really do need to use an optional value as an Any value, you can use the as operator to explicitly cast the optional to Any, as shown below.
let optionalNumber: Int? = 3 things.append(optionalNumber) // Warning things.append(optionalNumber as Any) // No warning