0

Язык программирования swift

Данная книга содержит исчерпывающую информацию для всех желающих научиться программировать на замечательном языке Swift с целью создания собственных iOS-приложений (в том числе и для macOS, tvOS и watchOS) или программ для операционной системы Linux. В ходе чтения книги вы встретите не только теоретические сведения, но и большое количество практических примеров и заданий, выполняя которые вы углубите свои знания изучаемого материала.
В ходе долгого и плодотворного общения со многими из вас была выработана масса идей, благодаря которым новое издание стало по-настоящему полезным. В сравнении с предыдущим изданием данная книга содержит следующие изменения и дополнения:
— Весь материал актуализирован в соответствии со Swift версии 4.1 и Xcode 9.
— Добавлено большое количество нового учебного материала, в частности, связанного с практической разработкой приложений под iOS.
— Доработана глава, посвященная типу данных String.
— Учтены пожелания и замечания пользователей по оформлению и содержанию.
— Исправлены найденные опечатки. Выделен материал, предназначенный для начинающих программистов, в отдельные блоки, чтобы позволить читателям с опытом разработки на других языках не отвлекаться на ненужный им материал.

Структура книги

Книга состоит из пяти больших частей и одного приложения:
Часть I. Подготовка к разработке Swift-приложений. В первой части вы начнете ваше путешествие в мир Swift, выполните самые важные и обязательные шаги, предшествующие началу разработки собственных приложений. Вы узнаете, как завести собственную учетную запись Apple ID, как подключиться к программе apple-разработчиков, где взять среду разработки Swift-приложений, как с ней работать.
Часть II. Базовые возможности Swift. После знакомства со средой разработки Xcode, позволяющей приступить к изучению языка программирования, вы изучите базовые возможности Swift. Вы узнаете, какой синтаксис имеет Swift, что такое переменные и константы, какие типы данных существуют и как всем этим пользоваться при разработке программ.
Часть III. Основные средства Swift. Третья часть фокусируется на рассмотрении и изучении наиболее простых, но очень интересных средств Swift. О некоторых из них (например, о кортежах) вы, возможно, никогда не слышали, другие (например, массивы) вы, вероятно, использовали и в других языках.
Часть IV. Нетривиальные возможности Swift. В четвертой части подробно описываются приемы работы с наиболее мощными и функциональными средствами Swift. Материал этой части вы будете использовать с завидной регулярностью при создании собственных приложений в будущем. Также отличительной чертой данной части является большая практическая работа по созданию первого интерактивного приложения.
Часть V. Основы разработки приложений. В конце долгого и увлекательного пути изучения языка и создания некоторых простых приложений в Xcode Playground вам предстоит окунуться в мир разработки полноценных программ. Из этой части вы узнаете основы создания интерфейсов и работы программ в Xcode «под капотом». Все это в будущем позволит вам с успехом осваивать новый материал и создавать прекрасные проекты.

Приложение. Изменения и нововведения Swift 4.0. Если вы изучали какую-либо из предыдущих версий Swift, то информация, приведенная в данном приложении, позволит вам оперативно ознакомиться со всеми нововведениями и изменениями, которые принесла новая версия языка программирования.

Отрывок из книги. 29.3. Ограничения типа

Иногда бывает полезно указать определенные ограничения, накладываемые на типы данных универсального шаблона. В качестве примера мы уже рассматривали тип данных Dictionary, где для ключа существует требование: тип данных должен соответствовать протоколу Hashable.
Универсальные шаблоны позволяют накладывать определенные требования и ограничения на тип данных значения. Вы можете указать список типов, которым должен соответствовать тип значения. Если элементом этого списка является протокол (который также является типом данных), то проверяется соответствие типа значения данному протоколу; если типом является класс, структура или перечисления, то проверяется, соответствует ли тип значения данному типу.
Для определения ограничений необходимо передать перечень имен типов через двоеточие после заполнителя имени типа. Реализуем функцию, производящую поиск элемента в массиве и возвращающую его индекс (листинг 29.7).
ПРИМЕЧАНИЕ Для обеспечения функционала сравнения двух значений в Swift существует специальный протокол Equatable. Он обязывает поддерживающий его тип данных реализовать функционал сравнения двух значений с помощью операторов равенства (==) и неравенства (!=). Другими словами, если тип данных поддерживает этот протокол, то его значения можно сравнивать между собой.
Листинг 29.7
1 func findIndex<T: Equatable>(array: , valueToFind: T) -> Int? { 2 for (index, value) in array.enumerated() { 3 if value == valueToFind { 4 return index 5 } 6 } 7 return nil 8 } 9 var myArray = 10 let firstIndex = findIndex(array: myArray, valueToFind: 0.1) // 1 11 let secondIndex = findIndex(array: myArray, valueToFind: 31) // nil
Параметр типа записывается как <T: Equatable>. Это означает «любой тип, поддерживающий протокол Equatable». В результате поиск в переданном массиве выполняется без ошибок, поскольку тип данных Int поддерживает протокол Equatable, следовательно, значения данного типа могут быть приняты к обработке.

29.4. Расширения универсального типа

Swift позволяет расширять описанные универсальные типы. При этом имена заполнителей, использованные в описании типа, могут указываться и в расширении.
Расширим описанный ранее универсальный тип Stack, добавив в него вычисляемое свойство, возвращающее верхний элемент стека без его удаления (листинг 29.8).
Листинг 29.8
1 extension Stack { 2 var topItem: T? { 3 return items.isEmpty ? nil : items 4 } 5 }
Свойство topItem задействует заполнитель имени типа T для указания типа свойства. Данное свойство является опционалом, так как значение в стеке может отсутствовать. В этом случае возвращается nil.

29.5. Связанные типы

При определении протокола бывает удобно использовать связанные типы, указывающие на некоторый, пока неизвестный, тип данных. Связанный тип позволяет задать заполнитель типа данных, который будет использоваться при заполнении протокола. Фактически тип данных не указывается до тех пор, пока протокол не будет принят каким-либо объектным типом. Связанные типы указываются с помощью ключевого слова associatedtype, за которым следует имя связанного типа.
Определим протокол Container, использующий связанный тип ItemType (листинг 29.9).
Листинг 29.9
1 protocol Container { 2 associatedtype ItemType 3 mutating func append(item: ItemType) 4 var count: Int { get } 5 subscript(i: Int) -> ItemType { get } 6 }
Протокол Container (контейнер) может быть задействован в различных коллекциях, например в описанном ранее типе коллекции Stack. В этом случае тип данных, используемый в свойствах и методах протокола, заранее неизвестен.
Для решения проблемы используется связанный тип ItemType, который определяется лишь при принятии протокола типом данных. Пример принятия протокола к исполнению типом данных Stack представлен в листинге 29.10.
Листинг 29.10
1 struct Stack<T>: Container { 2 typealias ItemType = T 3 var items = () 4 var count: Int { 5 return items.count 6 } 7 init(){} 8 init(_ elements: T…){ 9 self.items = elements 10 } 11 subscript(i: Int) -> T { 12 return items 13 } 14 mutating func push(item: T) { 15 items.append(item) 16 } 17 mutating func pop() -> T { 18 return items.removeLast() 19 } 20 mutating func append(item: T) { 21 items.append(item) 22 } 23 }
Так как тип Stack теперь поддерживает протокол Container, в нем появилось три новых элемента: свойство, метод и сабскрипт. Ключевое слово typealias указывает на то, какой тип данных является связанным в данном объектном типе.
ПРИМЕЧАНИЕ Обратите внимание на то, что при описании протокола используется ключевое слово associatedtype, а при описании структуры — typealias.
Так как заполнитель имени использован в качестве типа аргумента item свойства append и возвращаемого значения сабскрипта, Swift может самостоятельно определить, что заполнитель T указывает на тип ItemType, соответствующий типу данных в протоколе Container. При этом указывать ключевое слово associatedtype не обязательно: если вы его удалите, то тип продолжит работать без ошибок.
» Более подробно с книгой можно ознакомиться на сайте издательства
Привет, Хабр! 2 июня все мы воочию могли наблюдать, как компания Apple начала творить революцию в стане Objective-C разработчиков, представив миру свой новый язык программирования – Swift. Вместе с этим, она выложила в открытый доступ небольшую документацию по языку, которую мы решили перевести, если на то будет спрос. Предлагаем вашему вниманию перевод первой главы. Если тема будет интересна, то мы продолжим публиковать перевод каждую неделю.

Добро пожаловать в Swift
О Swift
Введение в 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

О языке Swift

Swift – это новый язык программирования для разработки iOS и OS X приложений, который сочетает в себе все лучшее от C и Objective-C, но лишен ограничений, накладываемых в угоду совместимости с C. В Swift используются паттерны безопасного программирования и добавлены современные функции, превращающие создание приложения в простой, более гибкий и увлекательный процесс. Swift, созданый нами с чистого листа, – это возможность заново представить себе, как разрабатываются приложения.
Swift разрабатывался нами несколько лет. Основой нового языка программирования послужили существующие компилятор, отладчик и фреймворки. Мы упростили процесс управления памятью с помощью механизма автоматического подсчета ссылок – Automatic Reference Counting (ARC). Наши фреймворки также подверглись серьезной модернизации. Objective-C начал поддерживать блоки, литералы и модули – все это создало благоприятные условия для внедрения современных технологий. Именно эта подготовительная работа послужила фундаментом для нового языка программирования, который будет применяться для разработки будущих программных продуктов для Apple.
Разработчикам Objective-C Swift покажется знакомым. Он сочетает в себе читабельность именованных параметров и мощь динамической объектной модели Objective-C. Он открывает доступ к уже существующим фреймворкам Cocoa и совместим с кодом, написанным на Objective-C. Построенный на этой общей основе язык предлагает множество новых возможностей и унифицирует процедурные и объектно-ориентированные аспекты языка программирования.

Swift не отпугнет и начинающих программистов. Это первый мощный язык программирования, такой же понятный и увлекательный, как скриптовый язык. Он поддерживает так называемые playground-ы, которые позволяют программистам экспериментировать с кодом, видя результат в режиме реального времени без необходимости компилировать и запускать приложение.
Swift вобрал в себя все лучшее от современных языков и разработан с учетом обширного опыта компании Apple. Наш компилятор – синоним производительности, наш язык оптимизирован для разработки без оглядки на компромиссы. Он спроектирован таким образом, чтобы вы смогли легко разработать и ваше первое приложение «hello, world!», и даже целую операционную систему. Все это делает Swift важным инструментом для разработчиков и для самой компании Apple.
Swift – это новый фантастический способ создавать приложения для iOS и OS X, и мы продолжим развивать его, добавляя новый функционал и представляя новые возможности. Наша цель – амбициозна. И мы с нетерпением ждем, чтобы увидеть, что вы сумеете создать при помощи него.

