Nowości w języku programowania Swift 2.2
Słowem wstępu
Wersja Swift 2.2 zawiera kilka poprawek, kilka nowości a sporo metod oznaczonych zostało jako przestarzałe (deprecated). Jest to przygotowanie do wydania wersji 3, w której spodziewam się jeszcze większych zmian. Wersja Swift 2.2 jest załączona do Xcode 7.3 wydanego w pełnej wersji w lutym.
Zmiany idą w naprawdę dobrym kierunku i wraz z wersją 3 można spodziewać się w pełni dojrzałego języka programowania, który będzie mógł być wykorzystywany nie tylko do rozwoju aplikacji na iOS (patrz projekt http://perfect.org)
Zapraszam do lektury, punkty starałem napisać od najmniej istotnych do najważniejszych. Ostatni punkt dla programistów języków wywodzących się z C będzie szokiem.
Składnia Tuple splat oznaczona jako przestarzała
Tuple splat jest techniką przekazywania do metody mającej kilka parametrów, Tupla którego typy odpowiadają typom metody. Najłatwiej zobrazuje to przykład:
1 2 3 4 5 6 |
func showBookPrice(title: String, price: Double) { print("Book \(title) cost \(price)") } let book = ("Pan Samochodzik", price: 39.0) showBookPrice(book) |
Konstrukcja rzadko używana i widywana, na szczęście jest oznaczona jako przestarzała. Została uznana za mało czytelną i łamiącą zasadę samo dokumentującego się kodu.
Sprawdzanie wersji Swift
W nowym wydaniu języka zostało wprowadzone sprawdzanie jego wersji. Powinno mieć ono zastosowanie tylko w bibliotekach, które udostępniamy dla świata. W naszym kodzie unikajmy dyrektyw preprocesora, ponieważ czynią one kod mniej czytelnym, łatwiejszym na popełnianie błędów.
Wersje języka sprawdzamy poniższą konstrukcją:
1 2 3 4 5 6 7 |
#if swift(>=3) print("To jest Swift 3 lub nowszy") #elseif swift(>=2.2) print("To jest Swift 2.2 lub nowszy, ale nie 3") #else print("Starsza wersja Swifta") #endif |
Słowa kluczowe jako argumenty metody
Starsze wersje Swift pozwalały używać słów kluczowych jako argumentów funkcji po ujęciu je w znaku pojedynczego cudzysłowu.
1 2 3 4 |
func find(street: String, 'in' city: String) { print("Where is \(street) street in \(city)?") } find("Piotrkowska", 'in': "Łódź") |
Aktualnie można używać słów kluczowych (oprócz inout, var i let) bez żadnych znaków. Dodatkowo otwierając projekt używający takiej konstrukcji w Xcode zostanie on automatycznie poprawiony przez Fix-it do poniższej postaci:
1 2 3 4 |
func find(street: String, in city: String) { print("Where is \(street) street in \(city)?") } find("Marszałkowska", in: "Warszawa") |
Porównywanie tupli
Ponieważ tuple są fundamentalnym typem w Swift, dziwne, że ta funkcjonalność dopiero teraz jest wprowadzona, ale lepiej późno niż wcale. W Swift 2.2 zostało wprowadzone porównywanie dwóch tupli. Porównanie polega na porównaniu każdego elementu jednego tupla do elementu drugiego tupla. Zwracana jest prawda, jeśli wszystkie elementy są takie same.
1 2 3 4 5 6 7 8 |
let firstCar = (company: "Honda", color: "Red", engine: 1.6) let secondCar = (company: "Toyota", color: "Silver", engine: 1.2) if firstCar == secondCar { print("This is the same car") } else { print("Cars are different") } |
Dodatkową własnością porównywania jest to, że nieporównywane są nazwy elementów tylko ich wartości, czyli można porównać samochód do osoby i może wyjść, że są sobie równe.
1 2 3 4 5 6 7 8 |
let car = (color: "Red", name: "Iwon", engine: 1.6) let person = (name: "Red", firstname: "Iwon", height: 1.6) if car == person { print("Strange, but person and car are the same.") } else { print("Different") } |
Jedno ważne ostrzeżenie, porównywanie działa tylko do maksymalnie 6 elementów, powyżej tej wartości zwracany jest fałsz.
Parametry var są przestarzałe
Do wersji Swift 2.2 parametry metody mogliśmy oznaczać jako var jeśli chcieliśmy zmodyfikować je wewnątrz metody:
1 2 3 4 5 6 7 8 |
func adding(var count: Int) { count = count + 10 print("Now is \(count)") } var count = 1 adding(count) print("After adding function count is \(count)") |
W nowej wersji języka należy ściśle zdefiniować, że chcemy zmieniać parametr i jawnie przesłać go przez referencje. Wprowadza to poprawę czytelności kodu, bez zastanawiania się, jaka będzie wartość parametru po wywołaniu metody.
Przesłanie przez referencję wygląda następująco i jak można się spodziewać wynik now jest 11, after również 11.
1 2 3 4 5 6 7 8 |
func addingInOut(inout count: Int) { count = count + 10 print("Now is \(count)") } var count = 1 addingInOut(&count) print("After adding function count is \(count)") |
Takie zmiany jak powyższa tworzą Swift coraz lepszym językiem. Im mniej nieścisłości można utworzyć tym lepiej. Konstrukcja języka wymusza pisanie dobrego, jasnego kodu.
Zmiana wywoływania identyfikatorów trybu debug
Kompilator języka dostarcza nam dyrektywy kompilatora, czyli polecenia zawarte w kodzie źródłowym, które dla translatora są poleceniami wykonania pewnej czynności. W nowej wersji Swifta zostały one uproszczone i wywoływane są po znaku #.
W trybie debug można teraz uzyskać informacje o nazwie pliku, linii, kolumnie i nazwie metody gdzie uruchomiona jest dyrektywa.
Stare konstrukcje wyglądały następująco:
1 |
print("I'm in file \(__FILE__), line \(__LINE__), column \(__COLUMN__), and function \(__FUNCTION__).") |
Natomiast teraz należy ich używać następująco:
1 |
print("I'm in file \(#file), line \(#line), column \(#column), and function \(#function)") |
O ile zmiana powyższych nie jest tak istotna, to dwa punkty dalej opiszę naprawdę bardzo ważną zmianę związana z dyrektywami i selektorami.
Dodanie do kolekcji metody removeFirst()
W Swift zawsze istniała operacja removeLast() na tablicach. Od teraz została dodana również metoda removeFirst() zwracająca i usuwająca pierwszy element tablicy.
Przykłady użycia metod na tablicach:
1 2 3 4 5 6 7 8 9 |
var array = [1, 2, 3, 4, 5, 6] let last = array.removeLast() // usunie 6 let first = array.removeFirst() // usunie 1 array.removeFirst(2) // usunie 2 i 3, czyli dwie pierwsze // array.removeFirst(3) // wywołanie spowoduje błąd array.removeFirst(2) // tablica będzie pusta array.removeFirst() // wywołanie na pustej tablicy spowoduje błąd array.removeLast() // wywołanie na pustej tablicy spowoduje błąd var lastFromPop = array.popLast() // zwraca opcjonalny element, nie spowoduje błędu wywołane na pustej tablicy. |
Jak można zauważyć, wywołanie metody na pustej tablicy spowoduje wygenerowanie błędu, także należy przed wywołaniem sprawdzić ilość elementów w tablicy.
Sprawdzanie nazw selektorów podczas kompilacji
To jest zmiana, która można nazwać genialną. Wywoływanie metod po nazwie przekazywanej w stringu jest kuriozalne, błędogenne i utrudniające refaktoring kodu.
Wywoływanie zostało zmienione z wywoływania nazwy w stringu na wywoływanie w dyrektywie preprocesora #selector().
Stary sposób wyglądał tak:
1 |
NSTimer.scheduledTimerWithTimeInterval(2, target: self, selector: "onTimer:", userInfo: nil, repeats: true) |
Według nowego podejścia należy wywołać następująco:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
class TimerClass { var count = 1 func startTimer() { NSTimer.scheduledTimerWithTimeInterval(2, target: self, selector: #selector(onTimer), userInfo: nil, repeats: true) } @objc func onTimer(timer:NSTimer!) { print("Time left \(count) time") count += 1 } } var timer = TimerClass() timer.startTimer() CFRunLoopRun() // uruchamiamy w pętli |
Jeśli popełnimy literówkę w nazwie metody wywoływanej, kompilator zgłosi nam to podczas kompilacji. W starym podejściu o błędzie dowiadywaliśmy się dopiero podczas uruchomienia błędnego kawałka kodu. W skrajnym przypadku błąd mógł objawić się u użytkownika po pobraniu z App Store.
Pętle znane z języka C oznaczone jako przestarzałe
Zmiana z pętlami wynika z kolejnego punktu, ale cierpliwości. Zmiana polega na oznaczeniu jako przestarzałe konstrukcji pętli for którą znamy z języków z rodziny C.
Konstrukcja wygląda następująco:
1 2 3 |
for var i = 1; i <= 10; i++ { print("line number \(i) ") } |
Aktualnie należy stosować prostszej składni, która wygląda następująco:
1 2 3 |
for i in 1...10 { print("line number \(i) ") } |
Jeśli potrzebujemy odliczać od 10 do 1, nie możemy użyć konstrukcji in 10…1 ponieważ spowoduje ona błąd, ale używamy poniższej konstrukcji:
1 2 3 |
for i in (1...10).reverse() { print("line number \(i) ") } |
Jeżeli chcemy odliczyć od jednego wzwyż ale co któryś element używamy poniższej konstrukcji:
1 2 3 |
for i in 1.stride(to: 10, by: 2) { print("line number \(i) ") } |
Konstrukcje for in dla osób, które dopiero zaczynają się uczyć programować, mogą wydawać się bardziej sensowne i prostsze do zrozumienia, a dla starych programistów skrócą zapis.
Operatory ++ inkrementacji i — dekrementacji oznaczone jako przestarzałe.
Ta zmiana to prawdziwa bomba. Rozmawiałem z kilkoma programistami i każdy był bardzo zdziwiony, jak to możliwe, dlaczego, po co, świat się kończy!
Konstrukcje zwane i++ inkrementacją i i-– dekrementacją powodujące zwiększenie lub zmniejszenie wartości liczby i o jeden, zostały oznaczone jako przestarzałe i wraz z wersją Swift 3 zostaną usunięte.
Tak więc kod:
1 2 3 4 |
i++ i-- ++i --i |
zastępujemy
1 2 |
i += 1 i -= 1 |
Według autorów konstrukcja zwiększa trudność zrozumienia i nauki języka Swift, zapis i++ nie jest znacznie krótszy od zapisu i += 1, i dodatkowo może powodować trudności ze zrozumieniem bardziej zawiłych konstrukcji np foo(++a, a++).
No cóż, czasy się zmieniają, nowym programistom trudno zrozumieć konstrukcje inkrementacji. Trzeba przyznać jednak, że osoby zatwierdzające zmiany w języku Swift nie robią tylko zmian kosmetycznych, robią zmiany, które można nazwać rewolucją.
Podsumowując
Język Swift, który jest aktualnie rozwijany w modelu Open Source staje się w pełni dojrzałym, bardzo nowoczesnym językiem. Czerpie garściami z najlepszych wzorców i nie boi się zmian. Powyższe przykłady powodują, że programowanie aplikacji dla iOS i OS X staje się jeszcze przyjemniejsze niż dotychczas.
Wszystkie przykłady możecie uruchomić w trybie Playground, jeśli coś jest niejasne, pisz w komentarzu, spróbuję rozwinąć temat i dodać jakieś przykłady.