📜 ⬆️ ⬇️

Swift programming language. Russian version

image Hi, Habr! On June 2, we all could witness how Apple began to revolutionize the Objective-C camp of developers by introducing its new programming language, Swift, to the world. At the same time, she posted in open access a small documentation on the language, which we decided to translate, if there is demand. We offer you the translation of the first chapter. If the topic is interesting, we will continue to publish the translation every week.

Table of contents


Welcome to swift
About Swift
Introduction to 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 all the best from C and Objective-C, but lacks the limitations imposed for compatibility with C. Swift uses secure programming patterns and adds modern functions that turn Creating an application in a simple, 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 have simplified the memory management process with the Automatic Reference Counting (ARC) mechanism. Our frameworks also underwent 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 the 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 procedural and object-oriented aspects of a programming language.

Swift does not scare novice programmers. This is the first powerful programming language, as intuitive and fascinating 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 absorbed all the best from modern languages ​​and is designed with the extensive experience of Apple. Our compiler is a synonym for performance, our language is optimized for development without looking at compromises. It is designed in such a way 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 new fantastic way to create applications for iOS and OS X, and we will continue to develop it, adding new functionality and introducing new features. Our goal is ambitious. And we look forward to see what you can create with it.

Introduction to Swift

According to a long tradition, the first program in a new language should display the words “Hello, world” . With Swift, this is done like this:

 println("Hello, world") 

If you have ever developed C or Objective-C this syntax should seem painfully 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 I / O to the console or work with strings. The code written in the global scope is the entry point to the program, so the main function is 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 incomprehensible to you - we will explain everything in detail in subsequent chapters.

Comment
For a better understanding of the material, we recommend using the playground mode in Xcode. Playground allows you to see the result immediately in the code editing process without having to compile and run the application.

Simple data types

Use let to create a constant and var to create a variable. You don't need to specify a constant type; you can assign a value to it only once.

 var myVariable = 42 myVariable = 50 let myConstant = 42 

The types of the constant and the variable must match the types of corresponding values ​​assigned to them. However, this does not mean that you must directly indicate their type. The compiler will automatically determine the type of the constant and the variable when they are assigned a value. So, in the example above, the compiler will determine that myVariable is of integer type.

If the initializer is missing or does not provide sufficient information, you can specify the type yourself after the variable, separating the name and type by 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 a 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 parentheses 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'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<String, Float>() 

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 if and switch , to create loops - for-in , for , while and do-while . In this case, it is not necessary to select conditions and initializing expressions in parentheses, while curly brackets 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 if should be logical, which in particular means that the expression if score {…} is erroneous, since there is no explicit comparison (for example, with zero).

