PWA w Angular 7

Progressive Web Apps (PWA) wdarły się na programistyczne salony jako alternatywa dla wytwarzania dedykowanej wersji oprogramowania na każdą z platform z osobna.

Dziś opiszę proces przekształcenia na PWA, stworzonej przeze mnie w Angular 7 aplikacji budżetu domowego.

Nie będę w niej korzystał z zaawansowanych możliwości jakie daje PWA (dostępu do sprzętu, importu multimediów) ponieważ moja aplikacja ich nie wykorzystuje. Skupię się na podstawowej operacji dostarczenia w postaci PWA typowej aplikacji web napisanej w Angular.

Szczypta teorii

Google nie zbudowało PWA aby ulżyć nam - programistom. Jest to pozytywny efekt uboczny. Niewiele firm deweloperskich stać na to, aby wytworzyć oprogramowanie na wszystkie platformy, na których trwonią swój czas potencjalni konsumenci. Trzeba wybierać te, na ktorych stopa zwrotu z inwestycji jest najwyższa lub wlaśnie te platformy traktuje się z większą dbałością.

Pierwszą firmą, która doszła do powyższego wniosku był Microsoft, czego owocem stała się technologia o nazwie Uniwersal Windows Platform. Pomysł był bardzo ciekawy. Microsoft dostarczył API dla kilku języków programowania (w tym dla JavaScript), zdecydowanie tym samym zmniejszając próg wejścia dla programistów web, w świat aplikacji desktopowych.

Było to w czasach, gdy Microsoft jeszcze chyba wierzył w powodzenie projektu Windows Mobile i z pewnością mógł on być jednym z głównych beneficjentów UWP.

Problem UWP polegał jednak na jego zamknięciu w obrębie ekosystemu Microsoft, więc mógł przyciągnąć tylko tych deweloperów, którzy na ten właśnie system zdecydowali się wytwarzać oprogramowanie. Po śmierci Windows Mobile, odszedł ważny argument za wytwarzaniem oprogramowania w ten wlaśnie sposób.

Google boryka się z podobnym problemem. Mnóstwo aplikacji przygotowanych dla systemu Android nie przekłada się na ilość aplikacji w sklepie ich desktopowego Chrome OS.

Nietrudno domyślić się, że władający Google doszli do podobnych wniosków, do których doszedł Microsoft. Obie firmy łączy coś jeszcze. Obie patrzą z zazdrością na ich głównego konkurenta - Apple. Producent nadgryzionych jabłek sukcesywnie bije kolejne rekordy sprzedaży generowanej przez App Store. Często zdaża się też, że aplikacje produkowane na kilka platform jednocześnie, aktualizacje na systemy Apple traktują z większym priorytetem.

PWA jest w pewnym sensie precedensem. Chyba po raz pierwszy, dwa duże podmioty wytwarzające systemy operacyjne i dotychczas spoglądające na siebie jak na konkurenta, postanowiły połączyć siły i pracować nad projektem, który w dalszej perspektywie, żadnej z nich nie da przewagi nad tą drugą - przecież każde PWA na Windowsie zadziała też na Androidzie i Chrome OS.

Z drugiej strony, co nas to interesuje. Ważne, że raz z mozołem wyklepany kod, możemy dystrybuować gdzie popadnie.

Dość filozofii, odrobina technikaliów.

Aplikacja PWA uruchamia się w odrębnym dla siebie procesie, dzięki temu z łatwością możemy ocenić jej apetyt na zasoby urządzenia.

Proces

API Progressive Web Apps ciągle rośnie i już teraz dostarcza powiadomienia push, stan połączenia z siecią, w pewnym stopniu komunikację z usb i bluetooth, lokalizację, narzędzia do instalacji i aktualizacji i wiele więcej.

Zalety i wady PWA