Введение в Swift

По давней традиции первая программа на новом языке должна выводить на экран слова «Hello, world”. С помощью Swift это делается так:
println(«Hello, world»)
Если вы когда-нибудь разрабатывали на C или Objective-C этот синтаксис должен казаться вам до боли знакомым – в Swift эта строчка кода является законченной программой. Вам больше не нужно импортировать отдельные библиотеки для обеспечения базового функционала вроде ввода/вывода в консоль или работы со строками. Код, написанный в глобальной области видимости, является точкой входа в программу, таким образом функция main больше не нужна. Также обратите внимание на отсутствие точки с запятой в конце каждой строки.
Это введение содержит достаточно информации, чтобы начать писать код на Swift. Не переживайте, если вам будет что-то непонятно – мы все детально объясним в последующих главах.
Замечание
Для лучшего понимания материала мы рекомендуем использовать режим playground в Xcode. Playground позволяет вам видеть результат сразу в процессе редактирования кода без необходимости компилировать и запускать приложение.

Простые типы данных

Используйте let для создания константы и var для создания переменной. Тип константы указывать не нужно, вы можете присвоить ей значение лишь единожды.
var myVariable = 42 myVariable = 50 let myConstant = 42
Типы константы и переменной должны совпадать с типами присваиваемых им соответствующих значений. Однако это не означает, что вы должны напрямую указывать их тип. Компилятор автоматически определит тип константы и переменной при присваивании им значения. Так, в приведенном примере компилятор определит, что myVariable имеет целочисленный тип.
Если же инициализатор отсутствует или не предоставляет достаточной информации, вы можете указать тип самостоятельно после переменной, разделив название и тип двоеточием:
let implicitInteger = 70 let inplicitDouble = 70.0 let inplicitDouble: Double = 70
Давайте поэкспериментируем
Создайте константу с типом Float и проинициализируйте ее числом 4.
Значения никогда не конвертируются в другой тип неявно. Если вам необходимо конвертировать значение в другой тип, делайте это явно:
let label = «The width is » let width = 94 let widthLabel = label + String(width)
Давайте поэкспериментируем
Попробуйте удалить явное преобразование к типу String в последней строке. Какую ошибку вы получите?
Имеется более простой способ включения значений в строки: для этого заключите выражение в скобки и поставьте перед ними обратный слэш (\). Пример:
let apples = 3 let oranges = 5 let appleSummary = «I have \(apples) apples.» let fruitSummary = «I have \(apples + oranges) pieces of fruit.»
Давайте поэкспериментируем
Попробуйте использовать конструкцию \() и выведите на экран строку, включающую результат суммы двух целочисленных переменных и чье-нибудь имя.
При работе с массивами и ассоциативными массивами (словарями, dictionary) используются квадратные скобки ():
var shoppingList = shoppingList = «bottle of water» var occupations = occupations = «Public Relations»
Чтобы создать пустой массив или dictionary, используйте следующий синтаксис:
let emptyArray = String() let emptyDictionary = Dictionary<String, Float>()
Для создания пустых массивов и словарей используйте и соответственно, – например, когда вы присваиваете новое значение переменной или передаете аргумент в функцию.
shoppingList = // Went shopping and bought everything.

Условия и циклы

Для создания условий используются операторы if и switch, для создания циклов – for-in, for, while и do-while. При этом выделять круглыми скобками условия и инициализирующие выражения необязательно, тогда как фигурные скобки обязательны.
let individualScores = var teamScore = 0 for score in individualScores { if score > 50 { teamScore += 3 } else { teamScore += 1 } } teamScore
Условие внутри оператора if должно быть логическим, это в частности означает, что выражение if score {…} является ошибочным, поскольку здесь нет явного сравнения (например, с нулем).
Условный оператор if можно использовать совместно с let и var для работы с константами и переменными, которые могут иметь значение nil. Такие константы и переменные называются опциональными (то есть они могут либо принимать какое-либо значение, либо быть равны nil). Чтобы создать опциональную переменную или константу добавьте знак вопроса (?) после указания типа.
var optionalString: String? = «Hello» optionalString == nil var optionalName: String? = «John Appleseed» var greeting = «Hello!» if let name = optionalName { greeting = «Hello, \(name)» }
Давайте поэкспериментируем
Измените optionalName на nil. Что вы видите на экране? Добавьте блок else для обработки случая, когда optionalName равен nil.
Если опциональное значение равно nil, условие будет ложным и код в фигурных скобках после if выполнен не будет. В противном случае переменной greeting будет присвоено новое значение.
Оператор множественного выбора switch поддерживает внутри себя множество других операторов сравнения и не ограничен лишь простыми сравнениями:
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.» }
Давайте поэкспериментируем
Попробуйте удалить условие по умолчанию. Какую ошибку вы получите?
После выполнения подходящего блока кода, программа покидает оператор switch, не проверяя последующие условия. Таким образом вам не нужно вручную добавлять операторы прерывания (break) в конце каждого блока case.
Для перебирания элементов ассоциативного массива используйте оператор for-in совместно с указанием пары имен для каждой пары ключ-значение.
let interestingNumbers = , «Fibonacci»: , «Square»: , ] var largest = 0 for (kind, numbers) in interestingNumbers { for number in numbers { if number > largest { largest = number } } } largest
Давайте поэкспериментируем
Добавьте еще одну переменную, которая позволит выяснить, к какому из трех типов относится найденное максимальное число.
Оператор цикла while позволяет выполнять блок кода внутри него до тех пор, пока условие не станет ложным. Условие также может быть указано после блока, который в таком случае будет выполнен по крайней мере один раз.
var n = 2 while n < 100 { n = n * 2 } n var m = 2 do { m = m * 2 } while m < 100 m
Оператор for можно использовать для перебора последовательности чисел с помощью двух точек (..) или с помощью инициализатора, условия и инкремента. Посмотрите, эти два цикла делают одно и то же:
var firstForLoop = 0 for i in 0..3 { firstForLoop += i } firstForLoop var secondForLoop = 0 for var i = 0; i < 3; ++i { secondForLoop += 1 } secondForLoop
При создании цикла используйте две точки (..), если не хотите включать большее значение в диапазон, и три точки (…), чтобы включить как меньшее, так и большее значения.

