Xamarin Forms – DependencyService, czyli dostęp do kodu specyficznego dla danej platformy

W ostatnim wpisie poświęconym technologii Xamarin.Forms pokazałem, w jaki sposób stworzyć cross-platformową aplikację mobilną na trzy platformy jednocześnie. Wszystko to bez pisania ani jednej linijki natywnego kodu dla Androida, Windowsa, czy iOS’a. Wspominałem też w moim pierwszym wpisie poświęconym tej technologii, że jest to możliwe dzięki temu, że Xamarin podczas komplikacji konwertuje pisany przez nas kod do natywnego dla każdej z platform. Dotyczy to głównie kodu layout’u aplikacji, który w tym podejściu jest współdzielony pomiędzy wszystkie systemy (dzięki bibliotece Xamarin.Forms, do której referencję posiadają wszystkie projekty w rozwiązaniu). Jednak aplikacja to nie tylko jej layout, to także logika. Problem może zacząć się w momencie, kiedy zorientujemy się, że przy użyciu podejścia Xamarin.Forms PCL, nie możemy zrealizować podstawowych operacji np. pobrać pliku, czy na nim operować… Ale może zacznijmy od początku.

Wprowadzenie

Na wstępie należy sobie zdać sprawę z jednej rzeczy. Każda z platform realizuje różne zadania przy pomocy różnych mechanizmów. Przykładowo: Na każdej z platform istnieją przyciski, jednak ich obsługa jest realizowana w trochę inny sposób. Pisząc w Xamarin.Forms’ach nie musimy się akurat tym przejmować. Biblioteka ta udostępnia nam klasy, które podczas kompilacji zostaną przetłumaczone na kod odpowiedni dla każdej z platform (klasa Button itp). Jeżeli chodzi o sam wygląd aplikacji, to ponad 95% rzeczy da się zrobić przy użyciu formsów, bez bawienia się w pisanie kodu natywnego dla każdej z platform (natywnego w tym sensie, że przeportowanego do C#, przy użyciu podejścia Xamarin Native ;)). Jednak nie na pisaniu layout’u, tworzenie aplikacji się kończy. Napisać trzeba też logikę i tutaj sprawa nieco się komplikuje. Podejście Xamarin.Forms PCL polega na tym, że w projekcie przenośnym(portable) można używać klas/metod, które są dostępne we wszystkich pozostałych projektach, które posiadają referencję do projektu przenośnego(portable). Jaśniej? Chodzi o to, że jeżeli w projekcie UWP nie jest dostępna np. klasa WebClient, to takiego kodu w projekcie współdzielonym użyć nie możemy, chociaż w pozostałych dwóch projektach (Android i iOS) jest to wykonalne. Zresztą jest to zupełnie zrozumiałe – kompilacja aplikacji UWP, zwyczajnie by się nie powiodła. Z kolei użycie odpowiadającej klasy/metody HttpClient.GetByteArrayAsync(uri) będzie możliwe dopiero po instalacji dodatkowego pakietu NuGet (System.Net.Http) dla Androida i iOS’a (bo domyślnie ta klasa nie jest na nich dostępna). Dochodzą do tego problemy z zapisaniem takiego pliku w jakiejś lokalizacji… (każda z platform operuje na innych ścieżkach, a w UWP właściwie nie powinno się ich używać). Do tego wszystkiego każda platforma posiada specyficzne klasy do obsługi plików. Chociaż również ten problem powinno dać się rozwiązać przy pomocy wspomagaczy z NuGet’a (np. PCLStorage, btw – u mnie nie do końca się sprawdziło). Załóżmy jednak, że paczka System.Net.Http dla Androida i iOS’a nie istnieje, a z samym zapisem pliku do jakiejś lokalizacji też musimy zmierzyć się sami. Sytuacja hipotetyczna, jednak idę o zakład, że znajdzie się masa podobnych, których nie będzie dało się rozwiązać w inny sposób, niż przy pomocy… DependencyService. Do głowy przychodzi mi jeszcze tylko jeden przykład (zwrócenie ścieżki do folderu roboczego aplikacji). Pewnie gdybym siedział w Xamarinie dłużej, mógłbym wymienić ich sporo więcej 🙂

Czym jest DependencyService?

Mówiąc krótko: DependencyService, jest mechanizmem udostępnianym przez Xamarina umożliwiającym implementację funkcji specyficznych dla każdej platformy. Funkcje te z kolei możemy wywoływać z naszego projektu przenośnego (portable) przy pomocy konstrukcji DependencyService.Get<interface>. Całość dość dobrze obrazuje poniższy rysunek (lekko spolszczony, oryginał można znaleźć na stronie Xamarina):

Jak tego używać?

Skoro wiesz już czym jest DependencyService, warto byłoby pokazać jak tego używać. Przedstawię to na przykładzie funkcji pobierającej i zapisującej na urządzeniu dowolny plik. Do dzieła!

Deklaracja interfejsu

Pierwszą rzeczą, jaką należy zrobić, jest zadeklarowanie interfejsu, który będą implementować klasy pisane dla poszczególnych platform. Ma być on umieszczony w naszym projekcie przenośnym (portable). Ja zrobiłem to w taki sposób:

Implementacja klas dla każdej z platform

Kolejną rzeczą jaką trzeba zrobić, jest stworzenie we wnętrzu wszystkich projektów przeznaczonych na konkretne platformy klas, które będą implementować powyższy interfejs:

Android:

iOS:

UWP:

Używanie funkcji specyficznych dla platformy

Jak widać, powyższy kod dla każdego z systemów jest trochę inny. Wynika to z tego, że używane są w nim klasy/metody niedostępne dla pozostałych systemów (np. Android.App.Application.Context.GetExternalFilesDir() – klasa bardzo specyficzna dostępna tylko dla Androida). Pewnie powyższy problem można rozwiązać w lepszy sposób, ale używam go tutaj tylko w celu pokazania zasady działania mechanizmu DependencyService. Swoją drogą… Jeżeli macie jakieś pomysły, jak go rozwiązać to dajcie znać, będę bardzo wdzięczny.

Przechodząc do użycia powyższego cudeńka z poziomu projektu portable, wygląda ono następująco:

Tylko tyle 🙂 Xamarin zadba już o to, żeby odpowiednie umieszczone w pozostałych projektach metody zostały uruchomione na odpowiednim dla nich systemie. Proste, a jakże przydatne. Wydaje mi się, że nie da się napisać żadnego większego projektu bez użycia tego mechanizmu (gotowe paczki NuGet też z niego korzystają).

Tyle ode mnie. Jak zwykle zachęcam do zadawania pytań i zgłaszania swoich uwag w komentarzach. Przy okazji może zachęcę do polubienia fanpage bloga na facebook’u (jeżeli jeszcze go „nie lubisz”), bo dawno tego nie robiłem. Do zobaczenia! 😉

176 total views, 1 views today

Jedno przemyślenie nt. „Xamarin Forms – DependencyService, czyli dostęp do kodu specyficznego dla danej platformy

Możliwość komentowania jest wyłączona.