Zalety:

  • jeden kod, wiele platform
  • wrażenie “natywności” aplikacji, skrót na pulpicie
  • banalne wdrożenie w porównaniu z wytworzeniem kilku aplikacji natywnych
  • stale rozszerzające się API, które już dziś dla większości aplikacji będzie wystarczające
  • zdecydowany wzrost konwersji

Wady:

  • Niepełne wsparcie w systemie iOS, choć już widać działania aby je zapewnić
  • API PWA nadal nie pozwala zrobić tych wszystkich zaawansowanych rzeczy, ktore zrobimy produkując aplikację natywną, np. obsługa portu szeregowego
  • zdecydowanie niższa wydajność niż przy aplikacji natywnej
  • monetyzacja aplikacji musi być zapewniona po stronie dewelopera (w App Store tego nie sprzedasz)

Z powyższego porownania maluje się pewien dość oczywisty obraz. Obraz, który można odnieść też do większości technologii hybrydowych. Aplikacje, które nie potrzebują komunikacji z urządzeniami, porządnego silnika bazodanowego, nie pracują na dużych ilościach danych czy nad ich obróbką, z powodzeniem mogą pójść w stronę PWA.

Moja aplikacja spełnia powyższe warunki, a zatem zaczynamy.

Potrzebne pakiety

W pierwszej kolejności zainstalujmy dwa pakiety, które są dostarczone przez deweloperów Angular.

1
2
npm install @angular/pwa
npm install @angular/service-worker

Plik manifest.json

Najważniejszym plikiem definiujący aplikację PWA jest manifest.json. Powinny się w nim znaleźć dane wykorzystywane przez Chrome do zbudowania natywnego kontenera, w którym zostanie osadzona aplikacja.

src/manifest.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
{
  "short_name": "Budget",
  "name": "Budget",
  "icons": [
    {
      "src": "/images/icons-192.png",
      "type": "image/png",
      "sizes": "192x192"
    },
    {
      "src": "/images/icons-512.png",
      "type": "image/png",
      "sizes": "512x512"
    }
  ],
  "start_url": "/",
  "background_color": "#ffffff",
  "display": "standalone",
  "scope": "/",
  "theme_color": "#3f51b5"
}

Większość powyższych zmiennych jest dość oczywista. Kilka z nich wypada opisać bardziej szczegółowo:

display

Jest to sposób wyświetlania aplikacji. Wyróżniamy cztery typy wyświetlania:

  • fullscreen - aplikacja uruchamia się w natywnym oknie systemu operacyjnego w trybie pełnoekranowym
  • standalone - aplikacja uruchamia się w natywnym oknie systemu operacyjnego
  • minimal-ui - aplikacja uruchamia się w oknie natywnym wzbogaconym o przeglądarkowe kontrolki nawigacji
  • browser - aplikacja uruchamia się w nowym oknie przeglądarki

start_url

Startowy adres URL, z którego startuje aplikacja.

scope

Scieżka bezwzględna do aplikacji, w ramach której funkcjonuje PWA. Jeśli użytkownik wyjdzie poza scope, zostanie przekierowany do nowej karty zwykłej przeglądarki, np. gdy w aplikacji umieścimy jakiś zewnętrzny link.

Service worker

Service worker jest najważniejszym narzędziem w PWA. Jest to obszerne API wspierające funkcjonowanie aplikacji. W podstawowym zastosowaniu zajmuje się on cache’owaniem aplikacji oraz wyników komunikacji z API do storage, aby móc serwować je w przypadku utracenia połączenia z siecią.

W bardziej wymagających zastosowaniach, umożliwia wysyłanie notyfikacji push, przeprowadzenie procesu instalacji i aktualizacji i sporo innych fajnych rzeczy. Jego możliwości z każdą kolejną aktualizacją wzrastają. Szerzej o działaniu service worker’a dowiesz się w MDN, a także tutaj i tutaj.

