My minor
Picture for Realise article

Realise

Fitphone

Het is de bedoeling dat de applicatie zowel op iOS als Android werkt. Daarom hebben we gekozen om een hybrid framework te gebruiken om onze applicatie te ontwikkelen, aangezien we niet de mankracht hebben om een native iOS en Android applicatie te ontwikkelen. Als hybrid framework kiezen we tussen React Native en Flutter omdat beide frameworks redelijk populair zijn binnen ons team. Uiteindelijk hebben we gekozen voor React Native omdat we verwachten dat het een kleinere learning curve heeft dan Flutter. Ook zijn de meeste mensen al bekend met React waardoor de development sneller kan verlopen.

Coen en ik hebben het voortouw genomen om de applicatie op te zetten en bruikbaar te maken voor de groep. Coen heeft een eerste opzet gemaakt van de architectuur van de app. Op advies van mij heb ik samen met Coen een modulair architectuur opgezet die wat beter bij het project past. Voor de styling hebben Coen en ik Styled Components toegevoegd, dit maakt het stylen van componenten in React Native duidelijker voor nieuwe gebruikers en laat je normale CSS gebruiken in plaats van de variant die React Native gebruikt. We hebben ook een styling bestand toegevoegd om ervoor te zorgen dat er een standaard thema gebruikt kan worden in de applicatie, in dit bestand staan dingen zoals; kleuren, spacing en lettergrootte. Voor het ontwikkelen van componenten heb ik Storybook toegevoegd, een library voor het testen en ontwikkelen van componenten in isolatie. De groep was hier nog niet bekend mee dus heb ik een presentatie gegeven met uitleg en demo. Omdat ik nog veel vragen kreeg heb ik een Gitlab Wiki geschreven over Storybooks.

Frontend

In de eerste weken is er gewerkt aan het ontwikkelen van de components die op meerdere plekken terug komen in de app. Coen en ik zijn ook voornamelijk bezig met het nakijken van merge request aangezien wij hier de meeste ervaring in hebben. Vervolgens moest de frontend met de backend gaan communiceren. Voor het fetchen en globaal beschikbaar maken van de data gebruiken wij Redux. Ik heb geen ervaring met Redux dus ik heb veel moeten vragen aan Coen om het werkend te krijgen. Om de rest met redux te laten werken heb ik een Gitlab Wiki geschreven met uitleg. Deze is uiteindelijk aangevuld door Coen omdat het nog niet helemaal duidelijk was voor de developers.

De volgende uitdaging was het integreren van een Android Native Module. Sidney heeft een module ontwikkeld die aan te roepen is binnen de React Native applicatie. Deze data wordt ongefilterd doorgegeven. Ik heb samen met Niels een React hook geschreven die de data omzet in bruikbare data.

Backend

De backend is de eerste 15 weken niet beveiligd met Json Web Tokens (JWT). Dit had al in het begin moeten gebeuren gezien de tijdsduur van het project. Niels en ik hebben de taak op ons genomen om dit te implementeren in de backend. Nick had al een werkende opzet gemaakt maar deze moest nog aangepast worden. We liepen tegen het probleem aan dat tokens maar één uur geldig zijn. Dit is in de backend niet te verversen, in de frontend wel. We kwamen tot de conclusie dat de authenticatie met Firebase, het in- en uitloggen, niet in de backend thuis hoort. Voor nu werkt het maar de gebruiker wordt per uur uitgelogd van de app wat niet wenselijk is.

Hybrid

In de tweede week van de minor zijn Coen en ik begonnen aan het realiseren van de walkie talkie app. Wij zijn eerst na gaan denken over hoe audio real-time verstuurd kan worden. Daardoor kwamen wij uit bij websockets. Vervolgens zijn wij ons gaan richten op het maken van een simpele websocket server die bytes over een websocket connection verstuurd. Verder moesten wij ook gaan onderzoeken hoe je audio kunt opnemen via React Native. Coen is gelijk aan de slag gegaan met React Native en ik met de websocket server.

Ik heb er voor gekozen om de websocket server te ontwikkelen met node.js in combinatie met typescript. Deze keuze is gebaseerd op mijn interesse om iets te ontwikkelen met node, maar ook omdat een websocket server snel op te zetten is met de Socket.IO library. Daarom leek het mij de beste keuze voor een snel prototype neer te zetten.

Websockets