Функции и замыкания.

Для объявления функций используйте ключевое слово func. Вызов функции производится через указание ее имени и списка аргументов в круглых скобках. Возвращаемый тип следует отделить от перечня формальных аргументов с помощью ->.
func greet(name: String, day: String) -> String { return «Hello \(name), today is \(day).» } greet(«Bob», «Tuesday»)
Давайте поэкспериментируем
Удалите параметр day. Вместо него добавьте переменную, обозначающую наименование подаваемого на обед блюда.
Если функция возвращает множество значений, следует использовать кортеж:
func getGasPrices() -> (Double, Double, Double) { return (3.59, 3.69, 3.79) } getGasPrices()
Функции также могут иметь неопределенное число аргументов:
func sumOf(numbers: Int…) -> Int { var sum = 0 for number in numbers { sum += number } return sum } sumOf() sumOf(42, 597, 12)
Давайте поэкспериментируем
Напишите функцию, позволяющую находить среднее арифметическое произвольного числа своих аргументов.
Функции могут вкладываться друг в друга. Вложенная функция может обращаться к переменным, объявленным во внешней функции. Используйте вложенные функции, чтобы привести в порядок код сложной или большой функции.

func returnFifteen() -> Int { var y = 10 func add() { y += 5 } add() return y } returnFifteen()
Функции являются объектами первого класса (first-class type), иными словами, функция в качестве свого результата может возвращать другую функцию.
func makeIncrementer() -> (Int -> Int) { func addOne(number: Int) -> Int { return 1 + number } return addOne } var increment = makeIncrementer() increment(7)
Функция также может принимать другую функцию в качестве одного из аргументов.
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 = hasAnyMatches(numbers, lessThanTen)
Функции являются частным случаем замыканий. Вы можете создать замыкание, не указывая его имени и окружив тело замыкания фигурными скобками ({}). Для отделения аргументов и типа возвращаемого значения от тела замыкания используйте оператор in.
numbers.map({ (number: Int) -> Int in let result = 3 * number return result })
Давайте поэкспериментируем
Перепишите замыкание таким образом, чтобы оно возвращало ноль для всех лишних чисел.
Существует несколько техник, позволяющих делать замыкания более лаконичными. Если тип замыкания априори известен (например, это callback делегата), можно опустить указание типа его параметров и/или типа возвращаемого значения. Замыкания, состоящие из единственного выражения, неявно возвращают результат этого выражения.
numbers.map({ number in 3 * number })
В замыкании вместо указания имени переменной, вы можете использовать ее порядковый номер – это особенно полезно при написании коротких замыканий. Замыкание, являющееся последним аргументом функции, может быть передано в нее сразу после круглых скобок с перечнем остальных параметров.
sort() { $0 > $1 }

Объекты и классы

Для создания класса используется зарезервированное слово class. Члены класса объявляются точно так же, как и обычные константы и переменные. Более того, методы класса объявляются как обычные функции.
class Shape { var numberOfSides = 0 func simpleDescription() -> String { return «A shape with \(numberOfSides) sides.» } }
Давайте поэкспериментируем
Добавьте константу-член класса и метод класса, принимающую ее в качестве своего аргумента.
Чтобы создать экземпляр (объект) класса, достаточно добавить круглые скобки после названия класса. Доступ к методам и членам класса осуществляется через точку.
var shape = Shape() shape.numberOfSides = 7 var shapeDescription = shape.simpleDescription()
В этом примере мы упустили одну важную деталь – конструктор класса, метод 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.» } }
Обратите внимание, как член класса name при помощи self отделен от аргумента конструктора name. Аргументы передаются в конструктор обычным образом, как и в любой другой метод класса. Обратите внимание на то, что каждый член класса должен быть проинициализирован – либо при объявлении (как, например, numberOfSides), либо в конструкторе (как name).
Деструктор класса – метод deinit, который можно переписать в случае необходимости.
Чтобы наследовать класс от уже существующего класса, после указания имени дочернего класса следует поставить двоеточие и указать название родительского. В Swift нет никаких ограничений по обязательному наследованию какого-либо стандартного класса.
Переопределенные дочерним классом методы должны быть помечены ключевым словом override – переопределение методов без override приведет к ошибке. Компилятор также выявляет методы, маркированные override, но не переопределяющие какие-либо методы своего родительского класса.
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()
Давайте поэкспериментируем
Создайте класс Circle и наследуйте его от класса NamedShape. Конструктор класса Circle принимает два аргумента – радиус и название. Переопределите методы area и describe этого класса.
Члены класса могут также иметь собственные getter и 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
В setter-е переменной perimeter новое присваиваемое значение неявно называется newValue. Вы можете изменить название этой переменной, указав его в скобках сразу после set.
Обратите внимание на структуру конструктора класса EquilateralTriangle. Этот метод включает в себя три последовательных шага:

  1. инициализация членов дочернего класса;
  2. вызов конструктора родительского класса;
  3. изменение значений членов родительского класса.

Если вам необходимо выполнить определенный код до или после присваивания нового значения переменной, вы можете переопределить методы willSet и didSet нужным вам образом. Например, в приведенном ниже классе гарантируется, что длина стороны треугольника всегда будет равна длине стороны квадрата.
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
У методов классов имеется одно важное отличие от функций. Названия аргументов функции используются только в пределах этой функции, тогда как в методе класса параметры также используются при вызове этого метода (кроме первого параметра). По умолчанию метод класса имеет одинаковые названия параметров как при вызове, так и внутри себя. Однако вы можете указать другое название (в примере ниже – times), которое будет использовано только внутри этого метода. При этом для вызова этого метода необходимо использовать первое название (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)
При работе с опциональными значениями добавьте знак вопроса (?) перед методами, членами класса и т.д. Если значение перед знаком вопроса равно nil, все, что следует после (?) игнорируется и значение всего выражения равно nil. В противном случае выражение вычисляется обычным образом. В обоих случаях результатом всего выражения будет опциональное значение.
let optionalSquare: Square? = Square(sideLength: 2.5, name: «optional square») let sideLength = optionalSquare?.sideLength

Перечисления и Структуры

Для создания перечислений используется ключевое слово enum. Отметим, что перечисления также могут иметь в своем составе методы.
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()
Давайте поэкспериментируем
Напишите функцию, которая сравнивает 2 перечисления типа Rank по их значениям.
В вышеприведенном примере элементы перечисления первоначально имеют целочисленный тип, и вам достаточно указать значение только первого элемента – значения остальных элементов будут определены в соответствии с порядком их следования. В качестве исходного типа (raw value) значений элементов вы также можете выбрать строковый или вещественные типы.
Для преобразования исходного типа значения в тип перечисления используйте функции toRaw и fromRaw.
if let convertedRank = Rank.fromRaw(3) { let threeDescription = convertedRank.simpleDescription() }
Отметим, что значения элементов перечисления являются фактическими, а не просто иной записью своих исходных значений. Вообще говоря, вы можете и не указывать их исходные значения.
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()
Давайте поэкспериментируем
Добавьте метод Color, возвращающий строку «black” для Spades и Clubs и «red” для Hearts и Diamonds.
Обратите внимание на то, как осуществляется доступ к члену Hearts перечисления Suit. При присваивании значения константе hearts используется полное имя Suit.Hearts, поскольку мы явно не указываем тип этой константы. А в switch мы используем сокращенную форму .Hearts, поскольку тип значения self априори известен. Вы можете использовать сокращенную форму повсеместно, если тип переменной явно указан.

Для создания структур используется ключевое слово struct. Структуры имеют множество схожих черт с классами, включая методы и конструкторы. Одним из наиболее существенных отличий структур от классов является то, что экземпляры структур, в отличие от экземпляров классов, передаются в функции по значению (то есть предварительно создается их локальная копия), тогда как экземпляры классов передаются по ссылке.
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()
Давайте поэкспериментируем
Добавьте в структуру Card метод, который создает полную колоду карт.
Экземпляр члена перечисления может иметь собственные значения и они могут быть разными. Вы присваиваете эти значения при создании экземпляра перечисления (константа success в примере). Связанные и исходные значения это разные вещи: исходное значение члена перечисления всегда постоянно для всех экземпляров перечисления и указывается при его объявлении.
Рассмотрим пример получения с сервера времени восхода и заката Солнца. Сервер отправляет в ответ либо соответствующую информацию, либо сообщение об ошибке.
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)» }
Давайте поэкспериментируем
Добавьте третий вариант в оператор множественного выбора switch
Обратите внимание, каким образом из объекта ServerResponse «вытаскиваются” время восхода и заката.

Протоколы и Расширения.

Для объявления протокола используйте ключевое слово protocol.
protocol ExampleProtocol { var simpleDescription: String { get } mutating func adjust() }
Протоколы могут поддерживаться классами, перечислениями и структурами.
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
Давайте поэкспериментируем
Создайте перечисление, которое будет реализовывать этот протокол.
Обратите внимание на ключевое слово mutating в определении структуры SimpleStructure, которое информирует компилятор о том, что соответствующий метод подвергает структуру изменениям. В противовес этому методы класса SimpleClass не нужно маркировать как mutating, поскольку методы класса всегда могут беспрепятственно его изменять.
Для добавления новых методов или членов класса в уже существующий тип необходимо использовать расширения – extensions. Вы также можете использовать расширения для реализации протокола уже существующим типом, даже если он импортирован из какой-либо библиотеки или фреймворка.
extension Int: ExampleProtocol { var simpleDescription: String { return «The number \(self)» } mutating func adjust() { self += 42 } } 7.simpleDescription
Давайте поэкспериментируем
Создайте расширение типа Double с переменной-членом absoluteValue.
Вы можете использовать название протокола как и любой другой тип – например, чтобы создать массив объектов разного типа, но реализующих общий протокол. Заметьте, что при работе с объектами такого типа методы, объявленные вне протокола, будут недоступны.
let protocolValue: ExampleProtocol = a protocolValue.simpleDescription // protocolValue.anotherProperty // Uncomment to see the error
Несмотря на то, что во время выполнения программы переменная protocolValue имеет тип SimpleClass, компилятор считает, что ее тип – ExampleProtocol. Это означает, что вы не сможете случайно получить доступ к методам или членам класса, которые реализуются вне протокола ExampleProtocol.

Обобщенные типы (generics)

Для создания обобщенного типа, заключите имя в угловые скобки (<>).
func repeat<ItemType>(item: ItemType, times: Int) -> ItemType { var result = ItemType() for i in 0..times { result += item } return result } repeat(«knock», 4)
Создавайте обобщенные функции, классы, перечисления и структуры.
// Reimplement the Swift standard library’s optional type enum OptionalValue<T> { case None case Some(T) } var possibleInteger: OptionalValue<Int> = .None possibleInteger = .Some(100)
Если вы хотите задать обобщенные типу определенные требования, такие, как, например, реализация протокола или требование быть наследованным от определенного класса, используйте 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(, )
Давайте поэкспериментируем
Измените функцию anyCommonElements таким образом, чтобы она возвращала массив общих элементов.
В простых случаях вы можете опустить where и написать имя протокола или класса после двоеточия. Выражение <T: Equatable> эквивалентно выражению <T where T: Equatable>.
Хотите внедрить подписки в iOS-приложение за 10 минут? Интегрируйте Apphud и:
— оформляйте покупки с помощью лишь одного метода;
— автоматически отслеживайте состояние подписки каждого пользователя;
— легко интегрируйте Subscription Offers;
— отправляйте события о подписках в Amplitude, Mixpanel, Slack и Telegram с учетом локальной валюты пользователя;
— уменьшайте Churn rate в приложениях и возвращайте отписавшихся пользователей.

На основе статьи «Internationalization and localization of Apps in Xcode 6 and Swift»

Поддержка нескольких локализаций важна для любого приложения. Это может позитивно сказаться на его дальнейшем продвижении, да и всегда приятно видеть программу на родном языке, ведь не каждый может владеть, например, английским. В этой статье мы научимся создавать в мобильном приложении для iOS локализации текста, даты, чисел, валюты и изображений на примере русского и английского языков.

В итоге мы должны получить такой результат:

Для работы нам понадобится компьютер с macOS и средой разработки Xcode версии 8 и выше.

Подготовка проекта

Создадим новый проект. Для этого открываем Xcode и выбираем в меню File → New → Project:

В появившемся окне нужно выбрать «Single View Application» для iOS:

После нажатия на кнопку Next нужно ввести название проекта (Locale), выбрать команду разработчиков (поле можно оставить пустым), указать организацию (в примере используется «GS Develop», но можно выбрать любое другое название), создать идентификатор приложения (любой, но необходимо соблюдать формат, указанный на скриншоте), язык проекта (Swift) и устройство (iPhone).

После этого сохраним приложение в любом удобном месте на компьютере.

Настройка Main.storyboard

Откроем файл Main.storyboard и создадим копию того, что изображено на скриншоте ниже:

Стоит отметить, что мы будем создавать приложение только для iPhone. Никакой оптимизации под разные экраны мы проводить не будем. Нам нужно добавить 9 элементов Label и 1 элемент ImageView. Это не вызовет у вас трудностей, если вы уже работали с Xcode. Настраивайте внешний вид по своему усмотрению.

Назначаем IBOutlets для UIControls

Для этого откроем Assistant Editor и перетащим поля «text», «number», «currency», «date» и «UIImageView» в файл ViewController.swift. В результате мы получим следующее:

Прописываем значения для текстовых полей

Теперь мы можем закрывать Assistant Editor и переходить непосредственно к файлу ViewController.swift. Здесь мы создаем функцию populateValues() и вызываем ее в override func viewDidLoad():

func populateValues() { textLabel.text = «Good Morning» numberLabel.text = «9999999.999» currencyLabel.text = «50000» dateLabel.text = «30/05/2017» imageView.image = UIImage(named: «adele-hello») }

Далее нужно найти в интернете картинку (в нашем примере это картинка с надписью «Hello») и добавить ее в Assets.xcassets. Следующим шагом следует вернуться к Main.storyboard и выбрать это изображение для ImageView. При запуске проекта мы должны получить результат, представленный на скриншоте ниже:

Локализируем значения для даты, валюты и цифр

Для локализации значений нам нужно добавить три переменных в функцию populateValues().

var numberFormatter: NumberFormatter { let formatter = NumberFormatter() formatter.numberStyle = .decimal return formatter } var currencyFormatter: NumberFormatter { let formatter = NumberFormatter() formatter.numberStyle = .currency return formatter } var dateFormatter: DateFormatter { let formatter = DateFormatter() formatter.dateStyle = .medium formatter.timeStyle = .medium return formatter }

Необходимо переписать и саму функцию. Теперь она будет выглядеть так:

func populateValues() { var numberFormatter: NumberFormatter { let formatter = NumberFormatter() formatter.numberStyle = .decimal return formatter } var currencyFormatter: NumberFormatter { let formatter = NumberFormatter() formatter.numberStyle = .currency return formatter } var dateFormatter: DateFormatter { let formatter = DateFormatter() formatter.dateStyle = .medium formatter.timeStyle = .medium return formatter } textLabel.text = «Good Morning» numberLabel.text = numberFormatter.string(from: 9999999.999) currencyLabel.text = currencyFormatter.string(from: 5000) dateLabel.text = dateFormatter.string(from: NSDate() as Date) imageView.image = UIImage(named: «adele-hello») }

После внесения этих изменений при запуске приложения можно будет увидеть локализованные поля:

Добавляем поддержку второго языка

Чтобы добавить поддержку русского языка, нужно добавить его в наш проект. Для этого переходим в Project target и во вкладке Localization выбираем русский язык:

В окне добавления файлов локализации мы можем добавить поддержку нескольких языков для главного экрана и экрана запуска. В этой статье мы будем использовать только локализацию для главного экрана:

Настройка локализаций на главном экране приложения

Откроем новые элементы для Main.strings (Russian) и введем русские названия для некоторых значений:

/* Class = «UILabel»; text = «Image:»; ObjectID = «O8K-TD-2G8»; */ «O8K-TD-2G8.text» = «Изображение:»; /* Class = «UILabel»; text = «Date:»; ObjectID = «fL7-x8-mUA»; */ «fL7-x8-mUA.text» = «Дата:»; /* Class = «UILabel»; text = «Number:»; ObjectID = «hw4-lj-ril»; */ «hw4-lj-ril.text» = «Число:»; /* Class = «UILabel»; text = «Text:»; ObjectID = «iKI-eF-xxs»; */ «iKI-eF-xxs.text» = «Текст:»; /* Class = «UILabel»; text = «Currency:»; ObjectID = «pLd-MI-tDr»; */ «pLd-MI-tDr.text» = «Валюта:»;

Готово!

Тестируем на симуляторе

Чтобы проверить работу локализаций, нам нужно установить русский язык в симуляторе. Для этого переходим в Scheme Option. В появившемся окне переходим во вкладку Options, выбираем Application Language и устанавливаем русский язык:

Теперь при запуске приложения мы видим частично локализованное приложение:

Осталось локализировать изображение и текст «Good Morning».

Локализация текста с использованием NSLocalizedString

Чтобы заменить текст «Good Morning» на «Доброе утро», нужно добавить в проект файл типа Strings и назвать его Localizable.strings:

Теперь откроем этот файл в File Inspector, перейдем в пункт Localize и выберем русский язык. Затем отметим галочками английский и русский языки:

Теперь у нас появились файлы Localizable.strings (Russian) и Localizable.strings (English):

Добавим следующие строки в файл Localizable.Strings (English):

GOOD_MORNING=»Good Morning”;

Добавим также строки в файл Localizable.Strings (Russian):

GOOD_MORNING=»Доброе Утро»;

После этого вернемся к файлу ViewController.swift и заменим строку:

textLabel.text = «Good Morning»

на строку:

textLabel.text = NSLocalizedString(«GOOD_MORNING»,comment:»Good Morning»)

В итоге наш код должен выглядеть так:

textLabel.text = NSLocalizedString(«GOOD_MORNING»,comment:»Good Morning») numberLabel.text = numberFormatter.string(from: 9999999.999) currencyLabel.text = currencyFormatter.string(from: 5000) dateLabel.text = dateFormatter.string(from: NSDate() as Date) imageView.image = UIImage(named:»adele-hello»)

При запуске приложения мы должны увидеть новую надпись:

Локализация изображений

Чтобы локализировать изображение, добавляем в каталог Assets.xcassets картинку с русским текстом (можно взять любую подходящую). После этого переходим в Localizable.Strings и прописываем такие строки:

Для Localizable.Strings (English):

IMG=»adele-hello»;

Для Localizable.Strings (Russian):

IMG=»hello»;

Затем вернемся к файлу ViewController.swift и заменим строку с названием изображения:

imageView.image = UIImage(named: «adele-hello»)

на строку:

imageView.image = UIImage(named: «\(NSLocalizedString(«IMG»,comment:»adele-hello»))»)

Теперь при запуске приложения мы сможем увидеть локализованное изображение:

На этом все. Исходные файлы проекта можно скачать на Github.

Хинт для программистов: если зарегистрируетесь на соревнования Huawei Honor Cup, бесплатно получите доступ к онлайн-школе для участников. Можно прокачаться по разным навыкам и выиграть призы в самом соревновании.

Предисловие

Недавно Apple представила общественности достаточно важное изменение в разработке iOS приложений, анонсировав новый язык программирования Swift. Я принял решение: изучая этот язык пошагово, я буду в своих статьях рассказывать обо всём, что мне удалось найти. Это лишь первый пост из многих на эту тему, но я надеюсь, что вы решите изучать язык вместе со мной!
Весьма вероятно, что многие примеры кода, представленные в постах, будут позже изменены. Это отчасти потому, что мой стиль программирования – написать сейчас, чтобы проверить идею, а потом заняться рефакторингом, а также отчасти это потому, что для меня (как и для многих других) Swift – абсолютно новый язык программирования. Так что, скорее всего, как только я узнаю что-нибудь новое, ход этих уроков будет меняться.
Итак, я собираюсь начать с довольно простого приложения. Также я буду объяснять, как работает код. Готовы? Поехали…
UPD: Статья написана в соответствии с изменениями в XСode 6 Beta 5

Основы

