Bezpośrednio - Tworzymy aplikacje natywne z React Native i NativeScript

Opublikowane:

01.10.2018

Frameworki javascriptowe React Native i NativeScript tworzą pomost między tworzeniem aplikacji webowych i natywnych. W niniejszym artykule przyjrzymy się zaletom tych natywnych frameworków w porównaniu z klasycznym frameworkiem do tworzenia aplikacji javascriptowych, Meteorem.

Autor: Andreas Moeller i Kristian Kissling

Dwa otwarte frameworki, React Native [1] i NativeScript [2], pomagają programistom tworzyć natywne aplikacje dla Androida i IOS-a za pomocą JavaScriptu. Podejście to ma szereg zalet w porównaniu z klasycznym podejściem do tworzenia aplikacji w JavaScripcie, np. za pomocą Meteora [3], który wykorzystuje Apache Cordova [4] i WebView.

Słowem wstępu

Natywne aplikacje dla Androida tworzy się zazwyczaj w Javie lub Kotlinie, natomiast te na iOS-a – w Objective-C i Swifcie [5]. Dodatkowo programiści Androida mogą korzystać z natywnego kodu C/C++ za pośrednictwem Java Native Interface (JNI) [6]. Środowiska programistyczne Android Studio [7] i Xcode [8] pomagają tworzyć aplikacje i wprowadzić je do sklepów – Apple AppStore i Google Play.

Takie podejście ma jednak swoje wady. Instalacja Android Studio jest złożona i długo trwa, a Xcode działa wyłącznie na macOS-ie. Jeśli chcemy przenieść aplikację androidową na iOS-a, musimy przepisać kod na Objective-C lub Swift, co nie tylko wymaga dodatkowej pracy, ale również wprowadza dodatkowe źródła potencjalnych błędów przez redundancję kodu.

f01-jsnative-meteor

Rysunek 1: Cordova: bez Javy i Android Studio twórca aplikacji androidowych niczego nie stworzy w Ubuntu.

f02-grafik-react-native-bridge

Rysunek 2: Most łączy świat JavaScriptu z kodem natywnym.

Listing 1: Instalacja Expo na Ubuntu 17.10

01 curl -sL https://deb.nodesource.com/setup_8.x | sudo -E bash -

02 sudo apt-get install nodejs

03 sudo npm install exp --global

04 exp init react-test

05 cd react-test

06 exp start

Z kolei klasyczne aplikacje webowe wykorzystują HTML, CSS i JavaScript, działając w przeglądarkach na różnych systemach. Aplikacje te mają jednak ograniczony dostęp do systemu: uruchamiane są w piaskownicy przeglądarki i korzystają tylko z niektórych API, co ogranicza ich możliwości.

W 2009 r. udostępniono projekt PhoneGap (obecnie Apache Cordova), który umożliwia lepsze połączenie aplikacji webowych z systemem. Dzięki Cordovie aplikacje działają w WebView – jak w przeglądarce. Dzięki różnym wtyczkom możemy dodatkowo zwiększyć integrację z systemem działających w ten sposób aplikacji.

f03-jsnative-exp

Rysunek 3: Expo ułatwia programistom życie, udostępniając kod QR do instalacji aplikacji.

f04-jsnative-boilerplate2

Rysunek 4: The app uses theUIAlertControllerclass on iOS.

Meteor

Framework javascriptowy Meteor [3] ewoluował stopniowo z frameworka służącego do tworzenia aplikacji webowych działających w czasie rzeczywistym [9] do otwartoźródłowej platformy, którą twórcy reklamują jako najlepszy sposób na uruchamianie aplikacji. I faktycznie – instalacja i utworzenie podstawowej aplikacji w powłoce w Ubuntu 17.10 sprowadza się do wydania polecenia:

curl https://install.meteor.com/ | sh meteor create example

Jednak próba zbudowania wersji dla Androida poleceniem meteor add-platform android kończy się niepowodzeniem (Rysunek 1), ponieważ pod maską Meteor bazuje na Cordovie, która wymaga Javy i Android Studio. Co więcej, w Ubuntu nie możemy zbudować wersji na iOS-a.

Tworząc aplikację, możemy przynajmniej wybrać między własnym frameworkiem javascriptowym Meteora, czyli Blaze, a AngularJS-em [10] i Reactem [11]. Meteor nie pomaga jednak zbytnio programiście w dystrybucji aplikacji. Choć polecenie meteor build tworzy odpowiednie wersje pakietów na Androida i iOS-a, to jednak umieszczenie aplikacji w sklepach wymaga ręcznej pracy.

