Що нового в Swift 3?

image

Як ми всі давно знаємо, Apple інтегрувала Swift 3 в Xcode 8. Це перша версія мови з відкритим вихідним кодом, який працює як macOS, так і на Linux. Якщо ви стежили за процесом розвитку мови на Swift Evolution з грудня минулого року і встигли з ним поекспериментувати в IBM sandbox, ви вже напевно зрозуміли, що в ньому з'явилося безліч змін. Абсолютно впевнений, що при компіляції існуючого проекту в Xcode 8, код здивує вас наявністю помилок. Але це можна виправити. Давайте познайомимося з деякими змінами в новій версії мови.

Зміни в новій версії, можна розділити на дві основні категорії:
  • Видалені функції, які вже застаріли з Swift 2.2
  • Поліпшення мови
Давайте почнемо з категорії віддалених функцій, оскільки її легше зрозуміти і ви, напевно, зустрічалися з цими функціями, коли отримували попередження в Xcode версій 7.3.

Оператори ++ і --
Оператори инкремента і декремента спадщини мови C і їх функціональність проста — додати або відняти 1 до або приплюсувати певної змінної:

var i = 0
i++
++i
i--
--i

Тим не менш, все стає складніше, коли справа доходить до прийняття рішення, який з них вибрати. Кожен з них може бути представлений у двох можливих варіантах: префікс і постфікс — всі вони функціонують завдяки движку і повертають значення, які ви можете використовувати або відхилити завдяки перезаггрузке операторів.

Це більше ніж достатньо для початківців, так як вони були видалені використовуйте оператори присвоювання та складання += і віднімання -= замість:

var i = 0
i += 1
i= 1


Звичайно, ви можете використовувати оператори додавання ( + ), віднімання (-), а також — спільно використовувати оператори присвоювання, завдяки чому ви зможете економити свій час при написанні коду, хоча:

i = i + 1
i = i - 1


Для ознайомлення: Якщо ви хочете більше дізнатися про мотиви цього зміни, подивіться пропозицію Кріса Латнера з цього приводу.

Стиль мови для написання циклів вже історія
Найбільш поширеним прикладом використання операторів инкрементов і декрементів є стиль мови для написання циклу. Видалення операторів означає видалення всього що з ними пов'язано, оскільки з цим усім ви вже нічого не зможете зробити, на відміну від того, що ви робили раніше з виразами і оператором для вказівки діапазону.

Наприклад, якщо у вас є кілька знанні, ви, ймовірно, будете використовувати цикл for для відображення числа від 1 до 10:

for (i = 1; i < = 10; i++) {
print(i)
}


В Swift 3 це неможливо. Це його прототип в мові Swift 3 — давайте розглянемо оператор закритого діапазону (...) в дії:

for i in 1...10 {
print(i)
}


В якості альтернативи, ви можете також використовувати його для кожного циклу з замкнутими виразами і скороченими аргументами — більш детальну інформацію ви можете знайти на здесь.

(1...10).forEach {
print($0)
}


Для ознайомлення: Якщо ви хочете дізнатися більше про мотивацію цього зміни, подивіться пропозицію Еріки Садун.

Видалили змінну з параметрів функції
Параметри функції зазвичай визначаються як константи, оскільки їх не потрібно змінювати в межах функцій. Тим не менш, існують певні випадки, коли оголошення їх як змінні може стати в нагоді. В Swift 2, ви можете відзначити параметр функції як змінну з ключовим словом var. Після того, як буде вказано параметр var, він створить локальну копію значення, тому ви зможете змінювати її значення в тілі функції.

В якості прикладу, наступна функція визначає найбільший загальний дільник двох заданих чисел — якщо ви упустили математичний курс в середній школі, почитайте більше про це тут:

func gcd(var a: Int, var b: Int) -> Int {

if (a == b) {
return a
}

repeat {
if (a > b) {
a = a - b
} else {
b = b - a
}
} while (a != b)

return a
}


Алгоритм простий: якщо обидва числа вже рівні, виконується повернення одного з них. В іншому випадку, порівняйте їх, виконайте віднімання меншого більшого і привласнюйте результат до більшого, поки вони не стануть рівними і виконайте повернення будь-якого з них. Як ви бачите, позначивши а і Ь в якості змінних, ми можемо змінити їх значення функції.

Swift 3 більше не дозволяє розробникам встановлювати параметри функції, як змінні, оскільки розробники Swift можуть заплутатися між var та inout. Таким чином, остання версія Swift просто видаляє var з параметрів функції.

Тому, щоб написати ту ж саму функцію gcd Swift 3, необхідний інший підхід. Вам потрібно зберегти значення параметрів функції для локальних змінних:

func gcd(a: Int, b: Int) -> Int {

if (a == b) {
return a
}

var c = a
var d = b

repeat {
if (c > d) {
c = c - d
} else {
d = d - c
}
} while (c != d)

return c
}


Якщо ви хочете дізнатися більше про мотиви цього рішення, ви можете прочитати оригінальна пропозиція.

Послідовна поведінка лейбли для параметрів функції
Списки параметрів функції є кортежі, так що ви можете використовувати їх для виклику функцій, до тих пір, поки структура кортежу збігається з прототипом функції. Візьмемо функцію gcd() в якості прикладу. Ви можете виконати виклик наступним чином:

gcd(8, b: 12)


Або ви можете навіть викликати функцію, як показано нижче:

let number = (8, b: 12)
gcd(number)


Як ви бачите в Swift 2, вам не потрібно вказати мітку першого параметра. Тим не менш, ви повинні вказати мітку для другого (і інших параметрів при виклику функції.

Цей синтаксис є заплутаним для початківців розробників, тому він призначений для стандартизації поведінки міток. У новій версії мови Swift, ви можете викликати функцію наступним чином:

gcd(a: 8, b: 12)


Ви повинні явно вказати мітку для першого параметра. Якщо ви не зробите цього, Xcode 8 покаже вам повідомлення про помилку.

Ваша перша реакція на це зміна може виглядати як «OMG! Мені доведеться зробити багато змін в існуючому коді ». Ви праві. Це тонни змін. Таким чином, компанія Apple пропонує спосіб придушити першу мітку параметра виклику функції. Ви можете додати підкреслення до першого параметру, як показано нижче:

func gcd(_ a: Int, b: Int) -> Int {

...

}


Роблячи це, ви можете викликати функцію, використовуючи старий спосіб — без вказівки першої мітки. Це допоможе зробити міграцію коду з Swift 2 на Swift 3 набагато простіше.

Про мотивації і намір цього зміни, ви можете прочитати це пропозиція.

Селектори як рядки більше не використовуються
Давайте створимо кнопку і виконаємо будь-які дії, при натисканні на неї — використовуйте тільки playground, а не storyboard:

// 1
import UIKit
import XCPlayground

// 2
class Responder: NSObject {

func tap() {
print("Button pressed")
}
}
let responder = Responder()

// 3
let button = UIButton(type: .System)
button.setTitle("Button", forState: .Normal)
button.addTarget(responder, action: "tap", forControlEvents: .TouchUpInside)
button.sizeToFit()
button.center = CGPoint(x: 50, y: 25)

// 4
let frame = CGRect(x 0, y: 0, width: 100, height: 50)
let view = UIView(frame: frame)
view.addSubview(button)
XCPlaygroundPage.currentPage.liveView = view


Там відбувається досить багато подій, так що давайте розіб'ємо їх на етапи:
  1. Імпорт фреймворків UIKit і XCPlayground — вам необхідні щоб створити кнопку і показати його в редакторі playground.
  2. Визначте методу натискання, який буде виконуватися, коли користувач натискає на кнопку і створіть цільової? логічний об'єкт кнопки її базовий клас NSObject, оскільки селектори працюють тільки з методами Objective-C.
  3. Оголошення кнопки і установка властивостей.
  4. Створення view певного розміру, додавання кнопки на view і відображення його асистента редактора Playground.


Подивіться на виділений код. Селектор кнопки є рядком. Якщо ви введете його неправильно, то код буде складати, але станеться збій під час його виконання, так як відповідний метод може бути не знайдено.

Щоб виправити потенційну проблему під час компіляції, в Swift 3 замінили рядок селекторів ключовим словом #selector(). Це дозволяє компілятору виявити проблему раніше, якщо ви не правильно вкажіть ім'я методу.

button.addTarget(responder, action: #selector(Responder.tap), for: .touchUpInside)


Для розуміння причин цієї зміни, ви можете прочитати пропозицію Доуга Грегора.

Це що стосується віддалених функцій. Тепер давайте перейдемо до основних моментів модернізації мови.

Key-paths у вигляді рядка
Ця функція аналогічна до попередньої, але вона відноситься key value coding (KVC) і ключовому-значенням спостереження (KVO):

class Person: NSObject {
var name: String = ""

init(name: String) {
self.name = name
}
}
let me = Person(name: "Cosmin")
me.valueForKeyPath("name")


Ви створюєте клас Person, який відповідає ключовому-значенням кодування, створюєте мою ідентичність з класом призначеним инициализатором і використовуєте KVC, щоб встановити ім'я.

Знову ж таки, якщо ви зробите це неправильно, все вибухне!

На щастя, це більше не станеться в Swift 3. Key-path, були замінені виразом #keyPath():

class Person: NSObject {
var name: String = ""

init(name: String) {
self.name = name
}
}
let me = Person(name: "Cosmin")
me.value(forKeyPath: #keyPath(Person.name))


Для розуміння причин цієї зміни, ви можете прочитати пропозицію Девіда Хартс.

Видалений префікс базових типів NS
Прибрали префікс NS для базових типів. Подивимося, як це працює. Типовим прикладом є робота з JSON:

let file = NSBundle.mainBundle().pathForResource("tutorials", ofType: "json")
let url = NSURL(fileURLWithPath: file!)
let data = NSData(contentsOfURL: url)
let json = try! NSJSONSerialization.JSONObjectWithData(data!, options: [])
print(json)


Ви можете використовувати базові класи для підключення до файлу та вилучення даних у форматі JSON відповідним чином: NSBundle -> NSURL -> NSData -> NSJSONSerialization.

Префікс NS Swift 3 не використовується, так що все це зводиться доBundle -> URL -> Data -> JSONSerialization():

let file = Bundle.main().pathForResource("tutorials", ofType: "json")
let url = URL(fileURLWithPath: file!)
let data = try! Data(contentsOf: url)
let json = try! JSONSerialization.jsonObject(with data)
print(json)


Для цієї процедури зміни присвоєння імен, ви можете перевірити це пропозиція, написане Тоні Паркером і Філіпом Хауслером.

M_PI vs .pi
Давайте з'ясуємо кола і площа круга з заданим радіусом:

let r = 3.0
let circumference = 2 * M_PI * r
let area = M_PI * r * r


Для старих версій Swift, ви використовуєте M_PI для позначення константи pi. Swift 3 інтегрує константу pi тип Float, Double та CGFloat:

Float.pi
Double.pi
CGFloat.pi


Вище представлений фрагмент коду буде написаний, як це в Swift 3:

let r = 3.0
let circumference = 2 * Double.pi * r
let area = Double.pi * r * r


За допомогою логічного виводу типу, ви можете навіть опустити тип. Ось коротка версія:

let r = 3.0
let circumference = 2 * .pi * r
let area = .pi * r * r


Grand Central Dispatch
Grand Central Dispatch використовується для мережевих операцій, які не блокують користувальницький інтерфейс на основному потоці. Воно написано на мові C і його API є великою складністю для початківців розробників, навіть для виконання пошукових завдань, таких як створення асинхронної черги і виконання будь-яких операцій:

let queue = dispatch_queue_create("Swift 2.2", nil)
dispatch_async(queue) {
print("Swift 2.2 queue")
}


Swift 3 усуває весь шаблонний код і надлишковий матеріал, приймаючи об'єктно-орієнтований підхід:

let queue = DispatchQueue(label: "Swift 3")
queue.async {
print("Swift 3 queue")
}


Для отримання додаткової інформації про цю зміну, ви можете прочитати написане Меттом Райт.

Core Graphics тепер більше Swifty
Core Graphics являє собою потужний графічний фреймворк, але він використовує інтерфейс в стилі, схожий на GCD:

let frame = CGRect(x 0, y: 0, width: 100, height: 50)

class View: UIView {

override func drawRect(rect: CGRect) {
let context = UIGraphicsGetCurrentContext()
let blue = UIColor.blueColor().CGColor
CGContextSetFillColorWithColor(context, blue)
let red = UIColor.redColor().CGColor
CGContextSetStrokeColorWithColor(context, red)
CGContextSetLineWidth(context, 10)
CGContextAddRect(context, frame)
CGContextDrawPath(context, .FillStroke)
}
}
let aView = View(frame: frame)


Ви створюєте view frame, розширюючи клас UIView, якщо перевизначити метод DrawRect ().

Swift 3 приймає зовсім інший підхід — спочатку розгортає поточний графічний контекст і виконує всі операції малювання, згодом пов'язані з ним:

let frame = CGRect(x 0, y: 0, width: 100, height: 50)

class View: UIView {

override func draw(_ rect: CGRect) {
guard let context = UIGraphicsGetCurrentContext() else {
return
}

let blue = UIColor.blue().cgColor
context.setFillColor(blue)
let red = UIColor.red().cgColor
context.setStrokeColor(red)
context.setLineWidth(10)
context.addRect(frame)
context.drawPath(using: .fillStroke)
}
}
let aView = View(frame: frame)


Примітка: Контекст буде нульовим перед викликом методу DrawRect(), тому вам необхідно розгорнути за допомогою виразу guard — докладніше про це здесь.

Угода про присвоєння імен дієслів і іменників
Час для граматики! Групи методів в Swift 3 діляться на дві категорії: методи, які повертають деяке значення — маються на увазі як іменники і методи, які виконують певний вид дій — маються на увазі як дієслова.

Тут виконується висновок від 10 до 1:

for i in (1...10).reverse() {
print(i)
}


Ви використовуєте метод reverse(), для зміни діапазону. Swift 3 обробляє цю операцію як іменник, так як він повертає вихідний діапазон в зворотному порядку. Він додає суфікс «ed» до методу:

for i in (1...10).reversed() {
print(i)
}


Найбільш поширене використання кортежів/tuples, щоб вивести вміст масиву:

var array = [1, 5, 3, 2, 4]
for (index, value) in array.enumerate() {
print("\(index + 1) \(value)")
}


Swift 3 обробляє цей метод як іменник, так як він повертає кортеж, що містить поточний індекс і значення цього масиву, і додає суфікс «ed» до нього:

var array = [1, 5, 3, 2, 4]
for (index, value) in array.enumerated() {
print("\(index + 1) \(value)")
}


Іншим прикладом може служити сортування масиву. Ось приклад того, як ви можете відсортувати масив в порядку зростання:

var array = [1, 5, 3, 2, 4]
let sortedArray = array.sort()
print(sortedArray)


Swift 3 обробляє цю операцію як іменник, так як він повертає відсортований масив. Метод sort тепер називається sorted:

var array = [1, 5, 3, 2, 4]
let sortedArray = array.sorted()
print(sortedArray)


Давайте відсортуємо масив, без використання проміжної константи. В Swift 2, ви можете викликати функцію наступним чином:

var array = [1, 5, 3, 2, 4]
array.sortInPlace()
print(array)


Ви використовуєте sortInPlace() для сортування змінюваного масиву. Swift 3 обробляє цей метод як дієслово, так як він виконує фактичну сортування без повернення значень. Він використовує тільки базове слово, яке описує дію. Так sortInPlace() тепер називається sort():

var array = [1, 5, 3, 2, 4]
array.sort()
print(array)


Більш детальну інформацію про угоду про іменах, ви можете перевірити в API Design Guidelines.

API в мові Swift
Swift 3 приймає просту філософію для своїх API, — опустити непотрібні слова, так що якщо що-то є зайвим чи може бути виведено з контексту, видаліть його:

  • XCPlaygroundPage.currentPage стає PlaygroundPage.current
  • button.setTitle(forState) стає button.setTitle(for)
  • button.addTarget(action, forControlEvents) стає button.addTarget(action, for)
  • NSBundle.mainBundle () стає Bundle.main ()
  • NSData (contentsOfURL) стає URL(contentsOf)
  • NSJSONSerialization.JSONObjectWithData() стає JSONSerialization.jsonObject(with)
  • UIColor.blueColor () стає UIColor.blue ()
  • UIColor.redColor () стає UIColor.red ()


Enum
Swift 3 обробляє перерахування як властивості, так що використовуйте lowerCamelCase замість upperCamelCase з них:

  • .System стає .system
  • .TouchUpInside стає .touchUpInside
  • .FillStroke стає .fillStroke
  • .CGColor стає .cgColor


@discardableResult
В Swift 3, Xcode покаже вам попередження, якщо ви не використовуєте обчислене значення функції або методу. Ось приклад:

image

У наведеному вище коді, метод printMessage повертає отримане повідомлення для абонента об'єкта. Тим не менш, повернуте значення не використовується. Це може бути потенційною проблемою, тому що компілятор в Swift 3 дасть вам попередження.

В даному випадку, якщо це не є оброблюваним її обчислене значення. Ви можете придушити попередження шляхом додавання @discardableResult в оголошенні методу:

override func viewDidLoad() {
super.viewDidLoad()

printMessage(message: "Hello Swift 3!")
}

@discardableResult
func printMessage(message: String) -> String {
let outputMessage = "Output : \(message)"
print(outputMessage)

return outputMessage
}


Висновок
Це все про Swift 3. Нова версія Swift є одним з основних релізів, що робить мову ще краще. Він містить багато фундаментальних змін, які, безумовно, можуть вплинути на ваш існуючий код. Я сподіваюся, що ця стаття допоможе вам краще зрозуміти зміни, і, сподіваюся, допоможе вам заощадити деякий час, щоб перенести свій проект на Swift.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.