EXPONENT - LAUFZEITUMGEBUNG FUR NATIVE APPS Laufzeitumgebung

Unter nativen Apps versteht man heute fast ausschließlich Anwendungen für die beiden größten Mobil-Plattformen iOS und Android. Obwohl native und Web-Apps oft als sehr unterschiedlich dargestellt werden, gibt es aber auch immer mehr Gemeinsamkeiten: Webanwendungen können durch den Fortschritt moderner Webbrowser immer mehr Features nutzen, die man ursprünglich nur in nativen Apps vorfand.

Gleichzeitig übernehmen auch native Apps Errungenschaften, die sich erst durch Webanwendungen etabliert haben: Responsives UI-Design oder Deep-Linking sind dabei nur zwei typische Themen. Auch wenn die jeweiligen Anhänger ihren technologischen Ansatz gern als Sieger über den anderen erklären: Bis heute sind beide Arten von Apps wichtig und je nach Anwendungsszenario auszuwählen.

Web-Vorteil: Browser als Laufzeitumgebung

Ein unschätzbarer Vorteil von Webanwendungen ist, dass man lediglich einen Webbrowser benötigt. Die Anwendung selbst kann sowohl auf Desktop-Rechnern als auch auf mobilen Geräten funktionieren. Man besucht einfach die Website und kann die App sofort nutzen. Eine Einstiegshürde gibt es nicht. Auch eine Installation ist nicht notwendig. Doch nicht nur für die Nutzer sind Webanwendungen einfacher: Als Webentwickler benötigt man eigentlich nicht mehr als einen Webbrowser und einen Texteditor. Zählt man webbasierte Entwicklungsumgebungen wie Cloud9 dazu, dann kann man sogar alles über einen modernen Webbrowser erledigen.

Diese einfache Zugänglichkeit erleichtert den Einstieg in die Entwicklung von Webanwendungen enorm. Im Gegensatz dazu sind die Anforderungen bei nativen Apps bis heute enervierend hoch. Der Entwickler muss die jeweiligen Entwickler-Tools des Herstellers installieren. Er benötigt möglicherweise ein Entwicklerkonto. Bei iOS benötigt man auch zwingend einen Mac, denn Xcode und die iOS-SDKs funktionieren nur unter macOS. Möchte man die eigenen Apps ausprobieren, so sind dafür ein geeigneter Simulator oder physische Geräte notwendig.

Die Bereitstellung der Apps auf den Testgeräten ist wesentlich umständlicher und komplizierter, als Webentwickler dies gewohnt sind. Ohne die passenden Zertifikate auf den Geräten funktioniert nichts. Möchte man am Ende die eigene App anderen zur Verfügung stellen, dann muss man den ebenfalls aufwendigen Prozess der Einstellung in einen App Store durchlaufen.
Exponent-Website: So präsentiert sich die Projektseite von Exponent (Bild 1)
Mit React Native können Webentwickler native Apps entwickeln und dabei mit JavaScript und React auf Technologien zurückgreifen, die sie bereits aus dem Webumfeld kennen. Dennoch müssen sie auch mit React Native noch mit den herstellerspezifischen Werkzeugen Apps bauen.

Exponent (Bild 1) führt den Ansatz von React Native noch einen weiteren Schritt weiter in Richtung Web: In Apples App Store und Googles Play Store kann man kostenlos die App Exponent herunterladen. Dabei handelt es sich nur um eine Shell-Anwendung, mit der man in React Native implementierte und über das Exponent SDK bereitgestellte native Apps auf den eigenen Geräten ausführen kann.

Gewissermaßen ist dies das Gegenstück des Webbrowsers. Der Entwickler benötigt dafür weder ein Entwicklerkonto bei Apple oder Google, noch müssen die Geräte als Entwicklergeräte angemeldet werden. Es genügt vollkommen, die frei verfügbare App Exponent zu installieren. Bild 2 zeigt, wie die App nach dem Start unter iOS aussieht. Über das Textfeld kann der Nutzer einen Exponent link eingeben. In einer Liste darunter sind einige Beispielprojekte lediglich einen Fingertipp entfernt. Die App registriert auch einen Link-Handler mit den Mobil-Betriebs-systemen, sodass man auch einfach im Mo-bil-Webbrowser oder Mail-Client auf einen Exponent-Link klicken kann, um die zugehörige App zu öffnen.
Installation des Exponent SDK

Während der Exponent-Client auf den Mobilgeräten bereitsteht, so dient auf der Entwicklermaschine die XDE (Exponent Developer Environment, UI des Exponent SDK) dazu, die Quelltexte in ein Bundle zu über-
A Exponent
Go to an Exponent link...
Animations Showcase

@community/animations-showcase
Featured
React Native for Curious People

@exponent/react-native-for-curious-peo|
Pomodoro

@exponent/pomodoro
Native Component List

@notbrent/native-component-list
Der Exponent-Client unter iOS (Bild 2)
EXPONENT - LAUFZEITUMGEBUNG FUR NATIVE APPS
Laufzeitumgebung

Es gibt ein Spannungsfeld zwischen Webanwendungen und nativen Apps.


setzen. Dieses wird dann vom ExponentClient geladen und ausgeführt. Die Installation der XDE ist einfach und entspricht den gängigen Konventionen unter den unterstützten Betriebssystemen (macOS, Windows, Linux). Bild 3 zeigt, wie man die XDE unter macOS installiert, indem man wie gewohnt die App einfach in den Programme-Ordner zieht. Nach dem Start erscheint ein Login-Fenster (Bild 4). Hat man sich darüber angemeldet, öffnet sich das XDE-Projektfenster.

XDE startet für das in ihr laufende Projekt zwei Serverprozesse. Der eine Prozess ist der React Native Packager Server. Er beobachtet die Quelltexte des Projekts und generiert bei Änderungen ein neues Bundle. Der zweite Prozess ist der Exponent Development Server. Dieser stellt die Verwaltungsund Deployment-Schnittstelle dar. Der Exponent-Client auf dem Mobilgerät ruft zuerst vom Exponent Development Server eine Manifest-Datei (JSON) ab. Darin sind der Name des Projekts und alle weiteren für die Entwicklung relevanten Metadaten enthalten. Danach lädt er das Bundle vom Packager Server und dazu eventuell noch weitere Assets wie in der Manifest-Datei beschrieben.

Netzwerk-Transparenz

Damit der Exponent-Client die beiden Serverprozesse finden kann, übergibt man ihm einen URL, der das XDE-Projekt-fenster anzeigt. Normalerweise ist das ein URL mit dem Cus-tom-Schema exp und einem öffentlich verfügbaren DomainNamen, der auf exp.direct endet. XDE nutzt automatisch den sicheren Tunnel-Dienst ngrok (http://ngrok.com).

Auf diese Weise sind die Serverprozesse auch durch Firewalls hindurch erreichbar. Möchte man dies nicht, so kann man mit dem Zahnrad neben dem URL für die Option Host statt Tunnel entweder LAN oder localhost wählen. Bei LAN wird kein Tunnel mehr nach außen freigegeben, statt-dessen stehen die Serverprozesse unter der LAN-Adresse des Entwicklerrechners zur Verfügung. Wählt man localhost, dann kann man nur noch direkt vom Entwicklerrechner auf die Prozesse zugreifen. Dies kann sinnvoll sein, wenn man

XDE-Login:

Anmeldung zur Exponent Developer Environment (Bild 4)
entweder mit Simulatoren arbeitet oder eigene Tunneling-Werkzeuge verwenden möchte.

Entwicklungsmodus

Eine weitere Option hinter dem ZahnradSymbol ist der Development Mode. Solange die Anwendung im Development Mode bereitgestellt wird, lässt sie sich live neustarten und in Chrome debuggen. Dies kann jedoch die Anwendung verlangsamen. Stellt man den Development Mode ab, dann verliert man diese Möglichkeiten, kann jedoch die volle Geschwindigkeit der App prüfen.

Über die Schaltfläche mit dem Blitz-Symbol gelangt man ins Projektmenü und kann mit New Project ein neues Projekt anlegen. Schon direkt nach dem Erstellen des Projekts baut der React Native Packager ein initiales Bundle. Der im Projektfenster angezeigte URL kann im Exponent-Client geöffnet werden. Man kann ihn entweder abtippen oder einfach per Send Link-Schaltfläche als E-Mail verschicken. Auf dem Testgerät reicht ein Klick auf den Link in der E-Mail, dann öffnet sich die Exponent-Client-App und startet das Bundle. Die Hauptview der initialen App fordert auf, die JavaScriptDatei main.js zu öffnen, um weiterzuentwickeln (Listing 1).

Der Kenner sieht sofort, dass es sich dabei um eine sehr einfache React-Native-App handelt. Das einzig Auffällige ist, dass am Ende Exponent.registerRootComponent(App) anstelle des sonst üblichen AppRegistry.registerComponent benutzt wird.

Bearbeitet man den Text Open up main.js to start working on your app! und schreibt stattdessen Hallo Welt, dann wird die Anwendung sofort nach dem Abspeichern von main.js neu gestartet und das Hallo Welt erscheint auf dem Testgerät. Gegenüber einem nur auf React Native aufbauenden Entwicklungs-Setup spart man sich mit Exponent die Installation von Xcode oder des Android SDK und kann dennoch direkt auf den Testgeräten testen.

On-Device-Entwicklermenü

Der Exponent-Client bietet eine Reihe von eingebauten Features, die das Debuggen und Analysieren der App erleichtern sollen. Indem man das Gerät schüttelt, öffnet sich das Entwicklermenü (Bild 5). Mit Reload kann man die App neustarten. Debug JS Remotely verbindet sich mit einem Chrome auf der Entwicklermaschine und führt das JavaScript der App in einem Web Worker aus. So kann man die Anwendung mit dem von Chrome gewohnten Debugger debuggen. Leider funktionieren die Source Maps in Chrome unzuverlässig, so-dass Breakpoints, die man interaktiv setzt, nicht immer wie gewünscht funktionieren.

Stattdessen wird empfohlen, direkt im Sourcecode mit debugger-Statements zu arbeiten. Der Entwickler kann sich per Entwicklermenü auch zwischen Live Reload und Hot Reloading entscheiden. Bei Letzterem wird der Zustand der Anwendung bei Änderungen im Code so weit wie möglich beibehalten.
Q Exponent XDE 2.12.0
X 2 Objekte - )-
Exponent
Exponent XDE m  Applications
Q Exponent XDE 2.12.0

Installation der Exponent XDE (Bild 3)
Da der Elements-Tab in den Chrome-Entwickler-Tools beim Debuggen der Exponent-App nicht funktioniert, kann man die in der App gerenderten Komponenten so nicht überprüfen. Allerdings kann man im Entwicklermenü des Expo-nent-Clients mit Show Inspector direkt auf dem Gerät ein Overlay einblenden, mit dem man Informationen über die Elemente und Komponenten ermitteln kann (Bild 6). Auch ein Performance-Monitor (Bild 7) lässt sich einblenden (Show Perf Monitor). Dieser zeigt unter anderem an, wie viel RAM die App belegt und wie viele Views es gibt.

Native Features nutzen

Ein einfacheres Entwicklungs-Setup ist nur einer der Vorteile von Exponent. React Native bietet ohne native Erweiterungen nur ein sehr minimales Set von Anbindungen an die nativen Plattform-APIs. Alles Weitere muss der Entwickler selbst über die React Native Bridge implementieren.

Für Exponent ist das noch in einer anderen Hinsicht kritisch: Aufgrund der App-Store-Beschränkungen kann man keine nativen Erweiterungen nachinstallieren; infolgedessen funktioniert nur das, was mit dem jeweils installierten Stand des Exponent-Clients mitgeliefert wird. Glücklicherweise bietet die App von Beginn an ein vielfältiges Angebot an plattformübergreifend implementierten nativen Features an.

Mit den modernen Mobil-Betriebssystemen sind auch neue Modelle des Rechtemanagements etabliert worden. Der Nutzer muss zustimmen, dass eine App beispielsweise Zugriff auf
Das Exponent-Client-

Entwicklermenü (Bild 5)
seinen Standort erhält oder Push Notifications empfangen darf. Als Entwickler muss man dazu auf native APIs zugreifen. Exponent bietet dafür das plattformübergreifend implementierte Permissions-Modul an.

Mit Permissions.askAsync() kann man das System interaktiv nach einem bestimmten Recht fragen. Hat der Benutzer bereits zugestimmt, so wird nicht nachgefragt. Hat er noch nicht zugestimmt, wird der übliche Systemdialog angezeigt. Möchte man stattdessen einfach nur programmatisch - ohne den Benutzer danach zu fragen - ermitteln, ob ein bestimm-
ExponentRootComponent ' App '

ExNavigatorComponent(ExNavigationStack) ' ExNavConnect ’ Connect(ExNavigationStack) ' ExNavigationStack ' NavigationTransitioner ' ExNavigationBar * ExNavigationBarTitle ( Text ]* RCTText
Inspect Perf Network Touchables
1 margin 0
padding 0
0 o {16'315) 0  343 x 20.5 0 0
0

Exponent Client Inspektor (Bild 6)
import Exponent from 'exponent'; import React from 'react'; import {

StyleSheet, Text, View,

} from 'react-native'; class App extends React.Component { render() { return (

<View style={styles.container}>

<Text>Open up main.js to start working on your app!</Text>

</View>

);

}

}

const styles = StyleSheet.create({ container: { flex: 1,

backgroundColor: '#fff', alignltems: 'center', justifyContent: 'center',

},

});

Exponent.registerRootComponent(App);
async trackPosition() {

const { Location, Permissions } = Exponent; const { status } = await Permissions.askAsync(Permissions.LOCATION); if (status — 'granted') { this._locationWatcher = Location.watchPositionAsync({ enableHighAccuracy: true, timelnterval: 1000, // Millisekunden distancelnterval: 5 // Meter

},

({coords})=>{ this.setState({coords: coords})

});

}

},

componentWillMount () { trackPosition()

}

componentWillUnmount () { if (this._locationWatcher) { this._locationWatcher.remove()

}

}
^ Listing 1: main.js
^ Listing 2: Location.watchPositionAsync()
tes Recht genehmigt ist, dann ruft man statt-dessen die Funktion Permissions. getAsync() auf. Das Beispiel zeigt auch gleich ein weiteres durch Exponent angebotenes Modul: Location. Damit kann man den aktuellen Standort ermitteln (Location.getCurrentPosition-Async()). Möchte man kontinuierlich über die Änderungen des Standorts benachrichtigt werden, so kann man mit Location.watch-PositionAsync() einen Callback registrieren (Listing 2).

Die Beispiel-Methode trackPosition kann in einer React-Komponente wie oben gezeigt verwendet werden. Die einfachen Implementierungen von componentWillMount() und componentWillUnmount() zeigen, wie man das Tracking starten und stoppen kann, wenn die Komponente ins DOM hinzugefügt oder entfernt wird.

Mit dem Modul Contacts kann man plattformübergreifend auf die Kontakte-Datenbank zugreifen. Das Modul bietet dafür bislang die Funktion Contacts.getContactsAsync() an. Dieser Funktion kann man ein Array aus den gewünschten Datenfeldern übergeben (Listing 3).

Im Beispiel werden mit Contacts.getContactsAsync() die Kontakte abgerufen und in React Native in eine ListView.Da-taSource gesteckt. Unter iOS wird der Nutzer beim ersten Zugriff auf die Kontakte automatisch per Dialog um Erlaubnis gefragt. Die Exponent-Entwickler haben bereits angekündigt, dass man in kommenden Versionen auch die Profil-Thumbnails der Kontakte abrufen können soll.

Bilder und Kamerazugriff

Ein weiterer sehr gängiger Anwendungsfall für mobile Apps ist der Zugriff auf die gespeicherten Fotos oder die im Gerät verbauten Kameras. Exponent bietet dafür das ImagePickerModul an (Listing 4).
Dieser Beispielcode implementiert eine Komponente GalleryView, die Bilder in einem Gitter darstellt. Mit einer Tippfläche an der Unterseite kann man per Exponent.ImagePicker ein Foto aus der Fotobibliothek des Geräts auswählen. Dazu wird einfach ImagePicker.launchlmageLib-raryAsync() aufgerufen und mittels await auf ein Ergebnis gewartet.

Wenn der Nutzer den Dialog abgebrochen und kein Foto ausgewählt hat, dann ist die Eigenschaft cancelled des Ergebnisobjekts wahr. Ist das nicht der Fall, dann wird im Beispiel einfach der URI des ausgewählten Fotos in ein Array auf dem State der Gallery View-Komponente gesteckt. Diese wird somit automatisch neu gerendert und stellt das neue Bild dar (Bild 8). Möchte man stattdessen ein Foto mit der Kamera aufnehmen und hinzufügen, so muss der Aufruf ImagePicker.launchImageLibraryAsync({}) gegen Image-Picker.launchCameraAsync({}) ausgetauscht werden.

Accelerometer und Gyroskop

Fast jedes heutige Mobilgerät enthält einen Beschleunigungssensor, auch Accelerometer genannt. Exponent bietet dafür die Funktionen Exponent.Accelerometer.addListener(), Exponent.Accelerometer.removeAllListeners() und Expo-nent.Accelerometer.setUpdateInterval() an. Damit kann sich die Anwendung beim Beschleunigungssensor registrieren und erhält als Ereignisdaten Objekte mit den Attributen x, y und z, welche die jeweilige Beschleunigung in die entsprechende Richtung angeben.

Auf die gleiche Weise funktioniert das API für den Zugriff auf die Gyroskop-Daten: Exponent.Gyroscope.addListener(), Exponent.Gyroscope.removeAllListeners() und Exponent. ►
Der Exponent Client Performance Monitor (Bild 7)
Listing 3: Contacts.getContactsAsync()
import React from 'react'

import {ListView, Text, View, StyleSheet}

from 'react-native'

import {Contacts} from 'exponent'

const Row = ({contact}) => (

<View style={styles.row}>

<View style={styles.avatar}/>

<Text style={styles.text}>

{contact.name}

</Text>

</View>

)

export default class ContactsView extends React.Component { state = {

contacts: new ListView.DataSource({

rowHasChanged: (r1, r2) => rl.id !== r2.id }
})

}

async componentDidMount () {

let contacts = await Contacts.getContactsAsync([ Contacts.PHONE_NUMBERS
])

this.setState({

contacts: this.state.contacts.cloneWithRows (contacts)

})

}

render () {

return (<ListView dataSource={this.state.contacts} renderRow={this.renderRow}/>)

}

renderRow = (data)=>(<Row contact={data}/>)
Gyroscope.setUpdateInterval() können dazu genutzt werden. Das Ereignisobjekt enthält wieder die Eigenschaften x, y und z - die Werte entsprechen hierbei jedoch der Änderung des Rotationswinkels der jeweiligen Achse.

Ein sehr wichtiges Feature für Mobilanwendungen sind Notifications. Es gibt dabei einerseits die typischen Push Notifications, die entfernt durch einen Netzwerkdienst ausgelöst und durch das Gerät empfangen werden, und es gibt Local Notifications, die durch Apps oder das Betriebssystem selbst erzeugt werden. Mit Exponent.Notifications.addListener() kann die App auf lokale oder Push-Notifications lauschen.

Lokale Notifications verschickt man einfach aus der App heraus mit Exponent.presentLocalNotificationAsync(). Sollen sie zeitlich verzögert verschickt werden, dann nutzt man dafür Exponent.scheduleLocalNotificationAsync(). So kann die Notification zu einem bestimmten Zeitpunkt oder in festge-
legten Intervallen ausgelöst werden. Mit Exponent.cancel-ScheduledNotificationAsync() kann eine geplante Notification abgebrochen werden. Ist eine lokale Notification einmal präsentiert, dann kann man sie mit der Funktion Exponent. dismissNotificationAsync() programmatisch wieder entfernen. Mit Exponent.dismissAllNotificationsAsync() kann man alle durch die App präsentierten lokalen Notifications auf einmal entfernen.

Das Versenden von Push Notifications ist dagegen nicht so einfach. Denn zusätzlich zur App benötigt man dafür auch einen Dienst im Internet. Exponent selbst stellt einen kostenlosen Dienst bereit, der die Auslieferung der Push Notifications an die Android- oder iOS-Geräte übernimmt. Dazu können die Geräte in der App einen sogenannten Push-Token abrufen, der dann zum Beispiel an einen eigenen Server weitergereicht werden kann. Dort kann er beispielsweise mit dem
Listing 4: GalleryView.js
import React from 'react' <Image key={img}
import { source={{uri:img}}
View, style={{width:width/3, height:width/3}}
Text, />))}
Image, </View>
Dimensions, )
TouchableHighlight, }
StyleSheet const Button = ({onPress, label}) => (
} from 'react-native' <TouchableHighlight onPress={()=>{onPress()}}>
import {ImagePicker} from 'exponent' <Text style={styles.button}>{label}</Text>
const styles = StyleSheet.create({ </TouchableHighlight>
gallery: { )
alignltems: 'stretch', export default class GalleryView extends
justifyContent: 'center', React.Component {
flex: 1, state = {
paddingTop: 20, images: []
backgroundColor: '#555' }
}, render() {
grid: { return (
flex: 1, <View style={styles.gallery}>
flexDirection: 'row', <ImageGrid images={this.state.images}/>
flexWrap: 'wrap' <Button label="Add Image"
}, onPress={this.addImage}/>
button: { </View>
padding: 12, )
fontSize: 16, }
textAlign: 'center', addImage = async() => {
lineHeight: 53, let result = await
backgroundColor: '#ddd' ImagePicker.launchImageLibraryAsync({})
} if (!result.cancelled) {
}) this.setState({
const ImageGrid = ({images}) => { images: this.state.images.concat([result.uri])
const {width} = Dimensions.get('window') })
return ( }
<View style={styles.grid}> }
{images.map((img) => ( }
Nutzer der App verknüpft werden. Möchte man eine Push Notification absenden, dann sendet der eigene Server einen POST-HTTP-Request mit dem Push-Token und der PushNachricht an den Exponent-Server mit dem Domain-Namen https://exp.host/. Eine Implementierung für Node.js wurde in dem Node-Modul exponent-server-sdk realisiert.

Bei dem aus dem Webbrowser bekannten React gehört auch SVG zu den möglichen Elementen. React Native kennt in der Basisfassung kein SVG, es gibt aber die native Erweiterung react-native-svg. Diese implementiert SVG-Komponenten für iOS und Android. Exponent liefert diese Erweiterung bereits mit, sodass man SVG ohne weiteren Aufwand verwenden kann (Listing 5).

Die Komponente SVGClock realisiert eine Uhr, deren Zeiger auf die per props übergebenen Werte für Stunde, Minute und Sekunde eingestellt sind. Die Zeiger werden alle in derselben Richtung (Position 0/12 Uhr) gezeichnet und dann mit der SVG-prop rotate auf die passende Ausrichtung rotiert. Bis auf das Modul react-native-svg ist der Code auch im Web einsetzbar. In React Native kann die Uhr wie in Listing 6 gezeigt eingesetzt werden. Beim Einhängen der CurrentTime-Clock-Komponente wird ein Intervall gestartet, das jede Se-
kunde den State der Komponente auf die aktuelle Uhrzeit setzt (updateTime(). Die Komponente rendert die SVGClock dann anhand dieses States.

GLView und Video

Exponent ergänzt React Native auch um Erweiterungen für OpenGL und Video. Die Komponente GLView realisiert ein zu Web-GL ähnliches API für React Native unter iOS und Android. Mit Exponent.Components. Video wiederum kann man ein Video innerhalb anderer React-Native-UI-Elemente darstellen.

Komplexe Single-Page-Webanwendungen verwenden üblicherweise einen Router, um die Anwendung in verschiedene Bereiche aufzuteilen, zwischen denen der Nutzer navigieren kann. Auch bei nativen Apps kennt man ähnliche Konzepte eines NavigationsControllers - beispielsweise die Storyboards und Segues bei iOS, mit denen einzelne Views und Übergänge dazwischen modelliert werden können.

React Native bietet mit Navigator und NavigatorlOS bereits zwei Komponenten an, um die Navigation innerhalb der App zu implementieren. Navigator ist dabei eine reine Java-Script-Implementierung, wird aber seit einiger Zeit nicht ►
Add Image
GalleryView mit dem ImagePicker erstellen (Bild 8)
Listing 5: SVG-Beispiel
import React from 'react' cx={cx}
import Svg, {Circle, Line} from 'react-native-svg' cy={cy}
const Handle = ({cx, cy, size, weight, rotate, color}) size={1/2}
=> ( weight="8"
<Line rotate={hour/12*360} />
x1={cx} <Handle
y1={cy} cx={cx}
x2={cx} cy={cy}
y2={cy-cy*size} size={5/6}
stroke={color||'red'} weight="5"
strokeWidth={weight} rotate={minute/60*360} />
rotate={rotate}//{hour/12*360} <Handle
origin={'${cx}, ${cy}'} /> ) cx={cx}
const SVGClock = ({hour, minute, second, size}) => { cy={cy}
let cx = size / 2 + 10 size={5/6}
let cy = size / 2 weight="2"
return ( color="blue"
<Svg height={size} width={size}> rotate={second/60*360} />
<Circle <Circle
cx={cx} cx={size/2+10}
cy={cy} cy={size/2}
r={(size/2)-20} r={size/60}
stroke="black" stroke="black"
strokeWidth="2" strokeWidth="2" />
fill="transparent" /> </Svg>)
<Handle }
mehr weiterentwickelt, daher ist von der Nutzung abzuraten. NavigatorlOS basiert auf dem unter iOS verfügbaren UINa-vigationController. Dieser funktioniert deshalb auch nur unter iOS. Ein sehr neuer Ansatz ist das Modul NavigatorExperimental, das in der offiziellen React-Native-Dokumentation bereits empfohlen wird - es gilt als nicht mehr so experimentell, wie der Name zuerst vorgibt, ist jedoch eher Low-Level.

Das Exponent-Team arbeitet zum Teil sehr eng mit den React-Native-Entwicklern zusammen und hat basierend auf NavigatorExperimental das Modul ExNavigation entwickelt. Diese stellt eine auf Routen basierte High-Level-Navigations-Library für React Native dar, die sowohl mit iOS als auch mit Android funktioniert (Listing 7).

Mit createRouter erzeugt man eine globale Router-Instanz. Dazu übergibt man eine Funktion, die ein Objekt zurückgibt, das die Namen der Routen auf React-Komponenten abbildet. Routen sind bei ExNavigation einfach nur React-Komponen-ten. Als Top-Level-Komponente der App wird mit dem NavigationProvider der Router registriert. Die im Beispiel benutz-

Listing 6: SVGClock

import {View, Text, Dimensions, SVG}

from 'react-native'

function getTime() {

let date = new Date()

return {

hour: date.getHours(), minute: date.getMinutes(), second: date.getSeconds()

}

}

class CurrentTimeClock extends React.Component {

componentWillMount() { this.setState(getTime)

this._timer = setInterval(this.updateTime, 1000)

}

componentWillUnmount() { clearInterval(this._timer)

}

render() {

let {width, height} = Dimensions.get("window") let maxSize = Math.min(width, height) - 20 return (

<SVGClock size={maxSize} hour={this.state.hour} minute={this.state.minute} second={this.state.second}

/>

)

}

updateTime = () => { this.setState(getTime)

}

}
te Komponente StackNavigation implementiert eine Navigation, bei der mit pop() und push() Routen auf einen Navigations-Stack gelegt und wieder entfernt werden können. Mit der prop initialRoute kann man die Route festlegen, die beim Start der App gewählt werden soll. Als Routen benutzte Komponenten benötigen eine Eigenschaft route an ihrem Prototyp-Objekt. Dies kann man mit der ES2015-Klassensyntax am einfachsten per static-Schlüsselwort erreichen (Listing 8).

Diese Komponente HomeScreen rendert einfach nur einen Text und danach eine Tippfläche, mit der man auf einen Op-tionsScreen wechseln können soll. Die statische Eigenschaft route enthält Optionen, die von ExNavigation ausgewertet werden. In diesem Fall soll der Titel der Navigationsleiste den Text Home enthalten. Tippt man auf Go to Options, dann wird die in Listing 9 gezeigte Komponente mit this.props.navigator. push() auf den Navigations-Stack gelegt.

Auch hier gibt es eine Tippfläche, mit der man diesmal zurücknavigieren kann. Dazu wird einfach mit this.props.navi-gator.pop() die aktuelle Komponente vom Navigations-Stack entfernt. ExNavigator kümmert sich dabei automatisch um eine passende Animation für die Übergänge zwischen den Screens. Mit der statischen Eigenschaft route kann man nicht nur den Titel festlegen, sondern auch, welche Komponenten auf der linken oder rechten Seite der Navigationsleiste dargestellt werden sollen. Der OptionsScreen muss sich dann aber noch beim back-Ereignis des Event-Emitters registrieren.

Bild 9 zeigt die beiden Screens des Beispiels und die Übergänge dazwischen. Leider verkompliziert diese Verschaltung

Listing 7: ExNavigation

import Exponent from 'exponent' import React from 'react' import {

createRouter,

NavigationProvider,

StackNavigation } from '@exponent/ex-navigation' import HomeScreen from './HomeScreen' import OptionsScreen from './OptionsScreen' const Router = createRouter(() => ({ home: () => HomeScreen, options: () => OptionsScreen }))

class App extends React.Component { render() { return (

<NavigationProvider router={Router}> <StackNavigation initialRoute={Router. getRoute('home')} />

</NavigationProvider>

)

}

}

Exponent.registerRootComponent(App)
Home  HomeScreen! «- ■ Back Options  Options

Navigations-Controller für Android und iOS (Bild 9)

die Komponente und erfordert, dass man die Ereignis-Hand-ler in einem componentWillUnmount() wieder beseitigt. Hier kann man hoffen, dass die bereits angekündigte Weiterentwicklung derartige Fälle einfacher macht.

ExNavigation hat noch eine große Zahl weiterer Features. Man kann damit auch sehr einfach eine Tab-Navigation oder Drawer-Navigation (ein Menü, das seitlich hereinfährt) verwirklichen. Nicht nur in Zusammenhang mit Exponent gilt ExNavigation deshalb momentan als eine der besten Lösungen für UI-Navigation in React Native.

Die Schaltfläche mit der Aufschrift Publish in der XDE verrät es bereits: Man kann damit erstellte Apps auch veröffentlichen. Nun wäre es natürlich ein echtes Wunder, wenn man durch einen Druck auf eine solche Schaltfläche die App in die
jeweiligen App Stores veröffentlichen könnte. Leider reicht dieses Feature nicht so weit. Der Unterschied ist, dass nach einem Publish das Bundle auch unabhängig von den XDE-Prozessen der Entwicklermaschine über Amazon CloudFront bereitgestellt wird. Wie im Entwicklungsmodus erhält man einen URI, den man mit jedem Exponent-Client öffnen kann.

Fazit

Exponent macht das Entwickeln nativer Apps für iOS und Android fast so einfach wie Webentwicklung. Der ExponentClient entspricht dem Webbrowser, und mit der XDE werden im Editor geänderte Quellen live auf den neuesten Stand gebracht. Neben den hier vorgestellten Features bietet Exponent noch eine ganze Reihe weiterer: Man kann beispielsweise mit der Kamera des Geräts Barcodes scannen lassen und erhält als Ergebnis die in den Barcodes codierten Daten. ■
Jochen H. Schmidt

ist als Autor, Berater und Software-Entwickler tätig. Schwerpunkte seiner Aktivitäten sind Webentwicklung und Webtechnologien. Er ist Verfasser von bekannten Fachbüchern zum Thema Common Lisp. redaktion@webundmobile.de
^ Listing 8: static-Schlüsselwort

import React from 'react'

import {View, Text, TouchableHighlight}

from 'react-native'

export default class HomeScreen extends React.Component { static route = {

navigationBar: { title: 'Home' }

}

render() { return (

<View style={{

alignltems: 'center', justifyContent: 'center', flex: 1 }}> <Text>HomeScreen!</Text> <TouchableHighlight onPress= {this.gotoOptions}>

<Text>Go to Options!</Text> </TouchableHighlight>

</View>

)

}

gotoOptions = () => {

this.props.navigator.push('options')

}

}
Listing 9: Komponente HomeScreen

import React from 'react'

import {View, Text, TouchableHighlight}

from 'react-native'

export default class OptionsScreen extends React. Component {

static route = { navigationBar: { title: 'Options' }

}

render() { return (

<View style={{

alignltems: 'center', justifyContent: 'center', flex: 1 }}>

<Text>Options</Text>

<TouchableHighlight onPress={this.gotoBack}> <Text>Go Back</Text>

</TouchableHighlight>

</View>

)

}

gotoBack = () => { this.props.navigator.pop()

}

}




You may also like

Keine Kommentare:

Blog-Archiv

Powered by Blogger.