In week 3 ben ik begonnen met het opzetten van de server. Ik heb al eens met websockets gewerkt en ik heb goede ervaringen gehad met Socket.IO, een library met een out-of-the-box implementatie van websockets. Ik heb de documentatie van Socket.IO gevolgd voor het opzetten van een simpele server. Na het installeren van Socket.IO was het opzetten van een server zo simpel als dit

const app = express(); const httpServer = createServer(app); const io = new Server(httpServer, { cors: { origin: ['https://admin.socket.io'], credentials: true } });

In week 4 zijn wij gaan proberen om de audio over de websockets te sturen. Deze audio bestaat uit chunks van bytes die over de websocket gestuurd worden. Wij kwamen er snel genoeg achter dat het omzetten van die bytes naar audio niet de bedoeling was voor onze use case. We hebben geprobeerd om de bytes weer om te zetten naar een audio file, maar hierdoor ontstond er een flinke delay in de audio. Dit kwam omdat wij constant bytes moesten wegschrijven naar een audio file, wat vrij intensief werk is door de frequentie waarin de bytes binnen komen.

WebRTC

Vervolgens ben ik gaan onderzoeken wat een betere optie is voor het streamen van audio. Ik kwam al snel uit bij WebRTC. WebRTC ondersteunt audio en video streaming en is beschikbaar in alle moderne browsers maar ook op native platforms zoals Android en iOS. Ik ben gaan zoeken of er ook een react-native module voor beschikbaar is en ben uitgekomen bij react-native-webrtc. Ik heb de library geimplementeerd in de frontend en heb de websocket server aangepast zodat er requests van de ene client naar de andere client gestuurd kunnen worden. Het is mij niet gelukt om dit werkend te krijgen en omdat we al in week 5 zaten hebben Coen en ik besloten om een third-party service te gebruiken.

Ik heb gewerkt aan het integreren van Agora, een third-party tool voor audio en video streaming. Een van de must-haves van de walkie-talkie is het kunnen selecteren en switchen van kanalen. De documentatie van Agora is zeer uitgebreid en biedt ook voorbeelden om een begin te kunnen maken aan de app. Binnen 1 dag hebben wij een werkend prototype weten te bouwen. Agora kan niet aangeven wanneer een gebruiker aan het praten is, dit heb ik dus zelf ontwikkeld in de websocket server.

Android

We zijn allebei nieuw met Android ontwikkeling, dus we zijn begonnen met het volgen van de "Build your first app" tutorial uit de documentatie. Coen is aan de slag gegaan met het uitproberen van de verschillende soorten sensoren die wij kunnen gebruiken in dit project.

Ik ben meer gaan kijken naar de pedometer voor het tellen van stappen. De stappenteller is een nuttige sensor om een stap van een gebruiker te tellen en is relatief eenvoudig te implementeren. Na het volgen van de tutorial had ik een werkende stappenteller. Het enige nadeel is dat het meer bedoelt is voor grotere afstanden. Bij kleinere afstanden update die pas heel laat en moet je veel stappen lopen voordat er iets geregistreerd wordt. Dit is helaas niet helemaal wat we gehoopt hadden.

We hebben besloten om een andere soort ‘progressie’ toe te voegen aan de game. Dus in plaats van dat de speler stappen moet lopen om over de ‘finish line’ te komen, gaan we gebruik maken van device shakes. De gebruiker moet dan heel hard schudden met de mobiel wat dan je score ophoogt. Dit maakt het spel ook wat moeilijker omdat de mobiel niet mag bewegen tijdens red light. Voor de ontwikkeling hebben we eerst de accelerometer gebruikt met een hardcoded threshold voor het detecteren van een shake. We kwamen erachter dat dit heel gevoelig was dus heb ik online gekeken of er packages waren die mobile shakes kunnen detecteren. Vervolgens hebben we het iets netter gemaakt met de package i.p.v. onze make shift oplossing.

iOS

Deze week kregen we een workshop die over Realitykit. Binnen ons concept hebben wij Reality Kit nodig voor het herkennen van album covers. Voor onze prototype hebben wij ons gericht op een select groepje covers, aangezien we die momenteel zelf moeten aanleveren.

Het herkennen van de album cover was een kwestie van een image toevoegen aan de AR resources, maar het weergeven van de gegevens in de AR view bleek een uitdaging te zijn. We zijn begonnen met het weergeven van een eenvoudig vierkant over de album cover en het herkennen van een touch gesture. Het weergeven van de vierkant was eenvoudig, maar het registreren van de touch niet. Wij hebben Ruud om hulp gevraagd en hij heeft ons code aangeleverd waarmee het werkt.

Vervolgens ben ik aan de slag gegaan met het weergeven van de naam van het album wanneer je op een cover klikt. De vervolgstap was het tonen van de album informatie. Eerst hadden wij voor ogen om dit ook als AR view te doen. Al snel bleek dat het lastig was om de tekst weer te geven dus hebben wij gekozen voor een on-screen pop-up modal die de data weergeeft. Coen is aan de slag gegaan met het maken van een UI start-up screen en ondertussen heb ik gewerkt aan het tonen van de modal wanneer de gebruiker op een album cover klikt.

We werkten ook aan het toevoegen van state management om de "recente albums" info te updaten en het juiste album te tonen op de pop-up modal. Maar hier kwamen wij een bug tegen. Elke keer dat de state werd bijgewerkt in de ARView crashte de hele view. Coen heeft in de kerstvakantie de bug opgelost en vervolgens ook state voor 'recent albums' toegevoegd. Ook heeft hij een Last.FM integratie toegevoegd waardoor de album data real-time wordt gefetched.

Freaky friday

Als front-end framework heb ik gekozen voor React omdat ik hier veel ervaring mee heb en snel een prototype kan opzetten. Ik heb voor dit project gekozen om het met vanilla javascript te schrijven ipv typescript omdat typescript geen toegevoegde waarde heeft binnen een project waar weinig met types gewerkt wordt. Ik merk vaak dat er meer boilerplate code geschreven moet worden om alles typed te krijgen. In het geval van een groot project heeft typescript veel toegevoegde waarde maar voor dit project niet. Ook maak ik geen gebruik van een CSS component library aangezien het project minimale opmaak bevat. Ik heb daarom gekozen voor aparte SCSS files.

Ik ben als eerste begonnen aan het maken van de navbar voor het tonen van de huidige tijd. Deze wordt per minuut geupdate in een interval. Om de actuele data op te halen over de treinen heb ik een developer account aangemaakt op het NS API platform. Deze API biedt een /departure endpoint aan:

Screenshot 2022-01-15 114419.png

Hier kun je aan mee geven van welk station je de departures wilt zien. Ik krijg een primary key van het platform waarmee ik authorized requests mag sturen. Deze key wordt in de volgende header meegestuurd:

Screenshot 2022-01-15 114431.png

En vraag het via de code op d.m.v. een simpele fetch

const getRides = () => { fetch( 'https://gateway.apiportal.ns.nl/reisinformatie-api/api/v2/departures?maxJourneys=25&lang=nl&station=wt', { headers: { 'Ocp-Apim-Subscription-Key': 'VERY_SECRET_KEY' } } ).then((res) => res.json().then((data) => { setData(data.payload.departures) }) ) }

Dit geeft de volgende response

actualDateTime: "2022-01-15T11:50:00+0100"
actualTimeZoneOffset: 60
cancelled: false
departureStatus: "ON_STATION"
direction: "Tilburg Universiteit"
messages: []
name: "NS  6444"
plannedDateTime: "2022-01-15T11:50:00+0100"
plannedTimeZoneOffset: 60
plannedTrack: "3a"
product: {number: '6444', categoryCode: 'SPR', shortCategoryName: 'NS Sprinter', longCategoryName: 'Sprinter', operatorCode: 'NS', …}
routeStations: (4) [{…}, {…}, {…}, {…}]
trainCategory: "SPR"

Nu ik weet hoe de data eruit ziet heb ik een component gemaakt die de data weer geeft. Met de actualDateTime en de plannedDateTime kan ik uitrekenen of een trein vertraging heeft en hoeveel vertraging. Hetzelfde geldt voor een wisseling van het spoornummer. Via de cancelled boolean kan ik laten zien of een trein helemaal uitvalt. De routeStations array bevat de tussenstations. Hier itereer ik over om alle tussenstations te tonen onder de naam van het eindstation. Alles bij elkaar geeft de volgende opmaak

Screenshot 2022-01-15 114214.png

Deployment

Voor de deployment heb ik een shell script geschreven die de volgende handelingen uitvoert:

  • Zippen van de React applicatie (build folder)
  • Connectie maken met de raspberry die zich op hetzelfde netwerk bevindt via SSH
  • Unzippen van de React applicatie in de web server directory van de raspberry en rebooten

Het heeft heel wat google searches geduurd voordat ik een werkende versie had maar ik ben er zeer blij mee dat het nu werkt. Op deze manier kan ik vanuit mijn computer aanpassingen maken en gelijk deployen op de raspberry.

Picture of Roy Appeldoorn

By Roy Appeldoorn