Wat is het verschil, technisch gezien?
Native betekent dat de app gebouwd is met het gereedschap van de fabrikant: Swift en SwiftUI voor iPhone, iPad, Mac en Apple Watch, Kotlin voor Android. De knoppen op het scherm zijn dan echte systeemknoppen, met gedrag dat niemand hoefde te programmeren.
Cross-platform betekent een gedeelde codebase, maar de bekende opties doen onderling iets heel anders.
- Flutter tekent alles zelf. Het draagt een eigen tekenmachine mee en schildert knoppen, lijsten en tekstvelden na. Wat je ziet lijkt op iOS, maar het is geen iOS.
- React Native gebruikt wel echte iOS-componenten, maar stuurt ze aan vanuit JavaScript. Het uiterlijk klopt daardoor beter; de koppeling tussen die twee werelden is waar het schuurt.
- Electron is een website in een venster, met een complete browser eromheen. Alleen relevant op de desktop.
Er is een vierde route die in Nederlandse vergelijkingen bijna nooit voorkomt: Kotlin Multiplatform. Daarbij deel je de logica (rekenregels, datamodel, netwerkcode) en bouw je de interface per platform native. Vaak de eerlijkste middenweg, omdat het precies deelt wat niet platformspecifiek hoeft te zijn.
Wanneer is cross-platform gewoon de betere keuze?
Cross-platform is geen noodoplossing, en de groep apps waarvoor het de juiste keuze is, is groter dan native-liefhebbers toegeven.
- De app is in de kern een venster op iets dat al bestaat: een webshop, een planningssysteem, een dossier. Lijsten, formulieren, detailpagina's.
- Je hebt iOS en Android op dag een nodig en je budget dekt geen twee teams.
- Het is een interne app voor collega's die hem moeten gebruiken, niet kiezen.
- Je bouwt om te leren of het idee klopt en verwacht de helft weg te gooien.
- Je hebt al een webteam dat React kent. Die kennis is meer waard dan de laatste tien procent afwerking.
In al die gevallen zit de waarde in wat er binnen de schermen gebeurt, niet in hoe diep de app in het toestel zit. Dan betaal je voor native vooral een tweede rekening voor hetzelfde resultaat.
Waarom loopt cross-platform vast op alles buiten het app-scherm?
Vrijwel elke vergelijking over dit onderwerp gaat over het scherm van de app. Daar zit het probleem niet. Het zit in alles daaromheen, en dat is precies waar Apple-gebruikers hun apps op beoordelen.
Widgets op het beginscherm, een Live Activity op het toegangsscherm en in de Dynamic Island, een Apple Watch-app, een toetsenbord dat in andere apps verschijnt, een knop in het deelblad, een Shortcuts-actie, iets in Bedieningspaneel: dat zijn allemaal losse uitbreidingen. Apple draait ze in een apart proces met een streng geheugenbudget, en bij widgets en Live Activities schrijft Apple voor dat de interface in SwiftUI getekend wordt. Je gedeelde Flutter- of React Native-scherm hergebruik je daar dus niet. Wie zo'n uitbreiding wil, schrijft Swift, ook in een Flutter-project.
Daarmee valt het hoofdargument onder cross-platform weg. Je hebt geen een codebase meer, maar drie: de gedeelde code, de Swift-kant voor de systeemintegratie, en de brug ertussen die niemand anders voor je onderhoudt.
Mijn eigen dicteerapp Transcribier bestaat grotendeels buiten zijn eigen venster: op de Mac een globale sneltoets die in elk tekstveld werkt, op iPhone en iPad een systeemtoetsenbord dat in andere apps verschijnt. Van dat product zou in een gedeelde codebase weinig overblijven.
Waarom voelt zo'n app anders, ook bij een vloeiende beeldsnelheid?
"Cross-platform haalt vrijwel native prestaties" is een uitspraak over beelden per seconde. Beeldsnelheid is zelden het probleem. Het verschil zit in gedrag dat je niet ziet maar wel merkt.
- Hoe een lijst afremt en terugveert als je hem loslaat, en dat je hem midden in die beweging weer kunt vastpakken.
- Tekstselectie met het vergrootglas, het contextmenu bij lang indrukken, slepen tussen apps.
- Het toetsenbord dat verschijnt en de inhoud precies ver genoeg opschuift.
- Iemand die zijn lettergrootte twee stappen omhoog zet, VoiceOver aanzet of beweging vermindert, en verwacht dat de hele app meebeweegt.
Elk van die dingen is los na te bouwen. Native krijg je ze omdat je de systeemcomponenten gebruikt die het al doen. En hoe dichter een nabootsing komt, hoe meer de resterende afwijkingen opvallen: een app die er voor negentig procent uitziet als iOS voelt vaak vreemder dan een app die zichtbaar zijn eigen weg gaat.
Twee dingen merk je direct. De download is groter, want de app draagt zijn eigen tekenmachine of runtime mee. En koud opstarten duurt langer, want die machine moet eerst op gang komen.
Wat gebeurt er als Apple in september iets nieuws uitbrengt?
Elk najaar verschijnt er een nieuwe versie van iOS en macOS, meestal met een aangepast uiterlijk en een reeks nieuwe mogelijkheden. Een native app compileer je opnieuw met de nieuwe SDK, en de systeemcomponenten passen zich grotendeels vanzelf aan. Nieuwe functies kun je dezelfde dag gebruiken.
Bij cross-platform wacht je twee keer: eerst op het framework, daarna op de maker van de plugin die de nieuwe functie ontsluit. Soms is dat een paar weken, soms komt het er niet, want veel van die plugins zijn het avondwerk van een enkeling. Dat is de kostenpost die in geen enkele offerte staat: de levensduur van je app hangt aan onderhoud waar je geen invloed op hebt.
Waarom geen Electron op de Mac?
Op de desktop is de afweging scherper, omdat Electron een volledige browser meelevert per app. Drie zulke apps zijn drie browsers in je geheugen, en op een laptop merk je dat aan de accu. Daar bovenop komt alles wat een Mac-app hoort te kunnen: een echte menubalk, vensterbeheer, sneltoetsen, tekstvervanging, Spotlight. Kleine dingen die zich opstapelen tot een app die zich als bezoeker gedraagt.
Toch is Electron niet altijd fout. Is je product al een webapp en wil je vooral een venster met notificaties en een pictogram in de Dock, dan is het verdedigbaar. Het wordt een fout zodra de desktop-app zelf het product is. Voor mijn eigen apps ligt daar de grens.
Een beslisregel die je zonder programmeerkennis kunt toepassen
Beantwoord vier vragen. Ze gaan niet over techniek, maar over waar de waarde van je app zit.
- Waar leeft je app? Schrijf je kernfuncties op en tel hoeveel ervan buiten het app-venster gebeuren: widget, toegangsscherm, Watch, toetsenbord, deelblad, Shortcuts, achtergrondtaken. Nul betekent dat cross-platform prima kan. Een of twee betekent een gemengde codebase, reken erop. Drie of meer betekent native, anders bouw je die functies alsnog twee keer.
- Is de app het product, of een venster op je product? Betalen klanten voor de app zelf, dan is afwerking geen luxe. Is de app een handiger toegang tot een dienst die je al levert, dan weegt bouwsnelheid zwaarder.
- Heb je Android echt op dag een nodig? Veel apps beginnen bewust bij een platform en groeien pas daarna. Als het antwoord "eigenlijk niet meteen" is, verdwijnt het belangrijkste argument voor cross-platform.
- Wie onderhoudt dit over drie jaar? Kies de techniek waarvoor je mensen kunt vinden en betalen, ook als je huidige bouwer er niet meer is.
Er staat bewust geen vijfde vraag over prestaties bij. Deze keuze loopt zelden mis op snelheid. Hij loopt mis op integratie.
Wat betekent dit als je iemand gaat inhuren?
De keuze bepaalt wie je zoekt, en de bouwer die je spreekt bepaalt vaak stilzwijgend de keuze. Drie vragen maken dat zichtbaar.
- Waar zitten de widgets, de Live Activity en de Watch-app in het plan? Luidt het antwoord "dat kan met een plugin", vraag dan wie die plugin onderhoudt.
- Wat gebeurt er in de week na een grote iOS-release?
- Wie zet de app in de App Store, en wie houdt hem daar?
Zelf ontwerp en bouw ik native voor iPhone, iPad, Mac en Apple Watch, en breng ik mijn apps zelf uit, van eerste schets tot App Store. Op app laten maken staat hoe zo'n traject loopt; op freelance iOS-developer staat wat ik als opdracht aanneem.