App mòbil amb React Native: arbres singulars de Barcelona

De wikijoan
Salta a la navegació Salta a la cerca

Introducció

Arbres barcelona.jpg

Es tracta de fer una app Android, com a procés d'aprenentatge de React Native per fer aplicacions natives. La idea és acabar tot el cicle del projecte fins a publicar l'app al Google Play Store.

L'app consta de dues pantalles:

  • la primera és un llistat dels arbres, on s'indica la distància dels arbres fins la nostra posició actual. Quan cliquem sobre un arbre es posa de color verd, que significa que l'hem visitat.
  • La segona pantalla ha de ser el mapa fet amb Openlayers/Leaflet. Però aquesta part de posar mapes amb Reactnative encara no ho tinc controlat. (mirar [1])

L'aplicació és una gamificació dels Arbres Singulars de Barcelona. Es tracta d'anar col·leccionant aquests arbres. Els arbres descoberts surten en verd, i t'indica el % dels arbres descoberts. Per tant, és un joc per passejar amb família mentre es van coneixent els arbres singulars i es coneixen nous barris de la ciutat. I si es vol anar ràpid o corrents, col·leccionar aquests arbres també és un alicient a la pràctica esportiva.

Github

MapView i react-native-maps (Google Maps)

Hi ha projectes per adaptrar el mòdul react-native-maps a OSM, però això ho faré més avall. De moment em conformo en fer funcionar el projecte amb Google Maps.

Documentació de expo, que és el sistema que estic fent servir per desenvolupar aplicacions reactivenative.

$ expo install react-native-maps
$ npm install react-native-maps --save-exact

Configuration

Apartat: Deploying to a standalone app on Android

Apartat: If you already have not configured Google Sign In

1. Android package name: el que he posat en el app.json. En aquest cas: "package": "org.joanillo.ArbresBarcelona",

2. Open your browser to the Google API Manager and create a project.

Actualizar la cuenta

Estás a un paso de desbloquear Google Cloud Platform íntegramente.

No se te aplicará ningún cargo hasta que tus créditos gratuitos se agoten o caduquen (lo que suceda primero).Más información

Projecte: ArbresBarcelona

3. Once it's created, go to the project and enable the Google Maps SDK for Android

API_KEY: ************************

4. Go back to https://console.developers.google.com/apis/credentials and click Create Credentials, then API Key.

5. In the modal that popped up, click RESTRICT KEY.

6. Choose the Android apps radio button under Key restriction.

7. Click the + Add package name and fingerprint button.

De moment aquesta informació no la necessito:

¿Cómo restrinjo mi clave de API a aplicaciones de Android concretas?
Si quieres restringir una clave de API a determinadas aplicaciones de Android, proporciona una huella digital de certificado de depuración o de publicación.

Huella digital de certificado de depuración
Linux o macOS:

$ keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android

Huella digital de certificado de publicación
$ keytool -list -v -keystore your_keystore_name -alias your_alias_name

Sustituye your_keystore_name por la ruta completa y el nombre del almacén de claves, incluida la extensión .keystore. Sustituye your_alias_name por el alias que asignaste al certificado cuando lo creaste.

Restringir el uso a tus aplicaciones de Android Añade el nombre del paquete y la huella digital del certificado de firma SHA‑1 para restringir el uso a tus aplicaciones para Android


8. Add your android.package from app.json (eg: ca.brentvatne.growlerprowler) to the Package name field.

En el nostre cas: org.joanillo.ArbresBarcelona

Huella digital del certificado SHA-1: he de posar la que em diu el punt 9

9. run expo fetch:android:hashes

$ expo fetch:android:hashes

Configuring credentials for joanillo_reactnative in project geo-example
Google Certificate Fingerprint:     ***************
Google Certificate Hash (SHA-1):    ***************
Google Certificate Hash (SHA-256):  ***************
Facebook Key Hash:                  ***************

Note: if you are using Google Play signing (no és el meu cas), this app will be signed with a different key after publishing to the store, and you'll need to use the hashes displayed in the Google Play console.

10. Copy Google Certificate Fingerprint from the output from step 9 and insert it in the "SHA-1 certificate fingerprint" field.

11. Copy the API key (the first text input on the page) into app.json under the android.config.googleMaps.apiKey field. See an example diff.

"android": {
    "package": "org.joanillo.ArbresBarcelona",
    "config": {
      "googleMaps": {
        "apiKey": "**************************"
      }
    }
  }

12. Press Save and then rebuild the app like in step 1.

Exemples

i ara ja puc provar els exemples de:

Concretament puc anar directament a la carpeta example/examples/, i provar els diferents exemples. La única cosa que he de fer és copiar el codi de l'exemple en el meu App.js del projecte. He provat els següents exemples:

Projecte react-native-open-street-map (sense Google Maps). Primavera 2020

És un fork de react-native-maps amb la idea d'utilitzar els mapes de OSM amb el component MapView.

al fitxer package.json posem a dependències:

"react-native-open-street-map": "https://github.com/enieber/react-native-open-street-map.git"

de manera que quan fem

$ npm install

s'instal·la aquesta dependència. Un cop instal·lat podem veure dins de node_modules/ la carpeta react-native-open-street-map, que significa que el mòdul ja està instal·lat.

Prove el parell de codis d'exemple que es donen. Ara bé, dóna problemes:

requireNativeComponent: OpenAirMap was not found in the UIManager

$ expo --version
$ expo-cli --version
3.20.1

i informant-nos una mica veiem que aquesta solució seria vàlida per al Android Studio i la manera com té de fer el build de l'aplicació (gradle), però jo estic utilitzant Expo que és una manera no-nativa, és una porta fàcil d'entrada al desenvolupament react-native, però té les seves limitacions.

Having React Native Maps is a huge plus in Expo. It would be great to have Open Street Maps integrated too in order to have a choice to Google Maps. Veure el que es comenta en el següents fils:

De moment (abril 2020) no hi ha suport de Expo als react-native-maps'. La informació que he trobat de react-native-open-street-map no és per expo, sinó per l'Android Studio directament (gradle).

Però això hauria de canviar en el futur, doncs hi ha interès per no dependre de Google Maps. Però la plataforma Expo s'hi han de posar en el tema. Com es comenta en el fil, aquí hi ha una solució que exploro en el següent apartat:

Mapes sense Google Maps i amb Expo (solució final). Primavera 2020

És una solució feta a mida que no depèn de MapView.

$ git clone https://github.com/wende60/hiker.git
$ cd hiker
$ npm install
$ npm start

Right now I am using the tiles from:

An API key is needed and has to be added in settings/mapConfig.js. For small tile usage you can get it for free.

Servidor de tiles:

Ja m'havia registrat anteriorment, i la API Key era: f695b42************

Ara bé, el projecte d'entrada no se m'instal·la perquè, encara que és recent (octubre 2019), les versions de expo-cli del desenvolupador devien estar una mica desfasades.

Quan compilo el projecte amb expo:

32.0.0 is not a valid SDK version

$ expo --version
$ expo-cli --version
3.20.1

$ expo diagnostics

  Expo CLI 3.20.1 environment info:
    System:
      OS: Linux 4.15 Linux Mint 19.2 (Tina)
      Shell: 4.4.20 - /bin/bash
    Binaries:
      Node: 12.16.1 - ~/.nvm/versions/node/v12.16.1/bin/node
      npm: 6.13.4 - ~/.nvm/versions/node/v12.16.1/bin/npm
    npmPackages:
      expo: ^32.0.0 => 32.0.6 
      react: 16.5.0 => 16.5.0 
      react-native: https://github.com/expo/react-native/archive/sdk-32.0.0.tar.gz => 0.57.1 
    npmGlobalPackages:
      expo-cli: 3.20.1

Torno a reinstal·lar el expo-cli---

npm -g uninstall expo-cli
npm install --global expo-cli

però s'ha quedat tot amb les mateixes versions.

En el package.json tinc:
  "dependencies": {
    "expo": "^32.0.0",

  "dependencies": {
    "expo": "^32.0.0",
    "react": "16.5.0",
    "react-native": "https://github.com/expo/react-native/archive/sdk-32.0.0.tar.gz"

"react-native": "https://github.com/expo/react-native/archive/sdk-37.0.1.tar.gz"
que és la versió que tinc en d'altres projectes

i en el app.json poso:
        "sdkVersion": "32.0.0",

torno a fer 
npm install

Your project is in SDK version >= 33.0.0, but the expo package version seems to be older.

i tot i aquest missatge a la consola, avança en la instal·lació, però encara no funciona. Ara diu:
Unable to resolve "react-native/Libraries/Components/View/ViewStylePropTypes" from "node_modules/react-native-reanimated/src/createAnimatedComponent.js"
Failed building JavaScript bundle.

"react": "~16.9.0",

Solució: en comptes de fer un git clone, començar un projecte des de zero, i copiar els fitxers:

$ expo init hiker2

és un codi desactualitzat i he hagut de fer uns quants canvis: rutes relatives dels fitxers i maneres de fer imports als mòduls que són d'una versió més antiga.

I finalment ja funciona. Almenys ja tinc un codi que funciona amb react-native i expo, i que no depèn de Google Maps.

Depèn dels tiles de thunderforest.com (no confondre amb OSM, OSM de fet no és un servidor de Tiles) i té la restricció de 150000 peticions/mes.

És un bon punt de partida per començar a pintar els meus arbres sobre un mapa.

Desenvolupament

Solucionat el tema de com programar un mapa que no depengui de Google Maps, en el projecte (que de fet és un procés d'aprenentatge perquè no tenia experiència amb React) he hagut de resoldre aquestes qüestions.

1. componentDidMount() i render(). S'ha de vigilar, i és motiu d'errors. El primer render() es fa abans del componentDidMount(), que és on faig el setState del contingut del fitxer JSON. componentWillMount() funciona, però està deprecated, no s'ha de fer servir. S'ha de tenir molt clar el cicle de vida de l'aplicació, i què es pot fer o no es pot fer en el render(). Concretament, dins el render() no podem canviar l'estat de cap variable.

2. event onStartShouldSetResponder: Tinc el problema de clicar sobre la icona de l'arbre per canviar de vermell a verd. Però no funciona el onPress sobre el component Image (ni sobre View). El onPress està pensat per als Touchables. Però si encapsulo la Image sobre un TouchableOpacity tampoc funciona (a més gent li passa, és una mica tricky). Per sort existeix l'event onStartShouldSetResponder que resol bé el meu problema.

3. Persistència de les dades: AsyncStorage: el component AsyncStorage em permet guardar els settings d'una aplicació. Concretament a mi m'interessa guardar un array dels arbres que ja s'han visitat (pintats de color verd), i aquesta informació s'ha de guardar entre ús i ús de l'aplicació, i també s'ha de carregar al principi. Com es comenta a [2], per llegir/escriure fitxers hi ha el mòdul react-native-fs, però si el que es vol es guardar els settings d'una aplicació amb persistència, amb AsyncStorage n'hi ha prou.

4. How to update a component’s prop:

Tinc el component Grid, on es visualitza el % d'arbres verds, i per això he creat la variable d'estat discoveredPercent. En el Grid es visualitza la capa de Tree, i per tant li passo com a prop aquest valor. Ara bé, és en el component Tree on descubreixo nous arbres, i aquí és on es fa la modificació del valor del percentatge. Per tant, hauria de fer una modificació del props.discoveredPercent i que se n'enteri el Grid, però això a ReactJS no es pot fer.

Whether you declare a component as a function or a class, it must never modify its own props.
React is pretty flexible but it has a single strict rule:
All React components must act like pure functions with respect to their props

Però en aquest enllaç hi ha una pista de com això es pot aconseguir.

Ha funcionat bé, però com es comenta en l'enllaç, aquesta manera de fer no és molt ortodoxa. Els canvis que s'han fet són:

a Grid.js


en el constructor (important):
        this.onDiscoveredPercentChange = this.onDiscoveredPercentChange.bind(this);

en el render():
                <Trees
                    gridData={this.state.gridData}
                    location={this.props.location}
                    zoom={this.props.zoom}
                    discoveredPercent={this.state.discoveredPercent}
                    onDiscoveredPercentChange={this.onDiscoveredPercentChange}
                />
dins del component, definim la funció:
    onDiscoveredPercentChange(newValue) {   
        this.setState({ discoveredPercent: newValue });
        //console.log(newValue)
    }

A Tree.js, en un parell de llocs he de posar:

   this.props.onDiscoveredPercentChange(percent.toFixed(2)) 

i d'aquesta manera el component Grid se n'entera del canvi de percentatge que he fet en el component Tree.

Versions

ArbresBarcelona v1.png

La versió 1.0 és sense mapes, només el FlatList amb el llistat de tots els arbres. Es pot activar el posicionament, i per tant sé quina és la direcció des de la meva posició actual fins a l'arbre. A més a més he calculat el heading, que és la direcció a la que em moc. I per tant, amb aquestes dues dades, ja puc saber en quina direcció m'he de moure/desviar, i això ho mostro amb unes brúixoles.

Tot això funciona, sembla ser que la FlatList està una mica carregada d'informació, i en fase de desenvolupament amb Expo no és molt fluïd i surt el missatge:

VirtualizedList: You have a large list that is slow to update - make sure your renderItem function renders components that follow React performance best practices like PureComponent, shouldComponentUpdate, etc. Object {

Ara bé, amb el APK va bé, fluid. S'ha de mirar a veure si es pot millorar.

Tot el desenvolupament l'he fet a la carpeta geo-example/, però ara ja he migrat a ArbresBarcelona/.

D'altra banda, he estat desenvolupant a la carpeta thunderforest/ la versió del mapa (finalment mapes sense google Maps, una solució nativa on no cal instal·lar cap llibreria). I ja funciona, i en la versió 2.0 hauré d'integrar la v1.0 amb aquesta funcionalitat (es podrà intercanviar entre les dues pantalles, el llistat i el mapa).

Publicar a la Google Store

Step 1: Create a Developer Account

Nom del desenvolupador: joanillo
joanqc@gmail.com
www.joanillo.org
+34-636517785
identificador del compte de desenvolupador: *******************

Step 2: Plan to Sell? Link Your Merchant Account De moment res.

Step 3: Create an App

  • idioma: català - ca
  • títol: Arbres Singulars de Barcelona
  • descripció breu: Visita i col·leccionar tots els arbres monumentals de Barcelona
  • descripció completa: Llistat de tots els arbres catalogats de Barcelona. Es mostra distància i direcció per arribar finns l'arbre, i pots marcar els arbres visitats. A veure si pots completar tota la col·lecció. Són 146 arbres distribuïts per tots els barris.

Ara falta acabar d'omplir tota la fitxa, crear la icona, captures de pantalla, etiquetes i més coses.

També enllaç a una política de privacitat, dir que l'aplicació és gratuïta, posar una categoria i etiquetes, i bastantes coses més. Després s'ha de pujar el APK que s'ha generat des de Expo. I un cop fet tot, ha de sortir el missatge llest per publicar. Amb aquest missatge no s'ha acabat, he d'aconseguir que surti el missage pendent de publicació. I aleshores esperar (degut al COVID-19, sembla ser que pot trigar una setmana).

Una de les coses que li passa al meu APK i que detecta el Google Store és que és massa gran (pesat). Això ja en sóc conscient, i ve de que s'ha desenvolupat amb el Expo. En el següent apartat es comenta de com es pot reduir el tamany.

React-Native i mapes (primavera 2022)

Resum

Després de dos anys, reprenem el projecte i miro si hi ha hagut algun progrés amb la possibilitat d'utiltizar la llibreria openlayers a react-native. La conclusió és que no especialment. Si vull utilitzar Expo, puc utilitzar react-native-maps amb Google Maps. Si vull utilitzar els mapes de OSM la única possibilitat és utilitzar el projecte hiker i modificar-lo (tal com s'explica). hiker ja m'havia funcionat el 2020, i ara el 2022 també em funciona i he aconseguit posar els mapes de OSM i de thunderforest.

Aquí va un resum de les proves que he fet, del que ha funcionat i del que no. Cal dir que hi ha coses que no han funcionat perquè són projectes pensats per a Android Studio i Gradle, i jo de moment vull continuar amb Expo com a entorn de desenvolupament. Si instal·lo i utilitzo Android Studio podria fer funcionar alguns dels projectes que es comenten.

A. react-native-maps

conclusió: funciona amb Expo i Google Maps. No he aconseguit fer funcionar els mapes de OSM.

Però sí que hi ha una possibilitat d'utilitzar aquest enllaç, com s'explica a:

You can use a custom Tile Overlay (OpenStreetMap too) in react-native-maps package: https://github.com/react-native-maps/react-native-maps

(scroll to Using a custom Tile Overlay section)

You need to add api key. You don't need to use Maps API at all and the key won't be used in that case.

Mans a l'obra:

Primer de tot creo una API key de Google Maps, doncs la necessitaré, encara que no es faria servir si vull els mapes de OSM:

key=API_KEY
AIzaSyDNocxQJ6Dt9CnT001Zfrk0qVF1pUa2kiw

El problema està en què aquest projecte està pensat per a Android Studio. Per exemple, la API_KEY està dins del Manifest.xml.

I react-native-maps amb Expo? Sí que és possible:

Dins del package.json puc posar la API KEY:

    "android": {
      "config": {
        "googleMaps": {
          "apiKey": "AIzaSyDNocxQJ6Dt9CnT001Zfrk0qVF1pUa2kiw"
        }
      }
    }

Creo el projecte:

$ expo init react-native-maps-exemple
$ expo install react-native-maps

copio a Apps.js el codi que se'ns proposa a l'enllaç, i fem npm start i ja està, ja tenim un mapa (de moment amb Google Maps)

$ npm start

El MapView bàsic de l'exemple, que representa un mapa de tot el món:

<MapView style={styles.map} />

I ara ja puc fer coses més interessants:

      <MapView
        initialRegion={{
        latitude: 37.78825,
        longitude: -122.4324,
        latitudeDelta: 0.0922,
        longitudeDelta: 0.0421,
        }} style={styles.map}
      />

Per tant, funciona bé amb Google Maps, i a partir d'aquí no hauria de ser difícil crear els markers i fer coses més interessants, com es mostra a la documentació.

I ara puc fer una cosa que no queda gens clara, en el MapView puc posar:

mapType={Platform.OS == "android" ? "none" : "standard"}

i efectivament em desapareix el mapa de Google Maps (ja no es renderitza), però no queda clar com s'ha de fer per renderitzar els mapes de OSM.

La solució vindria per react-native-maps-osmdroid, que és un clon de react-native-maps pensat per a OSM. Però és Android Studio

Si vull fer servir Google Maps aquesta sí que és una solució, que a partir de l'exemple bàsic el puc fer créixer i fer el mapa amb les meves necessitats.

B. Leaflet (per explorar)

Una possibilitat és utilitzar Leaflet:

C. (per explorar)

Anem a provar:

$ expo init react-native-map-exemple

No l'he pogut fer funcionar, no recordo per què.

D.react-native-open-street-map (no ha funcionat, Android Studio)

Una altra prova:

Hauria de fer primer crear un nou projecte i també fer el git clone, i barrejar-ho, doncs m'interessa el codi del clone, però jo desenvolupo amb expo i necessito l'esquelet del Expo.

$ expo init react-native-open-street-map-prova
$ git clone https://github.com/Multiware-Tecnologia/react-native-open-street-map
$ cd react-native-open-street-map/
$ npm install -g expo-cli (cal si ja ho havia fet? crec que no...)

Instal·lo totes les dependències

$ npm install

No funciona...

E. react-native-maps-osmdroid

El problema està en què també està pensat per a Andoid Studio. A veure si funciona...

$ expo init react-native-maps-osmdroid-exemple
$ cd react-native-maps-osmdroid-exemple
$ expo install react-native-maps
$ expo install react-native-maps-osmdroid

copio a Apps.js el codi que se'ns proposa a l'enllaç, i fem npm start i ja està, ja tenim un mapa (de moment amb Google Maps), igual que funcionava el react-native-maps. Però no funciona amb OSM

$ npm start

F. Mapbox, per explorar

Es recomana aquest enllaç, i s'hauria d'explorar

A mi en principi Mapbox no m'interessa, però per aquesta via tindria els tiles de OSM.

G. hiker: hiker4 i seg (funciona)

NOTA: funcionava el 2020, però ara el 2022 he començat de 0 i no havia repassat massa què havia fet en el passat. Com que el 2020 havia fet hiker/ i hiker2/, la prova per on començo el 2020 serà hiker3/ i següents.

NOTA: si no m'hagués funcionat el servidor de tiles de OSM, una possibilitat era utilitzar la descàrregar de tiles i així poder treballar offline, però aquesta opció no interessa. Per exemple, per descarregar tiles es pot utilitzar el Mobile Atlas Creator, que ja havia utilitzat en el seu dia. L'aplicació de mòbil OsmAnd, que utilitzo, també fa la descàrrega de tiles.

mirar aquest projecte que hauria de funcionar i està fet amb Expo:

git clone https://github.com/wende60/hiker
cd hiker
npm install
npm start

la clau de thunderforest ja la tenia: f695b42354e84626afef4de11a5d6080. S'ha de posar a settings/mapConfig.js

i aquí veig com també hi ha la possibilitat d'utilitzar OSM.

A partir d'aquí dóna una sèrie d'errors que vaig solucionant sobre la marxa:

this project uses SDK 32.0.0, but this version of Expo Go only supports the following SDKs: 44.0.0, 43, 42, 41...

solució: creo de nou un projecte buit amb la meva versió d'Expo, i copio els fitxers.

Com que va ser una versió anterior, hi ha alguna cosa que s'ha d'arreglar. Per exemple, ruta relativa dels fitxers

TypeError: undefined is not an object (evaluating '_expo.Constants

solució:

npm i --save expo-constants
import Constants from 'expo-constants';
[Unhandled promise rejection: ReferenceError: Can't find variable: Permissions

això dels Permissions ja ho havia solucionat a ArbresBarcelonaRN amb la pausa de 2 anys 2020-22, és un problema d'una versió anterior de react natove.

$ npm install expo-location

Més errors:

[Unhandled promise rejection: TypeError: undefined is not an object (evaluating '_expo.FileSystem.getInfoAsync')]

I work with "sdkVersion": "35.0.0". It seems that Expo changed its API. Now you need to install a separate dependency:

npm i --save expo-file-system
And then to import FileSystem object independently for your component:

import * as FileSystem from 'expo-file-system';

Més errors:

Animated.event now requires a second argument for options
at node_modules/react-native/Libraries/Animated/AnimatedEvent.js:141:6 in constructor
at node_modules/react-na
...
at src/components/Grid.js:45:32 in constructor

Per solucionar-ho és molt senzill:

a Grid.js s'ha d'afegir un segon argument i la cosa queda:

            onPanResponderMove: Animated.event([
                null,
                {
                    dx: this.state.pan.x,
                    dy: this.state.pan.y
                },
            ],{useNativeDriver: false}),

i ara ja funciona tot sense cap mena d'error, excepte que no es carreguen els tiles.

I d'altra banda, amb aquest sistema no sé ben bé com posaré uns markers (s'han de poder posar doncs es visualitza una creu i una rodoneta, que seran uns makers).

A partir d'aquí el meu problema serà poder pintar els tiles, ja siguin de thunderforest o de OSM. I per entendre el procés s'ha de tenir clar que els tiles es descarreguen d'un servidor de tiles (comprovar que la ruta és correcte), i que es guarden com a fitxers .png en el dispositiu (i que funciona com a cache sempre que cal).

Miro la ruta on es guarden els fitxers en el Android i penso que aquí pot haver-hi un problema.

file:///data/user/0/host.exp.exponent/files/ExperienceData/%2540joanillo_reactnative%252Fhiker2/outdoors/tiles/14/8290/6118.png

Mirant la ruta del fitxer on es guarden els png penso que podia haver un problema de doble codificació (però al final no...)

%2540 -> %40 que és el \x40, equivalent a l'@
%252F -> %2F que és el \x2F, equivalent a /

Vaig estar mirant de reemplaçar els caràcters i fent proves, però no tenia res a veure. La solució va venir per una altra banda:

Val, ja està, ja funciona hiker2 (maig 2022), amb thunderforest i la API KEY

La línia ha quedat de la següent manera:

this.mounted && this.setState({ tileSource: { uri: fileData.uri }}); //el format d'aquesta línia és diferent en el codi original

El problema és ara els tiles de OSM, que no funcionen.

funciona:
$ wget https://tile.thunderforest.com/outdoors/13/4130/3060.png?apikey=f695b42354e84626afef4de11a5d6080

no funciona: (però sí que es veu en el navegador)
$ wget https://b.tile.openstreetmap.org/13/4130/3060.png
--2022-05-02 10:48:38--  https://b.tile.openstreetmap.org/13/4130/3060.png
S'està resolent b.tile.openstreetmap.org (b.tile.openstreetmap.org)… 151.101.134.137, 2a04:4e42:1f::649
S'està connectant a b.tile.openstreetmap.org (b.tile.openstreetmap.org)|151.101.134.137|:443… conectat.
HTTP: s'ha enviat la petició, s'està esperant una resposta… 403 Forbidden
2022-05-02 10:48:38 ERROR: 403 Forbidden.

però així sí que funciona:
$ curl --output fitxer.png https://a.tile.openstreetmap.org/13/4130/3060.png

Per tant el problema és de permisos per accedir als servidors de tiles de OSM.

els tiles d'OSM no em funcionen des de react-native, i des del chrome del portàtil sí, perquè una de les condicions és que:

  • Valid HTTP User-Agent identifying application. Faking another app’s User-Agent WILL get you blocked.
  • If known, a valid HTTP Referer.
  • DO NOT send no-cache headers. (“Cache-Control: no-cache”, “Pragma: no-cache” etc.)
  • Cache Tile downloads locally according to HTTP Expiry Header, alternatively a minimum of 7 days.
  • Maximum of 2 download threads. (Unmodified web browsers’ download thread limits are acceptable.)
Mozilla/5.0 (Linux; Android 10; SM-G960U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.41 Mobile Safari/537.36
User-Agent: <product> / <product-version> <comment>
User-Agent: Mozilla/5.0 (Linux; Android 10; SM-G960U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.41 Mobile Safari/537.36

Per tant el que he de fer és posar una capçalera adequada en la meva petició, i simular que estic fent la petició al servidor des d'un navegador reconegut (el user-agent).

Ho he aconseguit!, he aconseguit posar els tiles de OSM. En el fileSystemHelper.js hem d'afegir els headers. La cosa queda:

            await FileSystem.downloadAsync(
                source,
                FileSystem.documentDirectory + file,
                { headers: { 'Content-Type': 'image/png', 'User-Agent': 'Mozilla/5.0 (Linux; Android 10; SM-G960U) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/101.0.4951.41 Mobile Safari/537.36' } }
            );

Recordar que la cache funciona, i per tant els nous tiles no es veuen. He d'esborrar la cache i els fitxers per tal de què es tornin a descarregar. En el Android fem:

  • Configuració > Aplicacions > Gestiona les aplicacions > Expo Go > Esborra les dades (totes o només la cache?)

I ja està, ja tenim funcionant el projecte hiker (aquesta versió li diem hiker4, i a partir d'aquí ja puc anar al hiker5 per tal d'afegir tots els markers dels arbres. Quan ja tingui afegits tots els arbres, ja ho podré integrar amb la resta de l'aplicació (que mentrestant ja sé com es fa el canvi de pantalles entre dos tabs).

Recordatori de com fer una app de React Native des de zero, amb Expo (maig 2022)

$ cd v1/
$ expo init TrBTT

│   There is a new version of expo-cli available (5.4.6).                 │
│   You are currently using expo-cli 5.4.3                                │
│   Install expo-cli globally using the package manager of your choice;   │
│   for example: `npm install -g expo-cli` to get the latest version 

$ npm install -g expo-cli

i ja tinc la última versió la 5.4.6

Tornem-hi. Per inicial el projecte (es crea el package.json):

$ expo init TrBTT 

- cd TrBTT
- npm start # you can open iOS, Android, or web from here, or run them directly with the commands below.
- npm run android
- npm run ios # requires an iOS device or macOS for access to an iOS simulator
- npm run web

Per tant:

$ cd TrBTT
$ npm start

Per pantalla em surt un codi QR i em diu que:

› Metro waiting on exp://192.168.1.74:19000
› Scan the QR code above with Expo Go (Android) or the Camera app (iOS)

Des del mòbil obro l'app de Expo Go, i llegeixo el codi QR.

I després d'esperar una estona em surt allò de 'Something went wrong. Network response timed out'.

Però això ja sé de què va, ha m'hi havia trobat.

expo go something went wrong network response timed out

What solved this for me was simply changing the connection from LAN to Tunnel. 
When you start an expo app, 
a browser window will open with a URL like http://localhost:19002/. 
Along the left side is a Connection option set to LAN by default. 
Changing it to Tunnel before scanning the QR code with the expo app solved it.

Finalment ja he aconseguit que em surtin les developers tools:

$ expo start
Starting project at /home/joan/projectes/OSM/tracking_btt/MA/v1/TrBTT
Developer tools running on http://localhost:19002

I ara ja puc marcar la connexió com a tunnel, i tornar a escanejar el codi QR

Si vull obrir directament amb el tunnel, l'opció és:

$ expo start --host tunnel

I això ho puc posar en el package.json per tal de què en el 'npm start' ja funcioni amb aquesta opció.

He hagut d'actualitzar l'aplicació Expo Go del mòbil, però ara ja funciona. ÉS a dir, tinc en el mòbil l'Aplicació funcionant (el portàtil i el mòbil es comuniquen via wifi). Quan faig un canvi a App.js es veu automàticament el canvi al mòbil.

Reduir el tamany de l'app generada per Expo

Juny 2022. Amb la nova manera de generar el APK (o el AAB), el tamany queda reduït a la meitat. La manera antiga caduca el gener de 2023.



creat per Joan Quintana Compte, maig 2020, maig 2022