React Native

Natywne frameworki stosują nieco inne podejście: za pomocą JavaScriptu tworzymy aplikacje działające natywnie na Androidzie i iOS-ie, dzięki czemu tworzymy most między aplikacjami webowymi a tymi działającymi na określonych platformach.

Podczas działania aplikacji React Native [1] pozostawia ją silnikowi javascriptowemu. Kod działa we własnym wątku i uzyskuje dostęp do natywnego kodu i API za pomocą mostu [12] (Rysunek 2). Graficzny interfejs użytkownika nie jest tworzony jak w Meteorze za pomocą drzewa DOM, lecz za pomocą odwołań do natywnych kontrolek w Javie lub Objective-C.

Kiedy użytkownik wyzwala jakieś zdarzenie, używając zrenderowanych elementów, React Native przenosi je z powrotem przez most i konwertuje do postaci zdarzenia javascriptowego. React Native próbuje zapobiec stratom wydajności spowodowanym synchronizacją między dwoma światami za pomocą wirtualnego DOM-a, dla którego model udostępnia javascriptowy framework React. W testach praktycznych przykładowe aplikacje React Native działały mniej więcej tak samo wolno jak natywne aplikacje w starszej wersji iOS-a.

Jeśli chcemy projektować aplikacje React Native, może nam w tym pomóc projekt Expo [13] zawierający szereg narzędzi, m.in. środowisko programistyczne XDE (Expo Development Environment), które można również uruchomić w przeglądarce pod nazwą Snack [14]. Można nim także zarządzać z wiersza poleceń po przeprowadzeniu procedury instalacyjnej z Listingu 1.

Listing 2: Przykładowa aplikacja React Native

01 import React from 'react';

02 import { StyleSheet, Button, View, NetInfo } from 'react-native';

03

04 export default class App extends React.Component {

05   render() {

06     return (

07       

08         

09       

10     );

11   }

12   getConnectionInfo() {

13     NetInfo.getConnectionInfo().then(info => alert(info.type+' ('+info.effectiveType+')'));

14   }

15 }

16

17 const styles = StyleSheet.create({

18   container: {

19     flex: 1,

20     backgroundColor: '#ddd,

21     alignItems: 'center',

22     justifyContent: 'center',

23   },

24 });

W wierszach 1 i 2 instalujemy bieżącą wersję Node.js v8 za pomocą debianowego menedżera pakietów Apt. Następnie menedżer pakietów Node, npm, pobiera Expo (exp), łącznie z React Native, i w wierszu 3 instaluje ho w systemie. Z kolei w wierszu 4 konfigurowana jest szablonowa aplikacja react-test. W wierszach 5 i 6 aplikacja jest pakowana, by można ją było uruchomić na smartfonie. Na Rysunku 3 widzimy powłokę po wykonaniu tych poleceń oraz kod QR służący do instalacji.

Aby przenieść spakowaną aplikację na telefon za pomocy kodu QR, użytkownik musi na nim najpierw zainstalować klienta Expo z Google Play lub Apple App Store, a następnie uruchomić go, wskanować kod QR i załadować szablonową aplikację (Rysunek 4).

Jeśli wymagania dotyczące aplikacji wykraczają poza ograniczenia React Native i Expo, możemy podpiąć pod nią kod natywny, który będzie kontrolowany przez JavaScript w aplikacji. W takim scenariuszu musimy jednak zainstalować Android Studio i Xcode.

Przykładowa aplikacja

Na Listingu 2 widzimy przykładowy sposób użycia React Native. Kod ten zachowuje plik App.jsw katalogu react-test. Podobnie jak inne aplikacje React Native, przedstawiony na Listing 2 program bazuje na frameworku javascriptowym React [11], który importujemy w wierszu 1. W kolejnym wierszu pobieramy z React Native komponenty StyleSheet, Button, View oraz NetInfo.

React Native tworzy również jako komponent klasę bazową App (wiersze 4–15) as a component. Jej metoda render() (wiersze 5–11) używa przypominający XML kod JSX, by stworzyć interfejs użytkownika odnoszący się do danego obszaru (wiersze 7–9). W wierszu 8 komponent View implementuje przycisk button [15]. W czasie działania programu Android wyświetli go za pośrednictwem klasy android.widget.Button [16], natomiast iOS – UIButton [17].

Wiersze 12–14 przechowują funkcję zwrotną getConnectionInfo(), na którą wskazuje wartość atrybutu onPress (wiersz 8). Kiedy przycisk zostanie naciśnięty, w wierszu 13 metoda przycisku określa status połączenia urządzenia mobilnego. Funkcja lambda odpowiada na asynchroniczny wynik, wywołując metodę then(), która powinna wyświetlić pozyskany typ połączenia w oknie dialogowym. Jak widzimy na Rysunku 4, podczas testów nie udało się określić statusu połączenia.

W wierszu 7 odniesienie do {styles.container} z wartości atrybutu style przypisuje informacje dotyczące stylu ze statycznego obiektu javascriptowego styles do komponentu View. W ten sposób narysowane zostanie szare tło, a przycisk będzie umieszczony w centrum (wiersze 17–24). Oprócz NetInfo React Native oferuje wiele innych obiektów API związanych z systemem, które zwiększają możliwości SDK.

f05-jsnative-cloudbuild

Rysunek 5: Expo udostępnia pliki dziennika ze zdarzeniami zachodzącymi podczas procesu budowania.

Listing 3: Przykładowa aplikacja w NativeScripcie

01 import { Component } from "@angular/core";

02  import  *  as  connectivity  from  "connectivity";

03

04  @Component({

05      selector:  "my-app",

06      template:  ``

07  })

08  export  class  AppComponent  {

09      getConnectionInfo()  {

10          switch(connectivity.getConnectionType())  {

11              case  0:

12                  alert('none');

13                  break;

14              case  1:

15                  alert('WiFi');

16                  break;

17              case  2:

18                  alert('Mobile');

19                  break;

20          }

21      }

22  }

Polecenie exp start w ostatnim wierszu Listingu 1 działa jak proces obserwujący. Jeśli zarejestruje zmianę w katalogu projektu, poinformuje o tym serwer pakujący, który również został uruchomiony. Następnie serwer zadba o to, by zaktualizowana wersja aplikacji została dostarczona do podłączonych klientów Expo.

Tabela 1: Porównanie frameworków

Framework

IDE

Budowanie w chmurze

Natywne UI

Natywne Moduły

Natywne Pakiety

Budowanie dla App Store

Meteor 1.6.1

Nie

Nie

Nie

Tak

Tak

Nie

React Native 52.0

XDE 2.22.1

Tak

Tak

Tak

Tak

Nie

NativeScript 3.4.3

Sidekick 1.5.1

Commercial

Tak

Tak

Tak

Nie

f06-jsnative-sidekick

Rysunek 6: Sidekick po zbudowaniu aplikacji na Androida. Zbudowanie wersji dla iOS-a jest płatne.

Jak to pokazuje przykładowa aplikacja react-test [18], programista może w dowolnej chwili opublikować aplikację za darmo, wykorzystując w tym celu swoje konto Expo; służy do tego przycisk Publish w XDE. Jeśli do pliku konfiguracyjnego app.json w katalogu projektu dodamy też buildidentifier i package, wtedy exp build:android utworzy aplikację androidową w chmurze dostawcy.

Za pomocą URL-a na koncie Expo możemy śledzić proces budowania, korzystając z wiersza poleceń. Na Rysunku 5 widzimy wpisy w plik u dziennika po udanym zbudowaniu pakietu na Androida. Pod maską Expo korzysta z instancji Android Studio lub Xcode. Podobnie jednak jak to jest w przypadku Meteora, dostawca nie zintegrował całkowicie ostatnich kilku etapów niezbędnych do załadowania aplikacji do sklepu – musimy więc ręcznie wykonać kilka czynności.

NativeScript

Framework javascripty NativeScript [2] również pozwala generować aplikacje natywne dla Androida i iOS-a. Podobnie jak zachodzi to w przypadku React Native, wykonanie kodu pozostawione jest odpowiedniemu silnikowi javascriptowemu; wykorzystywany jest tu Android v8 oraz iOS JavaScriptCore (JSC).

NativeScript umożliwia również sprawowanie kontroli nad kodem natywnym za pomocą mostu [19]. Środowisko uruchomieniowe dynamicznie przekazuje obiektom natywnym wywołania metod getter/setter obiektu javascriptowego.

Konfiguracja środowiska programistycznego dla NativeScriptu w Linuksie jest szczególnie podatna na problemy: budowanie aplikacji na iOS-a zawodzi z niewiadomych powodów. Jeśli nie ograniczają nas koszty (patrz Tabela 1), możemy tworzyć aplikacje w chmurze dostawcy podobnie jak w Expo. Pierwszych 100 procesów budowania jest darmowych.

W Ubuntu framework instalujemy poleceniem:

sudo npm install -g nativescript

Generowana jest wtedy przykładowa aplikacja szkieletowa:

tns create ns-test --template nativescript-template-ng-tutorial

Pobieramy środowisko programistyczne Sidekick [20], po czym instalujemy debianowym poleceniem dpkg:

sudo dpkg -i NativeScriptSidekick-amd64.deb

Następnie uruchamiamy Sidekicka, pisząc:

/opt/Native\ Script\ Sidekick/Native\ Script\Sidekick

W przeciwieństwie do React Native, NativeScript obsługuje różne frameworki (AngularJS [10] i Vue.js [21]) i języki (czysty JavaScript lub TypeScript [22]).

Na Listingu 3 wracamy do przykładowej aplikacji z Rysunku 4 i Listingu 2, tym razem korzystając z AngularJS i NativeScriptu. Kod powinien znaleźć się w pliku app/app.component.tsw katalogu ns-test wspomnianej wyżej aplikacji testowej. Pierwszy wiersz Listingu 3 importuje klasę Component z AngularJS, natomiast w wierszu 2 – obiekt connectivity z NativeScriptu. Dekorator w wierszach 4–7 konwertuje definicję klasy AppComponent (wiersze 8–22) do pochodnej klasy Component.

W wierszu 6 tworzymy obszar interfejsu użytkownika za pomocą komponentu Button, by wygenerować przycisk, który po naciśnięciu wywołuje funkcję zwrotną getConnectionInfo(). Kod NativeScriptu synchronicznie określa typ połączenia, a funkcja alert() przesyła wynik użytkownikowi.

Na Rysunku 6 widzimy pomyślne zakończenie budowania aplikacji dla Androida. Nie przetestowaliśmy procesu budowania na iOS-a, ponieważ wymagany certyfikat programisty iOS-a – w przeciwieństwie do dokumentacji – jest obecnie płatny.

Wnioski

Aplikacje Meteora łatwo skonfigurować i skalować dzięki usłudze chmurowej Galaxy. Problemy zaczynają się dopiero przy tworzeniu aplikacji mobilnych.

React Native i NativeScript oferują równorzędne rozwiązania. Radzą sobie bez WebView i wykorzystują natywny kod na iOS-ie i Androidzie. Zarówno natywne API, jak i komponenty interfejsu użytkownika korzystają z mostu. Z punktu widzenia programistów aplikacji przypomina to pracę z przeglądarką i DOM-em, natomiast użytkownikowi łatwiej się pracuje z natywnym interfejsem.

Projekt Expo uzupełnia React Native. Dzięki klientowi Expo, webowemu IDE Snack oraz darmowej przestrzeni w chmurze Expo oferuje idealne środowisko dla nowych projektów i ich społeczności. Dzięki budowaniu aplikacji w chmurze twórcy aplikacji nie muszą nawet instalować i konfigurować Android Studio ani Xcode.

Info

[1] React Native: https://facebook.github.io/react-native/

[2] NativeScript: https://www.nativescript.org

[3] Meteor: https://www.meteor.com

[4] Apache Cordova: https://cordova.apache.org

[5] Swift: https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/index.html

[6] Java Native Interface: https://developer.android.com/training/articles/perf-jni.html

[7] Android Studio: https://developer.android.com/studio/index.html

[8] Xcode: https://developer.apple.com/xcode/

[9] Andreas Moeller, Tworzenie aplikacji webowych za pomocą meteora, „Linux Magazine” 5/2014: https://linux-magazine.pl/archiwum/wydanie/126

[10] AngularJS: https://angularjs.org

[11] React: https://reactjs.org

[12] Most dla React Native: http://www.discoversdk.com/blog/how-react-native-works

[13] Expo: https://expo.io

[14] Snack: https://snack.expo.io

[15] Przycisk React Native: https://facebook.github.io/react-native/docs/button.html

[16] Przycisk androidowy: https://developer.android.com/reference/android/widget/Button.html

[17] Przycisk iOS-a: https://developer.apple.com/documentation/uikit/uibutton

[18] Przykładowa aplikacja react-test: https://expo.io/@pam/react-test

[19] Most NativeScript: https://developer.telerik.com/featured/nativescript-works/

[20] Sidekick: https://www.nativescript.org/nativescript-sidekick

[21] Vue.js: https://vuejs.org

[22] Tim Schuermann, Alternatywy dla JavaScriptu, „Linux Magazine” 10/2017: https://linux-magazine.pl/archiwum/wydanie/54

Aktualnie przeglądasz

Październik 2018 - Nr 176
october2016a

Top 5 czytanych

Znajdź nas na Facebook'u

Opinie naszych czytelników

Nasze wyróżnienia i partnerzy:partnerzy linux magazine
wiper-pixel