The conditional if can be used in conjunction with let and var to work with constants and variables that can be nil . Such constants and variables are called optional (that is, they can either take a value or be equal to 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
Change optionalName to nil . What do you see on the screen? Add an else block to handle the case when optionalName is nil .

If the optional value is nil , the condition will be false and the code in braces after the if will not be executed. Otherwise, the variable greeting will be assigned a new value.

The switch multiple select switch supports many other comparison operators within it 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 block of code, the program leaves the switch without checking the subsequent conditions. Thus, you do not need to manually add interrupts ( break ) at the end of each case block.

To select the elements of an associative array, use the for-in operator along with a pair of names 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 allow you to find out which of the three types the maximum number found is.

The while operator allows you to execute a block of code inside it until the condition becomes false. The condition can also be specified 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 

You can use the for statement to iterate through a sequence of numbers using two points ( .. ) or using an 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 smaller and larger values.

Functions and closures.

To declare functions, use the func keyword. The function is called by specifying its name and the 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 denoting the name of the dish served for lunch.

If the function returns a set of values, use a tuple:

 func getGasPrices() -> (Double, Double, Double) { return (3.59, 3.69, 3.79) } getGasPrices() 

Functions can also have an indefinite 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 its arguments.

Functions can be nested. A nested function can refer to variables declared in an external function. Use nested functions to tidy up a complex or large function code.

 func returnFifteen() -> Int { var y = 10 func add() { y += 5 } add() return y } returnFifteen() 

Functions are objects of the first class (first-class type), in other words, a function can 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 the 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 curly braces ( {} ). To separate the arguments and return type from the closure body, use the in operator.

 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 unnecessary numbers.

There are several techniques that allow making closures more concise. If the closure type is known a priori (for example, this is the delegate’s callback), 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 closure, instead of specifying the name of a variable, you can use its sequence number — this is especially useful when writing short circuits. The closure, which is the last argument of the function, can be passed to it immediately after the parentheses with the list of other parameters.

 sort([1, 5, 3, 12, 2]) { $0 > $1 } 


Objects and Classes

To create a class, use the reserved word 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 constant member and a class method that takes it as its argument.

To create an instance (object) of a class, it is enough to add parentheses after the class name. Access to methods and class members is done 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 init method.

 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 with self is separated from the argument of the name constructor. Arguments are passed to the constructor in the usual way, as in any other class method. Note that each member of the class must be initialized — either when declaring (as, for example, numberOfSides ), or in the constructor (as name ).

The destructor of the class is the deinit method, which can be rewritten if necessary.

To inherit a class from an already existing class, after specifying the name of the child class, you must put a colon and specify the name of the parent. In Swift, there are no restrictions on the mandatory inheritance of any standard class.

Methods override child class must be marked with the override keyword — overriding methods without override will result in an error. The compiler also identifies methods labeled override , but not overriding any methods of their 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 Circle class and inherit it from the NamedShape class. The constructor of the Circle class takes two arguments, the radius and the name. Override area methods and describe this class.

Class members can 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 perimeter variable perimeter new value assigned is implicitly called newValue . You can change the name of this variable by specifying it in brackets immediately after set .

Pay attention to the constructor structure of the EquilateralTriangle class. This method involves three consecutive steps:
  1. initialization of members of the child class;
  2. call the parent class constructor;
  3. change the values ​​of the members of the parent class.

If you need to execute a specific code before or after assigning a new value to a variable, you can override the willSet and didSet methods in the way you want. For example, in the class below, it is guaranteed that the length of the side 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 the function arguments are used only within this function, whereas in the class method, parameters are also used when calling this method (except for the first parameter). By default, the class method has the same parameter names both during the call and within itself. However, you can specify a different name (in the example below, times ) that 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 nil , everything that follows ( ? ) ? ignored and the value of the whole expression is nil . Otherwise, the expression is evaluated in the usual way. In both cases, the result of the entire expression is an optional value.

 let optionalSquare: Square? = Square(sideLength: 2.5, name: "optional square") let sideLength = optionalSquare?.sideLength 


Enumerations and Structures

The enum keyword is used to create enum . Note that enumerations 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 Rank type listings by their values.

In the above example, the enumeration elements are initially of integer type, and you only need to specify the value of the first element - the values ​​of the other elements will be determined in accordance with their order. You can also choose string or real types as the initial type (raw value) of the element values.

To convert a source value type to an enumeration type, use the toRaw and 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 specify their original 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 Color method that returns the string “black” for Spades and Clubs and “red” for Hearts and Diamonds .

Notice how the Hearts member accesses the Suit enumeration. When assigning a value to the constant of hearts , the full name Suit.Hearts , since we obviously do not indicate the type of this constant. And in switch we use the abbreviated form .Hearts , since the type of self is known a priori. You can use the short form everywhere if the variable type is explicitly specified.

The struct keyword is used to create structures. Structures have many similar features 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, they create a local copy first), 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 may have its own values ​​and they may be different. You assign these values ​​when you create an enumeration instance (the success constant in the example). Associated and initial values ​​are 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 an example of receiving from the server the time of sunrise and sunset. The server responds with either 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 a third option to the switch multiple select switch

Notice how the time of sunrise and sunset is “pulled out” of the ServerResponse object.

Protocols and Extensions.

To declare a protocol, use the protocol keyword.

 protocol ExampleProtocol { var simpleDescription: String { get } mutating func adjust() } 

Protocols can be supported by classes, enums, 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.

Notice the mutating keyword in the definition of the SimpleStructure structure, which informs the compiler that the corresponding method SimpleStructure structure change. In contrast, SimpleClass class SimpleClass do not need to be labeled as mutating , since class methods can always change it freely.

To add new methods or class members to an existing type, you must use the extension - extensions. You can also use extensions to implement a protocol of an already existing type, even if it is imported from any 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 extension Doublewith a member variable absoluteValue.

You can use a protocol name 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 at runtime, the variable protocolValueis of type SimpleClass, the compiler assumes that its type is ExampleProtocol. This means that you cannot accidentally gain access to 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<ItemType>(item: ItemType, times: Int) -> ItemType[] { var result = ItemType[]() for i in 0..times { result += item } return result } repeat("knock", 4) 

Create generic functions, classes, enums, and structures.

 // Reimplement the Swift standard library's optional type enum OptionalValue<T> { case None case Some(T) } var possibleInteger: OptionalValue<Int> = .None possibleInteger = .Some(100) 

If you want to specify certain requirements generic to a type, such as, for example, protocol implementation or the requirement to be inherited from a particular class, use where.

 func anyCommonElements <T, U where T: Sequence, U: Sequence, T.GeneratorType.Element: Equatable, T.GeneratorType.Element == U.GeneratorType.Element> (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.
Modify the function anyCommonElementsso that it returns an array of common elements.

In simple cases, you can omit whereand write the name of the protocol or class after the colon. An expression is <T: Equatable>equivalent to an expression <T where T: Equatable>.

Source: https://habr.com/ru/post/225841/


All Articles