Product development · HR tech · 12 minuti di lettura
Una guida pratica per CTO, product manager e team HR tech che devono decidere come costruire un’app per la gestione dei dipendenti. Con qualche retroscena vero dallo sviluppo di myGenius.
La domanda che tutti si fanno, ma che in pochi pongono nel modo giusto
Stai per costruire un’app per timbrature, ferie, malattie. A un certo punto, in riunione, qualcuno del team tecnico chiede: “La facciamo nativa o web?”
E la risposta onesta è: dipende. Ma non dipende dal budget, come si tende a pensare. Dipende da cosa deve fare davvero l’app.
Nel 2026 la guerra WebApp contro nativo non ha più molto senso come la si raccontava qualche anno fa. Oggi è una questione più concreta: quanto hardware del telefono ti serve toccare, quanto ti costa mantenere il tutto nel tempo, e quanto velocemente devi poter rilasciare una modifica. E nella maggior parte dei casi aziendali, la risposta migliore è un ibrido — un compromesso che in pochi sanno spiegare bene, e che è esattamente quello che abbiamo scelto per myGenius, il nostro prodotto HR per aziende italiane.
In questo articolo provo a raccontarti le differenze reali tra i due mondi, dove ciascuno smette di reggere, e perché alla fine abbiamo costruito una shell nativa attorno a una WebApp già esistente.
Partiamo dalle basi: cos’è una WebApp su mobile
Una WebApp, in pratica, è un sito ottimizzato per il mobile che si comporta come un’app. Non la scarichi dallo store: apri un URL e via.
Nel mondo enterprise questo spesso vuol dire semplicemente “il portale HR reso responsive”. E funziona bene: si aggiorna lato server, senza passare da nessuno store, e non c’è nessuna installazione da chiedere all’utente.
Cosa funziona bene
Il primo vantaggio, quello che salta subito all’occhio, è la velocità di aggiornamento: cambi il codice sul server e in un attimo tutti vedono la nuova versione. Niente review di Apple da aspettare, niente rollout graduale su Google Play. Anche i costi restano più bassi, perché hai un solo codebase e un solo team che ci lavora sopra. E poi c’è la trasversalità: la stessa WebApp gira su iOS, Android, Windows, Mac, ChromeOS — su qualunque cosa abbia un browser. Anche la manutenzione ne beneficia: un bug fix è un deploy, non due release su due store diversi.
Dove invece si scontra con la realtà
Il browser ha un suo overhead, e su form e liste non lo senti quasi, ma su animazioni un po’ più complesse sì. L’accesso all’hardware è limitato: niente NFC, niente Keychain, e la biometria passa solo dalle API Web, WebAuthn, che non coprono tutti i casi d’uso che potresti avere in mente. L’offline poi resta un tasto dolente — le PWA sono migliorate parecchio, ma una gestione offline seria, con cache strategica e sincronizzazione differita, è ancora molto più semplice da ottenere in un’app nativa. E infine c’è quella sensazione, difficile da mettere per iscritto ma che ogni utente percepisce, che un browser non “sembra” un’app: lo scroll, le transizioni, i gesti sono un’altra cosa.
E un’app nativa, invece?
Un’app nativa è scritta nel linguaggio della piattaforma — Swift o Objective-C per iOS, Kotlin o Java per Android — oppure con un framework cross-platform come React Native o Flutter, che compila in componenti nativi veri e propri.
I punti a favore sono innegabili
Qui hai accesso completo all’hardware: NFC, Face ID e Touch ID, Bluetooth Low Energy, sensori, fotocamera avanzata. Tutto disponibile senza compromessi. Le performance sono al massimo, perché non c’è l’overhead del browser e il rendering è diretto sulla GPU. L’esperienza utente è quella “vera”, con gesture e animazioni che rispettano le convenzioni della piattaforma — l’utente sente che l’app appartiene davvero al suo telefono. L’offline diventa naturale grazie a SQLite, file system, background task. E la distribuzione via store porta con sé scopribilità, recensioni, aggiornamenti gestiti — tutto il pacchetto.
Ma il conto arriva comunque
Il costo di sviluppo è più alto: due codebase distinte, o una cross-platform con i suoi compromessi, due store, due processi di review da affrontare. Gli aggiornamenti sono più lenti — una correzione urgente può richiedere anche 24-48 ore di attesa su Apple, un po’ meno su Google Play ma comunque non istantanea. La manutenzione si raddoppia: ogni bug va sistemato su entrambe le piattaforme, e le dipendenze native tendono a rompersi a ogni major update del sistema operativo. E poi c’è il peso: un binary nativo, anche per un’app semplice, arriva facilmente a 30-80 MB.
Cosa cambia davvero per un prodotto HR
Un’app HR aziendale ha esigenze molto specifiche, diverse da quelle di un’app consumer qualsiasi.
Per un form di richiesta ferie, o per una lista di presenze con filtri, WebApp e nativo funzionano allo stesso identico modo — perfetti entrambi, nessuna differenza percepibile. Le cose cambiano quando entra in gioco la timbratura NFC con badge: su iOS, in una WebApp, semplicemente non si può fare, mentre in nativo è immediato. Il login biometrico è un’altra zona grigia: via WebAuthn funziona, ma con dei limiti; in nativo è pieno e senza compromessi. Le notifiche push, in WebApp, dipendono dal fatto che l’utente abbia installato la PWA — in nativo sono semplicemente lì. Un aggiornamento urgente dell’interfaccia è istantaneo in una WebApp, mentre in nativo deve aspettare la review dello store. E un token JWT, che in WebApp finisce spesso in un cookie o in localStorage, in nativo può vivere in Keychain o Keystore, molto più al sicuro.
Messa così, la questione si chiarisce da sola: per gran parte delle funzionalità HR — form, liste, archivi — una WebApp basta e avanza. È quella fetta più piccola, quella che richiede hardware vero, NFC, biometria, storage crittografato, a spingere verso il nativo.
Il punto chiave: per molte funzionalità HR una WebApp è sufficiente. Il nativo diventa necessario quando il prodotto deve accedere a hardware, storage protetto o funzioni specifiche del sistema operativo.
Il vero discrimine: l’accesso all’hardware
Qui entriamo un po’ più nel tecnico — è la parte che i confronti generici tendono a saltare, ma è quella che in pratica decide tutto.
NFC su iOS: semplicemente non esiste nel browser
Apple non espone nessuna API NFC a Safari. Non c’è modo di aggirarlo, non esiste un equivalente di navigator.nfc su iOS. Se il tuo prodotto ha un flusso di timbratura con badge NFC — come succede con myGenius — quella funzione, su iPhone, in una WebApp non la puoi costruire. Punto.
Su Android va un po’ meglio, c’è una Web NFC API sperimentale in Chrome, ma il supporto resta parziale e non è pensato per reggere un’app aziendale in produzione.
Biometria: WebAuth contro un bridge nativo
WebAuthn — l’API web per l’autenticazione biometrica — esiste ed è supportata. Ma ha dei limiti concreti. Su Safari iOS funziona solo se il sito ha un dominio valido servito in HTTPS, e smette di funzionare del tutto dentro una WebView incorporata in un’app. Inoltre non puoi far comunicare WebAuthn con un token JWT salvato nel Keychain: sono due mondi che semplicemente non si parlano. E il fallback su PIN, quando la biometria non va, è gestito dal sistema operativo — tu, come sviluppatore, non hai controllo su quella UX.
Con un bridge nativo, come react-native-biometrics, la storia cambia: crei una chiave nel Secure Enclave su iOS o nell’Android Keystore, firmi una challenge, verifichi tutto lato server. Hai il controllo completo su interfaccia, fallback e ciclo di vita della chiave.
Keychain contro localStorage
Salvare un token JWT in localStorage è pratica comunissima nelle web app. Ma è anche il primo posto in cui un attaccante guarda in caso di XSS.
Sul mobile nativo esistono Keychain, iOS, e Keystore, Android: storage crittografato, gestito dal sistema operativo, legato fisicamente al dispositivo. Un XSS dentro una WebView non riesce a leggere quello che sta nel Keychain.
Per un’app che maneggia dati di dipendenti — presenze, ferie, malattie — la differenza tra localStorage e Keychain non è un dettaglio tecnico da nerd. È, a tutti gli effetti, una questione di conformità GDPR.
Il compromesso che funziona: shell nativa attorno a una WebApp
Ed eccoci alla parte che, alla fine, interessa davvero.
Invece di scegliere tra WebApp e nativo, si può fare entrambe le cose insieme: un’app nativa che al suo interno contiene una WebView, con qualche schermata nativa dedicata alle funzioni che richiedono hardware vero.
Nella pratica funziona così. L’app nativa — nel caso di myGenius, React Native — fa da shell: gestisce autenticazione, navigazione, ciclo di vita generale. La WebApp, servita da my-genius.it, resta il cuore applicativo: form, liste, archivi, tutta l’interfaccia di business di tutti i giorni. Le schermate native entrano in gioco solo quando serve davvero — NFC, biometria, download sicuro di documenti. E la comunicazione tra i due mondi passa tramite postMessage.
Il vantaggio più grande è che il 70% dell’app resta gestito da un solo team web, con aggiornamenti istantanei che non passano mai dallo store. L’hardware nativo copre esattamente i punti dove serve — NFC, Keychain, biometria, notifiche push. La distribuzione avviene comunque via store, con tutti i benefici di scopribilità e fiducia che questo comporta. E il token resta al sicuro nel Keychain nativo, non nel localStorage della WebView.
Il rovescio della medaglia, però, c’è, ed è bene dirlo chiaramente. La comunicazione via postMessage tra WebView e nativo aggiunge complessità architetturale: serve un controller dedicato, serve tipizzare i messaggi, serve gestire gli errori con cura. Il debug diventa un lavoro su due fronti — il WebView ha i suoi strumenti, Safari Web Inspector e Chrome DevTools, il nativo ha i suoi, Xcode e Android Studio, e un bug può nascondersi in uno qualsiasi dei due mondi. C’è anche un piccolo overhead di avvio: l’app deve gestire splash screen, verifica della connessione, sincronizzazione dei cookie tra WebView e nativo, prima ancora di mostrare qualcosa all’utente. E infine, la review dello store resta comunque necessaria per la shell, anche se la WebApp al suo interno si aggiorna da sola.
Il caso vero: perché myGenius è nato così
myGenius è la nostra app HR per aziende italiane: timbratura NFC, gestione ferie e permessi, archivio presenze e malattia.
Il prodotto esisteva già come WebApp, e funzionava. Aveva già form, liste, archivi, autenticazione tutti pronti e funzionanti. Riscrivere tutto da zero in nativo avrebbe voluto dire raddoppiare il team, raddoppiare i tempi di rilascio, e perdere quel vantaggio enorme dell’aggiornamento istantaneo lato server.
Il problema era uno solo: serviva l’NFC per la timbratura con badge, e su iOS il browser semplicemente non lo permette.
Così abbiamo costruito una shell nativa in React Native attorno alla WebApp esistente, con schermate native solo per NFC ed e-badge, lettura del tag e invio della timbratura, per ferie e permessi, sette schermate con un navigatore interno dedicato, per le presenze, storico timbrature con filtri, e per la malattia, archivio dei certificati INPS. Tutto il resto — dashboard, profilo, comunicazioni — è rimasto nella WebApp.
Come comunicano WebView e nativo
La WebApp manda messaggi alla shell nativa tramite postMessage. Per avviare uno scan NFC, per esempio, invia un messaggio con tipo “nfc” e azione “nfcOn”; per navigare verso la schermata delle ferie, invia un messaggio con tipo “screen” e azione “leaves”. Un componente che abbiamo chiamato messageController riceve questi messaggi nella shell nativa, fa uno switch su tipo e azione, ed esegue l’operazione corrispondente: avvia lo scan NFC, naviga verso una schermata nativa, scarica un documento, imposta un cookie.
Il token JWT: dal WebView al Keychain
Quando l’utente fa login nella WebApp, il server restituisce un JWT. La WebApp passa quel token alla shell nativa via postMessage, e la shell lo salva nel Keychain. Da quel momento in poi, ogni chiamata HTTP nativa — per l’NFC, per le ferie, per le presenze — usa il token letto dal Keychain, non dal cookie della WebView. Il risultato pratico è che anche se il WebView dovesse avere un XSS, l’attaccante non riuscirebbe comunque a rubare il token, perché non sta più nel localStorage: sta nel Keychain.
Cosa ci ha sorpreso, dopo mesi di lavoro
Tre cose, in particolare.
La prima: postMessage è fragile se non lo tipizzi fin da subito. All’inizio i messaggi erano semplici stringhe non strutturate, e dopo il terzo bug causato da un messaggio malformato abbiamo introdotto un formato rigido con tipo, azione e payload — con il messageController che fa uno switch esplicito e logga tutto ciò che non riconosce.
La seconda: i cookie cross-origin sono un vero incubo. Il WebView ha il suo cookie store, le chiamate native fatte con fetch ne hanno un altro, e sincronizzare il token tra i due mondi ha richiesto un servizio dedicato — lo abbiamo chiamato CookiesService — che crea il cookie su my-genius.it dal lato nativo subito dopo il login.
La terza: la gestione dell’offline cambia parecchio l’esperienza utente. Un WebView che perde la connessione mostra la classica, bruttissima pagina di errore del browser. La shell nativa invece intercetta la perdita di connessione con NetInfo e mostra una schermata di blocco pulita, con un semplice bottone “riprova”.
Come scegliere, in pratica
Se non ti serve NFC, biometria nativa o Keychain, se il tuo team è principalmente web, se l’aggiornamento istantaneo è la tua priorità, se il budget è limitato e i tuoi utenti accedono indifferentemente da desktop e da mobile — allora una WebApp è la scelta giusta, senza troppi dubbi.
Se invece hai bisogno di NFC, BLE, sensori o fotocamera avanzata, se la performance è un requisito critico per animazioni o elaborazioni locali, se l’offline è davvero irrinunciabile, se hai il budget per due codebase, o per un team cross-platform, e se l’esperienza nativa è per te un vero differenziatore competitivo — allora vale la pena costruire un’app nativa.
Se hai già una WebApp che funziona, ti serve hardware nativo solo per una o due funzioni specifiche e non per tutta l’app, vuoi aggiornamenti istantanei per l’interfaccia ma comunque una distribuzione via store, il tuo team è prevalentemente web ma con almeno una persona che sa muoversi nel nativo, e gestisci dati sensibili che richiedono uno storage crittografato — allora la strada della shell nativa attorno a una WebApp è probabilmente quella più sensata.
Le domande che ci fanno più spesso
Una PWA non risolve tutto?
Le Progressive Web App hanno fatto passi enormi, è vero. Su Android, una PWA installata ha accesso a parecchi sensori. Ma su iOS Apple limita molto le API disponibili alle PWA: niente NFC, biometria possibile solo via WebAuthn, notifiche push comunque limitate. Per un prodotto HR aziendale che deve funzionare bene su iPhone, una PWA da sola non basta ancora.
React Native non è già “nativo” di suo?
React Native compila in componenti nativi veri, non gira dentro una WebView, quindi in questo senso sì, è nativo — usa le primitive dell’interfaccia della piattaforma. Ma per accedere a hardware specifico come NFC, biometria o Keychain servono comunque librerie bridge dedicate, come react-native-nfc-manager o react-native-keychain. React Native è un’ottima base per costruire la shell nativa, ma non toglie il lavoro di gestire l’hardware in modo esplicito.
Quanto costa in più l’approccio ibrido?
Rispetto a una WebApp pura, il costo aggiuntivo è più o meno quello di uno sviluppatore React Native part-time dedicato alla shell e alle schermate native. Rispetto a un’app nativa completa, invece, si risparmia circa il 60-70%, perché il cuore applicativo resta comunque web. Nel caso di myGenius, la shell nativa rappresenta circa il 30% del codice totale.
E se domani Apple aprisse le API NFC al browser?
Se succedesse davvero, potremmo valutare di spostare la timbratura NFC dentro la WebApp e semplificare la shell. Ma non è uno scenario probabile a breve termine — Apple ha ottime ragioni strategiche per tenere le API NFC riservate alle app native, Apple Pay in primis. E comunque, anche se le API web dovessero migliorare, l’approccio ibrido resterebbe solido: la shell nativa si semplificherebbe, ma non sparirebbe.
Per concludere
Non esiste un vincitore assoluto tra WebApp e app nativa. Esiste la scelta giusta per il tuo prodotto, per il tuo team, per i vincoli che hai davanti.
Per un prodotto HR aziendale come myGenius, l’approccio ibrido — una shell nativa attorno a una WebApp — si è rivelato il compromesso migliore: aggiornamenti istantanei per il cuore dell’app, hardware nativo esattamente dove serve, token al sicuro nel Keychain, distribuzione via store con tutti i suoi vantaggi.
Il prezzo di questa scelta è la complessità architetturale — un messageController, un CookiesService, un livello di composizione che orchestra i vari package nativi. Ma è un prezzo che si paga una volta sola, in fase di setup, e che si ripaga a ogni singolo rilascio che non deve passare dalla review dello store.
Non scegliere una tecnologia per abitudine. Parti dalle funzioni che il prodotto deve svolgere, dall’hardware necessario e dal modo in cui il team dovrà mantenere l’app nel tempo.




