Ogni volta che abbiamo una nuova funzionalità che vogliamo aggiungere a un'app esistente, come sviluppatori web siamo abituati ad aggiornare sia il frontend che il backend contemporaneamente. E poi distribuiamo le modifiche all'ambiente di produzione e chiudiamo la giornata.
Nel caso delle app mobili, però, non sempre è applicabile lo stesso concetto. Perché non tutti aggiorneranno automaticamente l'app. Pertanto le nuove modifiche al codice backend potrebbero non funzionare come previsto nelle versioni precedenti dell'app mobile. Qual è la maggior parte degli utenti dopo una nuova versione.
Supponiamo di avere un'app che consente agli utenti di tenere traccia delle proprie spese mensili. Abbiamo un'app nativa React per ottenere gli input dagli utenti e un backend che memorizza le spese in un database. E di seguito è riportato il formato JSON che utilizziamo per comunicare con il backend:
1{2 data:[3{4"id":1,5"amount":100,6"date":"2021-01-01",7"category":"Food",8"subcategory":"Groceries",9"description":"Bought groceries for the month",10},11{12"id":2,13"amount":200,14"date":"2021-01-02",15"category":"Transportation",16"subcategory":"Gas",17"description":"Filled up the gas tank",18}19]20}Supponendo di avere un'app React Native che consuma l'API, mostriamo le spese in un elenco. In questo modo:
1<View>2{result?.data?.map((expense)=>(3<View>4<Text>{expense.amount}</Text>5<Text>{expense.date}</Text>6<Text>{expense.category}</Text>7<Text>{expense.subcategory}</Text>8<Text>{expense.description}</Text>9</View>10))}11</View>Ma cosa succederebbe se ora volessimo aggiungere una funzionalità che consenta agli utenti di archiviare anche i propri guadagni? Per raggiungere questo obiettivo dobbiamo aggiornare il codice backend per memorizzare anche i redditi nel database. E durante il recupero dei dati dobbiamo rispedirli nel seguente formato JSON:
1{2 data:{3 expenses:[4{5"id":1,6"amount":100,7"date":"2021-01-01",8"category":"Food",9"subcategory":"Groceries",10"description":"Bought groceries for the month",11},12{13"id":2,14"amount":200,15"date":"2021-01-02",16"category":"Transportation",17"subcategory":"Gas",18"description":"Filled up the gas tank",19}20],21 incomes:[22{23"id":1,24"amount":10000,25"date":"2021-01-01",26"category":"Salary",27"subcategory":"Full-time",28"description":"Received salary for the month",29},30{31"id":2,32"amount":2050,33"date":"2021-01-02",34"category":"Freelance",35"subcategory":"Writing",36"description":"Received freelance payment for the month",37}38]39}40}Pertanto l'app mobile dovrà aggiornare il codice per gestire il nuovo formato JSON.
1<View>2-{result?.data?.map((expense)=>(3+{result?.data?.expenses?.map((expense)=>(4<View>5<Text>{expense.amount}</Text>6<Text>{expense.date}</Text>7<Text>{expense.category}</Text>8<Text>{expense.subcategory}</Text>9<Text>{expense.description}</Text>10</View>11))}12</View>Ma nelle versioni precedenti dell'app mobile, il codice utilizzerà ancora il vecchio formato JSON. Quindi l'app andrà in crash. E lo stesso vale per il salvataggio dei dati nel backend. Le versioni precedenti dell'app mobile non saranno in grado di salvare i dati nel nuovo formato JSON nel caso in cui disponiamo di un validatore che controlla il formato JSON prima che venga salvato nel database. Ma qui parleremo solo del formato della risposta. Siamo sicuri che sarai in grado di gestire da solo la parte del "salvataggio condizionato nel database" una volta che avrai imparato il trucco.
Per risolvere questo problema possiamo restituire in modo condizionale il vecchio formato JSON per le versioni precedenti dell'app mobile. E restituisci il nuovo formato JSON per le versioni più recenti dell'app mobile. Ma come fa il server backend a sapere quale versione dell'app mobile sta utilizzando l'utente? È qui che entra in gioco la libreria chiamata "react-native-device-info". Usando questa libreria possiamo ottenere la versione dell'app mobile e inviarla al server backend utilizzando un'intestazione personalizzata "MobileAppVersion". Nel backend possiamo verificare se l'intestazione "MobileAppVersion" è presente ed è maggiore o uguale alla versione dell'app mobile che vogliamo supportare. Se lo è, possiamo restituire il nuovo formato JSON, altrimenti possiamo restituire quello vecchio.
Se stai usando apisauce (che è consigliato) puoi impostare le intestazioni in questo modo:
1import DeviceInfo from"react-native-device-info"2
3...4setAllHeaders(value?: string, appVersion?: string){5 api.apisauce.setHeader("Authorization",`Bearer ${value}`)6 api.apisauce.setHeader("MobileAppVersion", appVersion ||"")7}8...9
10setAllHeaders(token, DeviceInfo.getVersion())oppure se non stai utilizzando apisauce puoi impostare manualmente le intestazioni in questo modo:
1asyncfunctiongetData(url: string,appVersion: string){2const response =awaitfetch(url,{3headers:{4'Authorization':'Bearer Token',5'MobileAppVersion': appVersion ||""6}7})8const data =await response.json()9return data
10}11
12...13// get the data from the api (e.g. inside useEffect, or a button press)14const result =awaitgetData("https://api.example.com", DeviceInfo.getVersion())15...Quindi, nel codice backend, puoi restituire la risposta in modo condizionale in base alla versione dell'app.
1const express =require('express')2const app =express()3
4app.get('/',(req, res)=>{5const appVersion = req.headers['MobileAppVersion']6if(appVersion && appVersion >='2.0.0'){// if the app version is 2.0.0 or higher7 res.json({data:{expenses:[...],incomes:[...]}})8}9else{10// if the app version is lower than 2.0.0, older response format11 res.json({data:[]})12}13})o se stai usando Python con Django puoi fare qualcosa del genere:
1from django.http import JsonResponse
2
3classget_data(request):4defget(self, request):5 app_version = request.headers.get('MobileAppVersion','1.0.0')6if app_version and app_version >='2.0.0':7# if the app version is 2.0.0 or higher8return JsonResponse({'data':{expenses:[...], incomes:[...]}})9else:10# if the app version is lower than 2.0.0, older response format11return JsonResponse({'data':[...]})