< Previous: Wiązanie typów opcjonalnych (z ang. Optional chaining) | Next: Statyczne właściwości i metody > |
Typy wyliczeniowe, najczęściej nazywane po prostu "enumami", są sposobem na zdefiniowanie Twojego własnego rodzaju wartości w języku Swift. W niektórych językach programowania są one proste i nieskomplikowane. Swift dodaje do nich dużo możliwości jeśli zdecydujesz się wyjść poza ich podstawy.
Zacznijmy od prostego przykładu z wcześniej:
func getHaterStatus(weather: String) -> String? {
if weather == "sunny" {
return nil
} else {
return "Hate"
}
}
Funkcja powyżej przyjmuje ciąg znaków, który definiuje aktualną pogodę. Problem w tym, że ciąg znaków nie jest dobrym wyborem do tego typu danych - powinniśmy użyć "deszcz", "pada" czy "deszczowo"? A może "ulewa", "mżawka" lub "oberwanie chmury"? Co gdy jedna osoba napisze "Deszcz" z dużej litery a kolejna napisze "Desz", ponieważ nie patrzyła co wpisywała na klawiaturze?
Typy wyliczeniowe (z ang. Enums), dalej nazywane enumami, rozwiązują ten problem pozwalając Ci na zdefiniowanie nowego typu danych, a następnie zadeklarowanie potencjalnych wartości, które ten typ będzie mógł przyjąć. Przykładowo, możemy powiedzieć, że jest pięć rodzajów pogody: słońce, chmury, deszcz, wiatr i śnieg. Jeśli stworzymy z tego enum, będzie to oznaczać, że Swift przyjmie tylko te pięć wartości - każda inna spowoduje błąd. Warto zwrócić uwagę, że pod maską enumy to najczęściej proste liczby, z którymi, w porównaniu do ciągów znaków, komputery współpracują szybciej.
Spróbujmy zamienić to w kod:
enum WeatherType {
case sun, cloud, rain, wind, snow
}
func getHaterStatus(weather: WeatherType) -> String? {
if weather == WeatherType.sun {
return nil
} else {
return "Hate"
}
}
getHaterStatus(weather: WeatherType.cloud)
Spójrz na pierwsze trzy linie: pierwsza nadaje naszemu typowi nazwę - WeatherType
. Później użyjesz go w swoim kodzie zamiast typu String
czy Int
. Linia druga deklaruje pięć możliwych wartości jakie może przyjąć nasz enum. Przez wzgląd na konwencję, każdą z wartości zaczynamy małą literą. Linia trzecia jest tylko klamrą zamykającą, kończącą naszą deklarację.
Teraz spójrz jak został użyty: zmodyfikowałem gethaterStatus()
tak, że przyjmuje wartość typu WeatherType
. Przepisałem również warunkową instrukcję by przyrównywała do WeatherType.sun
, czyli jednej z naszych wartości. Pamiętaj, to przyrównanie jest pod spodem tylko liczbą, więc jest bardzo szybkie.
Teraz, przeczytaj powyższy kod ponownie, ponieważ wprowadzę do niego dwie bardzo ważne zmiany. Gotowy?
enum WeatherType {
case sun
case cloud
case rain
case wind
case snow
}
func getHaterStatus(weather: WeatherType) -> String? {
if weather == .sun {
return nil
} else {
return "Hate"
}
}
getHaterStatus(weather: .cloud)
Możesz zauważyć dwie zmiany. Pierwsza - każdy z typów pogody jest w oddzielnej linii. To może się wydawać nieznaczącą zmianą i rzeczywiście w tym przykładzie taką jest, ale stanie się bardzo ważna już wkrótce. Drugą jest fakt, że napisałem if weather == .sun
- nie musiałem jawnie wskazać, że miałem na myśli WeatherType.sun
, ponieważ Swift wie, że przyrównuje do zmiennej typu WeatherType
, więc potrafi sam wywnioskować typ (z ang. type inference).
Enumy są szczególnie przydatne w blokach switch/case
dlatego, że język Swift zna wszystkie możliwe wartości Twojego enuma, więc może się upewnić, że pokryjesz wszystkie jego przypadki. Dla przykładu możemy spróbować przepisać funkcję getHaterStatus()
na:
func getHaterStatus(weather: WeatherType) -> String? {
switch weather {
case .sun:
return nil
case .cloud, .wind:
return "dislike"
case .rain:
return "hate"
}
}
Tak, domyślam się, że "haters gonna dislike" nie jest najlepszym sloganem na świecie, ale jest to mało ważne bo kod i tak się nie skompiluje: nie pokrywa przypadku .snow
, a język Swift chcę pokryć wszystkie przypadki. Musisz obsłużyć ten przypadek albo dopisać przypadek domyślny (z ang. default case).
Jedną z najpotężniejszych cech języka Swift, jest fakt, że typy wyliczeniowe mogą posiadać przypisane im wartości, które to Ty możesz zdefiniować. Aby rozbudować nasz rosnąco niejasny przykład jeszcze bardziej, dodam wartość do przypadku .wind
abyśmy mogli zadeklarować prędkość wiatru. Zmodyfikuj kod do poniższej postaci:
enum WeatherType {
case sun
case cloud
case rain
case wind(speed: Int)
case snow
}
Jak widzisz, inne przypadki nie potrzebują wartości prędkości - dodałem ją tylko do przypadku .wind
. Teraz zaczyna się magia: język Swift pozwala nam na dodanie dodatkowych warunków do bloku switch/case
tak aby przypadek zgadzał się tylko wtedy gdy dane warunki zostaną spełnione. Poniższy przykład używa słowa kluczowego let
aby dostać się do wartości znajdującej się w przypadku, a następnie słowa kluczowego where
w celu przyrównania.
Oto nowa funkcja:
func getHaterStatus(weather: WeatherType) -> String? {
switch weather {
case .sun:
return nil
case .wind(let speed) where speed < 10:
return "meh"
case .cloud, .wind:
return "dislike"
case .rain, .snow:
return "hate"
}
}
getHaterStatus(weather: WeatherType.wind(speed: 5))
Zauważ, że .wind
pojawia się powyżej dwa razy, ale za pierwszym razem ten warunek będzie prawdziwy tylko wtedy gdy prędkość wiatru będzie mniejsza niż 10 kilometrów na godzinę. Jeśli prędkość będzie równa 10 albo więcej, warunek nie zostanie spełniony. Kluczowe jest tutaj użycie let
aby móc użyć wartości w środku enuma (tzn. zdeklarować nazwę stałej, do której możesz się odnieść) a potem użycie warunku where
do przyrównania.
Język Swift sprawdza blok switch/case
od góry do dołu i zatrzymuje się gdy tylko znajdzie pasujący warunek. To oznacza, że gdyby case .cloud, .wind:
pojawiłoby się przed case .wind(let speed) where speed < 10
to wykonałby się pierwszy warunek, a co za tym idzie - zmieniłby się wynik.
Dlatego zwracaj szczególną uwagę na kolejność swoich warunków!
Wskazówka: Typy opcjonalne w języku Swift są zaimplementowane przy użyciu typów wyliczeniowych z przypisanymi wartościami. Posiadają dwa przypadki: none
oraz some
, gdzie some
zawiera w sobie wartość odpowiednią dla danego typu opcjonalnego.
SPONSORED Take the pain out of configuring and testing your paywalls. RevenueCat's Paywalls allow you to remotely configure and A/B test your entire paywall UI without any code changes or app updates.
Sponsor Hacking with Swift and reach the world's largest Swift community!
Link copied to your pasteboard.