{"id":31619,"date":"2025-07-28T05:00:00","date_gmt":"2025-07-28T03:00:00","guid":{"rendered":"https:\/\/sii.pl\/blog\/?p=31619"},"modified":"2025-07-28T09:50:30","modified_gmt":"2025-07-28T07:50:30","slug":"wykorzystanie-najnowszych-funkcji-swiftui-w-developmencie-ios","status":"publish","type":"post","link":"https:\/\/sii.pl\/blog\/wykorzystanie-najnowszych-funkcji-swiftui-w-developmencie-ios\/","title":{"rendered":"Wykorzystanie najnowszych funkcji SwiftUI w developmencie iOS"},"content":{"rendered":"\n<p>W ostatnim czasie mo\u017cemy zaobserwowa\u0107, \u017ce tempo rozwoju technologii znacz\u0105co przyspieszy\u0142o. Apple nie przestaje nas zadziwia\u0107 szybko\u015bci\u0105 wprowadzania coraz to nowszych i ciekawszych <strong>API<\/strong> do swojego ekosystemu, a sam Swift UI jest bardzo mocno promowany przez Apple jako zalecany spos\u00f3b tworzenia aplikacji mobilnych.<\/p>\n\n\n\n<p>W kr\u00f3tkim artykule nie spos\u00f3b opisa\u0107 wszystkich bibliotek, kt\u00f3re pojawia\u0142y si\u0119 w <strong>iOS od wersji 15 do 18<\/strong>, dlatego chcia\u0142bym si\u0119 skupi\u0107 zaledwie na kilku ciekawych zagadnieniach, na kt\u00f3re uda\u0142o mi si\u0119 natrafi\u0107 zar\u00f3wno w codziennej pracy jak i w trakcie przegl\u0105dania najnowszych materia\u0142\u00f3w szkoleniowych oraz oficjalnej dokumentacji Apple.<\/p>\n\n\n\n<p>W dzisiejszym wpisie opisz\u0119 pokr\u00f3tce takie zagadnienia jak <strong>AGA (Automatic Grammar Agreement)<\/strong> i <strong>String Catalog<\/strong>, czyli nowe podej\u015bcie Apple do lokalizacji swoich aplikacji, oraz jak stworzy\u0107 i wykorzysta\u0107 w praktyce sw\u00f3j w\u0142asny <strong>EnvironmentKey().<\/strong><\/p>\n\n\n\n<p>Zatem \u2013 nie przed\u0142u\u017caj\u0105c przyd\u0142ugiego ju\u017c i tak wst\u0119pu, przejd\u017amy do meritum. Link do repozytorium znajdziecie na ko\u0144cu artyku\u0142u.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>AGA \u2013 Automatic Grammar Agreement<\/strong><\/h2>\n\n\n\n<p>My\u015bl\u0119, \u017ce ka\u017cdy z developer\u00f3w spotka\u0142 si\u0119 z <strong>problemem poprawnego odmieniania rzeczownik\u00f3w<\/strong> w zale\u017cno\u015bci od ilo\u015bci lub przynajmniej zastanawia\u0142 si\u0119 nad takim problemem. \u0141atwo wyobrazi\u0107 sobie przypadek, kiedy mamy za zadanie stworzy\u0107 widok, w kt\u00f3rym, korzystaj\u0105c przyk\u0142adowo ze <strong>Stepper(),<\/strong> zwi\u0119kszamy ilo\u015b\u0107 np. fili\u017canek kawy, kt\u00f3re klient mo\u017ce zam\u00f3wi\u0107 przy u\u017cyciu tworzonej przez nas aplikacji.<\/p>\n\n\n\n<p>Nawet w j\u0119zyku angielskim, gdzie sprawa sprowadza si\u0119 najcz\u0119\u015bciej do dodania literki \u201e<strong>s\u201d<\/strong> do danego s\u0142owa, w okre\u015blonych przypadkach poprawna odmiana mo\u017ce si\u0119 mocno skomplikowa\u0107. Przyk\u0142adowo z <strong>one goose<\/strong> robi si\u0119 <strong>two geese, a z one mouse \u2013 two mice<\/strong>.<\/p>\n\n\n\n<p>W j\u0119zyku polskim sprawa wygl\u0105da\u0142aby jeszcze ciekawiej, chocia\u017cby na przyk\u0142adzie p\u0105czk\u00f3w \u2013 <strong>jeden p\u0105czek, dwa p\u0105czki, 11 p\u0105czk\u00f3w,<\/strong> <strong>23 p\u0105czki<\/strong>\u2026 i prawdopodobnie z tego prostego powodu, Apple nie zdecydowa\u0142o si\u0119 na obj\u0119ciem <strong>AGA<\/strong> naszego ojczystego j\u0119zyka \ud83e\uddd0<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>AGA w praktyce<\/strong><\/h2>\n\n\n\n<p>Odstawmy na bok zawi\u0142o\u015bci gramatyczne i spr\u00f3bujmy zobrazowa\u0107 na prostym przyk\u0142adzie kodu, z czym przychodzi nam si\u0119 mierzy\u0107. Poni\u017cej mamy zdefiniowane dwa przyciski do zwi\u0119kszania i zmniejszania warto\u015bci oraz prosty tekst ze s\u0142owem <strong>\u201eBottle\u201d<\/strong> powi\u0105zanym z ilo\u015bci\u0105:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img decoding=\"async\" width=\"399\" height=\"464\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image1.jpg\" alt=\"Code snippet 1: Kod pocz\u0105tkowy\" class=\"wp-image-31620\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image1.jpg 399w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image1-258x300.jpg 258w\" sizes=\"(max-width: 399px) 100vw, 399px\" \/><figcaption class=\"wp-element-caption\">Ryc. 1 Code snippet 1: Kod pocz\u0105tkowy<\/figcaption><\/figure>\n\n\n\n<p>Efekt omawianego przyk\u0142adu nietrudno przewidzie\u0107 \u2013 bez wzgl\u0119du na wybran\u0105 ilo\u015b\u0107 butelek otrzymamy dok\u0142adnie ten sam String \u2013 <strong>\u201eBottle\u201d<\/strong> poprzedzony oczywi\u015bcie w\u0142a\u015bciw\u0105 ilo\u015bci\u0105, kt\u00f3r\u0105 wybrali\u015bmy, korzystaj\u0105c z dw\u00f3ch prostych przycisk\u00f3w.<\/p>\n\n\n\n<p>W zrzucie ekranu prezentowanym poni\u017cej mo\u017cecie zobaczy\u0107, jak to prezentuje si\u0119 wizualnie \u2013 raczej nie jest to co\u015b, co chcieliby\u015bmy pokaza\u0107 w czasie prezentacji na koniec sprintu naszym product ownerom.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img decoding=\"async\" width=\"280\" height=\"610\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image2-1.png\" alt=\"Screen 1: Ekran pocz\u0105tkowy\" class=\"wp-image-31622\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image2-1.png 280w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image2-1-138x300.png 138w\" sizes=\"(max-width: 280px) 100vw, 280px\" \/><figcaption class=\"wp-element-caption\">Ryc. 2 Screen 1: Ekran pocz\u0105tkowy<\/figcaption><\/figure>\n\n\n\n<p>W aplikacji chcieliby\u015bmy uzyska\u0107 taki efekt, gdzie zdefiniowane przez nas s\u0142owo <strong>\u201eBottle<\/strong>\u201d lub jakiekolwiek inne zostanie poprawnie odmienione zgodnie z obowi\u0105zuj\u0105cymi zasadami gramatyki.<\/p>\n\n\n\n<p>Pojawia si\u0119 zatem pytanie: W jaki spos\u00f3b mo\u017cemy osi\u0105gn\u0105\u0107 nasz cel bez konieczno\u015bci tworzenia dodatkowych funkcji zwracaj\u0105cych odpowiedni\u0105 warto\u015b\u0107 <strong>String <\/strong>dla odpowiedniej warto\u015bci liczbowej<strong>?<\/strong><\/p>\n\n\n\n<p>Tutaj z pomoc\u0105 przychodzi nam w\u0142a\u015bnie <strong>Automatic Grammar Agreement,<\/strong> kt\u00f3re dzia\u0142a na ten moment dla 6 najbardziej popularnych j\u0119zyk\u00f3w. Dla waszej wygody zebra\u0142em ca\u0142o\u015b\u0107 w tabel\u0119 zbiorcz\u0105 zaprezentowanej w dalszej cz\u0119\u015bci wpisu.<\/p>\n\n\n\n<p>Wracaj\u0105c do tematu w przedmiotowej dyskusji oraz mo\u017cliwo\u015bci rozwi\u0105zania problemu, to w naszym przypadku wystarczy, \u017ce zamienimy poprzedni zapis na:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nText(&quot;^&#x5B;\\(count) Bottle](inflect: true)&quot;)\n<\/pre><\/div>\n\n\n<p>I uzyskamy efekt, jaki chcieli\u015bmy uzyska\u0107 \u2013 angielskie s\u0142owo \u201e<strong>BOTTLE\u201d<\/strong> zostanie poprawnie odmienione w zale\u017cno\u015bci od wybranej ilo\u015bci.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>AGA w praktyce \u2013 kolejne przyk\u0142ady<\/strong><\/h2>\n\n\n\n<p>Zmie\u0144my nieco nasz kod, aby pokaza\u0107 wi\u0119cej przyk\u0142ad\u00f3w i przy okazji wystylizujmy nieco widok, korzystaj\u0105c z natywnych element\u00f3w <strong>Swift UI,<\/strong> dodaj\u0105c m.in. gradientowe t\u0142o, stylizuj\u0105c kontener, w kt\u00f3rym znajduj\u0105 si\u0119 przyk\u0142ady oraz wykorzystuj\u0105c <strong>Stepper() <\/strong>zamiast wcze\u015bniej wykorzystanego<strong> Button()<\/strong>.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img decoding=\"async\" width=\"686\" height=\"872\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image3.jpg\" alt=\"Code snippet 2: AGM Kod ko\u0144cowy\" class=\"wp-image-31624\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image3.jpg 686w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image3-236x300.jpg 236w\" sizes=\"(max-width: 686px) 100vw, 686px\" \/><figcaption class=\"wp-element-caption\">Ryc. 3 Code snippet 2: AGM Kod ko\u0144cowy<\/figcaption><\/figure>\n\n\n\n<p>Uzyskany efekt:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img decoding=\"async\" width=\"477\" height=\"476\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/Przechwytywanie.jpg\" alt=\"Screen 2 i 3: AGM Ekran ko\u0144cowy, liczba pojedyncza (po lewej) i mnoga (po prawej\" class=\"wp-image-31626\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/Przechwytywanie.jpg 477w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/Przechwytywanie-300x300.jpg 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/Przechwytywanie-150x150.jpg 150w\" sizes=\"(max-width: 477px) 100vw, 477px\" \/><figcaption class=\"wp-element-caption\">Ryc. 4 Screen 2 i 3: AGM Ekran ko\u0144cowy, liczba pojedyncza    (po lewej) i mnoga (po prawej)<\/figcaption><\/figure>\n\n\n\n<p>W tym miejscu warto wspomnie\u0107, \u017ce w warunkach produkcyjnych cz\u0119sto otrzymujemy warto\u015bci z innych miejsc w aplikacji np. z View Modelu. W tym przypadku konieczne jest wskazanie typu <strong>LocalizedStringKey,<\/strong> czyli dla naszego przyk\u0142adu, je\u015bli chcieliby\u015bmy zdefiniowa\u0107 warto\u015b\u0107 tekstow\u0105 np. w view modelu, to musieliby\u015bmy zapisa\u0107 nasz parametr w ten spos\u00f3b:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\nvar test: LocalizedStringKey {(&quot;^&#x5B;\\(count) Bottle](inflect: true)&quot;)}\n}\n<\/pre><\/div>\n\n\n<p><strong>AGM<\/strong> jest dost\u0119pne dla iOS wg tabeli poni\u017cej i mo\u017ce okaza\u0107 si\u0119 pomocne przy lokalizacji aplikacji, nad kt\u00f3r\u0105 pracujecie lub b\u0119dziecie pracowa\u0107 w przysz\u0142o\u015bci. Nawet je\u015bli nie przyjdzie Wam skorzysta\u0107 z tego rozwi\u0105zania, to przynajmniej ju\u017c b\u0119dziecie wiedzie\u0107, jak zbudowa\u0107 poprawnie liczb\u0119 mnog\u0105 od s\u0142owa Goose w j\u0119zyku angielskim \ud83d\ude42<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><tbody><tr><td rowspan=\"2\">iOS 15<\/td><td>Angielski<\/td><\/tr><tr><td>Hiszpa\u0144ski<\/td><\/tr><tr><td rowspan=\"3\">iOS 16<\/td><td>Francuski<\/td><\/tr><tr><td>W\u0142oski<\/td><\/tr><tr><td>Portugalski (Brazylia)<\/td><\/tr><tr><td rowspan=\"2\">iOS 17<\/td><td>Portugalski (Europa)<\/td><\/tr><tr><td>Niemiecki<\/td><\/tr><\/tbody><\/table><figcaption class=\"wp-element-caption\">Tab. 1 Wsparcie AGM wg wersji iOS<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>String Catalog \u2013 nowe podej\u015bcie do lokalizacji w XCode<\/strong><\/h2>\n\n\n\n<p>W ostatnim czasie Apple wprowadzi\u0142o nowocze\u015bniejsz\u0105 wersj\u0119 lokalizacji, kt\u00f3ra mo\u017cna wykorzysta\u0107 od wersji <strong>XCode 15.0<\/strong>. Zmianie uleg\u0142o podej\u015bcie do samej lokalizacji \u2013 wcze\u015bniej wykorzystywali\u015bmy <strong>Localizable.strings,<\/strong> w kt\u00f3rym mozolnie wpisywali\u015bmy kolejne klucze lokalizacji.<\/p>\n\n\n\n<p>W nowym wydaniu pojawi\u0142o si\u0119 narz\u0119dzie, kt\u00f3rego mo\u017cemy u\u017cy\u0107 jako alternatywy do wymienionych powy\u017cej <strong>Localizable.strings,<\/strong> je\u015bli oczywi\u015bcie chcieliby\u015bmy, aby nasza aplikacja obs\u0142ugiwa\u0142a wi\u0119cej ni\u017c jeden j\u0119zyk. Rozwi\u0105zanie to dzia\u0142a Out of the box, je\u015bli j\u0119zyk aplikacji jest zgodny z ustawieniami systemowymi.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>String Catalog w praktyce<\/strong><\/h2>\n\n\n\n<p>Pozw\u00f3lcie zatem przedstawi\u0107 Wam, jak to rozwi\u0105zanie dzia\u0142a w praktyce. Przejd\u017amy zatem do rzeczy i na przyk\u0142adzie kilku prostych t\u0142umacze\u0144 dodajmy lokalizacj\u0119 aplikacji oraz obs\u0142ug\u0119 zmiany j\u0119zyka wykorzystuj\u0105c\u0105 natywne narz\u0119dzia SwiftUI.<\/p>\n\n\n\n<p>W pierwszym kroku musimy doda\u0107 nowy katalog string\u00f3w do naszego projektu, kt\u00f3ry domy\u015blnie dostaje nazw\u0119 <strong>Localizable.xcstring<\/strong><\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img decoding=\"async\" width=\"734\" height=\"521\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image6.jpg\" alt=\"Dodanie String Catalog do projektu\" class=\"wp-image-31628\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image6.jpg 734w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image6-300x213.jpg 300w\" sizes=\"(max-width: 734px) 100vw, 734px\" \/><figcaption class=\"wp-element-caption\">Ryc. 5 Dodanie String Catalog do projektu<\/figcaption><\/figure>\n\n\n\n<p>Standardowo przechodzimy dalej i wybieramy nazw\u0119 dla naszego katalogu, a ja, na potrzeby artyku\u0142u, zostawiam warto\u015b\u0107 domy\u015bln\u0105.<\/p>\n\n\n\n<p>Jak mo\u017cecie zauwa\u017cy\u0107 \u2013 lista s\u0142\u00f3w jest pocz\u0105tkowo pusta.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img decoding=\"async\" width=\"609\" height=\"478\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image7.jpg\" alt=\"Wybranie nazwy katalogu\" class=\"wp-image-31630\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image7.jpg 609w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image7-300x235.jpg 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image7-370x290.jpg 370w\" sizes=\"(max-width: 609px) 100vw, 609px\" \/><figcaption class=\"wp-element-caption\">Ryc. 6 Wybranie nazwy katalogu<\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img decoding=\"async\" width=\"821\" height=\"493\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image8.jpeg\" alt=\"Startowy String Catalog\" class=\"wp-image-31632\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image8.jpeg 821w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image8-300x180.jpeg 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image8-768x461.jpeg 768w\" sizes=\"(max-width: 821px) 100vw, 821px\" \/><figcaption class=\"wp-element-caption\">Ryc. 7 Startowy String Catalog<\/figcaption><\/figure>\n\n\n\n<p>W kolejnym kroku mo\u017cemy doda\u0107 dodatkowe j\u0119zyki, kt\u00f3re chcemy, aby by\u0142y obj\u0119te lokalizacj\u0105. W moim przypadku doda\u0142em j\u0119zyk polski i hiszpa\u0144ski.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img decoding=\"async\" width=\"412\" height=\"475\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image9.jpeg\" alt=\"Dodanie dodatkowego j\u0119zyka\" class=\"wp-image-31634\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image9.jpeg 412w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image9-260x300.jpeg 260w\" sizes=\"(max-width: 412px) 100vw, 412px\" \/><figcaption class=\"wp-element-caption\">Ryc. 8 Dodanie dodatkowego j\u0119zyka<\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img decoding=\"async\" width=\"504\" height=\"319\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image10.jpg\" alt=\"Widok katalogu z kilkoma j\u0119zykami\" class=\"wp-image-31636\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image10.jpg 504w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image10-300x190.jpg 300w\" sizes=\"(max-width: 504px) 100vw, 504px\" \/><figcaption class=\"wp-element-caption\">Ryc. 9 Widok katalogu z kilkoma j\u0119zykami<\/figcaption><\/figure>\n\n\n\n<p>W przeciwie\u0144stwie do <strong>Localized.strings<\/strong> poszczeg\u00f3lne wpisy uzupe\u0142niaj\u0105 si\u0119 automatycznie w <strong>fazie budowania si\u0119<\/strong> naszego projektu. Je\u015bli wprowadzona warto\u015b\u0107 tekstowa b\u0119dzie zgodna z typem <strong>LocalizedStringKey<\/strong>, ju\u017c przy pierwszym uruchomieniu projektu, nawet w wersji z jednym widokiem, kt\u00f3ry zrobili\u015bmy do tej pory, uzyskamy co\u015b takiego:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large is-resized\"><img decoding=\"async\" width=\"1024\" height=\"233\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image11-1024x233.jpg\" alt=\"Klucze dodane do projektu przy pierwszym zbudowaniu projektu\" class=\"wp-image-31638\" style=\"width:840px;height:auto\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image11-1024x233.jpg 1024w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image11-300x68.jpg 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image11-768x175.jpg 768w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image11.jpg 1058w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Ryc. 10 Klucze dodane do projektu przy pierwszym zbudowaniu projektu<\/figcaption><\/figure>\n\n\n\n<p>Jak mo\u017cecie zauwa\u017cy\u0107, do t\u0142umaczenia po uruchomieniu projektu automatycznie pojawi\u0142y si\u0119 wszystkie wpisy z p\u00f3l <strong>Text()<\/strong>. W moim przypadku nie widz\u0119 potrzeby t\u0142umaczenia dw\u00f3ch pierwszych rekord\u00f3w, kt\u00f3re pojawi\u0142y si\u0119 na tej li\u015bcie, wi\u0119c z menu kontekstowego mog\u0119 wybra\u0107 opcj\u0119 <strong><u>\u201eDon\u2019t translate\u201d,<\/u><\/strong> co z automatu wy\u0142\u0105czy lokalizacj\u0119 dla wszystkich dodanych j\u0119zyk\u00f3w w katalogu.<\/p>\n\n\n\n<p>Dla przyk\u0142adu przet\u0142umaczmy sobie na pocz\u0105tek przynajmniej ten jeden string odpowiednio dla j\u0119zyka polskiego i hiszpa\u0144skiego. Po wprowadzeniu tej warto\u015bci mo\u017cemy zauwa\u017cy\u0107, \u017ce XCode oznaczy\u0142 nam wszystkie t\u0142umaczenia jako uzupe\u0142nione, natomiast je\u015bli nie jeste\u015bmy pewni, czy t\u0142umaczenie jest ok lub chcemy poprosi\u0107 o sprawdzenie, mo\u017cemy oznaczy\u0107 dany wiersz jako <strong><u>\u201eMark for review\u201d<\/u><\/strong>.<\/p>\n\n\n\n<p>Na tym etapie warto wspomnie\u0107, \u017ce <strong>String Catalog<\/strong> posiada funkcjonalno\u015b\u0107 r\u00f3\u017cnicowania wpis\u00f3w w zale\u017cno\u015bci od wariantu urz\u0105dzenia, czy te\u017c dodawania odpowiedniej formy dla liczby mnogiej danego wpisu.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img decoding=\"async\" width=\"641\" height=\"213\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image.jpeg\" alt=\"Uzupe\u0142niony pocz\u0105tkowy katalog\" class=\"wp-image-31640\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image.jpeg 641w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image-300x100.jpeg 300w\" sizes=\"(max-width: 641px) 100vw, 641px\" \/><figcaption class=\"wp-element-caption\">Ryc. 11 Uzupe\u0142niony pocz\u0105tkowy katalog<\/figcaption><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Implementacja lokalizacji w aplikacji<\/strong><\/h2>\n\n\n\n<p>Skoro ju\u017c wiemy, jak pos\u0142ugiwa\u0107 si\u0119 katalogiem, to mo\u017cemy przej\u015b\u0107 do implementacji lokalizacji<br>w aplikacji. W tym celu wykorzystamy @AppStorage oraz zmienne \u015brodowiskowe environment(). Dodamy te\u017c <strong>NavigationStack()<\/strong> oraz proste menu kontekstowe pozwalaj\u0105ce na zmian\u0119 j\u0119zyka aplikacji w czasie rzeczywistym.<\/p>\n\n\n\n<p>Na pocz\u0105tek stw\u00f3rzmy klas\u0119 <strong>AppData,<\/strong> gdzie b\u0119dziemy przechowa\u0107 ustawienia u\u017cytkownika.<br>W naszym przypadku na tym etapie b\u0119dzie to tylko jedna zmienna \u201e<strong>language\u201d<\/strong>, kt\u00f3r\u0105 dodatkowo oznaczymy jako <strong>@AppStorage.<\/strong><\/p>\n\n\n\n<p><strong>@AppStorage <\/strong>to nic innego jak Property Wrapper, kt\u00f3ry dzia\u0142a w spos\u00f3b podobny do <strong>User Defaults<\/strong>, przechowuj\u0105c podstawowe ustawienia u\u017cytkownika<strong>.<\/strong><\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img decoding=\"async\" width=\"484\" height=\"114\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image13.jpg\" alt=\"Code snippet 3: AppData aplikacji\" class=\"wp-image-31643\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image13.jpg 484w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image13-300x71.jpg 300w\" sizes=\"(max-width: 484px) 100vw, 484px\" \/><figcaption class=\"wp-element-caption\">Ryc. 12 Code snippet 3: AppData aplikacji<\/figcaption><\/figure>\n\n\n\n<p><strong>Ten krok nie jest wymagany, je\u015bli nie chcemy mie\u0107 pe\u0142nej kontroli nad ustawieniami j\u0119zyka w aplikacji, poniewa\u017c domy\u015blnie aplikacja b\u0119dzie dzia\u0142a\u0142a w oparciu o ustawienia systemowe<\/strong>.<\/p>\n\n\n\n<p>Aby osi\u0105gn\u0105\u0107 zamierzony cel, dodatkowo wykorzystamymodyfikatory<strong> environment() <\/strong>oraz<strong> environmentObject(), <\/strong>kt\u00f3re nale\u017cy doda\u0107 do root view naszej aplikacji.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img decoding=\"async\" width=\"676\" height=\"320\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image14.jpg\" alt=\"Code snippet 4: Root view aplikacji uwzgl\u0119dniaj\u0105cy parametry lokalizacji\" class=\"wp-image-31647\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image14.jpg 676w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image14-300x142.jpg 300w\" sizes=\"(max-width: 676px) 100vw, 676px\" \/><figcaption class=\"wp-element-caption\">Ryc. 13 Code snippet 4: Root view aplikacji uwzgl\u0119dniaj\u0105cy parametry lokalizacji<\/figcaption><\/figure>\n\n\n\n<p>Nast\u0119pnie tworzymy widok pomocniczy zawieraj\u0105cy <strong>Menu()<\/strong> do wyboru odpowiedniego j\u0119zyka:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img decoding=\"async\" width=\"433\" height=\"698\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image15-1.jpg\" alt=\"Code snippet 5: Language Options Menu\" class=\"wp-image-31649\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image15-1.jpg 433w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image15-1-186x300.jpg 186w\" sizes=\"(max-width: 433px) 100vw, 433px\" \/><figcaption class=\"wp-element-caption\">Ryc. 14 Code snippet 5: Language Options Menu<\/figcaption><\/figure>\n\n\n\n<p>Potrzebujemy jeszcze jeden dodatkowy widok pomocniczy, gdzie zaprezentujemy kilka prostych s\u0142\u00f3w. Podobnie jak poprzednio po zbudowaniu projektu te s\u0142owa od razu pojawi\u0105 si\u0119 w katalogu string\u00f3w, gdzie b\u0119dzie mo\u017cna je przet\u0142umaczy\u0107 analogicznie do zrzut\u00f3w ekranu pokazanych wcze\u015bniej.<\/p>\n\n\n\n<p>Przyk\u0142adowy kod dla omawianego widoku:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img decoding=\"async\" width=\"728\" height=\"719\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image16.jpg\" alt=\"Code snippet 6: Widok pomocniczy zawieraj\u0105cy proste stringi\" class=\"wp-image-31651\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image16.jpg 728w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image16-300x296.jpg 300w\" sizes=\"(max-width: 728px) 100vw, 728px\" \/><figcaption class=\"wp-element-caption\">Ryc. 15 Code snippet 6: Widok pomocniczy zawieraj\u0105cy proste stringi<\/figcaption><\/figure>\n\n\n\n<p>W przyk\u0142adzie powy\u017cej u\u017cy\u0142em typu <strong>LocalizedStringKey<\/strong>. Nie jest to jednak niezb\u0119dne, aby zachowa\u0107 oczekiwan\u0105 przez nas funkcjonalno\u015b\u0107. Chcia\u0142em jedynie pokaza\u0107, jaki typ wykorzystywany jest przez Swift do procesowania lokalizacji.<\/p>\n\n\n\n<p>Ostatnim etapem jest umieszczenie naszego widoku w <strong>NavigationStack(),<\/strong> oraz dodanie przygotowanego wcze\u015bniej przycisku menu z opcjami zmiany j\u0119zyka. W przyk\u0142adzie poni\u017cej dodatkowo umie\u015bci\u0142em widoki w <strong>TabView,<\/strong> aby urozmaici\u0107 prezentacj\u0119 i zobrazowa\u0107 wykorzystanie tego<strong> API <\/strong>w aplikacjach mobilnych.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img decoding=\"async\" width=\"700\" height=\"670\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image17.jpg\" alt=\"Code snippet 7: Widok w TabView\" class=\"wp-image-31653\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image17.jpg 700w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image17-300x287.jpg 300w\" sizes=\"(max-width: 700px) 100vw, 700px\" \/><figcaption class=\"wp-element-caption\">Ryc. 16 Code snippet 7: Widok w TabView<\/figcaption><\/figure>\n\n\n\n<p><strong>Wa\u017cne:<\/strong><\/p>\n\n\n\n<p>Zwr\u00f3\u0107cie uwag\u0119, \u017ce je\u015bli chcemy korzysta\u0107 z obiekt\u00f3w \u015brodowiskowych i <strong>Preview,<\/strong> koniecznie musimy doda\u0107 <strong>environmentObject()<\/strong> r\u00f3wnie\u017c w <strong>Preview<\/strong>. W innym przypadku nie b\u0119dzie dzia\u0142a\u0107 i nie b\u0119dziemy mogli skorzysta\u0107 z tej funkcjonalno\u015bci przy projektowaniu widoku, kt\u00f3ry wykorzystuje dany element \u015brodowiskowy.<\/p>\n\n\n\n<p>Efekt ko\u0144cowy prezentuj\u0119 si\u0119 nast\u0119puj\u0105co \u2013 zmiana j\u0119zyka nast\u0119puje natychmiastowo po klikni\u0119ciu w wybrany przycisk menu. Zmieniaj\u0105 si\u0119 jednocze\u015bnie wszystkie s\u0142owa, kt\u00f3re zosta\u0142y zdefiniowane wcze\u015bniej w katalogu, uwzgl\u0119dniaj\u0105c r\u00f3wnie\u017c elementy menu.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img decoding=\"async\" width=\"961\" height=\"663\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/Przechwytywanie-2.jpg\" alt=\"Screen 3: Efekt ko\u0144cowy\" class=\"wp-image-31655\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/Przechwytywanie-2.jpg 961w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/Przechwytywanie-2-300x207.jpg 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/Przechwytywanie-2-768x530.jpg 768w\" sizes=\"(max-width: 961px) 100vw, 961px\" \/><figcaption class=\"wp-element-caption\">Ryc. 17 Screen 3: Efekt ko\u0144cowy<\/figcaption><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Podsumowanie<\/strong><\/h3>\n\n\n\n<p>W mojej ocenie nowe rozwi\u0105zanie to czytelna alternatywa dla poprzedniego rozwi\u0105zania Apple, kt\u00f3ra ma szereg ciekawych funkcjonalno\u015bci. Dodatkowo, nawet je\u015bli w Waszym projekcie wykorzystywana jest poprzednia wersja lokalizacji, to migracja do katalogu string\u00f3w jest bardzo prosta.<\/p>\n\n\n\n<p>Na sam koniec tej sekcji warto wspomnie\u0107, \u017ce je\u015bli zajrzycie g\u0142\u0119biej do projektu, to zauwa\u017cycie, \u017ce <strong>String Catalog <\/strong>jest generowany w formacie<strong> JSON,<\/strong> co daje dodatkowe mo\u017cliwo\u015bci na etapie developmentu samej aplikacji, a sam katalog mo\u017cna \u0142atwo wygenerowa\u0107 w XCode (Xcode, Product -&gt; Export Localizable) i przes\u0142a\u0107 do t\u0142umaczenia.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Environment Key \u2013 jak stworzy\u0107 i wykorzysta\u0107 customowe klucze \u015brodowiskowe<\/strong><\/h2>\n\n\n\n<p>Pracuj\u0105c ze SwiftUI, zapewne spotkali\u015bcie si\u0119 z wykorzystaniem r\u00f3\u017cnego rodzaju obiekt\u00f3w \u015brodowiskowych, kt\u00f3re definiujemy jako <strong>@Environment(\\.key) var yourKeyName<\/strong>.<strong><\/strong> W Swift UI znajdziemy ich mn\u00f3stwo, wystarczy zacz\u0105\u0107 wpisywanie i pojawi si\u0119 ca\u0142a lista:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img decoding=\"async\" width=\"1018\" height=\"560\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image21.jpg\" alt=\"Code snippet 7: Przyk\u0142ady zmiennych enviromnent\" class=\"wp-image-31659\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image21.jpg 1018w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image21-300x165.jpg 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image21-768x422.jpg 768w\" sizes=\"(max-width: 1018px) 100vw, 1018px\" \/><figcaption class=\"wp-element-caption\">Ryc. 18 Code snippet 7: Przyk\u0142ady zmiennych enviromnent<\/figcaption><\/figure>\n\n\n\n<p>Przyk\u0142adem u\u017cycia mo\u017ce by\u0107 chocia\u017cby colorScheme:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n@Environment(\\.colorScheme) var colorScheme\n<\/pre><\/div>\n\n\n<p>Kt\u00f3ry mo\u017cemy wykorzysta\u0107 do np. zdefiniowania koloru teksu czy jego wielko\u015b\u0107 w zale\u017cno\u015bci od ustawie\u0144 preferencji systemowych:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n.foregroundStyle(colorScheme == .dark ? .primary : .secondary)\n<\/pre><\/div>\n\n\n<p>Oczywi\u015bcie, w tym przypadku mo\u017cemy zdefiniowa\u0107, jaki kolor zostanie u\u017cyty odpowiednio <strong>dla dark mode i light mode<\/strong> w <strong>Assetach<\/strong>. Niemniej, warto mie\u0107 w swoim arsenale umiej\u0119tno\u015bci r\u00f3wnie\u017c tak\u0105 wiedz\u0119, zw\u0142aszcza, \u017ce wykorzystanie innych zmiennych \u015brodowiskowych, jak np.\u00a0 .horizontalSizeClass, mo\u017ce okaza\u0107 si\u0119 ju\u017c nie tak samo oczywiste, ale r\u00f3wnie pomocne.<\/p>\n\n\n\n<p>W mojej ocenie umiej\u0119tno\u015b\u0107 wykorzystanie zmiennych \u015brodowiskowych obok tworzenia w\u0142asnych styli oraz Extension do praktycznie dowolnego typu jest czym\u015b, co podnosi umiej\u0119tno\u015bci Swift UI na wy\u017cszy poziom.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Tworzenie nowych kluczy<\/strong><\/h2>\n\n\n\n<p>Ale co mo\u017cemy zrobi\u0107 w sytuacji, kiedy interesuj\u0105cy nas klucz nie istnieje lub jego funkcjonalno\u015b\u0107 nie spe\u0142nia naszych oczekiwa\u0144?<\/p>\n\n\n\n<p>O ile tworzenie nowych styli dla element\u00f3w UI w SwiftUI opiera si\u0119 na stworzeniu <strong>struct<\/strong> zgodnego z odpowiednim protoko\u0142em, jak np. <strong>ButtonStyle(), DatePickeStyle(), ProgressBarStyle() TextFieldStyle(), <\/strong>to w przypadku kluczy \u015brodowiskowych po prostu musimy stworzy\u0107 nowy.<\/p>\n\n\n\n<p>Na potrzeby niniejszego wpisu om\u00f3wimy sobie przyk\u0142ad pola, kt\u00f3re jest wymagane do wprowadzenia, aby p\u00f3j\u015b\u0107 dalej w danym formularzu, czyli co\u015b, co nietrudno nam b\u0119dzie sobie wyobrazi\u0107 w codziennym u\u017cyciu.<\/p>\n\n\n\n<p>Zacznijmy od stworzenia nowego klucza o nazwie isRequired:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img decoding=\"async\" width=\"894\" height=\"520\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image22.jpg\" alt=\"Code snippet 8: Tworzenie nowego klucza\" class=\"wp-image-31661\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image22.jpg 894w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image22-300x174.jpg 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image22-768x447.jpg 768w\" sizes=\"(max-width: 894px) 100vw, 894px\" \/><figcaption class=\"wp-element-caption\">Ryc. 19 Code snippet 8: Tworzenie nowego klucza<\/figcaption><\/figure>\n\n\n\n<p>Nast\u0119pnie, jako \u017ce chcemy wykorzysta\u0107 nasz klucz przy tworzeniu nowych p\u00f3l tekstowych i zak\u0142adamy, \u017ce b\u0119dziemy wykorzystywa\u0107 ten styl wi\u0119cej ni\u017c raz, zdefiniujmy sobie CustomTextFieldStyle(), kt\u00f3ry wykorzystamy do stworzenia naszego widoku. Jak ju\u017c wspomnia\u0142em wcze\u015bniej wykorzystanie <strong>styli<\/strong> to dosy\u0107 cz\u0119sta praktyka przy tworzeniu aplikacji mobilnych w Swift UI, wi\u0119c warto wiedzie\u0107, jak mo\u017cna to zrobi\u0107.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large\"><img decoding=\"async\" width=\"1024\" height=\"778\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image23-1024x778.jpeg\" alt=\"Code snippet 9: Custom Text Field Style z parametrem isRequired\" class=\"wp-image-31663\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image23-1024x778.jpeg 1024w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image23-300x228.jpeg 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image23-768x583.jpeg 768w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image23.jpeg 1119w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><figcaption class=\"wp-element-caption\">Ryc. 20 Code snippet 9: Custom Text Field Style z parametrem isRequired<\/figcaption><\/figure>\n\n\n\n<p>Przy okazji tego widoku wykorzysta\u0142em dodatkowy Extension do View z funkcj\u0105 \u2013\u00a0.<strong>if(), <\/strong>kt\u00f3ry u\u0142atwia wykorzystanie r\u00f3\u017cnych element\u00f3w UI. Bardzo \u0142atwo mo\u017cna go znale\u017a\u0107 w sieci, ale poka\u017c\u0119 go r\u00f3wnie\u017c tutaj. Jednocze\u015bnie dodaj\u0119 funkcj\u0119 isRequired, kt\u00f3ra u\u0142atwi nam u\u017cycie zmiennej \u015brodowiskowej isRequired<strong>:<\/strong><\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img decoding=\"async\" width=\"929\" height=\"379\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image24.jpeg\" alt=\"Code snippet 10: Pomocnicze funkcje .if oraz isRequired\" class=\"wp-image-31665\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image24.jpeg 929w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image24-300x122.jpeg 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image24-768x313.jpeg 768w\" sizes=\"(max-width: 929px) 100vw, 929px\" \/><figcaption class=\"wp-element-caption\">Ryc. 21 Code snippet 10: Pomocnicze funkcje .if oraz isRequired<\/figcaption><\/figure>\n\n\n\n<p>Nast\u0119pnie tworzymy widok pomocniczy <strong>CustomInputField(), <\/strong>w kt\u00f3rym wykorzystamy nasz <strong>customowy<\/strong> <strong>styl<\/strong> oraz nasz nowy <strong>klucz \u015brodowiskowy, <\/strong>kt\u00f3ry przeka\u017cemy do stylu<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: plain; title: ; notranslate\" title=\"\">\n@Environment(\\.isRequired) var isRequired\n<\/pre><\/div>\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img decoding=\"async\" width=\"791\" height=\"916\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image25.jpeg\" alt=\"Code snippet 11: Custom Input Field\" class=\"wp-image-31667\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image25.jpeg 791w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image25-259x300.jpeg 259w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image25-768x889.jpeg 768w\" sizes=\"(max-width: 791px) 100vw, 791px\" \/><figcaption class=\"wp-element-caption\">Ryc. 22 Code snippet 11: Custom Input Field<\/figcaption><\/figure>\n\n\n\n<p>Na sam koniec pozostaje nam jedynie zbudowanie widoku, kt\u00f3ry mo\u017cemy wykorzysta\u0107 w aplikacji. Dodatkowo w przyk\u0142adzie pokaza\u0142em, jak mo\u017cna wykorzysta\u0107 <strong>@FocusState<\/strong> do zarz\u0105dzaniem <strong>focusem<\/strong> dla poszczeg\u00f3lnych p\u00f3l, co jest bardzo przydatne, je\u015bli mamy wi\u0119cej jak jedno pole tekstowe.<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-large\"><img decoding=\"async\" width=\"794\" height=\"1024\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image26-794x1024.jpg\" alt=\"Code snippet 12: FocusState\" class=\"wp-image-31669\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image26-794x1024.jpg 794w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image26-233x300.jpg 233w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image26-768x991.jpg 768w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image26-170x220.jpg 170w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/image26.jpg 1180w\" sizes=\"(max-width: 794px) 100vw, 794px\" \/><figcaption class=\"wp-element-caption\">Ryc. 23 Code snippet 12: FocusState<\/figcaption><\/figure>\n\n\n\n<p>Efekt ko\u0144cowy naszych prac przedstawia si\u0119 nast\u0119puj\u0105co:<\/p>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><img decoding=\"async\" width=\"734\" height=\"730\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/Przechwytywanie-3.jpg\" alt=\"Efekt ko\u0144cowy\" class=\"wp-image-31671\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/Przechwytywanie-3.jpg 734w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/Przechwytywanie-3-300x298.jpg 300w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/Przechwytywanie-3-150x150.jpg 150w\" sizes=\"(max-width: 734px) 100vw, 734px\" \/><figcaption class=\"wp-element-caption\">Ryc. 24 Efekt ko\u0144cowy<\/figcaption><\/figure>\n\n\n\n<figure class=\"wp-block-image aligncenter size-full\"><a href=\"https:\/\/sii.pl\/oferty-pracy\/\" target=\"_blank\" rel=\"noreferrer noopener\"><img decoding=\"async\" width=\"737\" height=\"170\" src=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/praca-PL-k-2.jpg\" alt=\"oferty pracy\" class=\"wp-image-31673\" srcset=\"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/praca-PL-k-2.jpg 737w, https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/praca-PL-k-2-300x69.jpg 300w\" sizes=\"(max-width: 737px) 100vw, 737px\" \/><\/a><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Podsumowanie<\/strong><\/h2>\n\n\n\n<p>Na zako\u0144czenie powy\u017cszej sekcji warto zaznaczy\u0107, \u017ce przy wykorzystaniu istniej\u0105cych czy stworzonych przez nas kluczy \u015brodowiskowych nale\u017cy zachowa\u0107 czujno\u015b\u0107 i rozwag\u0119. Mo\u017ce si\u0119 bowiem okaza\u0107, \u017ce aplikacja zacznie nam generowa\u0107 trudne do zdiagnozowania problemy, je\u015bli zapomnieli\u015bmy u\u017cy\u0107 odpowiedniego klucza w odpowiednim miejscu i we w\u0142a\u015bciwy spos\u00f3b. Do minus\u00f3w tego rozwi\u0105zania mo\u017cna zaliczy\u0107 trudniejsze testowanie naszej aplikacji.<\/p>\n\n\n\n<p>Mam nadziej\u0119, \u017ce uda\u0142o mi si\u0119 cho\u0107by w niewielkim stopniu przyku\u0107 Wasz\u0105 uwag\u0119 i \u017ce omawiane tematy przydadz\u0105 si\u0119 Wam w codziennej pracy.<\/p>\n\n\n\n<p>Poni\u017cej znajdziecie link do repozytorium z omawianymi przyk\u0142adami oraz bibliografi\u0119, kt\u00f3ra pomog\u0142a mi w napisaniu tego artyku\u0142u.<\/p>\n\n\n\n<p>***<\/p>\n\n\n\n<p><strong>Link do repozytorium z przyk\u0142adami przestawianymi w blogu:<\/strong> <a href=\"https:\/\/github.com\/Theordius\/SwiftUIBlogExamples\" target=\"_blank\" rel=\"noopener\" title=\"\" rel=\"nofollow\" >https:\/\/github.com\/Theordius\/SwiftUIBlogExamples<\/a><\/p>\n\n\n\n<p>***<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Bibliografia<\/strong><\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Hudson P., \u201ePro SwiftUI\u201d, Hacking with Swift, 2022<\/li>\n\n\n\n<li><a href=\"https:\/\/www.hackingwithswift.com\/forums\/swiftui\/automatic-grammar-agreement\/20355\" target=\"_blank\" rel=\"noopener\" title=\"\" rel=\"nofollow\" >Automatic grammar agreement<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/blorenzop.medium.com\/learn-how-to-unleash-the-power-of-automatic-grammar-agreement-in-swift-42f1c9178942\" target=\"_blank\" rel=\"noopener\" title=\"\" rel=\"nofollow\" >How to use Automatic Grammar Agreement in Swift<\/a><\/li>\n\n\n\n<li><a href=\"https:\/\/www.linkedin.com\/pulse\/grammar-agreement-swift-chanakya-hirpara-rsgkf\/\" target=\"_blank\" rel=\"noopener\" title=\"\" rel=\"nofollow\" >Grammar Agreement in Swift<\/a><\/li>\n<\/ul>\n\n\n<div class=\"kk-star-ratings kksr-auto kksr-align-left kksr-valign-bottom\"\n    data-payload='{&quot;align&quot;:&quot;left&quot;,&quot;id&quot;:&quot;31619&quot;,&quot;slug&quot;:&quot;default&quot;,&quot;valign&quot;:&quot;bottom&quot;,&quot;ignore&quot;:&quot;&quot;,&quot;reference&quot;:&quot;auto&quot;,&quot;class&quot;:&quot;&quot;,&quot;count&quot;:&quot;4&quot;,&quot;legendonly&quot;:&quot;&quot;,&quot;readonly&quot;:&quot;&quot;,&quot;score&quot;:&quot;5&quot;,&quot;starsonly&quot;:&quot;&quot;,&quot;best&quot;:&quot;5&quot;,&quot;gap&quot;:&quot;11&quot;,&quot;greet&quot;:&quot;&quot;,&quot;legend&quot;:&quot;5\\\/5 ( votes: 4)&quot;,&quot;size&quot;:&quot;18&quot;,&quot;title&quot;:&quot;Wykorzystanie najnowszych funkcji SwiftUI w developmencie iOS&quot;,&quot;width&quot;:&quot;139.5&quot;,&quot;_legend&quot;:&quot;{score}\\\/{best} ( {votes}: {count})&quot;,&quot;font_factor&quot;:&quot;1.25&quot;}'>\n            \n<div class=\"kksr-stars\">\n    \n<div class=\"kksr-stars-inactive\">\n            <div class=\"kksr-star\" data-star=\"1\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"2\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"3\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"4\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" data-star=\"5\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n    <\/div>\n    \n<div class=\"kksr-stars-active\" style=\"width: 139.5px;\">\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n            <div class=\"kksr-star\" style=\"padding-right: 11px\">\n            \n\n<div class=\"kksr-icon\" style=\"width: 18px; height: 18px;\"><\/div>\n        <\/div>\n    <\/div>\n<\/div>\n                \n\n<div class=\"kksr-legend\" style=\"font-size: 14.4px;\">\n            5\/5 ( votes: 4)    <\/div>\n    <\/div>\n","protected":false},"excerpt":{"rendered":"<p>W ostatnim czasie mo\u017cemy zaobserwowa\u0107, \u017ce tempo rozwoju technologii znacz\u0105co przyspieszy\u0142o. Apple nie przestaje nas zadziwia\u0107 szybko\u015bci\u0105 wprowadzania coraz to &hellip; <a class=\"continued-btn\" href=\"https:\/\/sii.pl\/blog\/wykorzystanie-najnowszych-funkcji-swiftui-w-developmencie-ios\/\">Continued<\/a><\/p>\n","protected":false},"author":724,"featured_media":31676,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_editorskit_title_hidden":false,"_editorskit_reading_time":0,"_editorskit_is_block_options_detached":false,"_editorskit_block_options_position":"{}","inline_featured_image":false,"footnotes":""},"categories":[1314],"tags":[2852,2851,1546,1512,521],"class_list":["post-31619","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-development-na-twardo","tag-aga","tag-swiftui-2","tag-przeglad-narzedzi","tag-poradnik","tag-ios"],"acf":[],"aioseo_notices":[],"republish_history":[],"featured_media_url":"https:\/\/sii.pl\/blog\/wp-content\/uploads\/2025\/07\/Code.jpg","category_names":["Development na twardo"],"_links":{"self":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/31619"}],"collection":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/users\/724"}],"replies":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/comments?post=31619"}],"version-history":[{"count":3,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/31619\/revisions"}],"predecessor-version":[{"id":31687,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/posts\/31619\/revisions\/31687"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/media\/31676"}],"wp:attachment":[{"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/media?parent=31619"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/categories?post=31619"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/sii.pl\/blog\/wp-json\/wp\/v2\/tags?post=31619"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}