Wszyscy życzylibyśmy sobie, aby rzeczywistość projektowa była nieco bardziej przewidywalna, oczekiwania biznesowe i potrzeby klienta określone jak najwcześniej i możliwie niezmienne, a do zastosowania wyłącznie technologie, w których czujemy się w danym czasie najlepiej. I równie mocno jak moglibyśmy sobie tego życzyć, tak mocno wiemy, że zazwyczaj realia projektowe są dużo bardziej dynamiczne.

Chciałbym dziś przybliżyć Wam rozwiązanie, które wykorzystaliśmy w jednym z projektów dla naszego niemieckiego klienta – firmy zajmującej się ułatwianiem wyszukiwania kampingów (nawiasem mówiąc u naszych sąsiadów ten rodzaj wypoczynku jest nieporównywalnie bardziej popularny niż w Polsce, a co za tym idzie oferta lokalizacji kempingowych jest bardzo bogata i zróżnicowana).

Za chwilę opiszę nieco dokładniej kontekst biznesowy, a w niniejszym artykule postaram się odpowiedzieć na następujące pytania:

  • Czym jest Capaticor?

  • Jak skorzystać z Capacitora w towarzystwie Next.js?

  • W jakich sytuacjach warto go zastosować?

W przypadku wspomnianego projektu spotkała nas sytuacja, gdy mieliśmy już dla klienta niemal gotową stronę internetową serwisu – napisaną w Next.js. Klient jednak doszedł do wniosku, że chciałby również wypuścić dedykowaną aplikację mobilną na AppStore oraz Google Store. Aplikacja miała być zgodnie z oczekiwaniami klienta dokładnym odzwierciedleniem serwisu webowego. Naszym zadaniem było przenieść ją do użytku na urządzeniach mobilnych jak najwierniej i w krótkim czasie. Po researchu zdecydowaliśmy się na wykorzystanie Capacitora.

Czym jest Capacitor?

Capacitor to środowisko uruchomieniowe stworzone przez zespół Ionic, którego zadaniem jest zapewnienie nowoczesnego i elastycznego sposobu integracji aplikacji internetowych z platformami natywnymi. Umożliwia on pisanie aplikacji przy użyciu dowolnej platformy, takiej jak React, Angular, czy Vue, a następnie uruchamianie jej w systemie iOS, Android lub w środowisku webowym.

Capacitor wykorzystuje mechanizmy osadzania stron internetowych wewnątrz natywnych aplikacji. Dla Appla to WKWebView, a dla Androida to po prostu WebView. Można myśleć o Capacitorze jak o Elektronie tyle, że dla urządzeń mobilnych.

Stworzony przez nas projekt to zatem strona www pracująca w natywnym środowisku uruchomieniowym na potrzeby urządzeń mobilnych. Ponieważ aplikacja powstała na bazie aplikacji webowej, nie musieliśmy wykorzystywać żadnych modułów natywnych. Dlatego użycie pluginów Capacitora nie było konieczne.

W naszym przypadku potrzebowaliśmy tylko efektywnie wyeksportować stronę internetową do aplikacji mobilnej. Na koniec dnia można ocenić, że nasze wykorzystanie tego narzędzia było i tak dosyć zawężone – to co musieliśmy zrobić na potrzeby tego procesu to wygenerować zestaw statycznych plików JavaScript/HTML/CSS. Następnie Capacitor „opinał” te pliki w swoje natywne warstwy, a my wystawialiśmy gotowe aplikacje w obu sklepach. Brzmi to prosto, ale nie do końca tak było.

Sprawy nie ułatwiał nam wybrany już na samym początku projektu stack technologiczny na czele z Next.js, który opiera się na optymalizacjach renderingowych (SSR vs SSG vs CSR). Aby móc wykorzystać Capacitora musieliśmy te optymalizacje w dużej mierze ograniczyć.

To oznacza, że musieliśmy wyeksportować naszą aplikację napisaną w Next.js do statycznych, gotowych plików. Z tego powodu należało zrezygnować z szeregu możliwości oferowanych przez ten framework, a dodatkowo umożliwienie współpracy Next.js i Capacitora wymagało od nas dość długiej konfiguracji.

Zapytacie zatem czym był podyktowany wybór takiego stacku, skoro wydaje się, że nie składa się to w płynnie działającą całość. I rzeczywiście początkowe wybranie Next.js mogło nam narobić nieco komplikacji, ale pamiętajmy, że decyzja o stworzeniu aplikacji mobilnej zapadła już po stworzeniu niemal całego webowego serwisu. Gdyby w takiej sytuacji życzeniem klienta był plot twist czasowy i budżetowy (czysto teoretycznie :D) to pewnie rozważalibyśmy napisanie całości przy pomocy React Native.

Gdy już jednak przeanalizowaliśmy wszystkie „za i przeciw” stanęły przed nami kolejne wyzwania, które wynikały przede wszystkim z niezbyt przyjemnego dopasowania Next.js do Capacitora. Udało się nam to jednak skonfigurować, choć było to okupione rezygnacją z wspomnianych feature'ów.

W jaki sposób zmusiliśmy Next.js do wyeksportowania rzeczonych plików?

Kluczem do generacji statycznych plików w next.js jest next export. Niestety ta funkcjonalność wiąże się ze znacznymi technicznymi ograniczeniami, których pełną listę znajdziecie tutaj.

Szczęśliwie, w naszej aplikacji zdecydowana większość niewspieranych przy next-exporcie funkcjonalności nie miała zastosowania. Jedynym wyjątkiem było oczywiście dociąganie danych, dlatego musieliśmy zrezygnować z typowego SSR z wykorzystaniem funkcji getServerSideProps i przejść na znacznie mniej popularną funkcję getInitialProps. W przeciwienstwie do getServerSideProps funkcja ta pozwala nam na dociąganie danych nie na serwerze, lecz w przeglądarce (tuż przed renderowaniem).

Kiedy i dlaczego warto zdecydować się na zastosowanie Capacitora?

W dużej mierze wyjaśnia to kontekst naszego projektu – gdy prace nad aplikacją webową są już na wykończeniu lub na bardzo zaawansowanym etapie, a dynamicznie pojawia się potrzeba stworzenia aplikacji mobilnej (na dodatek jak najwierniej odpowiadającej serwisowi webowemu) za użyciem Capacitora mogą przemawiać kwestie budżetowe, choć React Native wydaje się tu być rozwiązaniem zdecydowanie bardziej komfortowym. Zatem jeśli w takiej sytuacji czasowo i budżetowo klient jest skłonny cofnąć się te przysłowiowe 4 kroki w tył – React Native będzie na pewno wygodniejszy w użyciu. Jeśli jednak rzeczywistość projektowa na to nie pozwala – Capacitor jest rozwiązaniem, które przy pewnym wysiłku pozwala osiągnąć oczekiwany rezultat.