Swift programming language. Russian version

imageHello, 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.

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 mainno 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 letto create a constant and varto 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 myVariablehas 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 ifand switchto create cycles - for-in, for, whileand 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 ifmust 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 ifcan be used in conjunction with letand varto 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
Change optionalNameto nil. What do you see on the screen? Add a block elseto handle the case when optionalNameequal nil.

If the optional value is equal nil, the condition will be false and the code in braces afterwards ifwill not be executed. Otherwise, the variable greetingwill be assigned a new value.

The multiple choice operator switchsupports 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 switchwithout 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-intogether 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 whileallows 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 forcan 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 selfis 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 deinitthat 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 keywordoverride- 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 class Circleand inherit it from the class NamedShape. The constructor of the class Circletakes two arguments - the radius and the name. Override methods areaand describethe class.

Class members may also have their own getterand 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 setterith variable, the perimeternew 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:
  1. initialization of members of the child class;
  2. calling the constructor of the parent class;
  3. 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 willSetand didSetdates 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 enumerations Rankby 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 toRawand 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 method Colorthat returns a string “black”for Spadesand Clubsand “red”for Heartsand Diamonds.

Note how access is made to an Heartsenumeration 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 switchwe use the abbreviated form .Hearts, since the type of value is a selfpriori 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 successin 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 ServerResponsesunrise 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 mutatingin the structure definition SimpleStructurethat the compiler informs that the corresponding method is subjecting the structure to changes. In contrast, class methods SimpleClassdo 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 extension Doublewith a member variable absoluteValue.

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 protocolValuehas 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 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 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.

Also popular now: