Начальная

Windows Commander

Far
WinNavigator
Frigate
Norton Commander
WinNC
Dos Navigator
Servant Salamander
Turbo Browser

Winamp, Skins, Plugins
Необходимые Утилиты
Текстовые редакторы
Юмор

File managers and best utilites

Новый язык программирования Swift: комментарии разработчиков. Язык программирования swift реферат


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

Язык программирования SwiftЯзык программирования SwiftНе так давно всеми нами известная компания Apple представила новый язык программирования, под названием Swift. Новый язык для разработки программ на IOS и OS X, вобравший в себя лучшие качества от своих предков, а именно языка С и Objective-C, при этом отодвинув в сторону их главные недостатки.

Как сказали в Apple, язык создан практически с нуля и использует паттерны безопасного программирования, с использованием современных функций, что должно сделать процесс создания приложений более быстрым и простым[конечно, если вы понимаете как вообще эти приложения надо создавать].

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

 

К слову скажу, что язык появился 2-го июня 2014 года и уже многие IT-сайты[такие как ХАБР] уже успели обсудить все плюсы, минусы и так далее, этого языка. Вся оценка сошлась на том, что этот язык очень интересен и достоин того, чтобы его изучать.

Эта статья — это, своего рода, введение в язык Swift, потому что я планирую написать еще достаточно много уроков по нему, поэтому если вам он интересен подписывайтесь на обновления блога и мы будем вместе с вами его изучать.

К сожалению, а может и к счастью, нормальных, русскоязычных источников, по которым можно выучить этот язык — нет. Потому что, как все знают, 90% информации выпускают на англоязычном рынке и пока она будет переведена на русский, она уже теряет актуальность, поэтому единственный способ идти в ногу со временем — это читать книги на английском языке.

Чем я, собственно, и планирую заняться. Не так давно добил огромную книгу по языку С и думал переваливаться на Objective-C, но понял, что скоро он потеряет актуальность[если не полностью, то некоторой своей частью], поэтому выбор пал на Swift.

Welcome to Swift

Как оказалось, Swift был в проектировании не один год и, возможно, был задуман еще при Джобсе. Основу для языка компания Apple заложила в процессе продвижения своего компилятора, дебагера и framework инфраструктуры. Была существенно упрощена система управления памятью, с помощью автоматического подсчета ссылок[Automatic Reference Counting(ARC)].

Благодаря Objective-C, который начал поддерживать блоки, литералы и модули, появилась возможность для создания современной языковой технологии. Благодаря этой основе, компания Apple смогла создать новый язык, который станет будущем всей разработки софта для яблока.

Тем людям(разработчикам), которые уже знакомы с Objective-C, Swift покажется весьма знакомым, так как он сочетает в себе хорошую читабельность параметров и силу динамической объектной модели своего предка. Для многих будет огромной радостью то, что Swift совместим с Objective-C и может встраиваться в его код. Построенный на общей основе, Swift дает много возможностей и унифицирует процедурные и объектно ориентированные аспекты языка.

К счастью для нас, людей которые хотят научиться программировать на Swift’e, язык достаточно дружелюбен и не требует IQ за 200 для освоения. Это, как говорят разработчики, первый язык, который обладает достаточной мощностью и при этом так же интересен и прост как скриптовый язык[или как его еще называют сценарный язык].

Имеется поддержка playground, что очень сложно адекватно перевести, но если объяснять простыми словами — это поддержка функции позволяющей видеть приложение(программу), которое вы создаете, без компиляции и запуска проекта.Язык программирования SwiftЯзык программирования Swift

Язык программирования Swift вобрал лучшее от всех языков программирования и всю культуру, опыт инженеров компании Apple. Компилятор XCode сочетает в себе качество и высокую производительность, а язык максимально оптимизирован под разработчика, без каких либо компромиссов.

Расчет идет на то, что написать свою первую программу(а именно «Hello, World»), должно быть так же просто как и целую операционную систему. Все это делает Swift будущим разработки для компании Apple и всего мира в целом.

Swift — это фантастический путь в написании приложения для iOS и OS X. Цель языка амбициозна, а мы очень хотим посмотреть, что вы сможете создать с помощью этого!

The swift programming language — Apple

Вот и все, я думаю для введения хватит. Я попытался адекватно перевести введение из англоязычной книги по этому языку, параллельно разбавляя фразами от себя, надеюсь получилось не плохо. Если вас заинтересовал этот язык, то подписывайтесь на обновления блога и вместе со мной начнете изучать этот язык — всем удачи.

Автор: Evgeniy Zelenkov

Похожие статьи:

yhoome.ru

Swift (язык программирования) — WiKi

Старший вице-президент по разработке программного обеспечения Apple Крейг Федериги во время анонса этого продукта заявил, что язык программирования Swift был заложен ещё в платформе NeXT (ОС NeXTSTEP выпускалась в 1989—1995 годах), которая стала основой для современной macOS, а затем и iOS[8].

Разработка текущего варианта языка Swift началась в 2010 году Крисом Латтнеромruen, руководителем отдела разработки инструментов для создания программного обеспечения Apple и одним из основных разработчиков LLVM. Swift заимствовал идеи из «Objective-C, Rust, Haskell, Ruby, Python, C#, CLU, и еще из стольких многих языков, что сложно перечислить»[9].

2 июня 2014 года на конференции WWDC Swift был официально представлен вместе с бесплатным руководством по использованию языка объёмом в 500 страниц, доступным на сервисе «iBook Store»[10].

Версия Swift 1.0 была выпущена 9 сентября 2014 года, вместе с «Gold Master» версией Xcode 6.0 для iOS.

8 июня 2015 года компания Apple объявила о выпуске новой версии Swift 2.0, которая получила более высокую производительность, новое API обработки ошибок, улучшения синтаксиса языка, а также функцию проверки доступности функций Swift для целевых ОС[11].

3 декабря 2015 года была выпущена бета версия Swift 3.0 с поддержкой операционных систем OS X, iOS и Linux и лицензированная под открытой лицензий Apache 2.0 license with a Runtime Library Exception[12][13].

10 апреля 2016 года Google объявила о намерениях сделать Swift так называемым «первым языком» для Android. Язык программирования очень быстрый, поэтому Google планирует им воспользоваться. Скорее всего, это уменьшит количество приложений, которые сначала выходят на iOS, а позже на Android[14].[неавторитетный источник?]

19 сентября 2017 года была выпущена версия Swift 4.0.

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

Часть функций языка выполняется быстрее по сравнению с другими подобными языками. Например, сортировка комплексных объектов выполняется в 3,9 раз быстрее, чем в Python, и почти в 1,5 раза быстрее, чем в Objective-C.[15][неавторитетный источник? 591 день][16]

Код, написанный на Swift, может работать вместе с кодом, написанным на языках программирования C и Objective-C в рамках одного и того же проекта[2].

Репозитории Swift

Apple разделила код Swift на несколько открытых репозиториев.

  • Компилятор и стандартная библиотека:

Swift: основной Swift репозиторий, который содержит исходный код для компилятора Swift, стандартная библиотека и SourceKit;

Swift-Evolution: документы, относящиеся к продолжающемуся развитию Swift, включая цели для предстоящих выпусков, предложения для изменений и расширений Swift;

  • Библиотеки ядра:

Swift corelibs-foundation: исходный код для Foundation, который предоставляет общую функциональность для всех приложений;

Swift corelibs-libdispatch: исходный код для libdispatch, который предоставляет примитивы параллелизма для работы на многоядерном аппаратном обеспечении;

Swift corelibs-xctest: исходный код для XCTest, который обеспечивает фундаментальную инфраструктуру тестирования для Swift-приложений и библиотек;

  • Менеджер пакетов:

Swift package-manager: исходный код для менеджера пакетов Swift;

Swift llbuild: исходный код для llbuild, система низкого уровня, который использует Swift package-manager;

  • Клонированные репозитории:

Swift опирается на несколько других проектов с открытым кодом, особенно на компилятор LLVM.

Swift llvm: исходный код LLVM, с кусочками Swift-дополнений;

Swift clang: исходный код для Clang, с кусочками Swift дополнений;

Swift lldb: исходный код Swift-версии LLDB, для отладки Swift программ;

ru-wiki.org

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

imageПривет, Хабр! 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>.

habrahabr.ru

от Objective C к Swift / Хабрахабр

Чтобы отказаться от языка программирования, на котором уже ведется коммерческая разработка, и начать учить новый, программистам нужны серьезные основания. Но история ИТ-индустрии знает много случаев, когда выбор за них делало время, и переход происходил как бы сам по себе.

Что заставило множество разработчиков перейти на Objective C? Что сейчас заставляет отказаться от него и выбрать Swift?

Objective C является расширением языка Си, в который были добавлены новые возможности для объектно-ориентированного подхода программирования. Язык использует объектную модель Smalltalk. Полностью совместим с языком программирования Си. Компания Apple долгое время использовала Objective C как основной язык программирования для разработки своих продуктов.

Создателями Objective C являются Брэд Кокс и Том Лав. Они начали работать над ним в начале1980-х годов, когда еще были сотрудниками телекоммуникационной компании ITT Corporation. Примерно в то же время Кокс и Лав познакомились с языком программирования Smalltalk. Кокса тогда занимали проблемы повторного использования программного кода.

Вследствие увеличения объема кода в проектах и сложностей его поддержки, с 1960х начинает образовываться новая, объектно-ориентированная парадигма программирования, разбившая программы на еще более мелкие составляющие – типы данных. Каждый объект является переменной определенного программистом типа данных (так называемого класса). Определение такого специального пользовательского типа данных (класса) заключается в двух вещах: определении набора данных (инвариантов, членов) и набора подпрограмм (методов), которые будут их обслуживать.

Основным преимуществом объектно-ориентированного подхода стала возможность создавать новые классы на основе уже написанных (добавлять инварианты и методы, переопределять методы, использовать определенные в базовом классе методы как свои), названное наследованием.

Набор методов представляет собой интерфейс для взаимодействия с инвариантами.

OOPC

Брэд Кокос быстро понял, что Smalltalk не подойдет для решения задач в ITT Corporation: совместимость с языком С для них была критична. Однако Кокс решил модифицировать препроцессор для С в сторону Smalltalk. Модификация эта состояла в добавлении новых синтаксических конструкций и специальном препроцессоре для них (который, проходя по коду преобразовывал их в обычные вызовы функций С), а также новой библиотеке времени выполнения (эти вызовы обрабатывающей). Таким образом, изначально Objective C воспринимался как надстройка над C. В каком-то смысле это так и до сих пор: можно написать программу на чистом С, а после добавить к ней немного конструкций из Objective C (при необходимости), или же наоборот, свободно пользоваться С в программах на Objective C. Кроме того, все это касается и программ на С++. То, что получилось, Кокс назвал «OOPC» – Object-Oriented Pre-Compiler. В 1982 году Лав устроился в Schlumberger Research и получил возможность приобрести коммерческую версию Smalltalk-80. Это помогло им в дальнейшей работе над Objective C. В итоге в Objective C была реализована возможность создавать объекты и работать с ними.

Целью Кокса было создание языка, поддерживающего концепцию software IC, подразумевающей возможность собирать программы из готовых компонентов (объектов), подобно тому как сложные электронные устройства могут быть собраны из набора готовых интегральных микросхем. При этом язык должен быть простым и основанным на языке С, чтобы облегчить переход разработчиков на него.

Одной из целей было также создание модели, в которой сами классы являются полноценными объектами, поддерживалась бы интроспекция и динамическая обработка сообщений. Любому объекту можно послать любое сообщение. Объект может вместо обработки сообщения переслать его другому объекту для обработки (делегирование), в частности, так можно реализовать распределённые (то есть находящиеся в различных адресных пространствах и даже на разных компьютерах) объекты.

Привязка сообщения к соответствующей функции происходит на этапе выполнения.

Язык Objective C поддерживает работу с метаинформацией — так, на этапе выполнения можно узнать класс объекта, список его методов (с типами передаваемых аргументов) и instance-переменных, проверить, является ли класс потомком заданного и поддерживает ли он заданный протокол и так далее.

NeXT

В 1986 году Кокс опубликовал книгу Object-Oriented Programming, An Evolutionary Approach, в которой разместил описание своего языка программирования, разобрал проблему повторного использования кода и указал преимущества Objective C при ее решении. Лав и Кокс также создали компанию Productivity Products International (PPI), который должен был помочь монетизировать компилятор Objective C с библиотеками классов. Далее фонд был переименован в StepStone.

В 1988 году компания NeXT Software лицензировала язык Objective C, усовершенствовала его библиотеки и добавила новые – AppKit и Foundation Kit. На их основе позднее была создана среда разработки NEXTSTEP.

В 1992 к усовершенствованию языка и компилятора подключились разработчики проекта GNU в рамках проекта OpenStep. С тех пор GCC поддерживает Objective C.

Не добившись успеха на рынке в качестве производителя компьютеров, компания NeXT переключило все свое внимание на создание и продажу средств разработки ПО.

20 декабря 1996 года компания Apple купила NeXT Software, а среда разработки NEXTSTEP стала основной средой разработки для будущей основной версии операционной системы Apple — OS X.

После покупки NeXT, Apple взяла их SDK (компилятор, библиотеки, IDE) за основу для своих дальнейших разработок. IDE для разработки кода получила название Xcode, а для GUI – Interface Builder. Фреймворк Cocoa для GUI разработок (и не только) на сегодня является наиболее значимой средой разработки программ на Objective C.

В 2007 году компания Apple презентовала обновленную версию языка и назвала его Objective C 2.0, данная версия языка является актуальной в настоящее время. Используется в разработке Apple OS X, iOS.

Swift

В 2014 году компания Apple представила новый язык программирования – Swift. Он стал самым быстрорастущим языком программирования в истории.

«С открытием исходного кода Swift разработчики всего мира могут вносить свой вклад в этот язык программирования и делать его доступным на новых платформах, — сказал тогда Крейг Федериги, старший вице-президент Apple по программному обеспечению. — Эффективность и простота Swift дадут молодым программистам стимулы к обучению, к тому же теперь они смогут распространять свои идеи повсюду: от мобильных устройств до облачных систем».

Создатель языка Swift – Крис Латтнер. Он пришел в Apple в 2005 году. Там он занимался разработкой LLVM. Apple использовала LLVM для того, чтобы изменить способ использования Objective C при создании приложений.

LLVM (Low Level Virtual Machine) — это универсальная система анализа, трансформации и оптимизации программ или, как её называют разработчики, «compiler infrastucture». Крис Латтнер курирует все инструменты разработчиков Apple: все, с помощью чего создаются программы для телефонов, планшетов и компьютеров Apple, как сторонними разработчиками, так и инженерами компании. Будучи аспирантом университета штата Иллинойс, он создал своего рода средства для разработчика под названием LLVM, которые сегодня лежат в основе Xcode. Латтнер также использовал LLVM в качестве основы для Swift. Эти два продукта специально были созданы для работы в тандеме.

Над Swift он начал работать летом 2010 года. Латтнер держал это в секрете полтора года. Он создавал Swift в свободное от основной работы время.

Через полтора года он рассказал о проекте топ-менеджерам Apple. Высоко оценив работу Латтнера, они направили ему в помощь нескольких разработчиков. Однако проект был все еще окутан ореолом таинственности. Даже люди, косвенно принимавшие участие в создании языка и помогавшие Крису, были сильно удивлены, над чем именно он работал. Еще через полтора года проект Латтнера попал в список главных направлений компании, а команда разработчиков существенно расширилась.

По мнению Apple, язык Swift имеет все шансы изменить ИТ-индустрию.

2 июня 2014 года компания выпустила тестовую версию для сторонних разработчиков и программистов. Apple позиционирует это язык как более быстрый и эффективный способ создания программ для iPhone, iPad и Mac.

Расширение аудитории

Языку программирования необходимо несколько лет, чтобы стать популярным хотя бы в узких кругах энтузиастов. Достаточно показательна ситуация с языком Go, который компания Google представила еще в 2009 году. Над Go работали лучшие умы (Кен Томпсон и Роб Пайк), однако и по сей день прикладываются немалые усилия для его популяризации. Однако Apple делает все, чтобы Swift стал исключением и последовал примеру языков Java и C# в 1990-х и начале 2000-х соответственно.

Swift создан для среднестатистического программиста. На языке можно писать даже самые простые приложения, а наделенный довольно умными инструментами, язык предлагает эффективный способ учиться написанию самостоятельно. Но главная причина будущей популярности языка кроется в другом. Сотни тысяч разработчиков сегодня создают приложения для устройств Apple, используя Objective C. Устройства Apple пользуются большой популярностью, а значит эти разработчики продолжат создавать для них приложения, но уже на Swift. По мнению Apple, он значительно лучше и эффективнее, чем Objective C.

3 декабря 2015 года компания открыла исходный код Swift. Всемирное сообщество разработчиков — от создателей приложений до компаний и учебных заведений — может развивать Swift и оптимизировать язык, чтобы сделать его доступным на новых вычислительных платформах.

«Не было никакого реального стимула использовать Google Go» — говорит Пол Янсен, который отслеживал прогресс различных языков программирования в течение около пятнадцати лет. «Swift отличается наличием стимула».

В рейтинге TIOBE Swift занимает 14-ю позицию. За год он поднялся на 3 позиции. Язык Go переместился с 95 позиции на 20, что очень впечатляет. А Objective C опустился с 6 места на 15. Таким образом, можно сказать, что Swift технически обошел своего предшественника.

Сегодня на GitHub, популярном хранилище разработок с открытым исходным кодом, более 2500 проектов используют Swift.

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

Swift быстр (скорость реализации некоторых алгоритмов в 3,9 раза больше, чем на Python) и лаконичен (разработчики избавились от многословности Objective C). Ещё одно важное нововведение — это возможность писать код и видеть результаты в режиме реального времени.

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

P.S. Apple не первая компания, которая выпустила новый язык программирования в недавнем прошлом. Это уже сделали Facebook, Google и Mozilla. К чему это приведет — покажет время.

habrahabr.ru

Руководство по Swift для абсолютных новичков | SwiftBook

Почему Swift?

На случай, если вы еще не слышали, Apple недавно представила новый язык для разработчиков iOS и OS X под названием Swift. Как оказалось, Swift разрабатывался с 2010 года, а это 2 года после выпуска первого SDK. Apple видела ограниченность Objective-C, которому уже почти 30 лет, и решила, что пришло время для перемен. Тем не менее, по подлинной манере Apple, они не хотели выпускать полусырой язык. Они поняли, что несмотря на недостатки Objective-C, они еще могут выйти за пределы возможного с ним, и так они и сделали.

Прошло шесть лет с момента выхода первого SDK и 1,2 млн. приложений были опубликованы в App Store. Миллионы разработчиков со страданием изучали тайной синтаксис и ограничения Objective-C. В последнее время, во весь голос говоря о своих бедах от устаревшего языка.

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

Когда речь идет о Swift, Apple ссылается на три ключевые парадигмы: Безопасность, Современность и Мощность. Swift впитал в себе все эти пункты. Ниже приведены основы, которые помогут приступить к работе со Swift. Если вы уже знаете какой-нибудь язык программирования, вы увидите много общего с другими современными языками. Вероятно, вы даже задумаетесь, зачем нужно было изобретать совершенно новый язык, но это уже обсуждение для другой записи в блоге.

Как пользоваться Swift?

Во-первых, вам придется скачать и установить Xcode 6. После того как вы установили его, откройте и выберите в меню File -> New -> File -> выберите слева источник, систему iOS или OSX -> Playground. Дайте имя вашему Playground, и все вы готовы начать работу.

Кроме того, как альтернативу, можно использовать REPL (Read Evaluate Print Loop) из терминала.

Инструкция для запуска в терминале:
  1. Откройте Терминал
  2. Если у вас есть две или более версий установленных Xcode, то вам нужно будет выбрать Xcode 6 в качестве версии по умолчанию. Если у вас только Xcode 6, тогда перейдите к шагу 3, а в противном случае идите дальше и выполните следующую строку:  sudo xcode-select -s /Applications/Xcode6-Beta.app/Contents/Developer/

    На момент написания этого поста, бета-версия Xcode 6 называлась "Xcode6-Beta". Пожалуйста, проверьте имя вашего приложения в папке "Приложения" и выпишите соответствующий путь при использовании xcode-select.

  3. Чтобы запустить REPL напечатайте:  xcrun swift

Основы

Переменные

Как и в любом языке программирования у нас есть переменные, которые позволяют хранить данные. Чтобы объявить переменную вы должны использовать ключевое слово var.

var greeting: String = "Hello World"

Приведенный выше код указывает системе, что мы хотим создать переменную с именем greeting, которая имеет тип String, и будет содержать текст "Hello World".

Swift достаточно умен, чтобы догадаться, что, если вы присваиваете строку в переменную, то переменная будет иметь тип String. Таким образом, вы не должны явно указывать тип, как в приведенном выше примере. А лучший и распространенный способ написания приведенного выше примера будет выглядеть так:

var greeting = "Hello World" // Предугадан тип String

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

  1. var greeting = "Hello World" // Предугадан тип String
  2. greeting = "Hello Swift"

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

Если мы изменим наш пример приветствия, используя let вместо var, то вторая строка даст нам ошибку компиляции, поскольку мы не можем изменить значение greeting.

  1. let greeting = "Hello World"
  2. greeting = "Hello Swift" // Ошибка компиляции

Давайте возьмем другой пример, чтобы вы поняли, зачем и когда использовать let.

  1. let languageName: String = "Swift"
  2. var version: Double = 1.0
  3. let introduced: Int = 2014
  4. let isAwesome: Bool = true

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

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

  1. let languageName = "Swift" // предугадан как String
  2. var version = 1.0 // предугадан как Double
  3. let introduced = 2014 // предугадан как Int
  4. let isAwesome = true // предугадан как Bool

Строки

В нашем примере выше мы уже писали тип String. Давайте посмотрим, как мы можем соединить две строки с помощью оператора + .

  1. let title = "Руководство по Swift для абсолютных новичков"
  2. let review = "Он потрясающий!"
  3. let description = title + " - " + review
  4. // description = "Руководство по Swift для абсолютных новичков - Он потрясающий!"

У строк есть мощная функция интерполяции, с помощью которого легко можно использовать переменные для создания строк.

  1. let datePublished = "19 Июля , 2014"
  2. let postMeta = "Запись в блоге опубликована: \(datePublished)"
  3. // postMeta = "Запись в блоге опубликована: 19 Июля, 2014"

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

Другие типы

Кроме строк у нас есть Int для целых чисел. Double и Float для чисел с плавающей точкой и Bool для булевых значений, таких как: true или false. Эти типы предугадываются точно так же как и строки, так что вы не обязаны явно указать их при создании переменной.

Float и Double различаются по точности и по величине хранимого числа.

  • Float: представляет собой 32-разрядное число с плавающей точкой , но точность Float может быть всего лишь 6 десятичных цифр.
  • Double: представляет собой 64-разрядное число с плавающей точкой и имеет точность не менее 15 десятичных цифр.

По умолчанию, когда мы пишем число с плавающей точкой, компилятор предугадывает его как Double.

var version = 1.0 // предугадывает как Double

Но вы можете явно указать Float.

var version: Float = 1.0

Типы коллекций

Массив

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

  1. var cardNames : [String] = ["Jack", "Queen", "King"]
  2. // Swift может предугадать [String] так что мы можем также написать:
  3. var cardNames = ["Jack", "Queen", "King"] // предугадан как [String]

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

Для доступа к элементу массива нужно использовать индекс:

println(cardNames[0])

Примечание: мы использовали выше функцию println, которая печатает значение "Jack" в консоль, а затем добавит символ новой строки.

Изменение массива

Давайте создадим новый массив, содержащий список задач.

var todo = ["Написать блог","Сделать форум"]

Убедитесь, что вы используете ключевое слово var, чтобы мы могли изменить наш массив.

Чтобы добавить еще один элемент к нашему массиву todo, мы используем оператор '+=' :

todo += ["Сходить в магазин"]

Чтобы добавить несколько элементов в наш массив todo мы просто добавляем массив:

todo += ["Отправить письмо", "Постирать вещи"]

Чтобы заменить существующий элемент массива просто укажите индекс этого элемента и присвойте новое значение:

todo[0] = "Корректировать запись в блоге"

Чтобы заменить диапазон элементов:

todo[2..<5] = ["Постирать вещи", "Сходить в магазин", "Приготовить ужин"]

Словарь

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

Например, мы можем указать наши карты, передавая их ключи и последующие значения.

var cards = ["Jack" : 11, "Queen" : 12, "King" : 13]

Выше мы указали имена карт в качестве ключей и их соответствующие числовые значения. Ключи не ограничены типом String, они могут быть любого типа.

Изменение словаря

Что, если мы хотим добавить "Ace" в наш словарь cards? Все, что нам нужно сделать, это использовать ключ в качестве индекса и присвоить ему значение. Примечание: cards объявляется как var, что означает, что он может быть изменен.

cards["Ace"] = 15

Мы сделали ошибку и хотим изменить значение "Ace". Еще раз просто используйте ключ в качестве индекса и присвойте ему новое значение.

cards["Ace"] = 1

Чтобы извлечь значение из словаря

println(cards["Ace"])

Управление потоком

Циклы

Насколько хороша коллекция, если мы не можем пройтись циклом по нему? Swift для этого дает нам while, do-while, for и for-in циклы. Давайте рассмотрим каждый из них.

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

  1. while !complete {
  2.  println("Загрузка...")
  3. }

Примечание: восклицательный знак перед переменной complete обозначает отрицание и читается как противоположное значение complete.

Кроме того, есть цикл do-while, который гарантирует, что ваш блок кода выполнится по крайней мере один раз.

  1. var message = "Начало загрузки"
  2. do {
  3.  println(message)
  4.  message = "Загрузка.."
  5. } while !complete

Последующие вызовы выражения println будут печатать "Загрузка.."

Есть еще обычный for цикл, где можно указать число, условие и инкремент:

  1. for var i = 1; i < cardNames.count; ++i {
  2.  println(cardNames[i])
  3. }

Или вы можете просто использовать for-in, он создает временную переменную и присваивает ей значение каждого элемента во время прохода по массиву.

  1. for cardName in cardNames {
  2.  println(cardName)
  3. }

Приведенный выше код будет печатать все имена карт в массиве. Мы можем также использовать диапазон. Диапазоны значений обозначаются либо двумя точками и знаком меньше, либо тремя точками.

Например:

  • 1...10 - это диапазон чисел от 1 до 10. Три точки называются замкнутым диапазоном, потому что последнее число включается в диапазон.
  • 1..<10 - это диапазон чисел от 1 до 9. Две точки со знаком меньше, называется полузакрытый диапазон, потому что последнее число не включается в диапазон.

Давайте распечатаем таблицу умножения на 2 используя for-in с диапазоном:

  1. for number in 1...10 {
  2.  println("\(number) умножить на 2 будет \(number*2)")
  3. }

Мы также можем перебрать словарь cards, чтобы распечатать оба ключ и значение:

  1. for (cardName, cardValue) in cards {
  2.  println("\(cardName) = \(cardValue)")
  3. }

If выражение

Чтобы контролировать поток нашего кода мы, естественно, имеем if выражение.

  1. if cardValue == 11 {
  2.  println("Jack")
  3. } else if cardValue == 12 {

     

  4.  println("Queen")
  5. } else {
  6.  println("Не найдено")
  7. }

Примечание: выражение if может иметь круглые скобки, но они не являются обязательными. Тем не менее, фигурные скобки {} являются обязательными в отличие от других языков.

Выражение switch

Switch выражение в Swift является очень гибким и имеет много особенностей. Некоторые основные правила выражения switch:

  • Оно не требует break, после каждого case выражения
  • Switch не ограничивается целочисленными значениями. Вы можете искать соответствие для любых значений, таких как: String, Int, Double или любых похожих объектов.
  • Выражение switch должно соответствовать хотя бы одному возможному значению, если нет, то вы должны указать блок default , что делает ваш код более безопасным. Если вы не напишите case для хотя бы одного возможного значения или блок default, то вы получите ошибку компиляции: "switch должен быть исчерпывающим".
  1. switch cardValue {
  2.  case 11:
  3.   println("Jack")
  4.  case 12:
  5.   println("Queen")
  6.  default:
  7.   println("Не найдено")
  8. }

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

  1. switch distance {
  2.  case 0:
  3.   println("не является допустимым расстоянием")
  4.  case 1,2,3,4,5:
  5.   println("рядом")
  6.  case 6,7,8,9,10:
  7.   println("далеко")
  8.  default:
  9.   println("слишком далеко")
  10. }

Есть моменты, когда даже несколько значений тоже мало. Для тех случаев, вы можете использовать диапазоны. А что, если, любое расстояние больше 10 и меньше 100 считается далеко?

  1. switch distance {
  2.  case 0:
  3.   println("не является допустимым расстоянием")
  4.  case 1..<10:
  5.   println("рядом")
  6.  case 10..<100:
  7.   println("далеко")
  8.  default:
  9.   println("слишком далеко")
  10. }

Сможете ли вы догадаться, что приведенный выше код будет печатать?

Функции

Мы использовали println во многих наших примерах. Он является примером того, как использовать функцию. Но как мы можем создать свою собственную функцию?

Это просто, вы должны использовать ключевое слово func и дать ему имя.

  1. func printCard() {
  2.  println("Queen")
  3. }

Что хорошего в функции, если она всегда будет просто печатать "Queen" в качестве имени карты? Что делать, если мы хотим передать ему параметр, чтобы он мог напечатать любое имя карты?

  1. func printCard(cardName: String) {
  2.  println(cardName)
  3. }

Конечно, мы не ограничены только одним параметром. Мы можем передать несколько параметров:

  1. func printCard(cardName: String, cardValue: Int) {
  2.  println("\(cardName) = \(cardValue)")
  3. }

Что, если мы просто хотим, чтобы наша функция создала строку и возвратила его значение, вместо того чтобы напечатать его? Тогда мы можем использовать ключевое слово return, и указать тип возвращаемого значения с помощью стрелки -> в конце объявления функции.

  1. func buildCard( cardName: String, cardValue: Int) -> String {
  2.  return "\(cardName) = \(cardValue)"
  3. }

В блоке кода выше мы говорим, что мы создаем функцию с именем buildCard, которая принимает два параметра и возвращает тип String.

Заключение

Если вы дошли до этого места, то примите наши поздравления! Теперь вы хорошо разбираетесь в основах Swift. Это довольно много для усвоения, но это только вершина айсберга, из всего на что Swift способен. На самом деле нам еще учиться и учиться. Оставайтесь с нами для получения свежих материалов!

swiftbook.ru

примеры кода реактивного программирования на языке Swift

Около года назад наш iOS-разработчик компании Noveo, Александр, заинтересовался RxSwift. Столкнувшись с нехваткой документации, Саша решил самостоятельно упорядочить все приобретенные в ходе изучения фреймворка знания — для себя и для других. Результатом стала одна из его статей о Swift 2.2. Со времени ее публикации, конечно, и RxSwift, и сам Swift эволюционировали, и материал нуждался в обновлении, и другой iOS-разработчик из Noveo, Михаил, адаптировал материал для Swift 3. После редакции статья заиграла свежими красками, и на этом мы передаем слово нашим коллегам.

Заинтересовавшись темой функционального программирования, я встал на распутье: какой фреймворк выбрать для ознакомления? ReactiveCocoa — ветеран в iOS-кругах, информации по нему вдоволь. Но он вырос из Objective-C, и хотя это не является проблемой, все же в данный момент я в основном пишу на Swift, и хотелось бы взять решение, изначально спроектированное с учетом всех плюшек этого языка. RxSwift же — порт Reactive Extensions; последний, конечно, имеет долгую историю, но сам порт свежий и написан как раз под Swift. На нем я и решил остановиться.

Как выяснилось, документация по RxSwift несколько специфична: описание всех команд ведет на http://reactivex.io/, а там в основном дается общая информация, у разработчиков еще не дошли руки сделать документацию именно для RxSwift, что не всегда удобно. Некоторые команды имеют тонкости в реализации, а есть такие, о которых в общей документации нет ничего, кроме упоминания.

Прочитав все главы вики с гитхаба RxSwift, я решил сразу поразбираться с официальными примерами; тут-то и стало ясно, что с RX такое не пройдет, нужно хорошо понимать основы, иначе будешь как мартышка с копипастом гранатой. Я начал разбирать самые сложные для понимания команды, а потом перешел к тем, что были вроде и понятны, но задавая себе вопросы по ним, я лишь догадывался, как верно ответить, и уверенности в моих ответах у меня не было.

В общем, я решил проработать все операторы RxSwift. Лучший способ что-то понять в программировании — запустить код и посмотреть, как он отработает. Учитывая специфику реактивного программирования, лучше дополнить это схемами (они бывают очень полезны), ну и кратким описанием на русском. Закончив сегодня работу, я подумал, что грех не поделиться результатами с тем, кто лишь присматривается к теме реактивного программирования.

Много картинок и текста ниже, очень много!

Предварительно я рекомендую просмотреть официальную документацию, у меня передана основная суть и специфика RxSwift команд, а не основы.Так же можно «поиграться» с шариками в схемах, так называемые RxMarbles, есть бесплатная версия под iPhone/iPad.

Итак, в этой статье я рассмотрю все (ну или почти все) команды RxSwift, дам для каждой краткое описание, схему (если это имеет смысл), код, результат выполнения, а при необходимости сделаю комментарии по выводу в лог результатов выполнения кода.В статье заголовок каждой команды — ссылка на на официальную документацию, т.к. я не ставил перед собой цели перевести все нюансы по командам.Вот и ссылка конкретно на PDF, где в виде mindMap собраны все команды, что позволяет быстро просмотреть их все. Кусочки кода в PDF приложены для того, чтобы увидеть, как и с каким параметрами нужно работать с командой. Изначально ради этого PDF я все и затеял — чтобы иметь под рукой документ, в котором наглядно видны все команды с их схемами. PDF получился огромным (в плане рабочего пространства, а не веса), но я проверял, даже на iPad 2 все нормально просматривается.

Обо всех ошибках просьба писать в личку, объем работ оказался слегка великоват, после четвертой вычитки текста мои глаза меня прокляли.

Что ж, надеюсь, моя работа кому-то пригодится. Приступим.

Заметки

Создание Observable

asObservablecreatedeferredemptyerrorintervaljustneverofrangerepeatElementtimer

Комбинирование Observable

ambcombineLatestconcatmergestartWithswitchLatestwithLatestFromzip

Фильтрация

distinctUntilChangedelementAtfilterignoreElementssamplesingleskipskip (duration)skipUntilskipWhileskipWhileWithIndextaketake (duration)takeLasttakeUntiltakeWhiletakeWhileWithIndexdebounce

Трансформация

bufferflatMapflatMapFirstflatMapLatestflatMapWithIndexmapmapWithIndexwindow

Операторы математические и агрегирования

reducescantoArray

Работа с ошибками

catchErrorcatchErrorJustReturnretryretryWhen

Операторы для работы с Connectable Observable

multicastpublishrefCountreplayreplayAll

debugdodelaySubscriptionobserveOnsubscribesubscribeOntimeoutusing

В схемах я буду использовать обозначение Source/SO в качестве Source Observable, RO/Result в качестве Result Observable.

Функция example просто позволяет отделять вывод в консоли, её код следующий (взят из RxSwift):

public func example(_ description: String, action: (Void) -> Void) { printExampleHeader(description) action() }

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

import PlaygroundSupport PlaygroundPage.current.needsIndefiniteExecution = true

Также подразумевается, что читатель имеет общее представление о реактивном программировании в общем и об RxSwift в частности. Не знаю, есть ли смысл городить очередную вводную.

asObservable

Этот метод реализован в классах RxSwift, если они поддерживают конвертацию в Observable. Например: ControlEvent, ControlProperty, Variable, Driver

example("\"asObservable\"") { let variable = Variable(0) variable .asObservable() .subscribe({ event in print(event) }) variable.value = 1 }

Консоль:

--- "asObservable" example --- next(0) next(1) completed

В данном примере мы Variable преобразовали в Observable и подписались на его события.

create

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

example("\"create\"") { Observable .create({ observer in observer.on(.next(1)) observer.on(.next(2)) observer.on(.next("A")) observer.on(.next("B")) observer.onCompleted() return Disposables.create() }) .subscribe({ event in print(event) }) }

Консоль:

--- "create" example --- next(1) next(2) next(A) next(B) completed

В данном примере мы создали Observable, который сгенерирует несколько значений, и в конце вызовется complete.

deferred

Этот оператор позволяет отложить создание Observable до момента подписки с помощью subscribe.

example("without \"deferred\"") { var i = 1 let observable = Observable.just(i) i = 2 observable .subscribe({ event in print(event) }) } example("with \"deferred\"") { var i = 1 let observable = Observable.deferred({ Observable.just(i) }) i = 2 observable .subscribe({ event in print(event) }) }

Консоль:

--- without "deferred" example --- next(1) completed --- with "deferred" example --- next(2) completed

В первом случае Observable создается сразу, с помощью Observable.just(i), и изменение значения i уже не влияет на генерируемый этой последовательностью элемент. Во втором же случае мы создаем Observable с помощью deferred и можем поменять значение i перед subscribe.

empty

Пустая последовательность, заканчивающаяся Completed.

 

example("\"empty\"") { Observable .empty() .subscribe({ event in print(event) }) }

Консоль:

--- "empty" example --- completed

error

Создаст последовательность, которая состоит из одного события — Error.

 

example("\"error\"") { Observable .error(RxError.unknown) .subscribe({ event in print(event) }) }

Консоль:

--- "error" example --- error(Unknown error occured.)

interval

Создает бесконечную последовательность, возрастающую с 0 с шагом 1 с указанной периодичностью.

example("\"interval\"") { Observable .interval(1, scheduler: MainScheduler.instance) .subscribe({ event in print(event) }) }

Консоль:

--- "interval" example --- next(0) next(1) next(2) next(3) .....

just

Создает последовательность из любого значения, которая завершается Completed.

example("\"just\"") { Observable .just(2) .subscribe({ event in print(event) }) }

Консоль:

--- "just" example --- next(2) completed

never

Пустая последовательность, чьи observer’ы никогда не вызываются, т.е. не будет сгенерировано ни одно событие.

example("\"never\"") { Observable .never() .subscribe({ event in print(event) }) }

Консоль:

--- "never" example ---

of

Последовательность из переменного количества элементов. После всех элементов генерируется Completed.

example("\"of\"") { Observable .of(2, 3, 5) .subscribe({ event in print(event) }) }

Консоль:

--- "of" example --- next(2) next(3) next(5) completed

В первом случае мы создали последовательность из двух чисел, во втором — из двух Observable, а затем объединили их между собой с помощью оператора merge.

range

Создает последовательность с конечным числом элементов, возрастающую с шагом 1 от указанного значения указанное число раз, после всех элементов генерируется Completed.

example("\"range\"") { Observable .range(start: 5, count: 3) .subscribe({ event in print(event) }) }

Консоль:

--- "range" example --- next(5) next(6) next(7) completed

Сгенерировались элементы, начиная с 5, 3 раза с шагом 1.

repeatElement

Бесконечно создавать указанный элемент, без задержек. Никогда не будут сгенерированы события Completed или Error.

 

example("\"repeatElement\"") { Observable .repeatElement(2, scheduler: MainScheduler.instance) .subscribe({ event in print(event) }) }

Консоль:

--- repeatElement example --- Next(2) Next(2) Next(2) .....

timer

Бесконечная последовательность, возрастающая с 0 с шагом 1, с указанной периодичностью и возможностью задержки при старте. Никогда не будут сгенерированы события Completed или Error.

example("\"timer\"") { Observable .timer(2, period: 3, scheduler: MainScheduler.instance) .subscribe({ event in print(event) }) }

Консоль:

--- "timer" example --- next(0) next(1) next(2) .....

В данном примере последовательность начнет генерировать элементы с задержкой в 2 секунды, каждые 3 секунды.

amb

SO = [Observable] или SO1, SO2 = Observable RO = Observable

Из всех Observable SO выбирается тот, который первым начинает генерировать элементы, его элементы и дублируются в RO, остальные SO игнорируются.

example("\"amb\"") { let subjectA = PublishSubject() let subjectB = PublishSubject() let subjectC = PublishSubject() let subjectD = PublishSubject() Observable .amb([subjectA.asObservable(), subjectB.asObservable(), subjectC.asObservable(), subjectD.asObservable()]) .subscribe({ event in print(event) }) subjectC.onNext("C1") subjectD.onNext("D1") subjectB.onNext("B1") subjectA.onNext("A1") subjectC.onNext("C2") subjectD.onNext("D2") subjectA.onNext("A2") }

Консоль:

--- "amb" example --- next(C1) next(C2)

Т.к. первым сгенерировал элемент subjectC, лишь его элементы дублируются в RO, остальные игнорируются.

combineLatest

SO = SO1, SO2,... SON = Observable RO = Observable<f(T,T)>

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

example("\"combineLatest\"") { let sequenceA = Observable .timer(0.15, period: 0.15, scheduler: MainScheduler.instance) .take(3) .map({ "A\($0)" }) let sequenceB = Observable .timer(0.0, period: 0.05, scheduler: MainScheduler.instance) .take(3) .map({ "B\($0)" }) Observable .combineLatest(sequenceA, sequenceB){$0.0 + " - " + $0.1} .subscribe({ event in print(event) }) }

Консоль:

--- "combineLatest" example --- next(A0 - B2) next(A1 - B2) next(A2 - B2) completed

В этом примере я создал Observable с помощью timer — для генерации элементов с разной задержкой, чтобы было видно, как перемешиваются элементы. К моменту появления первого элемента sequenceA появилось уже три элемента sequenceB. Поэтому первым элементом в RO последовательности стала пара объектов A0 — B2.

concat

SO = Observable<Observable> или SO1, SO2 = Observable RO = Observable

 

В RO элементы включают сначала все элементы первого Observable, и лишь затем следующего. Это означает, что если первый Observable никогда не сгенерирует Completed, элементы второго никогда не поступят в RO. Ошибка в текущем Observable пробрасывается в RO.

example("\"concat\"") { let sequenceA = Observable.of(1, 2, 3) let sequenceB = Observable.of("A", "B", "C") sequenceA .concat(sequenceB) .subscribe({ event in print(event) }) }

Консоль:

--- "concat" example --- next(1) next(2) next(3) next(A) next(B) next(C) completed

merge

SO = Observable<Observable> RO = Observable

Элементы RO включают элементы из исходных Observable в том порядке, в котором они были выпущены в исходных Observable.

example("\"merge\"") { let sequenceA = Observable .timer(0.0, period: 0.15, scheduler: MainScheduler.instance) .take(3) .map({ "A\($0)" }) let sequenceB = Observable .timer(0.0, period: 0.1, scheduler: MainScheduler.instance) .take(3) .map({ "B\($0)" }) Observable .of(sequenceA, sequenceB) .merge() .subscribe({ event in print(event) }) } }

Консоль:

--- "merge" example --- next(A0) next(B0) next(B1) next(A1) next(B2) next(A2) completed

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

startWith

SO = Observable RO = Observable

В начало SO добавляются элементы, переданные в качестве аргумента.

example("\"startWith\"") { Observable.of(1, 2, 3) .startWith(6,7) .subscribe({ event in print(event) }) }

Консоль:

--- "startWith" example --- next(6) next(7) next(1) next(2) next(3) completed

switchLatest

SO = Observable<Observable> RO = Observable

Изначально подписываемся на O1 генерируемого SO, его элементы зеркально генерируются в RO. Как только из SO генерируется очередной Observable, элементы предыдущего Observable отбрасываются, т.к. происходит отписка от O1, подписываемся на O2 и так далее. Таким образом в RO — элементы лишь из последнего сгенерированного Observable.

example("\"switchLatest\" without delays") { let variableA = Variable(0) let variableB = Variable(100) let variableC = Variable(variableA.asObservable()) variableC .asObservable() .switchLatest() .subscribe({ event in print(event) }) variableA.value = 1 variableA.value = 2 variableB.value = 3 variableC.value = variableB.asObservable() variableB.value = 4 variableA.value = 5 } example("\"switchLatest\" with delays") { var sequenceObserver: AnyObserver<Observable>? Observable<Observable> .create({ observer in sequenceObserver = observer return Disposables.create() }) .switchLatest() .debug("result\t\t") .subscribe() Observable .timer(0, period: 0.3, scheduler: MainScheduler.instance) .skip(1) .take(3) .subscribe(onNext: { element in let observable = Observable .timer(0.0, period: 0.1, scheduler: MainScheduler.instance) .map({ $0 + element }) .take(3) .debug("timer #\(element)\t") sequenceObserver?.onNext(observable) }, onCompleted: { sequenceObserver?.onCompleted() }) }

Консоль:

--- "switchLatest" without delays example --- next(0) next(1) next(2) next(3) next(4) completed --- "switchLatest" with delays example --- 2017-02-02 19:18:22.976: result -> subscribed 2017-02-02 19:18:23.282: timer #1 -> subscribed 2017-02-02 19:18:23.283: timer #1 -> Event next(1) 2017-02-02 19:18:23.283: result -> Event next(1) 2017-02-02 19:18:23.384: timer #1 -> Event next(2) 2017-02-02 19:18:23.384: result -> Event next(2) 2017-02-02 19:18:23.483: timer #1 -> Event next(3) 2017-02-02 19:18:23.483: result -> Event next(3) 2017-02-02 19:18:23.483: timer #1 -> Event completed 2017-02-02 19:18:23.483: timer #1 -> isDisposed 2017-02-02 19:18:23.581: timer #2 -> subscribed 2017-02-02 19:18:23.582: timer #2 -> Event next(2) 2017-02-02 19:18:23.582: result -> Event next(2) 2017-02-02 19:18:23.683: timer #2 -> Event next(3) 2017-02-02 19:18:23.683: result -> Event next(3) 2017-02-02 19:18:23.783: timer #2 -> Event next(4) 2017-02-02 19:18:23.783: result -> Event next(4) 2017-02-02 19:18:23.783: timer #2 -> Event completed 2017-02-02 19:18:23.783: timer #2 -> isDisposed 2017-02-02 19:18:23.881: timer #3 -> subscribed 2017-02-02 19:18:23.882: timer #3 -> Event next(3) 2017-02-02 19:18:23.882: result -> Event next(3) 2017-02-02 19:18:23.983: timer #3 -> Event next(4) 2017-02-02 19:18:23.983: result -> Event next(4) 2017-02-02 19:18:24.083: timer #3 -> Event next(5) 2017-02-02 19:18:24.083: result -> Event next(5) 2017-02-02 19:18:24.083: timer #3 -> Event completed 2017-02-02 19:18:24.083: timer #3 -> isDisposed 2017-02-02 19:18:24.083: result -> Event completed 2017-02-02 19:18:24.083: result -> isDisposed

В первом примере показано, как команда работает в статике, когда мы руками переподключаем Observable.Во втором примере оператором create создан Observable<Observable>, AnyObserver которого мы вынесли в переменную, по таймеру получающую новый Observable, который реализован в виде таймера. Т.к. задержки у таймеров разные, то можно наблюдать. как с помощью switchLatest в RO попадают значения из последнего сгенерированного таймера.

withLatestFrom

SO1, SO2 = Observable RO = Observable<f(T,T)>

Как только O1 генерирует элемент, проверяется, сгенерирован ли хоть один элемент в O2, и если да, то берутся последние элементы из O1 и O2 и используются в качестве аргументов для переданной функции, результат которой генерируется RO в качестве элемента.

example("\"withLatestFrom\" O2, where O2 has previous value") { let variableA = Variable(0) let variableB = Variable(10) variableA .asObservable() .withLatestFrom(variableB.asObservable()) {"\($0) - \($1)"} .subscribe({ event in print(event) }) variableA.value = 1 variableA.value = 2 variableB.value = 20 variableB.value = 30 variableA.value = 5 variableA.value = 6 } example("\"withLatestFrom\" O2, where O2 doesn't have previous value") { let publishA = PublishSubject() let publishB = PublishSubject() publishA .asObservable() .withLatestFrom(publishB.asObservable()) {"\($0) - \($1)"} .subscribe({ event in print(event) }) publishA.onNext(1) publishA.onNext(2) publishB.onNext(20) publishB.onNext(30) publishA.onNext(5) publishA.onNext(6) }

Консоль:

--- "withLatestFrom" O2, where O2 has previous value example --- next(0 - 10) next(1 - 10) next(2 - 10) next(5 - 30) next(6 - 30) completed --- "withLatestFrom" O2, where O2 doesn't have previous value example --- next(5 - 30) next(6 - 30)

zip

SO = Observable<Observable> RO = Observable<f(T,T)>

Элементы RO представляют собой комбинацию из элементов, сгенерированных исходными Observable, объединение идет по индексу выпущенного элемента.

example("\"zip\"") { let publishA = PublishSubject() let publishB = PublishSubject() Observable .zip(publishA.asObservable(), publishB.asObservable()) {"\($0) - \($1)"} .subscribe({ event in print(event) }) publishA.onNext(0) publishA.onNext(1) publishA.onNext(2) publishB.onNext("A") publishB.onNext("B") publishA.onNext(3) publishB.onNext("C") }

Консоль:

--- "zip" example --- next(0 - A) next(1 - B) next(2 - C)

Из примеров видно, что элементы комбинируются попарно в том порядке, в каком они были сгенерированы в исходных Observable.

distinctUntilChanged

Пропускаем все повторяющиеся подряд идущие элементы.

example("\"distinctUntilChanged\"") { Observable .of(1, 1, 2, 3, 3, 1, 1, 2) .distinctUntilChanged() .subscribe({ event in print(event) }) }

Консоль:

--- "distinctUntilChanged" example --- next(1) next(2) next(3) next(1) next(2) completed

Здесь тонкий момент: отбрасываются не уникальные для всей последовательности элементы, а лишь те, которые идут подряд.

elementAt

В RO попадает лишь элемент, выпущенный N по счету.