Swift отменяет использование стандарта объявления переменных, который использует имя типа перед объявлением переменной, вместо этого для объявления, как и в JavaScript, применяется ключевое слово var.
Так, например, эта строка Objective-C
NSString *myString = @»This is my string.»;
в Swift будет заменена на эту
var myString = «This is my string.»
Для объявления констант используется ключевое слово let
let kSomeConstant = 40
В данном случае kSomeConstant неявно определяется как целое число. Если же вы хотите конкретизировать тип, то вы можете это сделать так:
let kSomeConstant: Int = 40
Немножко о Чистом КодеАвтор перевода рекомендует пользоваться вторым примером объявления констант. Довольно показательный пример описан в официальной документации:
let implicitInteger = 70 let implicitDouble = 70.0 let explicitDouble: Double = 70
В этом автор перевода солидарен с Helecta (см. соответствующий пост)
И массивы, и словари описываются с помощью
var colorsArray = var colorsDictionary =
Это ещё далеко не всё, однако я считаю, что эти основы достаточно важны для дальнейшего чтения учебника. Итак, давайте перейдём к Hello, World!

Hello, World!

В первую очередь, мы напишем самое простое приложение, которое только можно представить, чтобы начать работу — Hello, World!
Наше приложение будет делать только одно: печатать фразу «Hello, World!» в консоль. Для этого нам потребуется установленная IDE XCode, для скачивания которой необходима учётная запись разработчика. Если у вас есть аккаунт, то смело качайте с официального сайта XCode 6 beta 4, желательно это сделать до прочтения ниже описанного.

Итак, вы установили свою копию IDE. Теперь давайте выведем «Hello, World!» на консоль.Этот пример не только демонстрирует простейшее приложение, которое можно написать, но и, что немаловажно, показывает, что среда разработки настроена правильно.
В XCode создайте проект с помощью шаблона приложения с одним видом («Single View Application»).

Убедитесь, что вы выбрали Swift в качестве языка приложения.

Теперь вы должны найти файл AppDelegate.swift в иерархии проекта. Внутри найдите следующую строку:
«// Override point for customization after application launch.»
Замените эту строку на наш изумительный код:
println(«Hello World»)

Теперь нажмите «Run». Вы должны увидеть загрузившееся пустое приложение и слова Hello, World!, напечатанные в консоли, расположенной в нижней части окна XCode. Заметьте, это не будет отображаться в симуляторе iPhone.

Поздравляю! Вы только что написали своё первое приложение на Swift! Правда оно не получит никаких премий, призов, только ваши собственные овации. А теперь, давайте копнём немножко глубже…

Добавление Table View

В этом разделе мы попробуем добавить материал на экран. Откройте в XCode файл Main.storyboard, перенесите из Библиотеки Объектов («Object Library») объект Table View на экран приложения, растяните таблицу так, чтобы она совпала с краями. Затем измените высоту, перетянув верхний край, оставив небольшое пространство сверху (это необходимо для строки состояния). Если вы запустите приложение, то увидите в симуляторе пустую таблицу.
Теперь необходимо создать делегат и источник данных для таблицы. Это легче всего сделать в конструкторе интерфейса. Нажмите клавишу «Command», кликните и перетащите Table View к объекту View Controller в иерархии файла .storyboard, и выберите «источник данных» («data source»). Повторите с опцией «delegate».
Примечание:Я получил целую тонну вопросов о вышесказанном, т.к. многие люди сообщают о том, что у них не устанавливается табличный вид. Поэтому, я записал видео, поясняющее как работает подключение объектов Storyboard в коде. Для просмотра перейдите в полноэкранный режим и выберите опцию 720p, чтобы вы могли заметить всё, что вас интересует. Это будет немного отличаться от интерфейса XCode, но функционально всё работает также.

А теперь давайте углубимся в методы протоколов для табличного представления. Из-за использования UITableViewDataSource и UITableViewDelegate мы должны изменить определение класса.
Откройте файл и замените строку
class ViewController: UIViewController {
следующей
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
Если вы нажмёте клавишу «Command» и кликните на один из этих протоколов, то увидите «обязательные» функции. Для табличного представления необходимы как минимум эти две:
func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell!
Изменим наш класс View Controller путём добавления этих двух функций:
func tableView(tableView: UITableView!, numberOfRowsInSection section: Int) -> Int { return 10 } func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! { let cell: UITableViewCell = UITableViewCell(style: UITableViewCellStyle.Subtitle, reuseIdentifier: «MyTestCell») cell.textLabel.text = «Row #\(indexPath.row)» cell.detailTextLabel.text = «Subtitle #\(indexPath.row)» return cell }

Первый метод возвращает количество строк в разделе. В нашем примере используется «магическое» число 10, однако вместо него, как правило, должна использоваться длинна контроллера массива. Наш же код умышленно упрощён.
Во втором методе происходит чистой воды волшебство. Мы создаём экземпляр класса UITableViewCell(cell), используя стиль ячейки Subtitle. Затем мы присваиваем текстовому значению этой ячейки значение «Row #\(indexPath.row)». В Swift подобным образом происходит встраивание значений переменных в строку без конвертации (таким образом, мы получим строки вида «Row #1», «Row #2»)
Детализированная текстовая метка (detailTextLabel) доступна только при использовании класса ячеек Subtitle, который мы используем в данном примере.
При запуске приложения вы увидите восхитительный список ячеек с заголовками и подзаголовками, показывающие номера их строк. Это один из наиболее распространённых способов отображения данных в iOS, вы убедитесь, он вам ещё не раз пригодится. Увидеть полный текст кода вы можете на github.
В следующем посте мы исследуем использование API поиска iTunes для создания приложения, способного искать и отображать альбомы внутри iTunes Store.

Вопрос о дальнейшем переводе

Уважаемые Хабралюди! Если вы прочли этот перевод, помогите автору определиться с будущим цикла.
Для этого всего лишь требуется поучаствовать в опросе.

admin

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *