Все о новом языке программирования Swift для iOS и OS X на Русском. Swift ios


Язык программирования Swift. Русская версия / Хабр

Привет, Хабр! 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 = ["catfish", "water", "tulips", "blue paint"] shoppingList[1] = "bottle of water" var occupations = [ "Malcolm": "Captain", "Kaylee": "Mechanic", ] occupations["Jayne"] = "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 = [75, 43, 103, 87, 12] 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 = [ "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 Давайте поэкспериментируем Добавьте еще одну переменную, которая позволит выяснить, к какому из трех типов относится найденное максимальное число. Оператор цикла 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 = [20, 19, 7, 12] hasAnyMatches(numbers, lessThanTen) Функции являются частным случаем замыканий. Вы можете создать замыкание, не указывая его имени и окружив тело замыкания фигурными скобками ({}). Для отделения аргументов и типа возвращаемого значения от тела замыкания используйте оператор in.numbers.map({ (number: Int) -> Int in let result = 3 * number return result }) Давайте поэкспериментируем Перепишите замыкание таким образом, чтобы оно возвращало ноль для всех лишних чисел. Существует несколько техник, позволяющих делать замыкания более лаконичными. Если тип замыкания априори известен (например, это callback делегата), можно опустить указание типа его параметров и/или типа возвращаемого значения. Замыкания, состоящие из единственного выражения, неявно возвращают результат этого выражения.numbers.map({ number in 3 * number }) В замыкании вместо указания имени переменной, вы можете использовать ее порядковый номер – это особенно полезно при написании коротких замыканий. Замыкание, являющееся последним аргументом функции, может быть передано в нее сразу после круглых скобок с перечнем остальных параметров.sort([1, 5, 3, 12, 2]) { $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([1, 2, 3], [3]) Давайте поэкспериментируем Измените функцию anyCommonElements таким образом, чтобы она возвращала массив общих элементов. В простых случаях вы можете опустить where и написать имя протокола или класса после двоеточия. Выражение <T: Equatable> эквивалентно выражению <T where T: Equatable>.

habr.com

Swift Часть 2: Простое iOS приложение

Во втором туториале вы узнаете как создать свое первое приложение для iOS. Конкретно, что мы сделаем тут, так это создадим пользовательский интерфейс для нашего класса TipCalculator, который был в прошлом туториале. Мы написали наши уроки так, чтобы они были полезны как для совсем начинающих программистов iOS, так и для бывалых программистов, знакомящихся со Swift.

Для этого туториала вам понадобится Xcode минимальной версии 6.1.1 (время написания этой статьи). Вам не нужен какой-либо опыт в программировании на Swift или Objective-C, но если он все таки есть, то он лишь ускорит процесс усвоения материала.

Поехали!

Запустите Xcode и пройдите по File\New\Project. Выберите iOS\Application\Single View Application и нажмите Next.

В графе Product Name (имя приложения) напишите TipCalculator, установите Language на Swift и смените Devices на iPhone. Use Core Data выбирать не нужно. После, нажмите Next.

Выберите директорию для сохранения проекта и нажмите Create.

Давайте взглянем, что создал для нас Xcode: в верхнем левом углу выберите iPhone Simulator и нажмите кнопку Play.

Если вы все сделали правильно, то вы увидите симулятор с белым экраном:

Xcode создал одностраничное приложение с пустым белым экраном. Но не переживайте, в этом туториале вы его заполните!

Создаем модель приложения

Первое должно быть первым - до того как мы приступим к пользовательскому интерфейсу, вы должны создать модель приложения. Модель приложения - класс (или несколько классов), который отображает данные вашего класса и операции, которые будет проводить ваше приложение с этими данными.

В этом туториале ваша модель будет просто представлена классом TipCalculator, который вы создали на прошлом уроке, только мы переименуем его в TipCalculatorModel.

Давайте добавим класс в ваш проект. Чтобы это сделать нужно пройти File\New\File и выбрать iOS\Source\Swift File. Называем файл TipClculatorModel.swift и жмем Create.

Заметка

Вы не можете обратиться к коду, который располагается в файле Playground'а. Playground нужен только для тестирования вашего или его макетирования. Если вы хотите использовать код из playground'а, то вам придется просто его перенести в файл Swift, как мы и сделаем тут.

Откройте TipCalculator.swift и скопируйте туда ваш класс TipCalculator (но только класс и больше ничего!) из файла предыдущего туториала и сделайте следующее:

  1. Переименуйте класс в TipCalculatorModel
  2. Поменяйте total и taxPct из констант в переменные (потому что пользователь будет менять эти данные, когда запустит приложение)
  3. Из-за этого вам нужно поменять subtotal в вычисляемое свойство. Замените свойство subtotal на следующее:
var subtotal: Double { get { return total / (taxPct + 1) } }

Фактически, вычисляемое свойство не хранит значения. Вместо этого оно его вычисляет, каждый раз, основываясь на других значениях. Здесь subtotal считается каждый раз, когда мы обращаемся к нему, основываясь на значениях total и taxPct.

Заметка

Вы также можете использовать метод setter для вычисляемого свойства, если вам нравится синтаксис вроде этого:

var subtotal: Double { get { return total / (taxPct + 1) } set(newSubtotal) { //... } }

Однако, setter будет обновлять ваши свойства total и taxPct, основываясь на newSubtotal, но для нас это бессмысленно, так что имплементировать это мы не будем.

  1. Удалите строку, которая устанавливает subtotal в init.
  2. Удалите все комментарии, которые есть в файле

В итоге у вас должно получиться следующее:

import Foundation   class TipCalculatorModel {   var total: Double var taxPct: Double var subtotal: Double { get { return total / (taxPct + 1) } }   init(total: Double, taxPct: Double) { self.total = total self.taxPct = taxPct }   func calcTipWithTipPct(tipPct: Double) -> Double { return subtotal * tipPct }   func returnPossibleTips() -> [Int: Double] {   let possibleTipsInferred = [0.15, 0.18, 0.20] let possibleTipsExplicit:[Double] = [0.15, 0.18, 0.20]   var retval = [Int: Double]() for possibleTip in possibleTipsInferred { let intPct = Int(possibleTip*100) retval[intPct] = calcTipWithTipPct(possibleTip) } return retval   }   }

С моделью приложения мы разобрались, настало время поработать над интерфейсом!

Вступление в Storyboards и в Interface Builder

Заметка

Если вы уже бывалый разработчик под iOS, то эта статья и следующая будут для вас легкими. Для ускорения процесса вы можете сразу перейти в секцию "Обзор View Controller".

Вы создаете интерфейс приложения в Storyboard. В Xcode есть встроенный инструмент для удобного редактирования Storyboard, который называется Interface Builder.

В Interface Builder вы можете размещать: кнопки, текст, ярлыки и другие элементы (которые называются Views). Осуществляется все это с помощью простого перетягивания с панели, на экран вашего приложения.

Двигаемся дальше, нажмите на Main.storyboard в левой части Xcode, для того, чтобы показать Storyboard в Interface Builder.

Тут много всякого для изучения, так что давайте не будем сильно торопиться и будем двигаться постепенно:

  1. В левом краю у вас есть Project Navigator или навигатор проекта, где отображаются все файлы вашего проекта.
  2. Слева в Interface Builder располагается Document Outline (схема документа), где вы можете быстро взглянуть на все элементы располагающиеся на вашем "экране". При нажатии на стрелочки вы получите развернутую иерархию элементов вашего приложения на текущем "экране" (View Controller). На данный момент у вас всего один View Controller или "экран" с одним пустым белым View (смотри в иерархии). Мы скоро добавим сюда некоторые элементы.
  3. Стрелка слева от View Controller свидетельствует о том, что это входной View Controller или "экран", то есть это именно тот вид, который появляется при загрузке приложения. Вы можете изменить первоначальный "экран" просто перетащив стрелку на другой, которого у нас нет.
  4. Внизу Interface Builder'а вы видите что-то вроде "w Any", "h Any". Это значит, что вы редактируете ваш внешний вид приложения, который будет работать в интерфейсе любого размера. Вы можете сделать это через опцию Auto Layout. Кликнув на область экрана, вы можете переключить редактирование отображения для устройств принадлежащих определенному классу размеров. Об этом вы узнаете из наших будущих статей.
  5. Наверху View Controller'а вы увидите маленькие иконки, которые отображают сам View Controller, First Responder, Exit. Если вы немного программировали в Xcode ранее, то вы заметили, что эти иконки ранее были внизу. Этими иконками в этой статье мы пользоваться не будем, так что пока не берите в голову.
  6. Внизу справа Interface Builder'а четыре иконки для Auto Layout. Ну о них мы поговорим в следующих туториальных.
  7. Справа вверху Interface Builder'а располагается Inspectors (инспекторы) для выбранного вами элемента. Если у вас ничего нет, то пройдите по меню View\Utilities\Show Utilities. Обратите внимание, что тут несколько вкладок, мы будем использовать их в этом туториале для конфигурации вида.
  8. Внизу справа Interface Builder'а располагаются библиотеки или Libraries. Это перечень различных элементов, которые вы можете добавить к виду вашего приложения. Уже очень скоро мы перетащим несколько элементов из Libraryes на View Controller(экран) вашего приложения.

Создание элементов интерфейса приложения

Помните, что ваш класс TipCalculatorModel имеет два значения для ввода: общая сумма (total) и процент налога (tax percentage).

Было бы здорово, если бы пользователь смог вводить значения с цифровой клавиатуры, таким образом, текстовое поле (Text Field) подходит идеально для этих целей. Что же касается ввода процента налога, то обычно запрещено использовать его для маленьких значений, так что лучше мы будем использовать слайдер (Slider).

В дополнение к текстовому вводу и слайдеру, нам также нужно: установить ярлыки (Label), панель навигации для отображения названия приложения, кнопку для выполнения вычислений и еще одно текстовое поле для вывода результата.

Давайте детально рассмотрим пользовательский интерфейс.

  1. Navigation Panel (или панель навигации). Вместо того, чтобы добавить панель навигации напрямую, выберите ваш View Controller, выделив его в иерархии документов как на рисунке:

    После этого идите в Editor\Embed In\Navigation Controller. Это установит вам панель навигации в ваш View Controller (отныне экран будем называть именно так). Сделайте двойной щелчок на панели навигации (Navigation Bar, которая внутри вашего View Controller) и установите имя Tip Calculator.

  2. Labels (или ярлыки). Перетащите Label или по-русски «ярлык» в ваш View Controller.

    Сделайте двойной щелчок мыши по ярлыку Label и напишите Bill Total (Post-Tax):. Выберите Label и нажмите на пятую вкладку в Inspector, установите там X=33 и Y=81. Повторите все то же самое и для второго ярлыка, только назовите его Tax Percentage (0%): X=20 и Y=120.

  3. Text Field (текстовое поле). Из библиотеки элементов перетащите объект под названием Text Field в ваш View Controller. В инспекторе атрибутов (atribute inspector) установите Keyboard Type = Decimal Pad. В инспекторе размеров (Size Inspector) установите: X=192, Y=77 и Width=392. 
  4. Slider (или слайдер). Перетащите Slider из библиотеки в ваш View Controller. В Attribute Inspector установите Minimum Value=0 (минимальное значение), Maximum Value=10 (максимальное значение) и Current Value=6 (текущее значение). В Size Inspector установите X=190, Y=116 и Width=396.
  5. Button (или кнопка). Из Object Library (привыкаем к английскому) перетаскиваем Button (или кнопку) в ваш View Controller. Сделайте двойной щелчок на кнопке и переименуйте в Calculate. В Size Inspector установите X=268, Y=154.
  6. Text View. Из Object Library перетаскиваем Text View в ваш View Controller. Сделайте двойной щелчек на Text View и удалите текст плейсхолдера. В Attribute Inspector убедитесь в том, что Editable и Selectable не выбраны! В Size Inspector установите X=16, Y=192, Width=568, Height=400.
  7. Tap Gesture Recognizer (элемента, распознающий движения). Из Object Library перетаскиваем Tap Gesture Recognizer в ваш Main View (используйте иерархию элементов для того, чтобы точно выделить Main View, а не элемент внутри него).

    Эта штука будет работать тогда, когда пользователь будет нажимать на элемент view для того, чтобы клавиатура исчезла.

  8. Auto Layout или (автопозиционирование). Interface Builder часто делает всю грязную работу за нас, когда устанавливает ограничения расположения элементов самостоятельно. В нашем случае мы как раз это и можем использовать. Для того, чтобы это сделать, выберите Main View в иерархии документа и нажмите третью кнопку в правой нижней части Interface Builder'а. Выберете Add Missing Constraints (или добавить недостающие ограничения расположения элементов).

Теперь запустите свой симулятор iPhone 6 и вы должны увидеть, что базовый интерфейс пользователя уже работает!

Тур по View Controller

Заметка

Если вы сразу прыгнули до этой секции, то вот вам наш plaground в zip!

Только что вы создали модель и внешний вид приложения, теперь самое время разобраться с View Controller!

Откройте ViewController.swift. Тут находится код вашего одиночного View Controller'а (экрана) приложения. Эта штуковина отвечает за взаимодействие ваших элементов с вашей моделью.

Вы увидите, что этот класс уже имеет вот такой код внутри:

\\ import UIKit // 2 class ViewController: UIViewController { // 3 override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. }   // 4 override func didReceiveMemoryWarning() { super.didReceiveMemoryWarning() // Dispose of any resources that can be recreated. }   }

Есть несколько моментов, c которыми в ы до сих пор не сталкивались. Давайте разберемся с ними по одному:

  1. iOS разделена на множество frameworks(или фреймворки), каждый из которых содержит различные наборы кода. До того как использовать эти наборы в вашем приложении, вы должны импортировать этот фреймворк. UIKit - фреймворк, который содержит базовые классы для работы с View Controller'ми и элементами управления, такими как: кнопки, поля ввода текста и множество других.
  2. Это первый пример, где вы видите класс, который является подклассом. Вы объявляете ViewController, который является подклассом UIViewController.
    Заметка

    Опытные разработчики заметили, что ненужно ставить префикс класса перед именем класса, как вы это делали в Objective-C для избежания совпадения имен (другими словами, вам не нужно называть это RWTViewController). Все потому, что в Swift есть пространство имен и классы, которые вы создали в вашем проекте.

    Для того, чтобы вы поняли, что мы имеем в виду, замените объявление класса следующим кодом:

    class UIViewController { }   class ViewController: UIKit.UIViewController {

    Здесь UIKit.UIViewController относится к классу UIViewController в пространстве имени UIKit. Так же как и TipCalculator.UIViewController будет относиться к к классу UIViewController в вашем проекте.

    Не забудьте удалить этот тестовый код и вернуть предыдующее объявление ViewController

  3. Этот метод вызван корневым view этого View Controller'а, когда к нему получен доступ. Всякий раз, когда вы переписываете метод в Swift, вы должны использовать ключевое слово override. Это помогает избежать вам случайного изменения метода.
  4. Этот метод вызывается, когда у вашего устройства остается мало памяти. Это удачное место для чистки ваших ресурсов, которые вы можете припасти для дальнейших операций.

Соединяем ваш View Controller с вашими Views (элементами)

Сейчас, у вас уже сложилось правильное понимание класса View Controller, что ж, давайте добавим некоторые свойства для ваших subviews (элементов, которые находятся внутри view в иерархии документа) и подключим их в interface builder'е.

Для того, чтобы сделать это, добавьте следующие свойства в ваш класс ViewController(прямо перед viewDidLoad):

@IBOutlet var totalTextField : UITextField! @IBOutlet var taxPctSlider : UISlider! @IBOutlet var taxPctLabel : UILabel! @IBOutlet var resultsTextView : UITextView!

Тут мы объявляем четыре переменные, прямо так, как вы изучили в нашем первом туториале: UITextField, UISlider, UILabel и UITextView.

Здесь всего два отличия:

  1. Вы пишите перед переменными ключевое слово @IBOutlet. Interface Builder сканирует код в поиске каких-либо свойств в вашем View Controller'е с этим словом. У найденных свойств он ищет соединения, так что вы смело можете их соединить с элементами (или Views).
  2. Вы обозначаете переменные восклицательным знаком (!). Это значит, что ваши переменные опциональны, но их значения неявно извлекаются. Этот замечательный способ сказать, что вы пишите код, предполагая, что они имеют значения, и ваше приложение завершится с ошибкой, если вдруг хотя бы одно из них окажется без значения, т.е. nil.

    Неявное извлекаемые опционалы - удобный способ для того, чтобы создать переменные, которым точно будут присвоены значения до того, как они будут использованы (как создание элементов пользовательского интерфейса в Storyboard), так что вы не должны извлекать значение опционала каждый раз перед использованием. Давайте попробуем соединить эти свойства с элементами пользовательского интерфейса.

Откройте Main.storyboard и выберите ваш View Controller (Tip Calculater в нашем случае) в иерархии документа (Document Outline). Откройте Connection Inspector (6 вкладка) и вы увидите все свойства, которые вы создали в секции Outlets.

Вы наверняка заметили маленький круг справа от resultsTextView. Удерживайте клавишу ctrl и перетащите курсор с него и до text view, который под Calculate button, и отпустите. Вы только что соединили свойство с элементом (или view).

Теперь, сами повторите тоже самое и для остальных трех свойств. Подключите каждое из них к соответствующему элементу пользовательского интерфейса.

Заметка

Есть даже более простой способ подключения свойств к элементам.

Пока вы держите Main.storyboard открытым, вы можете открыть Assiastand Editor (View\Assistant Editor\Show Assistant Editor), убедитесь, что ваш assistant editor отображает ваш код view controller'а.

После чего вы можете перетащить (с ctrl) ваш элемент в Assistant Editor, прямо до viewDidLoad. В появившемся окне вам будет нужно ввести имя свойства для того чтобы его создать, после чего жмем Connect.

Такой метод позволит вам создать свойство и сразу его подключить, и все это за один раз. Неплохо, правда?

Оба способа работают хорошо, а вот который вам ближе, решать вам.

Подсоединяем Actions(действия) к вашему View Controller'у

Точно также как вы соединили элементы со свойствами вашего view controller'а, вам нужно так же соединить определенные действия ваших элементов (например, нажатие кнопки) с методами вашего view controller'а.

Откроем ViewController.swift и добавим эти три метода в ваш класс:

@IBAction func calculateTapped(sender : AnyObject) { } @IBAction func taxPercentageChanged(sender : AnyObject) { } @IBAction func viewTapped(sender : AnyObject) { }

Когда вы объявляете функции обратного вызова для действий с элементом они всегда должны иметь ту же подпись - функция без возвращаемого значения, которая принимает один параметр типа AnyObject, как параметр, который представляет класс любого типа.

Заметка

AnyObject - эквивалент для id в Objective-C.

Для того чтобы Interface Builder заметил ваши новые методы, вам нужно использовать ключевое слово @IBAction (точно так же как и в случае с @IBOutlet).

Далее, откройте Main.stroryboard и убедитесь что в схеме документа вы выделили свой view controller. Также убедитесь, что ваш Connections Inspector (6 вкладка) открыт и вы увидите новые методы в секции Recieved Actions.

Найдите кружок справа от calculateTapped и перетащите его на кнопку Calculate. В появившемся окне выберите Touch Up Inside:

Другими словами мы говорим :"Вызывать мой метод calculateTapped: тогда, когда пользователь уже убирает палец с экрана".

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

  • Перетащим taxPercentageChanged на ваш слайдер и соединим его с действием Value Changed, которое вызывается каждый раз, как только пользователь двигает слайдер.
  • Перетащим viewTapped на Tap Gesture Recognizer в схему документа. Никаких действий для этого нет, так что ваш метод будет просто вызываться, когда распознает соответствующее движение.
Заметка

Точно так же как и в случае со свойствами, так и для методов, есть сокращенный вариант соединения с действиями, используя Interface Builder.

Вы можете просто ctrl-перетащить от, скажем, кнопки в ваш Swift код вашего View Controller'а в Assistant Editor. В появившемся окне вам нужно выбрать Action и дать имя вашего метода.

Таким образом вы создадите метод в вашем Swift файле и соедините его с действием всего за один шаг. И снова, используйте тот метод соединения, который вам больше всего нравится.

Соединение вашего View Controller'а с вашей моделью

Уже почти все закончили. Все что осталось сделать, так это соединить ваш View Controller с вашей моделью.

Откройте ViewController.swift и добавьте свойство для модели вашего класса и метод для обновления пользовательского интерфейса:

let tipCalc = TipCalculatorModel(total: 33.25, taxPct: 0.06) func refreshUI() { // 1 totalTextField.text = String(format: "%0.2f", tipCalc.total) // 2 taxPctSlider.value = Float(tipCalc.taxPct) * 100.0 // 3 taxPctLabel.text = "Tax Percentage (\(Int(taxPctSlider.value))%)" // 4 resultsTextView.text = "" }

Давайте пройдемся в refreshUI по одной строчке:

  1. В Swift вам нужно явно конвертировать один тип в другой. Здесь мы конвертируем tipCalc.total из Double в String
  2. Вы хотите, чтобы процент налога отображался как целое число (то есть от 0%-10%), чем дробное (что-то вроде 0.06). Так что просто умножим на 100.
  3. Помните, что это действие необходимо, так как свойство taxPctSlider.value является типом Float.
  4. Тут мы используем интерполяцию для обновления ярылка, отображающего процент налога.
  5. Очищаем результат в текстовом поле (text view) до тех пор пока пользователь не нажал кнопку Calculate.

Следующее, добавим вызов refreshUI внизу в viewDidLoad:

refreshUI()

Так же имплементируем taxPercentageChanged и viewTapped:

@IBAction func taxPercentageChanged(sender : AnyObject) { tipCalc.taxPct = Double(taxPctSlider.value) / 100.0 refreshUI() } @IBAction func viewTapped(sender : AnyObject) { totalTextField.resignFirstResponder() }

taxPercentageChanged просто обращает "умножение на 100", в то время как viewTapped вызывает resignFirstResponder в поле totalTextField, когда пользователь нажал на view(что и заставляет клавиатуру исчезнуть с экрана).

Остался один метод. Имплементируем calculateTapped:

@IBAction func calculateTapped(sender : AnyObject) { // 1 tipCalc.total = Double((totalTextField.text as NSString).doubleValue) // 2 let possibleTips = tipCalc.returnPossibleTips() var results = "" // 3 for (tipPct, tipValue) in possibleTips { // 4 results += "\(tipPct)%: \(tipValue)\n" } // 5 resultsTextView.text = results }

Давайте все разберем по порядку:

Заметка

Вот как это все работает, если вам это интересно.

Во время написания этого туториала класс String в Swift не имеет доступа ко всем методам, в отличии от NSString (NSString - класс в фреймворке Foundation). Если быть конкретным, то класс String в Swift не имеет метода, который бы преобразовывал тип String в тип Double, хотя NSSting такой метод имеет.

Вы можете вызвать (XXX as NSString)() в Swift для преобразования String в NSString. После чего вы можете использовать любой метод, доступный для NSString, например, метод, преобразующий String в Double.

  1. Тут нужно преобразовать тип String в Double. Это несколько коряво так делать, но думаем, уже скоро будет способ поприличнее.
  2. Здесь вы вызываете метод returnPossibleTips модели tipCalc, которая возвращает словарь с возможными процентами чаевых.
  3. Это то, как вы перебираете словарь через ключи и значения одновременно. Удобно, не так ли?
  4. Здесь мы используем интерполяцию строк, для создания строки, которую поместим в текстовое поле. Мы используем \n для начала новой строки.
  5. Наконец, мы присваиваем текстовому полю значение result.

Вот и все! Запускайте и наслаждайтесь вашим калькулятором чаевых!

Это конечный вариант нашего Xcode проекта.

Урок подготовил: Иван АкуловИсточник урока: http://www.raywenderlich.com/74904/swift-tutorial-part-2-simple-ios-app

swiftbook.ru

iOS программирование на Swift в примерах

FlatMap против nil

Если функция (метод или инициализатор) может вернуть nil, а мы будем использовать эту функцию в качестве преобразователя в .map, то можно сразу отсеять элементы nil и вернуть не опциональные элементы

// // proSwift.ru // class Event { init?(...) { ... return someValue else ... return nil } } objects.map(Event.init) // [obj1?, obj2?, nil, obj4?, nil, nil, obj6?] objects.flatMap(Event.init) // [obj1, obj2, obj4, obj6]

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

//

//  proSwift.ru

//

 

class Event {

    init?(...) {

        ...

        return someValue

 

        else ...

        return nil

    }

}

 

objects.map(Event.init) // [obj1?, obj2?, nil, obj4?, nil, nil, obj6?]

 

objects.flatMap(Event.init) // [obj1, obj2, obj4, obj6]

Вызовы Event.init могут вернуть nil, а flatMap на этих объектах удалит любые значения nil, поэтому на выходе мы получим объекты Event (не опциональные!)

Лог потоков

Вывод в консоль лога по потокам:

// // proSwift.ru // print("main: \(Thread.isMainThread)") // Проверка - это основной поток? print("Thread: \(Thread.current)") . // Вывод данных о потоке

//

//  proSwift.ru

//

 

print("main: \(Thread.isMainThread)") // Проверка - это основной поток?

print("Thread: \(Thread.current)") .  // Вывод данных о потоке

Консоль:

main: true Thread: <NSThread: 0x608000077680>{number = 1, name = main} ... main: false Thread: <NSThread: 0x608000274ac0>{number = 3, name = (null)}

main: true

Thread: <NSThread: 0x608000077680>{number = 1, name = main}

 

...

 

main: false

Thread: <NSThread: 0x608000274ac0>{number = 3, name = (null)}

Reduce для Observable

// // proSwift.ru // example(of: "reduce") { let source = Observable.of(1,2,3,4,5,6) let obs = source.reduce(0, accumulator: +) // выдаст элемент только когда source закончится. obs.subscribe(onNext: { print($0) }) }

//

//  proSwift.ru

//

 

example(of: "reduce") {

    let source = Observable.of(1,2,3,4,5,6)

    let obs = source.reduce(0, accumulator: +) // выдаст элемент только когда source закончится.

    obs.subscribe(onNext: { print($0) })

}

Reduce (_: _ 🙂 создает сводное (накопленное) значение только тогда, когда завершается исходный наблюдаемый. Применение этого оператора к последовательностям, которые никогда не завершатся, не испускает ничего. Это частый источник путаницы и скрытых проблем

Вконтакте

Facebook

Twitter

Google+

Одноклассники

proswift.ru

iDev Swift | Язык программирования Apple Swift на Русском для iOS

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

Swift разрабатывался компанией Apple несколько лет с нуля О Swift.

В основу нового языка программирования Apple заложила все лучшее от C и Objective-C. Swift использует паттерны безопасного программирования и содержит современные функции, которые помогают сделать программирование гибким и легким.

Swift как язык программирование на ios для начинающих дружелюбен. Это первый язык программирования промышленного качества, который же понятен и увлекателен, как скриптовый язык. Он поддерживает playground'ы - инновационная функция, которая позволяет экспериментировать с кодом Swift и видеть результат мгновенно,  без необходимости компилировать и запускать приложение.

Только лучшие краны bitcoin, c моментальными выплатами на FaucetBox. 

post

iOS 9 и El Capitan доступны для публичного бета-тестирования Стала доступна публичная бета El Capitan.

post

Про Стэнфордский курс разработки на Swift мы уже знаем давно. Но в iTunes U также доступен, совершенно бесплатно, курс от Высшей Школы Экономики на русском языке по Swift.

post

Согласно информации ресурса Stack Overflow, почти 80 процентов профессионалов с удовольствием работали или планируют работать с выпущенным не так давно инструментом разработки от Apple.

post

Новый Playgrounds В Xcode 6.3 beta 2  появились значительные изменения в Playground Swift. Изменения предоставляют большие возможности для авторов. Благодаря новым возможностям, теперь еще проще создавать интерактивные площадки — идеальные для документации, учебных пособий и образцов.

post

Выбираем нужный элемент в Storyboard или .Xib Если вы, при создании приложения, используете Storyboard или Xib-файлы, а на макете у вас очень много элементов. То есть 2 быстрых способа выбрать нужный из всего множества.

idev-swift.ru

Swift | Введение

Язык Swift и платформы iOS и Mac OS

Последнее обновление: 25.12.2017

Устройства компании Apple являются признанным лидером в сфере информационных технологий. По последним данным доля iOS среди других мобильных операционных систем колеблется в районе 15-20%, а доля Mac OSX среди настольных систем составляет по разным данным 15-20%. Подобное широкое распространение устройств от компании Apple рождает потребность в программном обеспечении для этих устройств.

Традиционно основным языком программирования под iOS и MacOS был Objective-C, однако 2 июня 2014 года на конференции разработчиков Apple WWDC 2014 был представлен новый и более удобный язык программирования - Swift. По сравнению с Objective-C Swift обладает следующими особенностями:

  • Swift является чистым объектно-ориентированным языком программирования

  • Простота, ясный и четкий синтаксис

  • Строгая типизированность. Каждая переменная имеет определенный тип

  • Автоматическое управление памятью

Однако при этом Swift полностью совместим с ранее написанными прикладными интерфейсами Cocoa API, для которых использовались C и Objective-C.

При этом Swift продолжает развиваться. 19 сентября 2017 года вышла версия 4.0, которая добавила новые возможности для разработки под iOS и Mac OS. В принципе каждый год выходят новые версии.

Swift является компилируемым языком программирования. То есть разработчик пишет исходный код и затем, используя компилятор, компилирует этот код в управляющую программу. Затем этот файл программы можно загрузить в AppStore и распространять среди других пользователей.

Что нужно для разработки для iOS?

В первую очередь необходима соответствующая операционная система Mac OS 10.12 Yosemite или выше. Без Mac OS практически невозможно скомпилировать программу. Данное обстоятельство сильно ограничивает возможности разработки, учитывая тот факт, что Mac OS может гарантированно работать лишь на компьютерах самой компании Apple (iMac, MacBook, MacBook Air, MacBook Pro), а также учитывая высокую стоимость этих самых компьютеров. Однако на обычном PC под управлением ОС Windows или ОС на базе Linux создавать приложения под iOS и Mac OS практически невозможно.

Существуют также варианты с виртуальными машинами, на которые установлена Mac OS, либо использование Хакинтош, однако работоспособность подобных вариантов не гарантирована.

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

Непосредственно для самой разработки нам потребуются инструменты языка Swift, текстовый редактор для написания кода, симуляторы iPhone и iPad для отладки приложения. Для всех этих и многих других функций разработки Apple предоставляет бесплатную среду разработки XCode.

Нужны ли реальные устройства iPhone или iPad для тестирования разрабатываемых приложений? По большому счету нет, так как XCode предоставляет симуляторы для тестирования, однако в некоторых отдельных случаях предпочтительнее тестировать на реальном смартфоне.

Регистрация разработчика

И также непосредственно перед тем, как приступить к созданию приложений, необходимо зарегистрироваться в центре Apple для разработчиков. Для этого надо пройти по ссылке https://developer.apple.com/register/:

Для регистрации надо будет войти на сайт с помощью своего идентификатора Apple ID и пароля. Если вдруг такого идентификатора у вас нет, то можно создать новую учетную запись, перейдя по ссылке Create Apple ID. После регистрации сайт перенаправит на страницу https://developer.apple.com/resources/, где можно найти различные материалы по разработке для самых разных аспектов.

metanit.com

Учебник Swift — разработка приложения для iOS8 [Часть 1, Hello World!] / Хабр

Предисловие
Недавно Apple представила общественности достаточно важное изменение в разработке iOS приложений, анонсировав новый язык программирования Swift. Я принял решение: изучая этот язык пошагово, я буду в своих статьях рассказывать обо всём, что мне удалось найти. Это лишь первый пост из многих на эту тему, но я надеюсь, что вы решите изучать язык вместе со мной!

Весьма вероятно, что многие примеры кода, представленные в постах, будут позже изменены. Это отчасти потому, что мой стиль программирования – написать сейчас, чтобы проверить идею, а потом заняться рефакторингом, а также отчасти это потому, что для меня (как и для многих других) Swift – абсолютно новый язык программирования. Так что, скорее всего, как только я узнаю что-нибудь новое, ход этих уроков будет меняться.

Итак, я собираюсь начать с довольно простого приложения. Также я буду объяснять, как работает код. Готовы? Поехали…UPD: Статья написана в соответствии с изменениями в XСode 6 Beta 5

Основы
Swift отменяет использование стандарта объявления переменных, который использует имя типа перед объявлением переменной, вместо этого для объявления, как и в JavaScript, применяется ключевое слово var. Так, например, эта строка Objective-CNSString *myString = @"This is my string."; в Swift будет заменена на этуvar myString = "This is my string." Для объявления констант используется ключевое слово letlet kSomeConstant = 40 В данном случае kSomeConstant неявно определяется как целое число. Если же вы хотите конкретизировать тип, то вы можете это сделать так:let kSomeConstant: Int = 40 Немножко о Чистом КодеАвтор перевода рекомендует пользоваться вторым примером объявления констант. Довольно показательный пример описан в официальной документации:let implicitInteger = 70 let implicitDouble = 70.0 let explicitDouble: Double = 70 В этом автор перевода солидарен с Helecta (см. соответствующий пост) И массивы, и словари описываются с помощью []var colorsArray = ["Blue", "Red", "Green", "Yellow"] var colorsDictionary = ["PrimaryColor":"Green", "SecondaryColor":"Red"] Это ещё далеко не всё, однако я считаю, что эти основы достаточно важны для дальнейшего чтения учебника. Итак, давайте перейдём к 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.

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

habr.com

Управление зависимостями в iOS-приложения на Swift со спокойствием / Хабр

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

Начав писать на Swift, мне пришлось столкнуться с многими проблемами, и одна из них — отсутствие конкуренции у IoC контейнеров на этом языке. По сути их всего два: Typhoon и Swinject. Swinject имеет мало возможностей, а Typhoon написан для Obj-С, что является проблемой, и работать с ним для меня оказалось большим стрессом.

И тут Остапа понесло я решил написать свой IoC контейнер для Swift, что из этого получилось читать под катом: Итак, знакомьтесь — DITranquillity, IoC контейнер для iOS на Swift, интегрированный со Storyboard.

Интересная история про название — после сотни разных идей, остановился на «спокойствие». При придумывании названия я отталкивался от того, что основной причиной написание IoC контейнера был Typhoon. Вначале были мысли, назвать библиотеку стихийным бедствием посильнее тайфуна, но понял, что надо думать по другому: тайфун — это стресс, а моя библиотека должна обеспечить обратное, то есть спокойствие.

Планировалось все проверять статически (к сожалению, полностью все не удалось), а не падать в середине выполнения приложения по непонятным причинам что при использовании тайфуна в больших приложения не так уж и редко происходит, и xcode иногда не собирает проект из-за тайфуна, а падает при сборке.

Любители Typhoon, возможно, слегка расстроятся, но, мой взгляд, на именование некоторых сущностей отличается от взгляда тайфуна. Он такой же, как у Autofac, но с учетом особенностей языка.

Начну с описания особенностей библиотеки:
  • Библиотека работает с чистыми Swift классами. Не надо наследоваться от NSObject и объявлять протоколы как Obj-C, c помощью данной библиотеки можно писать на чистом Swift;
  • Нативность — описание зависимостей происходит на родном языке, что позволяет делать легкий рефакторинг и...
  • Большая часть проверок на этапе компиляции — после Typhoon это может показаться раем, так как многие опечатки обнаруживаются на этапе компиляции, а не во время исполнения. К сожалению, похвастаться тем, что во время исполнения не могут возникнуть ошибки библиотека не может, но часть проблем, будьте уверены, отсекутся;
  • Поддержка всех паттернов Dependency Injection: Initializer Injection, Property Injection и Method Injection. Не знаю, почему это круто, но все пишут об этом;
  • Поддержка циклических зависимостей — библиотека поддерживает много различных вариантов циклических зависимостей, при этом без вмешательства программиста;
  • Интеграция со Storyboard — позволяет внедрять зависимости прямо во ViewController-ы.
А также:
  • Поддержка времени жизни объектов;
  • Указание альтернативных типов;
  • Разрешение зависимостей по типу и имени;
  • Множественная регистрация;
  • Разрешение зависимостей с параметрами инициализации;
  • Короткая запись для разрешения зависимости;
  • Специальные механизмы для «модульности»;
  • Поддержка CocoaPods;
  • Документация на русском (на самом деле, правильнее сказать черновая документация, там ошибок много).
И все это в 1500 строк кода, при этом порядка 400 строк из них, это автоматически генерируемый код, для типизированного разрешения зависимостей с разным количеством параметров инициализации.

Кратко

Начну с небольшого примера синтаксиса: да простит меня Autofac, за то, что написал их пример, адаптированный под свою библиотеку.// Classes class TaskRepository: TaskRepositoryProtocol { ... } class LogManager: LoggerProtocol { ... } class TaskController { var logger: LoggerProtocol? = nil private let repository: TaskRepositoryProtocol init(repository:TaskRepository) { self.repository = repository } ... } // Register let builder = DIContainerBuilder() builder.register(TaskRepository.self) .asType(TaskRepositoryProtocol.self) .initializer { TaskRepository() } builder.register(LogManager.self) .asType(LoggerProtocol.self) .initializer { LogManager(Date()) } builder.register(TaskController.self) .initializer { (scope) in TaskController(repository: *!scope) } .dependency { (scope, taskController) in taskController.logger = try? scope.resolve() } let container = try! builder.build() // Resolve let taskController: TaskController = container.resolve()

А теперь по-порядку

Базовая интеграция в проект

В отличии от Typhoon, библиотека не поддерживает «автоматическую» инициализацию из plist, или подобные «фичи». В принципе, несмотря на то, что тайфун поддерживает такие возможности, я не уверен в их целесообразности.

Чтобы интегрироваться с проектом, который планируется более-менее крупным, нам надо:

  1. Интегрировать саму библиотеку в проект. Это можно сделать с помощью Cocoapods:pod 'DITranquillity'
  2. Объявить базовую сборку с помощью библиотеки (опционально):import DITranquillity class AppAssembly: DIAssembly { // Объявляем нашу сборку var publicModules: [DIModule] = [ ] var intermalModules: [DIModule] = [ AppModule() ] var dependencies: [DIAssembly] = [ // YourAssembly2(), YourAssembly3() - зависимости на другие сборки ] }
  3. Объявить базовый модуль (опционально):import DITranquillity class AppModule: DIModule { // Объявляем наш модуль func load(builder: DIContainerBuilder) { // Согласно протоколу реализуем метод // Регистрируем типы } }
  4. Зарегистрировать типы в модуле (см. первый пример выше).
  5. Зарегистрировать базовую сборку в билдере и собрать контейнер:import DITranquillity @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { public func applicationDidFinishLaunching(_ application: UIApplication) { ... let builder = DIContainerBuilder() builder.register(assembly: AppAssembly()) try! builder.build() // Собираем контейнер // Если во время сборки произошла ошибка, то программа упадет, с описанием всех ошибок, которые нужно поправить } }

Storyboard

Следующим этапом, после написания пары классов, создается Storyboard, если его до этого еще не было. Интегрируем его в наши зависимости. Для этого нам нужно будет немного отредактировать базовый модуль:class AppModule: DIModule { func load(builder: DIContainerBuilder) { builder.register(UIStoryboard.self) .asName("Main") // Даем типу имя по которому мы сможем в будущем его получить .instanceSingle() // Говорим что он должен быть единственный в системе .initializer { scope in DIStoryboard(name: "Main", bundle: nil, container: scope) } // Регистрируем остальные типы } } И изменим AppDelegate:public func applicationDidFinishLaunching(_ application: UIApplication) { .... let container = try! builder.build() // Собираем наш контейнер window = UIWindow(frame: UIScreen.main.bounds) let storyboard: UIStoryboard = try! container.resolve(Name: "Main") // Получаем наш Main storyboard window!.rootViewController = storyboard.instantiateInitialViewController() window!.makeKeyAndVisible() }

ViewController'ы на Storyboard

И так мы запустили наш код порадовались, что ничего не упало и убедились, что у нас создался наш ViewController. Самое время создать какой-нибудь класс, и внедрить его во ViewController.

Создадим Presenter:

class YourPresenter { ... } Также нам понадобится дать имя (тип) нашему ViewController, и добавить инъекцию через свойства или метод, но в нашем коде мы воспользуемся инъекцией через свойства:class YourViewController: UIViewController { var presenter: YourPresenter! ... } Также не забудьте в Storyboard указать, что ViewController является не просто UIViewController, а YourViewController.

И теперь надо зарегистрировать наши типы в нашем модуле:

func load(builder: DIContainerBuilder) { ... builder.register(YourPresenter.self) .instancePerScope() // Говорим, что на один scope нужно создавать один Presenter .initializer { YourPresenter() } builder.register(YourViewController.self) .instancePerRequest() // Специальное время жизни для ViewController'ов .dependency { (scope, self) in self.presenter = try! scope.resolve() } // Объявляем зависимость } Запускаем программу, и видим что у нашего ViewController’а есть Presenter.

Но, погодите, что за странное время жизни instancePerRequest, и куда делся initializer? В отличии от всех остальных типов ViewController'ы, которые размещаются на Storyboard создаем не мы, а Storyboard, поэтому у нас нет initializer и они не поддерживают инъекцию через метод инициализации. Так как наличие initializer является одним из пунктов проверки при попытке создать контейнер, то нам надо объявить, что данный тип создается не нами, а кем-то другим — для этого существуем модификатор `instancePerRequest`.

Добавляем работу с данными

Дальше проект что-то должен делать и за частую на мобильных устройства приложения получают информацию из сети, обрабатывать её и отображают. Для простоты примера опустим шаг обработки данных и не будем вдаваться в детали получения данных из сети. Просто предположим, что у нас есть протокол Server, с методом `get` и соответственно есть реализация этого протокола. То есть у нас в программе появляется вот такой код:protocol Server { func get(method: String) -> Data? } class ServerImpl: Server { init(domain: String) { ... } func get(method: String) -> Data? { ... } } Теперь можно написать еще один модуль, который бы регистрировал наш новый класс. Конечно, можно пойти дальше и создать новую сборку, а саму работу с сервером перенести в другой проект, но это усложнит пример, хоть и покажет больше аспектов и возможностей библиотеки. Или, наоборот, встроить уже в существующий модуль.import DITranquillity class ServerModule: DIModule { func load(builder: DIContainerBuilder) { builder.register(ServerImpl.self) .asSelf() .asType(Server.self) .instanceSingle() .initializer { ServerImpl(domain: "https://your_site.com/") } } } Мы зарегистрировали тип ServerImpl, при этом в программе он будет известен под 2 типами: ServerImpl и Server. Это некоторая особенность поведения при регистрации — если указан альтернативный тип, то основной тип не используется, если не указать этого явно. Также мы указали, что сервер в нашей программе один.

Также слегка модифицируем нашу сборку, чтобы она знала о новом модуле:

class AppAssembly: DIAssembly { var publicModules: [DIModule] = [ ServerModule() ] } Различие между publicModules и internalModules

Существует два уровня видимости модулей: Internal и Public. Public — означает, что данный модуль будет виден, и в других сборках, которые используют эту сборку, Internal — модуль будет виден только внутри нашей сборки. Правда, надо уточнить, что так как сборка является всего лишь объявлением, то данное правило о видимости модулей распространяется на контейнер, по принципу: все модули из сборок которые были напрямую добавлены в builder, будут включены в собранный им контейнер, а модуля из зависимых сборок включаться в контейнер, только если он объявлены публичными.

Теперь поправим немного Presenter — добавим ему информацию о том, что ему нужен сервер:class YourPresenter { private let server: Server init(server: Server) { self.server = server } } Мы внедрили зависимость через метод инициализации, но могли сделать это, как и во ViewController’е — через свойства, или метод.

И дописываем регистрацию нашего Presenter — говорим, что мы будем внедрять Server в Presenter:

builder.register(YourPresenter.self) .instancePerScope() // Говорим, что на один scope нужно создавать один Presenter .initializer { (scope) in YourPresenter(server: *!scope) } Тут мы для получения зависимости использовали «быстрый» синтаксис `*!` который является эквивалентом записи: `try! scope.resolve()`

Запускаем нашу программу и видим, что у нашего Presenter'а есть Server. Теперь его можно использовать.

Внедряем логер

Наша программа работает, но у каких-то пользователей она неожиданно стала работать не корректно. Мы не можем воспроизвести проблему у себя и решаем — все пора, нам нужен логер. Но так как у нас уже проснулась вера в паранормальное, то логер должен писать данные в файл, в консоль, на сервер и еще в море мест, и все это должно легко включаться/отключаться и использоваться.

И так, мы создаем базовый протокол `Logger`, с функцией `log(message: String)` и реализуем несколько реализаций: ConsoleLogger, FileLogger, ServerLogger… Создаем базовый логер который дергает все остальные, и называем его — MainLogger. Дальше мы в те классы, в которых собираемся логировать добавляем строчку на подобии: `var log: Logger? = nil`, и… И теперь нам надо зарегистрировать все те действия, которые мы произвели.

Вначале создаем новый модуль `LoggerModule`:

import DITranquillity class LoggerModule: DIModule { func load(builder: DIContainerBuilder) { builder.register(ConsoleLogger.self) .asType(Logger.self) .instanceSingle() .initializer { ConsoleLogger() } builder.register(FileLogger.self) .asType(Logger.self) .instanceSingle() .initializer { FileLogger(file: "file.log") } builder.register(ServerLogger.self) .asType(Logger.self) .instanceSingle() .initializer { ServerLogger(server: "http://server.com/") } builder.register(MainLogger.self) .asType(Logger.self) .asDefault() .instanceSingle() .initializer { scope in MainLogger(loggers: **!scope) } } } И не забываем, добавить внедрение нашего логера, во все классы где мы его объявили, к примеру, вот так:builder.register(YourPresenter.self) .instancePerScope() // Говорим, что на один scope нужно создавать один Presenter .initializer { scope in try YourPresenter(server: *!scope) } .dependency { (scope, obj) in obj.log = *?scope } И после мы его добавляем в нашу сборку. Теперь стоит разобрать, что мы только что написали.

Вначале мы зарегистрировали 3 наших логера, которые будут доступны по имени Logger — то есть мы осуществили множественную регистрацию. При этом если мы уберем MainLogger, то в программе не будет единственного логера, так как если мы захотим получить один логер, то библиотека не сможет понять какой логер хочет от неё программист. Далее для MainLogger мы делаем две вещи:

  1. Говорим, что это стандартный логер. То есть если нам нужен единственный логер то это будет MainLogger, а не какой-то другой.
  2. В MainLogger передаем список всех наших логеров, за исключением самого себя (это одна из возможностей библиотеки, при множественном разрешении зависимостей исключаются рекурсивные вызовы. Но если мы сделаем тоже самое в блоке dependency, то у нас выдадутся все логеры в том числе MainLogger).Для этого используется быстрый синтаксис `**!`, который является эквивалентом `try! scope.resolveMany()`

Итоги

С помощью библиотеки мы смогли выстроить зависимости между несколькими слоями: Router, ViewController, Presenter, Data. Были показаны такие вещи как: внедрение зависимостей через свойства, внедрение зависимостей через инициализатор, альтернативные типы, модули, немного коснулись времени жизни и сборок.

Многие возможности были упущены: циклические зависимости, получение зависимостей по имени, время жизни, сборки. Их можно посмотреть в документации

Этот пример доступен по этой ссылке.

  • Добавление подробного логирования, с возможность указывать внешние функции, в которые приходят логи
  • Поддержка других систем (MacOS, WatchOS)
  • Typhoon — не поддерживает чистые swift типы, и его синтаксис, на мой взгляд является громоздким
  • Swinject — отсутствие альтернативных типов, и множественной регистрации. Менее развитые механизмы для «модульности», но это хорошая альтернатива
P.S.

На данный момент проект находится, на пререлизном состоянии, и мне бы хотелось, прежде чем давать ему версию 1.0.0 узнать мнения других людей, так как после “официального” выхода, менять что-то кардинально станет сложнее.

habr.com


scroll to top