ngsw-config.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
{
  "index": "/index.html",
  "assetGroups": [
    {
      "name": "app",
      "installMode": "prefetch",
      "updateMode": "prefetch",
      "resources": {
        "files": [
          "/index.html",
          "/manifest.json",
          "/assets/images/favicons/favicon.ico",
          "/*.css",
          "/*.js"
        ],
        "urls": [
          "https://fonts.googleapis.com/**",
          "https://fonts.gstatic.com/s/**"
        ]
      }
    }
  ],
  "dataGroups": [
    {
      "name": "budget-api",
      "urls": [
        "https://api.budget.rybczynski.io"
      ],
      "cacheConfig": {
        "strategy": "freshness",
        "maxSize": 10000,
        "maxAge": "6h",
        "timeout": "5s"
      }
    }
  ]
}

assetGroups

Definiuje które pliki aplikacji i jakie zewnętrzne urle mają być cache’owane oraz w jaki sposób ten cache traktować podczas instalacji i aktualizacji.

prefetch oznacza, że service-worker pobierze wszystkie pliki statyczne aplikacji na starcie. Wymaga to początkowo większej przepustowości, jednak lepiej się sprawdza w przypadku utracenia dostępu do sieci.

lazy sygnalizuje service-worker’owi aby cache’owal tylko te dane, które odwiedza użytkownik aplikacji.

dataGroups

Definiuje źródła danych, czas cache’owania, maksymalną wielkość, czas życia, timeout po przekroczeniu którego service-worker sięgnie do danych z cache.

Więcej o konfiguracji service-worker’a w Angular przeczytasz tutaj.

Zmiany w kodzie aplikacji

app.module.ts

W sekcji imports należy dodać:

1
ServiceWorkerModule.register('/ngsw-worker.js', { enabled: environment.production })

index.html

1
2
<meta name="theme-color" content="#3f51b5">
<link rel="manifest" href="/manifest.json">

angular.json

W sekcji projects.nazwa-projektu.architect.build.options.assets dodajemy:

1
"src/manifest.json"

W sekcji projects.nazwa-projektu.architect.build.configurations.production dodajemy:

1
"serviceWorker": true

https

Na koniec, musisz jeszcze udostępnić swoją aplikację oraz API przez https oraz automatycznie przekierowywać ruch z http na https. Ja to ogarnąłem po stronie konfiguracji apache, ale można też dodając plik .htaccess.

Lighthouse

Niezwykle przydatnym narzędziem do analizy czy aplikacja spełnia warunki dołączenia do zacnego grona Progressive Web Apps, jest Lighthouse. Dostarczony jest razem z Google Chrome.

Aby użyć narzędzia, włącz narzędzia developerskie i przejdź do zakładki Audits. Wypełnij formularz jak na poniższym screenie i kliknij Run audits Run audits

Na wyjściu dostajemy audyt strony (na screenie poniżej). Dopiero gdy spełni ona podstawowe wymagania dotyczące PWA, będziemy mogli ją zainstalować jako aplikację natywną. Co ważne, nie wszystkie z wymagań trzeba spełniać. Jak widzisz, ja olałem temat szybkości ładowania i umiejscowienia w kodzie tagu <noscript>, a mimo to moja aplikacja przeszła audyt pozytywnie.

Audits

Pełną checklistę wymagań odnośnie PWA znajdziesz tutaj.

Podsumowanie

Co tu dużo pisać, wdrożenie PWA to bułka z masłem w porównaniu z czasem, jaki musielibyśmy poświęcić aby przygotować oprogramowanie natywne wspierające choćby dwie z platform, które obsługuje dziś PWA.

Śmiem twierdzić, że zdecydowana większość wytwarzanych dziś aplikacji desktopowych i mobilnych, z powodzeniem może być dostarczona w formie PWA, a koszty takiego wdrożenia będa nieporównywalnie niższe od tych, które jeszcze kilka lat temu należałoby ponieść.

Ten artykuł był właściwie tylko liźnięciem tematu i z pewnością wróci jeszcze na tego bloga w postaci bardziej zaawansowanych przykładów, ale muszę do nich dojrzeć.