example("\"elementAt\"") { Observable .of(1, 8, 2, 3) .elementAt(1) .subscribe({ event in print(event) }) }

Консоль:

--- "elementAt" example --- next(8) completed

filter

Отбрасываются все элементы, которые не удовлетворяют заданным условиям.

example("\"filter\"") { Observable .of(2, 12, 5, 4, 30) .filter({ $0 > 10 }) .subscribe({ event in print(event) }) }

Консоль:

--- "filter" example --- next(12) next(30) completed

ignoreElements

Отбрасывает все элементы, передаёт только терминальные сообщения Completed и Error.

example("\"ignoreElements\"") { Observable .of(1, 8, 2) .ignoreElements() .subscribe({ event in print(event) }) }

Консоль:

--- "ignoreElements" example --- completed

sample

При каждом сгенерированном элементе последовательности семплера (воспринимать как таймер) — брать последний выпущенный элемент исходной последовательности и дублировать его в RO, ЕСЛИ он не был сгенерирован ранее.

let firstSequence = Observable .timer(0, period: 0.1, scheduler: MainScheduler.instance) .take(10) let secondSequence = Observable .timer(0, period: 0.17, scheduler: MainScheduler.instance) .take(10) firstSequence .sample(secondSequence) .subscribe({ event in print(event) })

Консоль:

--- "sample" example --- next(0) next(1) next(3) next(5) next(6) next(8) next(9) completed

single

Из исходной последовательности берется единственный элемент, если элементов > 1 — генерировать ошибку. Есть вариант с предикатом.

example("simple \"single\" with error") { Observable .of(1, 2, 3, 4) .single() .subscribe({ event in print(event) }) } example("predicate \"single\" with success") { Observable .of(1, 2, 3, 4) .single({ $0 == 2 }) .subscribe({ event in print(event) }) } example("predicate \"single\" with error") { Observable .of(1, 2, 3, 4) .single({ $0 < 0 }) .subscribe({ event in print(event) }) }

Консоль:

--- simple "single" with error example --- next(1) error(Sequence contains more than one element.) --- predicate "single" with success example --- next(2) completed --- predicate "single" with error example --- error(Sequence doesn't contain any elements.)

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

skip

Из SO отбрасываем первые N элементов.

example("\"skip\"") { Observable .of(1, 8, 2, 3) .skip(2) .subscribe({ event in print(event) }) }

Консоль:

--- "skip" example --- next(2) next(3) completed

skip (duration)

Из SO отбрасываем первые элементы, которые были сгенерированы в первые N.

example("\"skip\" with duration") { Observable .timer(0, period: 0.1, scheduler: MainScheduler.instance) .take(5) .skip(0.25, scheduler: MainScheduler.instance) .subscribe({ event in print(event) }) }

Консоль:

--- "skip" with duration example --- next(3) next(4) completed

skipUntil

Отбрасываем из SO элементы, которые были сгенерированы до начала генерации элементов последовательностью, переданной в качестве параметра.

example("\"skipUntil\"") { let firstSequence = Observable .timer(0, period: 0.1, scheduler: MainScheduler.instance) .take(5) let secondSequence = Observable .timer(0.25, period: 0.1, scheduler: MainScheduler.instance) .take(1) firstSequence .skipUntil(secondSequence) .subscribe({ event in print(event) }) }

Консоль:

--- "skipUntil" example --- next(3) next(4) completed

Генерация элементов в secondSequence была отложена на 1 секунду с помощью команды delaySubscription, таким образом элементы из firstSequence стали дублироваться в RO лишь через 1 секунду.

skipWhile

Отбрасываем из SO элементы до тех пор, пока функция, переданная в качестве параметра, возвращает true.

 

example("\"skipWhile\"") { Observable .timer(0, period: 0.1, scheduler: MainScheduler.instance) .take(5) .skipWhile({ $0 < 3 }) .subscribe({ event in print(event) }) }

Консоль:

--- "skipWhile" example --- next(3) next(4) completed

skipWhileWithIndex

Отбрасываем из SO элементы до тех пор, пока пока функция, переданная в качестве параметра, возвращает true. Отличие от skipWhile в том, что еще одним параметром, переданным в функцию, является индекс сгенерированного элемента.

example("\"skipWhileWithIndex\"") { Observable .of(1,2,5,0,7) .skipWhileWithIndex({ (value, index) -> Bool in return value < 4 || index < 2 }) .subscribe({ event in print(event) }) }

Консоль:

--- "skipWhileWithIndex" example --- next(5) next(0) next(7) completed

take

Из SO берутся лишь первые N элементов.

example("\"take\"") { Observable .of(1, 2, 3, 4) .take(2) .subscribe({ event in print(event) }) }

Консоль:

--- "take" example --- next(1) next(2) completed

take (duration)

Из SO берутся лишь элементы, сгенерированные в первые N секунд.

example("\"take\" with duration") { Observable .timer(0, period: 0.16, scheduler: MainScheduler.instance) .take(0.3, scheduler: MainScheduler.instance) .subscribe({ event in print(event) }) }

Консоль:

--- "take" with duration example --- next(0) next(1) completed

takeLast

Из SO берутся лишь последние N элементов. Что означает: если SO никогда не закончит генерировать элементы, в RO не попадет ни одного элемента.

example("\"takeLast\"") { Observable.of(1, 2, 3, 4) .takeLast(2) .subscribe({ event in print(event) }) }

Консоль:

--- "takeLast" example --- next(3) next(4) completed

Второй пример приведен для иллюстрации в задержке генерации элементов в RO из-за ожидания завершения генерации элементов в SO.

takeUntil

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

example("\"takeUntil\"") { let firstSequence = Observable .timer(0, period: 0.1, scheduler: MainScheduler.instance) .take(5) let secondSequence = Observable .timer(0.25, period: 0.1, scheduler: MainScheduler.instance) .take(1) firstSequence .takeUntil(secondSequence) .subscribe({ event in print(event) }) }

Консоль:

--- "takeUntil" example --- next(0) next(1) next(2) completed

takeWhile

Из SO берутся элементы до тех пор, пока функция, переданная в качестве параметра, возвращает true.

example("\"takeWhileWithIndex\"") { Observable .of(1,2,5,0,7) .takeWhileWithIndex({ (value, index) -> Bool in return value < 4 || index < 2 }) .subscribe({ event in print(event) }) }

Консоль:

--- "takeWhileWithIndex" example --- next(1) next(2) completed

takeWhileWithIndex

Из SO берутся элементы до тех пор, пока функция, переданная в качестве параметра, возвращает true. Отличие от takeWhile в том, что еще одним параметром, переданным в функцию, является индекс сгенерированного элемента.

example("takeWhileWithIndex") { let sequence = [1,2,3,4,5,6].toObservable() .takeWhileWithIndex{ (val, idx) in val % 2 == 0 || idx < 3 } sequence.subscribe { e in print(e) } }

Консоль:

--- takeWhileWithIndex example --- Next(1) Next(2) Next(3) Next(4) Completed

debounce

Из SO берутся лишь элементы, после которых не было новых элементов N секунд.

example("\"debounce\"") { let firstSequence = Observable .timer(0, period: 1.0, scheduler: MainScheduler.instance) .map({ "X\($0)" }) .take(7) .debug("\tfirst\t") let secondSequence = Observable .timer(0, period: 1.4, scheduler: MainScheduler.instance) .skip(1) .map({ "Y\($0)" }) .take(4) .debug("\tsecond\t") Observable .of(firstSequence, secondSequence) .merge() .debounce(0.9, scheduler: MainScheduler.instance) .debug("\tdebounce\t") .subscribe() }

Консоль:

--- "debounce" example --- 2017-01-31 15:18:40.601: debounce -> subscribed 2017-01-31 15:18:40.604: first -> subscribed 2017-01-31 15:18:40.605: second -> subscribed 2017-01-31 15:18:40.618: first -> Event next(X0) 2017-01-31 15:18:41.519: debounce -> Event next(X0) 2017-01-31 15:18:41.606: first -> Event next(X1) 2017-01-31 15:18:42.006: second -> Event next(Y1) 2017-01-31 15:18:42.606: first -> Event next(X2) 2017-01-31 15:18:43.406: second -> Event next(Y2) 2017-01-31 15:18:43.606: first -> Event next(X3) 2017-01-31 15:18:44.507: debounce -> Event next(X3) 2017-01-31 15:18:44.606: first -> Event next(X4) 2017-01-31 15:18:44.806: second -> Event next(Y3) 2017-01-31 15:18:45.605: first -> Event next(X5) 2017-01-31 15:18:46.206: second -> Event next(Y4) 2017-01-31 15:18:46.206: second -> Event completed 2017-01-31 15:18:46.206: second -> isDisposed 2017-01-31 15:18:46.605: first -> Event next(X6) 2017-01-31 15:18:46.605: first -> Event completed 2017-01-31 15:18:46.605: first -> isDisposed 2017-01-31 15:18:46.605: debounce -> Event next(X6) 2017-01-31 15:18:46.605: debounce -> Event completed 2017-01-31 15:18:46.605: debounce -> isDisposed

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

buffer

SO = Observable<>> RO = Observable<[T]>

Элементы из SO по определенным правилам объединяются в массивы и генерируются в RO. В качестве параметров передаются count (максимальное число элементов в массиве) и timeSpan (время максимального ожидания наполнения текущего массива из элементов SO). Таким образом, элемент RO являет собой массив [T] длиной от 0 до count.

example("\"buffer\"") { Observable .timer(0, period: 0.1, scheduler: MainScheduler.instance) .buffer(timeSpan: 0.16, count: 3, scheduler: MainScheduler.instance) .take(5) .subscribe({ event in print(event) }) }

Консоль:

--- "buffer" example --- next([0, 1]) next([2, 3]) next([4]) next([5, 6]) next([7, 8]) completed

Максимальное число элементов в массиве указано равное трем, и оно никак не влияет на наполнение массива в данном примере. За максимальное время ожидания наполнения таймер успевает сгенерировать не более 2 элементов. Однако период таймера и время ожидания наполнения выбраны таким образом, что третий массив успеет получить лишь один объект.

flatMap

Каждый элемент SO превращается в отдельный Observable, и все элементы из [O1, O2, O3…] объединяются в RO. Порядок генерации элементов в RO зависит от времени их генерации в исходных [O1, O2, O3…] (как в команде merge).

example("\"flatMap\"") { Observable .timer(0, period: 0.1, scheduler: MainScheduler.instance) .skip(1) .take(3) .flatMap({ (value: Int) -> Observable in return Observable .timer(0, period: 0.1, scheduler: MainScheduler.instance) .skip(value) .take(value) .map({"\(value) --> \($0)"}) }) .subscribe({ event in print(event) }) }

Консоль:

--- "flatMap" example --- next(1 --> 1) next(2 --> 2) next(2 --> 3) next(3 --> 3) next(3 --> 4) next(3 --> 5) completed

flatMapFirst

Каждый элемент SO превращается в отдельный Observable.1) Изначально подписываемся на O1, его элементы зеркально генерируются в RO. Пока O1 генерирует элементы, все последующие Observable, сгенерированные из SO, отбрасываются, на них не подписываемся.2) как только O1 оканчивается, если будет сгенерирован новый Observable, на него подпишутся, и его элементы будут дублироваться в RO.Повторяем пункт 1, но вместо O1 берем последний сгенерированный Observable.

example("\"flatMapFirst\"") { Observable .timer(0, period: 0.1, scheduler: MainScheduler.instance) .skip(1) .take(3) .debug("\tвнешний\t\t") .flatMapFirst({ (value: Int) -> Observable in return Observable .timer(0, period: 0.1, scheduler: MainScheduler.instance) .skip(value) .take(value) .map({"\(value) --> \($0)"}) .debug("\tвнутренний\t") }) .debug("\tрезультат\t") .subscribe() }

Консоль:

--- "flatMap" example --- 2017-01-31 17:41:02.078: результат -> subscribed 2017-01-31 17:41:02.079: внешний -> subscribed 2017-01-31 17:41:02.180: внешний -> Event next(1) // учитывается, т.к. впервые 2017-01-31 17:41:02.182: внутренний -> subscribed // начало 1 2017-01-31 17:41:02.281: внешний -> Event next(2) // не учитывается, т.к. 1 жив 2017-01-31 17:41:02.284: внутренний -> Event next(1 --> 1) 2017-01-31 17:41:02.284: результат -> Event next(1 --> 1) // РЕЗУЛЬТАТ 2017-01-31 17:41:02.284: внутренний -> Event completed // окончание 1 2017-01-31 17:41:02.284: внутренний -> isDisposed 2017-01-31 17:41:02.381: внешний -> Event next(3) // учитывается, т.к. 1 окончен 2017-01-31 17:41:02.382: внутренний -> subscribed // начало 3 2017-01-31 17:41:02.382: внешний -> Event completed 2017-01-31 17:41:02.382: внешний -> isDisposed 2017-01-31 17:41:02.682: внутренний -> Event next(3 --> 3) 2017-01-31 17:41:02.682: результат -> Event next(3 --> 3) // РЕЗУЛЬТАТ 2017-01-31 17:41:02.782: внутренний -> Event next(3 --> 4) 2017-01-31 17:41:02.782: результат -> Event next(3 --> 4) // РЕЗУЛЬТАТ 2017-01-31 17:41:02.883: внутренний -> Event next(3 --> 5) 2017-01-31 17:41:02.883: результат -> Event next(3 --> 5) // РЕЗУЛЬТАТ 2017-01-31 17:41:02.883: внутренний -> Event completed // окончание 3 2017-01-31 17:41:02.883: внутренний -> isDisposed 2017-01-31 17:41:02.883: результат -> Event completed 2017-01-31 17:41:02.883: результат -> isDisposed

В примере благодаря задержкам в генерации мы видим, что, пока не произойдет окончание первого Observable, никакой новый Observable его не заменит.

flatMapLatest

Каждый элемент SO превращается в отдельный Observable. Изначально подписываемся на O1, его элементы зеркально генерируются в RO. Как только из SO выпускается очередной элемент и на его основе генерируется очередной Observable, элементы предыдущего Observable отбрасываются, т.к. происходит отписка. Таким образом в RO — элементы из последнего генерированного Observable.

example("\"flatMapLatest\"") { Observable .timer(0, period: 0.1, scheduler: MainScheduler.instance) .skip(1) .take(3) .debug("внешний\t") .flatMapLatest({ (value: Int) -> Observable in return Observable .timer(0, period: 0.1, scheduler: MainScheduler.instance) .skip(value) .take(value) .map({"\(value) --> \($0)"}) .debug("внутренний\t") }) .debug("результат\t") .subscribe() }

Консоль:

--- "flatMapLatest" example --- 2017-01-31 18:35:25.941: результат -> subscribed 2017-01-31 18:35:25.942: внешний -> subscribed 2017-01-31 18:35:26.044: внешний -> Event next(1) // Новый сигнал 2017-01-31 18:35:26.046: внутренний -> subscribed // flatMapLatest 2017-01-31 18:35:26.144: внешний -> Event next(2) // Новый сигнал 2017-01-31 18:35:26.145: внутренний -> isDisposed // Отписался от прошлого 2017-01-31 18:35:26.145: внутренний -> subscribed // flatMapLatest 2017-01-31 18:35:26.244: внешний -> Event next(3) // Новый сигнал 2017-01-31 18:35:26.245: внутренний -> isDisposed // Отписался от прошлого 2017-01-31 18:35:26.245: внутренний -> subscribed // flatMapLatest 2017-01-31 18:35:26.245: внешний -> Event completed // Новых сигналов не будет 2017-01-31 18:35:26.245: внешний -> isDisposed 2017-01-31 18:35:26.546: внутренний -> Event next(3 --> 3) 2017-01-31 18:35:26.546: результат -> Event next(3 --> 3) // РЕЗУЛЬТАТ 2017-01-31 18:35:26.646: внутренний -> Event next(3 --> 4) 2017-01-31 18:35:26.646: результат -> Event next(3 --> 4) // РЕЗУЛЬТАТ 2017-01-31 18:35:26.746: внутренний -> Event next(3 --> 5) 2017-01-31 18:35:26.746: результат -> Event next(3 --> 5) // РЕЗУЛЬТАТ 2017-01-31 18:35:26.746: внутренний -> Event completed 2017-01-31 18:35:26.746: внутренний -> isDisposed 2017-01-31 18:35:26.746: результат -> Event completed 2017-01-31 18:35:26.746: результат -> isDisposed

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

flatMapWithIndex

Каждый элемент SO превращается в отдельный Observable, и все элементы из [O1, O2, O3…] объединяются в RO. Порядок генерации элементов в RO зависит от времени их генерации в исходных [O1, O2, O3…] (как в команде merge). Отличие от flatMap в том, что еще одним параметром, переданным в функцию, является индекс сгенерированного элемента.

example("\"flatMapWithIndex\"") { Observable .timer(0, period: 0.1, scheduler: MainScheduler.instance) .skip(1) .take(3) .flatMapWithIndex({ (value: Int, index: Int) -> Observable in return Observable .timer(0, period: 0.1, scheduler: MainScheduler.instance) .skip(value) .take(value) .map({"\(value, index) --> \($0)"}) }) .subscribe({ event in print(event) }) }

Консоль:

--- "flatMapWithIndex" example --- next((1, 0) --> 1) next((2, 1) --> 2) next((2, 1) --> 3) next((3, 2) --> 3) next((3, 2) --> 4) next((3, 2) --> 5) completed

map

Observable -> Observable

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

example("\"map\"") { Observable .of(1, 2, 3) .map({ $0 * 5 }) .subscribe({ event in print(event) }) }

Консоль:

--- "map" example --- next(5) next(10) next(15) completed

mapWithIndex

Observable -> Observable

Элементы SO преобразуются, не меняя порядок их генерации. Можно менять не только значение, но и тип элементов. Отличие от map в том, что еще одним параметром, переданным в функцию, является индекс сгенерированного элемента.

example("\"mapWithIndex\"") { Observable .of("A", "B", "C") .mapWithIndex({ $0.0 + "\($0.1)" }) .subscribe({ event in print(event) }) }

Консоль:

--- "mapWithIndex" example --- next(A0) next(B1) next(C2) completed

window

SO = Observable RO = Observable<Observable>

Элементы из SO по определенным правилам передаются в генерирующиеся новые Observable. В качестве параметров передаются count (максимальное число элементов, которые будут сгенерированы каждым Observable) и timeSpan (время максимального ожидания наполнения текущего Observable из элементов SO). Таким образом элемент RO являет собой Observable, число генерируемых элементов которого равно от 0 до N. Основное отличие от bufffer в том, что элементы SO зеркалятся сгенерированными Observable моментально, а в случае buffer мы вынуждены ждать указанное в качестве параметра максимальное время (если буфер не заполнится раньше).

example("\"window\"") { let firstSequence = Observable .timer(0, period: 0.1, scheduler: MainScheduler.instance) .window(timeSpan: 0.16, count: 3, scheduler: MainScheduler.instance) .take(5) .subscribe(onNext: { (observable: Observable) in observable .subscribe({ event in print(event) }) }) }

Консоль:

--- "window" example --- next(0) next(1) completed next(2) next(3) completed next(4) completed next(5) next(6) completed next(7) next(8) completed

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

reduce

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

 

example("\"reduce\"") { Observable .of(1, 2, 3, 4) .reduce(1) {$0 * $1} .subscribe({ event in print(event) }) }

Консоль:

--- "reduce" example --- next(24) completed

scan

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

example("\"scan\"") { Observable .of(1, 2, 3, 4) .scan(1) {$0 * $1} .subscribe({ event in print(event) }) }

Консоль:

--- "scan" example --- next(1) next(2) next(6) next(24) completed

toArray

SO = Observable RO = Observable<[T]>

Все элементы из SO после генерации терминального состояния объединяются в массив, и генерируются RO.

example("\"toArray\"") { Observable .of(1, 2, 3) .toArray() .subscribe({ event in print(event) }) }

Консоль:

--- "toArray" example --- next([1, 2, 3]) completed

catchError

Позволяет перехватить сгенерированную ошибку из SO и заменить ее на новый Observable, который теперь будет генерировать элементы.

example("without \"catchError\"") { Observable .create({ (observer) in observer.onNext(1) observer.onNext(2) observer.onError(RxError.unknown) observer.onNext(3) return Disposables.create() }) .subscribe({ event in print(event) }) } example("with \"catchError\"") { Observable .create({ (observer) in observer.onNext(1) observer.onNext(2) observer.onError(RxError.unknown) observer.onNext(3) return Disposables.create() }) .catchError({ (error) -> Observable in return Observable.just(3) }) .subscribe({ event in print(event) }) }

Консоль:

--- without "catchError" example --- next(1) next(2) error(Unknown error occured.) --- with "catchError" example --- next(1) next(2) next(3) completed

После генерации очередного элемента была сгенерирована ошибка, но мы её перехватили и вернули взамен новый Observable.

catchErrorJustReturn

Позволяет перехватить сгенерированную ошибку из SO и заменить её на указанный элемент, после этого SO генерирует Completed.

example("without \"catchErrorJustReturn\"") { Observable .create({ (observer) in observer.onNext(1) observer.onNext(2) observer.onError(RxError.unknown) observer.onNext(3) return Disposables.create() }) .subscribe({ event in print(event) }) } example("with \"catchErrorJustReturn\"") { Observable .create({ (observer) in observer.onNext(1) observer.onNext(2) observer.onError(RxError.unknown) observer.onNext(3) return Disposables.create() }) .catchErrorJustReturn(3) .subscribe({ event in print(event) }) }

Консоль:

--- without "catchErrorJustReturn" example --- next(1) next(2) error(Unknown error occured.) --- with "catchErrorJustReturn" example --- next(1) next(2) next(3) completed

После генерации очередного элемента была сгенерирована ошибка, но мы её перехватили и вернули взамен новый элемент.

retry

Позволяет перехватить сгенерированную ошибку из SO и в зависимости от переданного параметра попытаться запустить SO c начала нужное число раз в надежде, что ошибка не повторится.

example("\"retry\" once") { Observable .create({ (observer) in observer.onNext(1) observer.onNext(2) observer.onError(RxError.unknown) observer.onNext(3) return Disposables.create() }) .retry(2) .subscribe({ event in print(event) }) }

Консоль:

--- "retry" once example --- next(1) next(2) next(1) next(2) error(Unknown error occured.)

Передаваемое в оператор целое число означает количество попыток дождаться успешного окончания. 0 — ни одной попытки, т.е. цепочка ни разу не будет запущена, и просто произойдет completed событие. 1 — такое же поведение, как будто оператор и не не был применен. 2 — исходная попытка + дополнительная, и т.д. Если в оператор не передать параметр, то количество попыток повторить будет бесконечным.

retryWhen

Позволяет перехватить сгенерированную ошибку из SO, и в зависимости от типа ошибки мы либо повторно генерируем ошибку, которая пробрасывается в RO, и на этом выполнение заканчивается, либо генерируем Observable (tryObservable), генерация каждого корректного элемента которого выполнит повторную подписку на SO в надежде, что ошибка исчезнет. Если tryObservable заканчивается ошибкой, она пробрасывается в RO, и на этом выполнение заканчивается.

example("\"retryWhen\"") { var counter = 0 let sequenceWithError = Observable .create({ observer in observer.on(.next(1)) observer.on(.next(2)) counter += 1 switch counter { case 0.. .create({ observer in observer.on(.next(10)) // observer.onError(RxError.noElements) return Disposables.create() }) .debug("without\t") let retrySequence = sequenceWithError .retryWhen({ (observableError: Observable) -> Observable in return observableError .flatMap({ (error: RxError) -> Observable in switch error { case .unknown: return sequenceWithoutError default: return Observable.error(error) } }) }) .subscribe() }

Консоль:

--- "retryWhen" example --- 2017-02-03 15:23:18.563: with -> subscribed 2017-02-03 15:23:18.564: with -> Event next(1) 2017-02-03 15:23:18.564: with -> Event next(2) 2017-02-03 15:23:18.565: with -> Event error(Unknown error occured.) 2017-02-03 15:23:18.566: without -> subscribed 2017-02-03 15:23:18.566: without -> Event next(10) 2017-02-03 15:23:18.567: with -> isDisposed 2017-02-03 15:23:18.568: with -> subscribed 2017-02-03 15:23:18.568: with -> Event next(1) 2017-02-03 15:23:18.568: with -> Event next(2) 2017-02-03 15:23:18.568: with -> Event error(Unknown error occured.) 2017-02-03 15:23:18.569: without -> subscribed 2017-02-03 15:23:18.569: without -> Event next(10) 2017-02-03 15:23:18.569: with -> isDisposed 2017-02-03 15:23:18.570: with -> subscribed 2017-02-03 15:23:18.570: with -> Event next(1) 2017-02-03 15:23:18.570: with -> Event next(2) 2017-02-03 15:23:18.571: with -> Event next(3)

Я встроил инкремент переменной i в генерацию sequenceWithError, чтобы на 3й попытке ошибка исчезла. Если раскоментировать генерацию ошибки RxError.Overflow, мы её не перехватим в операторе retryWhen и пробросим в RO.

multicast

Позволяет проксировать элементы из исходной SO на Subject, переданный в качестве параметра. Подписываться нужно именно на этот Subject, генерация элементов Subject начнется после вызова оператора connect.

example("\"multicast\"") { let subject = PublishSubject() let multicast = getExternalObservable() .multicast(subject) subject .asObservable() .debug("\tinstant\t") .subscribe() delay(0.25, closure: { _ in multicast.connect() }) delay(0.45, closure: { _ in subject .asObservable() .debug("\tdelayed\t") .subscribe() }) } func getExternalObservable() -> Observable { let timer = Observable .timer(0, period: 0.1, scheduler: MainScheduler.instance) .take(7) .shareReplay(1) defer { timer.subscribe() } return timer }

Консоль:

--- "multicast" example --- 2017-02-02 20:20:14.446: instant -> subscribed 2017-02-02 20:20:14.721: instant -> Event next(2) 2017-02-02 20:20:14.743: instant -> Event next(3) 2017-02-02 20:20:14.844: instant -> Event next(4) 2017-02-02 20:20:14.901: delayed -> subscribed 2017-02-02 20:20:14.943: instant -> Event next(5) 2017-02-02 20:20:14.943: delayed -> Event next(5) 2017-02-02 20:20:15.043: instant -> Event next(6) 2017-02-02 20:20:15.043: delayed -> Event next(6) 2017-02-02 20:20:15.043: instant -> Event completed 2017-02-02 20:20:15.043: instant -> isDisposed 2017-02-02 20:20:15.043: delayed -> Event completed 2017-02-02 20:20:15.043: delayed -> isDisposed

publish

publish = multicast + replay subjectПозволяет создавать Connectable Observable, которые не генерируют события даже после subscribe. Для старта генерации таким Observable нужно дать команду connect. Это позволяет подписать несколько Observer к одному Observable и начать генерировать элементы одновременно, вне зависимости от того, когда был выполнен subscribe.

var disposeBag: DisposeBag? = DisposeBag() example("\"publish\"") { let publish = getExternalObservable() .debug("\tsequence\t") .publish() Observable .timer(0.0, period: 0.1, scheduler: MainScheduler.instance) .skip(1) .take(2) .subscribe(onNext: { value in let disposable = publish .asObservable() .debug("\tdelayed #\(value)\t") .subscribe() disposeBag?.insert(disposable) }) delay(0.25, closure: { _ in publish.connect() }) delay(0.55, closure: { _ in disposeBag = nil }) } func getExternalObservable() -> Observable { let timer = Observable .timer(0, period: 0.1, scheduler: MainScheduler.instance) .take(8) .shareReplay(1) defer { timer.subscribe() } return timer }

Консоль:

--- "publish" example --- 2017-02-02 20:43:47.691: delayed #1 -> subscribed // ожидание connect 2017-02-02 20:43:47.791: delayed #2 -> subscribed // ожидание connect 2017-02-02 20:43:47.859: sequence -> subscribed // connect случился 2017-02-02 20:43:47.860: sequence -> Event next(2) 2017-02-02 20:43:47.860: delayed #1 -> Event next(2) 2017-02-02 20:43:47.860: delayed #2 -> Event next(2) 2017-02-02 20:43:47.889: sequence -> Event next(3) 2017-02-02 20:43:47.889: delayed #1 -> Event next(3) 2017-02-02 20:43:47.889: delayed #2 -> Event next(3) 2017-02-02 20:43:47.989: sequence -> Event next(4) 2017-02-02 20:43:47.989: delayed #1 -> Event next(4) 2017-02-02 20:43:47.989: delayed #2 -> Event next(4) 2017-02-02 20:43:48.089: sequence -> Event next(5) 2017-02-02 20:43:48.089: delayed #1 -> Event next(5) 2017-02-02 20:43:48.089: delayed #2 -> Event next(5) 2017-02-02 20:43:48.170: delayed #1 -> isDisposed // все отписались 2017-02-02 20:43:48.170: delayed #2 -> isDisposed // все отписались 2017-02-02 20:43:48.188: sequence -> Event next(6) // продолжила генерировать 2017-02-02 20:43:48.289: sequence -> Event next(7) // продолжила генерировать 2017-02-02 20:43:48.289: sequence -> Event completed 2017-02-02 20:43:48.289: sequence -> isDisposed

Как видно, хоть подписка и была произведена в разное время, пока не вызвали команду connect — генерация элементов не началась. Зато благодаря команде debug видно, что даже после того как все отписались, последовательность продолжила генерировать элементы.

refCount

Позволяет создать обычный Observable из Connectable. После первого вызова subscribe к этому обычному Observable происходит подписка Connectable на SO.Получается что-то вродеpublishSequence = SO.publish()refCountSequence = publishSequence.refCount()

SO будет продолжать генерировать элементы до тех пор, пока есть хотя бы один подписанный на refCountSequence. Как только все подписки на refCountSequence аннулируются, происходит отписка и publishSequence от SO.

var disposeBag: DisposeBag? = DisposeBag() example("\"refCount\"") { let sequence = getExternalObservable() .debug("\tsequence\t\t") let refCount = sequence .publish() .refCount() .debug("\trefCount\t\t") Observable .timer(0.0, period: 0.1, scheduler: MainScheduler.instance) .take(2) .subscribe(onNext: { value in let disposable = refCount .debug("\tsubscribe #\(value)\t") .subscribe() disposeBag?.insert(disposable) }) delay(0.35, closure: { _ in disposeBag = nil }) } private func getExternalObservable() -> Observable { let timer = Observable .timer(0, period: 0.1, scheduler: MainScheduler.instance) .shareReplay(1) defer { timer.subscribe() } return timer }

Консоль:

--- "refCount" example --- 2017-02-02 21:03:14.487: subscribe #0 -> subscribed 2017-02-02 21:03:14.488: refCount -> subscribed 2017-02-02 21:03:14.488: sequence -> subscribed 2017-02-02 21:03:14.489: sequence -> Event next(0) 2017-02-02 21:03:14.489: refCount -> Event next(0) 2017-02-02 21:03:14.489: subscribe #0 -> Event next(0) 2017-02-02 21:03:14.572: sequence -> Event next(1) 2017-02-02 21:03:14.573: refCount -> Event next(1) 2017-02-02 21:03:14.573: subscribe #0 -> Event next(1) 2017-02-02 21:03:14.576: subscribe #1 -> subscribed 2017-02-02 21:03:14.576: refCount -> subscribed 2017-02-02 21:03:14.673: sequence -> Event next(2) 2017-02-02 21:03:14.673: refCount -> Event next(2) 2017-02-02 21:03:14.673: subscribe #0 -> Event next(2) 2017-02-02 21:03:14.673: refCount -> Event next(2) 2017-02-02 21:03:14.673: subscribe #1 -> Event next(2) 2017-02-02 21:03:14.773: sequence -> Event next(3) 2017-02-02 21:03:14.774: refCount -> Event next(3) 2017-02-02 21:03:14.774: subscribe #0 -> Event next(3) 2017-02-02 21:03:14.774: refCount -> Event next(3) 2017-02-02 21:03:14.774: subscribe #1 -> Event next(3) 2017-02-02 21:03:14.835: subscribe #0 -> isDisposed 2017-02-02 21:03:14.835: refCount -> isDisposed 2017-02-02 21:03:14.835: subscribe #1 -> isDisposed 2017-02-02 21:03:14.835: refCount -> isDisposed // отписался последний 2017-02-02 21:03:14.835: sequence -> isDisposed // отписка от SO

replay

Если SO обычный, — конвертирует его в Connectable. После этого все, кто подпишутся на него после вызова connect(), мгновенно получат в качестве первых элементов последние сгенерированные N элементов. Даже если отпишутся все — Connectable будет продолжать генерировать элементы.

var disposeBag: DisposeBag? = DisposeBag() example("\"replay\"") { let sequence = getExternalObservable() .replay(2) let disposable1 = sequence .debug("\tfirst\t") .subscribe() disposeBag?.insert(disposable1) delay(0.5, closure: { _ in let disposable2 = sequence .debug("\tsecond\t") .subscribe() disposeBag?.insert(disposable2) }) delay(0.3, closure: { _ in sequence.connect() }) delay(0.7, closure: { _ in disposeBag = nil }) } fileprivate func getExternalObservable() -> Observable { let timer = Observable .timer(0, period: 0.1, scheduler: MainScheduler.instance) .take(10) .shareReplay(1) defer { timer.debug("\ttimer\t").subscribe() } return timer }

Консоль:

--- "replay" example --- 2017-02-02 21:36:30.914: timer -> subscribed 2017-02-02 21:36:30.918: first -> subscribed 2017-02-02 21:36:30.931: timer -> Event next(0) 2017-02-02 21:36:31.016: timer -> Event next(1) 2017-02-02 21:36:31.116: timer -> Event next(2) 2017-02-02 21:36:31.216: timer -> Event next(3) 2017-02-02 21:36:31.248: first -> Event next(3) 2017-02-02 21:36:31.316: timer -> Event next(4) 2017-02-02 21:36:31.317: first -> Event next(4) 2017-02-02 21:36:31.416: timer -> Event next(5) 2017-02-02 21:36:31.416: first -> Event next(5) 2017-02-02 21:36:31.468: second -> subscribed 2017-02-02 21:36:31.468: second -> Event next(4) // мгновенно получил 2017-02-02 21:36:31.468: second -> Event next(5) // мгновенно получил 2017-02-02 21:36:31.515: timer -> Event next(6) 2017-02-02 21:36:31.516: first -> Event next(6) 2017-02-02 21:36:31.516: second -> Event next(6) 2017-02-02 21:36:31.616: timer -> Event next(7) 2017-02-02 21:36:31.617: first -> Event next(7) 2017-02-02 21:36:31.617: second -> Event next(7) 2017-02-02 21:36:31.688: first -> isDisposed // все отписались 2017-02-02 21:36:31.688: second -> isDisposed // все отписались 2017-02-02 21:36:31.716: timer -> Event next(8) // продолжают генерироваться 2017-02-02 21:36:31.816: timer -> Event next(9) // продолжают генерироваться 2017-02-02 21:36:31.817: timer -> Event completed 2017-02-02 21:36:31.817: timer -> isDisposed

replayAll

Если SO обычный, — конвертирует его в Connectable. Все, кто подпишутся на него, после вызова connect() получат сначала все элементы, которые были сгенерированы ранее. Даже если отпишутся все, Connectable будет продолжать генерировать элементы.

var disposeBag: DisposeBag? = DisposeBag() example("\"replayAll\"") { let sequence = getExternalObservable() .replayAll() let disposable1 = sequence .debug("\tfirst\t") .subscribe() disposeBag?.insert(disposable1) delay(0.5, closure: { _ in let disposable2 = sequence .debug("\tsecond\t") .subscribe() disposeBag?.insert(disposable2) }) delay(0.3, closure: { _ in sequence.connect() }) delay(0.7, closure: { _ in disposeBag = nil }) } fileprivate func getExternalObservable() -> Observable { let timer = Observable .timer(0, period: 0.1, scheduler: MainScheduler.instance) .take(10) .shareReplay(1) defer { timer.debug("\ttimer\t").subscribe() } return timer }

Консоль:

--- "replayAll" example --- 2017-02-02 21:39:27.174: timer -> subscribed 2017-02-02 21:39:27.178: first -> subscribed 2017-02-02 21:39:27.190: timer -> Event next(0) 2017-02-02 21:39:27.276: timer -> Event next(1) 2017-02-02 21:39:27.376: timer -> Event next(2) 2017-02-02 21:39:27.476: timer -> Event next(3) 2017-02-02 21:39:27.509: first -> Event next(3) // 1й элемент 2017-02-02 21:39:27.576: timer -> Event next(4) 2017-02-02 21:39:27.576: first -> Event next(4) // 2й элемент 2017-02-02 21:39:27.675: timer -> Event next(5) 2017-02-02 21:39:27.675: first -> Event next(5) // 3й элемент 2017-02-02 21:39:27.731: second -> subscribed // подписание 2017-02-02 21:39:27.731: second -> Event next(3) // сразу получил 1й 2017-02-02 21:39:27.731: second -> Event next(4) // сразу получил 2й 2017-02-02 21:39:27.731: second -> Event next(5) // сразу получил 3й 2017-02-02 21:39:27.775: timer -> Event next(6) 2017-02-02 21:39:27.775: first -> Event next(6) 2017-02-02 21:39:27.775: second -> Event next(6) 2017-02-02 21:39:27.875: timer -> Event next(7) 2017-02-02 21:39:27.876: first -> Event next(7) 2017-02-02 21:39:27.876: second -> Event next(7) 2017-02-02 21:39:27.946: first -> isDisposed // все отписались 2017-02-02 21:39:27.946: second -> isDisposed // все отписались 2017-02-02 21:39:27.975: timer -> Event next(8) // продолжают генерироваться 2017-02-02 21:39:28.076: timer -> Event next(9) // продолжают генерироваться 2017-02-02 21:39:28.076: timer -> Event completed 2017-02-02 21:39:28.076: timer -> isDisposed

debug

RO полностью дублирует SO, но логируются все события с временной меткой.

example("\"debug\"") { Observable .of(1, 2, 3) .debug("sequence") .subscribe{} }

Консоль:

--- "debug" example --- 2017-02-03 10:01:07.746: sequence -> subscribed 2017-02-03 10:01:07.748: sequence -> Event next(1) 2017-02-03 10:01:07.748: sequence -> Event next(2) 2017-02-03 10:01:07.748: sequence -> Event next(3) 2017-02-03 10:01:07.748: sequence -> Event completed 2017-02-03 10:01:07.748: sequence -> isDisposed

do

RO полностью дублирует SO, но мы встраиваем перехватчик всех событий из жизненного цикла SO.

example("\"do\"") { Observable .of(1, 2, 3) .do(onNext: { value in print("do(\(value))") }) .subscribe({ event in print(event) }) }

Консоль:

--- "do" example --- do(1) next(1) do(2) next(2) do(3) next(3) completed

delaySubscription

Дублирует элементы из SO в RO, но с временной задержкой, указанной в качестве параметра.

example("\"delaySubscription\"") { var timer: Observable = getExternalObservable() timer .debug("\tnormal\t") .subscribe() timer .delaySubscription(0.9, scheduler: MainScheduler.instance) .debug("\tdelayed\t") .subscribe() } private func getExternalObservable() -> Observable { let observable = Observable .timer(0, period: 0.2, scheduler: MainScheduler.instance) .take(3) defer { observable.subscribe() } return observable }

Консоль:

--- "delaySubscription" example --- 2017-02-03 10:58:13.985: normal -> subscribed 2017-02-03 10:58:13.989: delayed -> subscribed 2017-02-03 10:58:13.999: normal -> Event next(0) 2017-02-03 10:58:14.187: normal -> Event next(1) 2017-02-03 10:58:14.387: normal -> Event next(2) 2017-02-03 10:58:14.387: normal -> Event completed 2017-02-03 10:58:14.387: normal -> isDisposed 2017-02-03 10:58:14.890: delayed -> Event next(0) 2017-02-03 10:58:15.091: delayed -> Event next(1) 2017-02-03 10:58:15.290: delayed -> Event next(2) 2017-02-03 10:58:15.291: delayed -> Event completed 2017-02-03 10:58:15.291: delayed -> isDisposed

observeOn

Указывает, на каком Scheduler должен выполнять свою работу Observer, особенно критично при работе с GUI.

example("\"observeOn\"") { DispatchQueue.global(qos: .background).async { Observable .of(1, 2, 3) .observeOn(MainScheduler.instance) .subscribe({ event in print("Main: \(Thread.isMainThread)\t\tEvent: \(event)") }) Observable .of("A", "B", "C") .subscribe({ event in print("Main: \(Thread.isMainThread)\t\tEvent: \(event)") }) } }

Консоль:

--- "observeOn" example --- Main: false Event: next(A) Main: false Event: next(B) Main: true Event: next(1) Main: true Event: next(2) Main: true Event: next(3) Main: true Event: completed Main: false Event: next(C) Main: false Event: completed

Как видно, благодаря observeOn мы смогли выполнить код внутри subscribe на другом потоке, хотя оба Observable были запущены на background.

subscribe

Оператор, связывающий Observable с Observer, позволяет подписаться на все события из Observable.

example("\"subscribe\" with failure") { observable(failing: true) } example("\"subscribe\" with success") { observable(failing: false) } func observable(failing: Bool) { var observable: Observable! switch failing { case true: observable = Observable.error(RxError.argumentOutOfRange) case false: observable = Observable.of(1) } observable .subscribe(onNext: { value in print("next: \(value)") }, onError: { error in print("error: \(error)") }, onCompleted: { print("completed") }, onDisposed: { print("disposed") }) }

Консоль:

--- "subscribe" with failure example --- error: Argument out of range. disposed --- "subscribe" with success example --- next: 1 completed disposed

subscribeOn

Указывает, на каком Scheduler выполнять подписку на Observable. Редко используемый оператор. Для получения колбэков на нужном Scheduler следует пользоваться observeOn.

example("\"subscribeOn\"") { DispatchQueue.global(qos: .background).async { Observable .of(1, 2, 3) .subscribeOn(MainScheduler.instance) .subscribe({ event in print("Main: \(Thread.isMainThread)\t\tEvent: \(event)") }) Observable .of("A", "B", "C") .subscribe({ event in print("Main: \(Thread.isMainThread)\t\tEvent: \(event)") }) } }

Консоль:

--- "subscribeOn" example --- Main: true Event: next(1) Main: true Event: next(2) Main: true Event: next(3) Main: true Event: completed Main: false Event: next(A) Main: false Event: next(B) Main: false Event: next(C) Main: false Event: completed

timeout

Дублирует элементы из SO в RO, но если в течение указанного времени SO не сгенерировало ни одного элемента, RO генерирует ошибку.

delay(0.0) { _ in example("\"timeout\" with success") { observable(timeout: 0.3, delay: 0.1) .debug("\tsuccess\t") .subscribe() } } delay(0.5) { _ in example("\"timeout\" with failure") { observable(timeout: 0.3, delay: 0.4) .debug("\tfailure\t") .subscribe() } } func observable(timeout: RxTimeInterval, delay: RxTimeInterval) -> Observable { return Observable .timer(delay, period: 0.1, scheduler: MainScheduler.instance) .take(1) .timeout(timeout, scheduler: MainScheduler.instance) }

Консоль:

--- "timeout" with success example --- 2017-02-03 12:01:31.344: success -> subscribed 2017-02-03 12:01:31.447: success -> Event next(0) 2017-02-03 12:01:31.447: success -> Event completed 2017-02-03 12:01:31.447: success -> isDisposed --- "timeout" with failure example --- 2017-02-03 12:01:31.856: failure -> subscribed 2017-02-03 12:01:32.157: failure -> Event error(Sequence timeout.) 2017-02-03 12:01:32.157: failure -> isDisposed

using

using

Позволяет проинструктировать Observable создать ресурс, который будет жить лишь пока жив RO, в качестве параметров передаются 2 фабрики, одна генерирует ресурс, вторая — Observable из ресурса, у которых будет единое время жизни.

example("\"using\"") { Observable .of(1,3) .flatMap { value -> Observable in return Observable .using(resourceFactory(value), observableFactory: observableFactory()) } .subscribe({ event in print(event) }) } class Factory: Disposable { let value: Int init(_ value: Int) { self.value = value } var observable: Observable { let array = Array(repeating: value, count: value) return Observable.from(array) } func dispose() { print("Factory(\(value)) disposed") } } func resourceFactory(_ value: Int) -> (() -> Factory) { func resource() -> Factory { return Factory(value) } return resource } func observableFactory() -> ((Factory) -> Observable) { func observable(_ resource: Factory) -> Observable { return resource.observable } return observable }

Консоль:

--- "using" example --- next(1) Factory(1) disposed next(3) next(3) next(3) Factory(3) disposed completed

Как видно, после того, как Observable закончил генерировать элементы, у нашего ресурса Factory был вызван метод dispose.

За материал выражаем благодарность международной IT-компании Noveo.

tproger.ru

Новый язык программирования Swift: комментарии разработчиков

swift-developers1

Представленный на WWDC 2014 язык программирования Swift стал причиной большой шумихи в кругах разработчиков и породил массу вопросов. Нужно ли обучаться новому языку? Какие его основные преимущества перед Objective-C и C#? И что, собственно, делать тем, кто создает мультиплатформенные игрушки? Для того, чтобы получить ответы на эти вопросы, я обратился к разработчикам известных игр в App Store и подготовил небольшой опрос в формате «разработчик — его приложение — комментарий». Результат получились неоднозначными и где-то даже неожиданным.

swift-developers5

Игра Игры Дракона

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

swift-developers7

Игра Blek

Swift — язык интересный и многообещающий, но мы активно используем Unity, так что нам вопрос нового языка программирования комментировать здесь трудно. Мы не собираемся использовать Swift при разработке наших игр.

swift-developers9

Игра Demolition Master 3D

На первый взгляд можно сказать, что код на языке Swift будет писаться быстрее, так как отсутствую какие-либо лишние символы. Все очень лаконично, кратко. Также этот язык дает больше гибкости (чего только стоит возврат функцией нескольких значений). Язык являет собой неплохой симбиоз C/C++/Objective C/Java. Трудно пока оценить насколько эта гибкость и лаконичность повлияет на читаемость кода.

В связи отсутствия кросплатформеного компилятора, Swift пока можно использовать только для нативных разработок под iOS и Mac. Пока разработчики игр как и раньше будут отдавать предпочтения Unity 3D, Cocos 2DX, Corona и т.д.

swift-developers8

Игра Leo’s Fortune

1) Swift — чистый и современный. Приятно было услышать, что в нем используется LLVM для компиляции нативного кода плюс все Cocoa API адаптированы;2) Он — более легкий и удобочитаемый, чем Objective-C;3) Самый большой недостаток таких специфичных платформ, как Objective-C и Swift — вопрос портирования. Мы разрабатываем наши игры в C++, чтобы было легче портировать на другие платформы. Swift улучшить наш Cocoa-код и всё, что касается API компании Apple.

swift-developers10

Приложение Clean My Mac

Swift – что-то, что никто не ожидал увидеть на WWDC Keynote. Apple взяла все самое лучшее из всех известных мне языков и воплотила это в Swift. Swift дает большие возможности по упрощению кода. То, что разработчик ранее писал с помощью verbose (слишком многословных) конструкций, сейчас можно заменить несколькими символами. Я не вижу Swift заменой Objective-C. Как выяснилось, он еще немного сырой для полного перевода своих проектов на Swift. Но использовать его можно для задач, которые хочется оптимизировать или визуально упростить. Преимущество для разработчиков игр здесь только одно – если ты видел конструкции, используемые в Swift, в языке, который используешь повседневно – значит все хорошо, переход не займет много времени.

Этот язык создавался в строжайшем секрете с 2010 года. Один из сотрудников Apple в разговоре с Крейгом Федериги сказал, что не ожидал, что от его команды скроют такой продукт. Много разработчиков были растеряны после keynote (Денис присутствовал на WWDC — прим. ред). Но нам всем нужно идти дальше и учиться :)

swift-developers11

Игра Викторина IRC

С одной стороны, Apple, видимо, делает ставку на привлечение в разработку под свою платформу опытных разработчиков на других языках (в Swift чувствуется влияние многих из них). С другой стороны, в новом Xcode появился также новый режим – REPL (Read-Eval-Print-Loop), который позволяет видеть результат выполнения кода “на лету”, что очень удобно для новичков. Swift вполне может стать их первым языком программирования. Необходимость в создании нового отдельного языка для меня не совсем очевидна, но время покажет, стоило оно того или нет.Вообще, общее впечатление от API Swift — более простое, чистое, читабельное и понятное. Хоронить Objective-C, наверное, пока рано, но Swift вызвал у всех действительно неподдельный интерес. Единственная возможная причина медленного развития языка — если он так и останется “внутренним”, только для разработки под Apple.

FavoriteLoading В закладки undefined iPhones.ru Представленный на WWDC 2014 язык программирования Swift стал причиной большой шумихи в кругах разработчиков и породил массу вопросов. Нужно ли обучаться новому языку? Какие его основные преимущества перед Objective-C и C#? И что, собственно, делать тем, кто создает мультиплатформенные игрушки? Для того, чтобы получить ответы на эти вопросы, я обратился к разработчикам известных игр в... Костя Грибанов avatar

Костя Грибанов

@Kosmi4

Z236013786538 R371936473834

  • До ←

    Обзор нано-телефона Lexand Mini. Самого маленького телефона в России

  • После →

    Обзор акустики Philips S5X (BTS5000). Мощная Bluetooth-стереосистема для дома

www.iphones.ru


Смотрите также

 

..:::Новинки:::..

Windows Commander 5.11 Свежая версия.

Новая версия
IrfanView 3.75 (рус)

Обновление текстового редактора TextEd, уже 1.75a

System mechanic 3.7f
Новая версия

Обновление плагинов для WC, смотрим :-)

Весь Winamp
Посетите новый сайт.

WinRaR 3.00
Релиз уже здесь

PowerDesk 4.0 free
Просто - напросто сильный upgrade проводника.

..:::Счетчики:::..

 

     

 

 

.