Swift programming language. Russian version
Hello, Habr! On June 2, we all could personally witness how Apple began to revolutionize the camp of Objective-C developers, introducing the world to its new programming language - Swift. Along with this, she posted openly a small documentation on the language, which we decided to translate, if there is a demand. We bring to your attention a translation of the first chapter. If the topic is interesting, then we will continue to publish the translation every week.
Welcome to Swift
About Swift
Introduction to the Swift
Language guide
The Basics
Basic Operators
String and Characters
Collection Types
Control Flow
Functions
Closures
Enumerations
Classes and Structures
Properties
Methods
Subscripts
Inheritance
Initialization
Deinitialization
Automatic Reference Counting
Optional Chaining
Type Casting
Nested Types
Extensions
Protocols
Generics
Advanced Operators
Language Reference
About the Language Reference
Lexical Structure
Types
Expressions
Statements
Declarations
Attributes
Patterns
Generic Parameters and Arguments
Summary of the Grammar
Trademarks
Swift is a new programming language for developing iOS and OS X applications, which combines the best of C and Objective-C, but lacks the restrictions imposed for the sake of compatibility with C. Swift uses secure programming patterns and adds modern features that turn Creating an application in a simpler, more flexible and fun process. Swift, created by us from scratch, is an opportunity to re-imagine how applications are developed.
Swift was developed by us for several years. The basis of the new programming language was the existing compiler, debugger and frameworks. We simplified the memory management process using the automatic reference counting (ARC) mechanism. Our frameworks have also undergone a major upgrade. Objective-C began to support blocks, literals and modules - all this created favorable conditions for the introduction of modern technologies. It was this preparatory work that served as the foundation for a new programming language that will be used to develop future software products for Apple.
Objective-C developers will find Swift familiar. It combines the readability of named parameters and the power of the Objective-C dynamic object model. It provides access to existing Cocoa frameworks and is compatible with code written in Objective-C. A language built on this common basis offers many new features and unifies the procedural and object-oriented aspects of a programming language.
Swift will not scare away novice programmers. This is the first powerful programming language, as clear and fun as a scripting language. It supports the so-called playgrounds, which allow programmers to experiment with the code, seeing the result in real time without having to compile and run the application.
Swift has incorporated all the best from modern languages and is designed based on Apple's vast experience. Our compiler is synonymous with performance, our language is optimized for development without regard to compromise. It is designed so that you can easily develop your first hello, world! Application, and even the whole operating system. All this makes Swift an important tool for developers and for Apple itself.
Swift is a fantastic new way to create apps for iOS and OS X, and we will continue to develop it by adding new features and introducing new features. Our goal is ambitious. And we look forward to seeing what you can create with it.
According to a long tradition, the first program in a new language should display words
If you've ever developed C or Objective-C, this syntax should seem familiar to you - in Swift, this line of code is a complete program. You no longer need to import separate libraries to provide basic functionality like input / output to the console or work with strings. Code written in the global scope is the entry point to the program, so the function is
This introduction contains enough information to start writing Swift code. Do not worry if something is unclear to you - we will explain everything in detail in the following chapters.
Use
The types of the constant and the variable must match the types of the corresponding values assigned to them. However, this does not mean that you should directly indicate their type. The compiler will automatically determine the type of constant and variable when they are assigned a value. So, in the above example, the compiler will determine that it
If the initializer is missing or does not provide enough information, you can specify the type yourself after the variable, separating the name and type with a colon:
Values are never converted to another type implicitly. If you need to convert the value to another type, do it explicitly:
There is an easier way to include values in strings: to do this, enclose the expression in brackets and put a backslash (
When working with arrays and associative arrays (dictionaries, dictionary), square brackets (
To create an empty array or dictionary, use the following syntax:
To create empty arrays and dictionaries, use
To create conditions use the operators
The condition inside the operator
The conditional operator
If the optional value is equal
The multiple choice operator
After executing a suitable code block, the program leaves the operator
To iterate over the elements of an associative array, use the operator
The loop operator
The operator
When creating a loop, use two points (
Use the keyword to declare functions
If a function returns multiple values, you should use a tuple:
Functions can also have an undefined number of arguments:
Functions can be nested together. A nested function can access variables declared in an external function. Use nested functions to tidy up complex or large function code.
Functions are first-class type objects, in other words, a function may return another function as its result.
A function can also take another function as one of its arguments.
Functions are a special case of closures. You can create a closure without specifying its name and surrounding the closure body with braces (
There are several techniques to make closures more concise. If the type of closure is a priori known (for example, it is the callback of the delegate), you can omit the indication of the type of its parameters and / or the type of the return value. Closures consisting of a single expression implicitly return the result of this expression.
In a circuit, instead of specifying a variable name, you can use its serial number - this is especially useful when writing short circuits. A closure, which is the last argument to a function, can be passed to it immediately after parentheses with a list of other parameters.
A reserved word is used to create the class
To create an instance (object) of a class, just add parentheses after the class name. Access to methods and class members is through a dot.
In this example, we missed one important detail - the class constructor, the method
Notice how the member of the name class
A class destructor is a method
To inherit a class from an existing class, after specifying the name of the child class, put a colon and indicate the name of the parent. In Swift there are no restrictions on the mandatory inheritance of any standard class.
Methods overridden by a child class must be marked with the keyword
Class members may also have their own
In the
Pay attention to the structure of the class constructor
If you need to execute some code before or after assigning a new value to a variable, you can override the methods
Class methods have one important difference from functions. The names of function arguments are used only within this function, whereas in a class method, parameters are also used when calling this method (except for the first parameter). By default, a class method has the same parameter names both when called and internally. However, you can specify a different name (in the example below -
When working with optional values, add a question mark (
Use the keyword to create the listings
In the above example, enumeration elements initially have an integer type, and you just need to specify the value of only the first element - the values of the remaining elements will be determined in accordance with their order. You can also select string or real types as the source type (raw value) of element values.
To convert the original value type to an enumeration type, use the
Note that the values of the enumeration elements are actual, and not just another record of their original values. Generally speaking, you may not indicate their initial values.
Note how access is made to an
The keyword is used to create structures.
An instance of an enumeration member can have its own values and they can be different. You assign these values when creating an instance of the enumeration (constant
Consider the example of receiving the sunrise and sunset times from the server. The server sends back either the relevant information or an error message.
Note how
Use the keyword to declare a protocol
Protocols can be supported by classes, enumerations, and structures.
Note the keyword
To add new methods or class members to an existing type, you must use the - extension
You can use the name of the protocol like any other type - for example, to create an array of objects of different types, but implementing a common protocol. Note that when working with objects of this type, methods declared outside the protocol will not be available.
Despite the fact that the variable
To create a generic type, enclose the name in angle brackets (
Create generic functions, classes, enumerations, and structures.
If you want to specify specific requirements generalized to a type, such as, for example, a protocol implementation or a requirement to be inherited from a specific class, use
In simple cases, you can omit
Table of contents
Welcome to Swift
About Swift
Introduction to the Swift
Language guide
The Basics
Basic Operators
String and Characters
Collection Types
Control Flow
Functions
Closures
Enumerations
Classes and Structures
Properties
Methods
Subscripts
Inheritance
Initialization
Deinitialization
Automatic Reference Counting
Optional Chaining
Type Casting
Nested Types
Extensions
Protocols
Generics
Advanced Operators
Language Reference
About the Language Reference
Lexical Structure
Types
Expressions
Statements
Declarations
Attributes
Patterns
Generic Parameters and Arguments
Summary of the Grammar
Trademarks
Welcome to swift
About Swift
Swift is a new programming language for developing iOS and OS X applications, which combines the best of C and Objective-C, but lacks the restrictions imposed for the sake of compatibility with C. Swift uses secure programming patterns and adds modern features that turn Creating an application in a simpler, more flexible and fun process. Swift, created by us from scratch, is an opportunity to re-imagine how applications are developed.
Swift was developed by us for several years. The basis of the new programming language was the existing compiler, debugger and frameworks. We simplified the memory management process using the automatic reference counting (ARC) mechanism. Our frameworks have also undergone a major upgrade. Objective-C began to support blocks, literals and modules - all this created favorable conditions for the introduction of modern technologies. It was this preparatory work that served as the foundation for a new programming language that will be used to develop future software products for Apple.
Objective-C developers will find Swift familiar. It combines the readability of named parameters and the power of the Objective-C dynamic object model. It provides access to existing Cocoa frameworks and is compatible with code written in Objective-C. A language built on this common basis offers many new features and unifies the procedural and object-oriented aspects of a programming language.
Swift will not scare away novice programmers. This is the first powerful programming language, as clear and fun as a scripting language. It supports the so-called playgrounds, which allow programmers to experiment with the code, seeing the result in real time without having to compile and run the application.
Swift has incorporated all the best from modern languages and is designed based on Apple's vast experience. Our compiler is synonymous with performance, our language is optimized for development without regard to compromise. It is designed so that you can easily develop your first hello, world! Application, and even the whole operating system. All this makes Swift an important tool for developers and for Apple itself.
Swift is a fantastic new way to create apps for iOS and OS X, and we will continue to develop it by adding new features and introducing new features. Our goal is ambitious. And we look forward to seeing what you can create with it.
Introduction to Swift
According to a long tradition, the first program in a new language should display words
“Hello, world”
. With Swift, this is done like this:println("Hello, world")
If you've ever developed C or Objective-C, this syntax should seem familiar to you - in Swift, this line of code is a complete program. You no longer need to import separate libraries to provide basic functionality like input / output to the console or work with strings. Code written in the global scope is the entry point to the program, so the function is
main
no longer needed. Also note the absence of a semicolon at the end of each line. This introduction contains enough information to start writing Swift code. Do not worry if something is unclear to you - we will explain everything in detail in the following chapters.
Note
For a better understanding of the material, we recommend that you use the playground mode in Xcode. Playground allows you to see the result immediately in the process of editing the code without having to compile and run the application.
Simple data types
Use
let
to create a constant and var
to create a variable. You do not need to specify the type of constant, you can assign it a value only once.var myVariable = 42
myVariable = 50
let myConstant = 42
The types of the constant and the variable must match the types of the corresponding values assigned to them. However, this does not mean that you should directly indicate their type. The compiler will automatically determine the type of constant and variable when they are assigned a value. So, in the above example, the compiler will determine that it
myVariable
has an integer type. If the initializer is missing or does not provide enough information, you can specify the type yourself after the variable, separating the name and type with a colon:
let implicitInteger = 70
let inplicitDouble = 70.0
let inplicitDouble: Double = 70
Let's experiment
Create a constant with type Float and initialize it with the number 4.
Values are never converted to another type implicitly. If you need to convert the value to another type, do it explicitly:
let label = "The width is "
let width = 94
let widthLabel = label + String(width)
Let's experiment.
Try removing the explicit conversion to the String type in the last line. What error do you get?
There is an easier way to include values in strings: to do this, enclose the expression in brackets and put a backslash (
\
) in front of them . Example:let apples = 3
let oranges = 5
let appleSummary = "I have \(apples) apples."
let fruitSummary = "I have \(apples + oranges) pieces of fruit."
Let's experiment
Try using the construct\()
and display a string that includes the result of the sum of two integer variables and someone else's name.
When working with arrays and associative arrays (dictionaries, dictionary), square brackets (
[]
) are used:var shoppingList = ["catfish", "water", "tulips", "blue paint"]
shoppingList[1] = "bottle of water"
var occupations = [
"Malcolm": "Captain",
"Kaylee": "Mechanic",
]
occupations["Jayne"] = "Public Relations"
To create an empty array or dictionary, use the following syntax:
let emptyArray = String[]()
let emptyDictionary = Dictionary()
To create empty arrays and dictionaries, use
[]
and, [:]
respectively, for example, when you assign a new value to a variable or pass an argument to a function.shoppingList = [] // Went shopping and bought everything.
Conditions and cycles
To create conditions use the operators
if
and switch
to create cycles - for-in
, for
, while
and do-while
. At the same time, it is not necessary to select conditions and initializing expressions with parentheses, while curly braces are required.let individualScores = [75, 43, 103, 87, 12]
var teamScore = 0
for score in individualScores {
if score > 50 {
teamScore += 3
} else {
teamScore += 1
}
}
teamScore
The condition inside the operator
if
must be logical, this in particular means that the expression if score {…}
is erroneous, since there is no explicit comparison (for example, with zero). The conditional operator
if
can be used in conjunction with let
and var
to work with constants and variables that may matter nil
. Such constants and variables are called optional (that is, they can either take any value or be equal nil
). To create an optional variable or constant, add a question mark ( ?
) after specifying the type. var optionalString: String? = "Hello"
optionalString == nil
var optionalName: String? = "John Appleseed"
var greeting = "Hello!"
if let name = optionalName {
greeting = "Hello, \(name)"
}
Let's experiment
ChangeoptionalName
tonil
. What do you see on the screen? Add a blockelse
to handle the case whenoptionalName
equalnil
.
If the optional value is equal
nil
, the condition will be false and the code in braces afterwards if
will not be executed. Otherwise, the variable greeting
will be assigned a new value. The multiple choice operator
switch
supports many other comparison operators within itself and is not limited to simple comparisons:let vegetable = "red pepper"
switch vegetable {
case "celery":
let vegetableComment = "Add some raisins and make ants on a log."
case "cucumber", "watercress":
let vegetableComment = "That would make a good tea sandwich."
case let x where x.hasSuffix("pepper"):
let vegetableComment = "Is it a spicy \(x)?"
default:
let vegetableComment = "Everything tastes good in soup."
}
Let's experiment
Try removing the default condition. What error do you get?
After executing a suitable code block, the program leaves the operator
switch
without checking the following conditions. Thus, you do not need to manually add interrupt operators ( break
) at the end of each block case
. To iterate over the elements of an associative array, use the operator
for-in
together with the name pair for each key-value pair.let interestingNumbers = [
"Prime": [2, 3, 5, 7, 11, 13],
"Fibonacci": [1, 1, 2, 3, 5, 8],
"Square": [1, 4, 9, 16, 25],
]
var largest = 0
for (kind, numbers) in interestingNumbers {
for number in numbers {
if number > largest {
largest = number
}
}
}
largest
Let's experiment
Add another variable that will help you find out which of the three types the maximum number found belongs to.
The loop operator
while
allows you to execute a block of code inside it until the condition becomes false. The condition may also be indicated after the block, which in this case will be executed at least once.var n = 2
while n < 100 {
n = n * 2
}
n
var m = 2
do {
m = m * 2
} while m < 100
m
The operator
for
can be used to iterate over a sequence of numbers using two points ( ..
) or using the initializer, condition, and increment. Look, these two cycles do the same thing:var firstForLoop = 0
for i in 0..3 {
firstForLoop += i
}
firstForLoop
var secondForLoop = 0
for var i = 0; i < 3; ++i {
secondForLoop += 1
}
secondForLoop
When creating a loop, use two points (
..
) if you do not want to include a larger value in the range, and three points ( …
) to include both a smaller and a larger value.Functions and closures.
Use the keyword to declare functions
func
. The function is called by specifying its name and a list of arguments in parentheses. The return type should be separated from the list of formal arguments with ->
.func greet(name: String, day: String) -> String {
return "Hello \(name), today is \(day)."
}
greet("Bob", "Tuesday")
Let's experiment.
Remove the day parameter. Instead, add a variable indicating the name of the meal served for lunch.
If a function returns multiple values, you should use a tuple:
func getGasPrices() -> (Double, Double, Double) {
return (3.59, 3.69, 3.79)
}
getGasPrices()
Functions can also have an undefined number of arguments:
func sumOf(numbers: Int...) -> Int {
var sum = 0
for number in numbers {
sum += number
}
return sum
}
sumOf()
sumOf(42, 597, 12)
Let's experiment.
Write a function that allows you to find the arithmetic mean of an arbitrary number of your arguments.
Functions can be nested together. A nested function can access variables declared in an external function. Use nested functions to tidy up complex or large function code.
func returnFifteen() -> Int {
var y = 10
func add() {
y += 5
}
add()
return y
}
returnFifteen()
Functions are first-class type objects, in other words, a function may return another function as its result.
func makeIncrementer() -> (Int -> Int) {
func addOne(number: Int) -> Int {
return 1 + number
}
return addOne
}
var increment = makeIncrementer()
increment(7)
A function can also take another function as one of its arguments.
func hasAnyMatches(list: Int[], condition: Int -> Bool) -> Bool {
for item in list {
if condition(item) {
return true
}
}
return false
}
func lessThanTen(number: Int) -> Bool {
return number < 10
}
var numbers = [20, 19, 7, 12]
hasAnyMatches(numbers, lessThanTen)
Functions are a special case of closures. You can create a closure without specifying its name and surrounding the closure body with braces (
{}
). Use the operator to separate arguments and return type from the body of the closure in
.numbers.map({
(number: Int) -> Int in
let result = 3 * number
return result
})
Let's experiment
rewrite the closure so that it returns zero for all the extra numbers.
There are several techniques to make closures more concise. If the type of closure is a priori known (for example, it is the callback of the delegate), you can omit the indication of the type of its parameters and / or the type of the return value. Closures consisting of a single expression implicitly return the result of this expression.
numbers.map({ number in 3 * number })
In a circuit, instead of specifying a variable name, you can use its serial number - this is especially useful when writing short circuits. A closure, which is the last argument to a function, can be passed to it immediately after parentheses with a list of other parameters.
sort([1, 5, 3, 12, 2]) { $0 > $1 }
Objects and Classes
A reserved word is used to create the class
class
. Class members are declared in the same way as regular constants and variables. Moreover, class methods are declared as ordinary functions.class Shape {
var numberOfSides = 0
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}
Let's experiment
Add a class member constant and a class method that takes it as its argument.
To create an instance (object) of a class, just add parentheses after the class name. Access to methods and class members is through a dot.
var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()
In this example, we missed one important detail - the class constructor, the method
init
.class NamedShape {
var numberOfSides: Int = 0
var name: String
init(name: String) {
self.name = name
}
func simpleDescription() -> String {
return "A shape with \(numberOfSides) sides."
}
}
Notice how the member of the name class
self
is separated by the constructor argument name
. Arguments are passed to the constructor in the usual way, like in any other class method. Please note that each member of the class must be initialized - either during the declaration (as, for example numberOfSides
), or in the constructor (as name
). A class destructor is a method
deinit
that can be rewritten if necessary. To inherit a class from an existing class, after specifying the name of the child class, put a colon and indicate the name of the parent. In Swift there are no restrictions on the mandatory inheritance of any standard class.
Methods overridden by a child class must be marked with the keyword
override
- overriding methods without override will result in an error. The compiler also detects methods that are labeled override
, but do not override any methods of its parent class.class Square: NamedShape {
var sideLength: Double
init(sideLength: Double, name: String) {
self.sideLength = sideLength
super.init(name: name)
numberOfSides = 4
}
func area() -> Double {
return sideLength * sideLength
}
override func simpleDescription() -> String {
return "A square with sides of length \(sideLength)."
}
}
let test = Square(sideLength: 5.2, name: "my test square")
test.area()
test.simpleDescription()
Let's experiment
Create a classCircle
and inherit it from the classNamedShape
. The constructor of the classCircle
takes two arguments - the radius and the name. Override methodsarea
anddescribe
the class.
Class members may also have their own
getter
and setter
.class EquilateralTriangle: NamedShape {
var sideLength: Double = 0.0
init(sideLength: Double, name: String) {
self.sideLength = sideLength
super.init(name: name)
numberOfSides = 3
}
var perimeter: Double {
get {
return 3.0 * sideLength
}
set {
sideLength = newValue / 3.0
}
}
override func simpleDescription() -> String {
return "An equilateral triagle with sides of length \(sideLength)."
}
}
var triangle = EquilateralTriangle(sideLength: 3.1, name: "a triangle")
triangle.perimeter
triangle.perimeter = 9.9
triangle.sideLength
In the
setter
ith variable, the perimeter
new assigned value is implicitly called newValue
. You can change the name of this variable by specifying it in brackets immediately after set
. Pay attention to the structure of the class constructor
EquilateralTriangle
. This method involves three consecutive steps:- initialization of members of the child class;
- calling the constructor of the parent class;
- changing the values of members of the parent class.
If you need to execute some code before or after assigning a new value to a variable, you can override the methods
willSet
and didSet
dates of your way. For example, in the class below, it is guaranteed that the side length of the triangle will always be equal to the length of the side of the square.class TriangleAndSquare {
var triangle: EquilateralTriangle {
willSet {
square.sideLength = newValue.sideLength
}
}
var square: Square {
willSet {
triangle.sideLength = newValue.sideLength
}
}
init(size: Double, name: String) {
square = Square(sideLength: size, name: name)
triangle = EquilateralTriangle(sideLength: size, name: name)
}
}
var triangleAndSquare = TriangleAndSquare(size: 10, name: "another test shape")
triangleAndSquare.square.sideLength
triangleAndSquare.triangle.sideLength
triangleAndSquare.square = Square(sideLength: 50, name: "larger square")
triangleAndSquare.triangle.sideLength
Class methods have one important difference from functions. The names of function arguments are used only within this function, whereas in a class method, parameters are also used when calling this method (except for the first parameter). By default, a class method has the same parameter names both when called and internally. However, you can specify a different name (in the example below -
times
), which will be used only inside this method. In this case, to call this method, you must use the first name ( numberOfTimes
).class Counter {
var count: Int = 0
func incrementBy(amount: Int, numberOfTimes times: Int) {
count += amount * times
}
}
var counter = Counter()
counter.incrementBy(2, numberOfTimes: 7)
When working with optional values, add a question mark (
?
) before methods, class members, etc. If the value before the question mark is equal nil
, everything that follows after ( ?
) is ignored and the value of the entire expression is equal nil
. Otherwise, the expression is evaluated in the usual way. In both cases, the result of the whole expression will be an optional value.let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square")
let sideLength = optionalSquare?.sideLength
Enumerations and Structures
Use the keyword to create the listings
enum
. Note that transfers can also include methods.enum Rank: Int {
case Ace = 1
case Two, Three, Four, Five, Six, Seven, Eight, Nine, Ten
case Jack, Queen, King
func simpleDescription() -> String {
switch self {
case .Ace:
return "ace"
case .Jack:
return "jack"
case .Queen:
return "queen"
case .King:
return "king"
default:
return String(self.toRaw())
}
}
}
let ace = Rank.Ace
let aceRawValue = ace.toRaw()
Let's experiment.
Write a function that compares 2 type enumerationsRank
by their values.
In the above example, enumeration elements initially have an integer type, and you just need to specify the value of only the first element - the values of the remaining elements will be determined in accordance with their order. You can also select string or real types as the source type (raw value) of element values.
To convert the original value type to an enumeration type, use the
toRaw
and functions fromRaw
.if let convertedRank = Rank.fromRaw(3) {
let threeDescription = convertedRank.simpleDescription()
}
Note that the values of the enumeration elements are actual, and not just another record of their original values. Generally speaking, you may not indicate their initial values.
enum Suit {
case Spades, Hearts, Diamonds, Clubs
func simpleDescription() -> String {
switch self {
case .Spades:
return "spades"
case .Hearts:
return "hearts"
case .Diamonds:
return "diamonds"
case .Clubs:
return "clubs"
}
}
}
let hearts = Suit.Hearts
let heartsDescription = hearts.simpleDescription()
Let's experiment
Add a methodColor
that returns a string“black”
forSpades
andClubs
and“red”
forHearts
andDiamonds
.
Note how access is made to an
Hearts
enumeration member Suit
. When assigning a value to a constant hearts
, the full name is used Suit.Hearts
, since we do not explicitly specify the type of this constant. And in switch
we use the abbreviated form .Hearts
, since the type of value is a self
priori known. You can use the short form everywhere if the type of the variable is explicitly specified. The keyword is used to create structures.
struct
. Structures have many similarities with classes, including methods and constructors. One of the most significant differences between structures and classes is that instances of structures, unlike instances of classes, are passed to functions by value (that is, a local copy of them is preliminarily created), while instances of classes are passed by reference.struct Card {
var rank: Rank
var suit: Suit
func simpleDescription() -> String {
return "The \(rank.simpleDescription()) of \(suit.simpleDescription())"
}
}
let threeOfSpades = Card(rank: .Three, suit: .Spades)
let threeOfSpadesDescription = threeOfSpades.simpleDescription()
Let's experiment
Add a method to the Card structure that creates a complete deck of cards.
An instance of an enumeration member can have its own values and they can be different. You assign these values when creating an instance of the enumeration (constant
success
in the example). Bound and initial values are two different things: the initial value of an enumeration member is always constant for all instances of the enumeration and is indicated when it is declared. Consider the example of receiving the sunrise and sunset times from the server. The server sends back either the relevant information or an error message.
enum ServerResponse {
case Result(String, String)
case Error(String)
}
let success = ServerResponse.Result("6:00 am", "8:09 pm")
let failure = ServerResponse.Error("Out of cheese.")
switch success {
case let .Result(sunrise, sunset):
let serverResponse = "Sunrise is at \(sunrise) and sunset is at \(sunset)."
case let .Error(error):
let serverResponse = "Failure... \(error)"
}
Let's experiment
Add the third option to the multiple choice operatorswitch
Note how
ServerResponse
sunrise and sunset times are “pulled out” of the object .Protocols and Extensions.
Use the keyword to declare a protocol
protocol
.protocol ExampleProtocol {
var simpleDescription: String { get }
mutating func adjust()
}
Protocols can be supported by classes, enumerations, and structures.
class SimpleClass: ExampleProtocol {
var simpleDescription: String = "A very simple class."
var anotherProperty: Int = 69105
func adjust() {
simpleDescription += " Now 100% adjusted."
}
}
var a = SimpleClass()
a.adjust()
let aDescription = a.simpleDescription
struct SimpleStructure: ExampleProtocol {
var simpleDescription: String = "A simple structure"
mutating func adjust() {
simpleDescription += " (adjusted)"
}
}
var b = SimpleStructure()
b.adjust()
let bDescription = b.simpleDescription
Let's experiment
Create an enumeration that will implement this protocol.
Note the keyword
mutating
in the structure definition SimpleStructure
that the compiler informs that the corresponding method is subjecting the structure to changes. In contrast, class methods SimpleClass
do not need to be labeled as mutating
, since class methods can always change it unhindered. To add new methods or class members to an existing type, you must use the - extension
extensions
. You can also use extensions to implement the protocol with an existing type, even if it is imported from a library or framework.extension Int: ExampleProtocol {
var simpleDescription: String {
return "The number \(self)"
}
mutating func adjust() {
self += 42
}
}
7.simpleDescription
Let's experiment.
Create a type extensionDouble
with a member variableabsoluteValue
.
You can use the name of the protocol like any other type - for example, to create an array of objects of different types, but implementing a common protocol. Note that when working with objects of this type, methods declared outside the protocol will not be available.
let protocolValue: ExampleProtocol = a
protocolValue.simpleDescription
// protocolValue.anotherProperty // Uncomment to see the error
Despite the fact that the variable
protocolValue
has a type during program execution SimpleClass
, the compiler considers its type to be ExampleProtocol
. This means that you cannot accidentally access methods or members of a class that are implemented outside the protocol ExampleProtocol
.Generic types
To create a generic type, enclose the name in angle brackets (
<>
).func repeat(item: ItemType, times: Int) -> ItemType[] {
var result = ItemType[]()
for i in 0..times {
result += item
}
return result
}
repeat("knock", 4)
Create generic functions, classes, enumerations, and structures.
// Reimplement the Swift standard library's optional type
enum OptionalValue {
case None
case Some(T)
}
var possibleInteger: OptionalValue = .None
possibleInteger = .Some(100)
If you want to specify specific requirements generalized to a type, such as, for example, a protocol implementation or a requirement to be inherited from a specific class, use
where
.func anyCommonElements (lhs: T, rhs: U) -> Bool {
for lhsItem in lhs {
for rhsItem in rhs {
if lhsItem == rhsItem {
return true
}
}
}
return false
}
anyCommonElements([1, 2, 3], [3])
Let's experiment.
Change the functionanyCommonElements
so that it returns an array of common elements.
In simple cases, you can omit
where
and write the name of the protocol or class after the colon. An expression is
equivalent to an expression
.Want to implement subscriptions in your iOS app in 10 minutes? Integrate Apphud and:
- make purchases using only one method;
- automatically track the status of each user's subscription;
- Easily integrate Subscription Offers;
- send subscription events to Amplitude, Mixpanel, Slack and Telegram taking into account the user's local currency;
- decrease the Churn rate in applications and return unsubscribed users.