Blogi
React Nativesta kahteen natiivisovellukseen muutamassa päivässä
Mikko McMenamin
Senior Software Developer
13.10.2026

Olin käyttänyt React Nativea sovellukseni kehitykseen yli vuoden verran, kunnes sain tarpeekseni.
Sovellus tuntui Androidilla tahmealta, käynnistyi hitaasti ja osa karusellien animaatioista nyki. Jahtasin jatkuvasti riippuvuuksien aiheuttamia Sentry-virheitä, enkä saanut sovelluksesta niin natiivin tuntuista kuin halusin.
Julkaisin SmartRepsin tammikuussa 2026 ja olen kehittänyt sitä siitä lähtien. Se on salitreenien suunnitteluun ja kirjaamiseen tarkoitettu sovellus, joka hyödyntää tekoälyä. Päätin korvata React Nativen iOS:llä SwiftUI:lla ja Androidilla Kotlinilla ja Jetpack Composella. Claude Code ja Codex tekivät suurimman osan koodauksesta. Varsinainen toteutus syntyi muutamassa päivässä.
SmartReps on keskikokoinen sovellus: treenien kirjaaminen, treenipohjat, tilastot, tilaukset, tekoälyominaisuudet ja alustaintegraatiot. Siirrettävää oli siis vähän enemmän kuin pari ruutua.
Kahden natiivisovelluksen ylläpito olisi aiemmin tuntunut minusta liian työläältä näin pienellä tiimillä. Koodausagenttien myötä yhtälö on selkeästi muuttumassa. Käytin kuitenkin paljon aikaa arkkitehtuuriin ja spekseihin ennen kuin päästin agentit toteutuksen kimppuun. Ilman tätä valmistelutyötä migraatio ei olisi onnistunut näin nopeasti.

Arkkitehtuuri koodausagenteille
Halusin agenttien pystyvän käyttämään sovellusta ja tarkistamaan muutoksensa itse. Muuten jokaisen ominaisuuden tarkistaminen kahdella alustalla jäisi edelleen minulle.
Molemmissa sovelluksissa on käyttöliittymästä riippumaton ydin eli headless core. Se hallitsee ominaisuuksien tilaa, navigaatiota ja työnkulkuja. Toimintaa ohjaavat säännöt on erotettu domain-kirjastoihin. SwiftUI ja Compose näyttävät ytimen tilan ja välittävät käyttäjän toiminnot takaisin sille tyypitettyinä komentoina eli intentioina. Näkymät eivät käytä backendia suoraan.
Kehitysversioita voi ohjata myös komentoriviltä. Agentti voi aloittaa treenin, siirtyä tietylle ruudulle, lukea sovelluksen tilan ja odottaa esimerkiksi treenin käynnistymistä:
smartreps send session.startFlexibleWorkout name="Push day" --remote 7411smartreps wait workoutTab=activeSession --remote 7411 --timeout 15000smartreps snapshot --remote 7411
Komentorivin ja käyttöliittymän komennot käsitellään samalla tavalla. Komentoriviltä treenin aloittava testi käyttää siis sovelluksen oikeaa työnkulkua. Snapshot-komento palauttaa samat tilatiedot, joita käyttöliittymä käyttää.

iOS:n ydintä voin ajaa Macillani kehitysbackendia vasten ilman simulaattoria. Androidin Convex SDK tarvitsee Android-ympäristön, joten integraatiotestit ajetaan kehityssovelluksessa emulaattorilla. Molempien ytimien testeissä voi käyttää myös oikean backendin korvaavaa testitoteutusta.
Tätä tarkoitan agenttisella sovellusarkkitehtuurilla. Agentin pitää voida käyttää sovellusta ja tarkastella sen tilaa. Se otetaan huomioon jo tilanhallinnan, navigaation ja käyttäjän toimintojen suunnittelussa.
Käyttöliittymä kuntoon
Vaikka arkkitehtuuri ja toimintaspeksit olivat paikoillaan, ensimmäisten ajojen käyttöliittymät eivät olleet lähelläkään alkuperäistä sovellusta. Ydin alkoi hahmottua, mutta käyttöliittymäkerros puuttui vielä suurelta osin.
Sisällöt, painikkeet, asettelut ja käyttöliittymän toiminta vaativat vielä paljon työtä. Pelkkä ”tee tästä hyvännäköinen” ei anna agentille kovin paljon eväitä.
Ohjausrajapinta auttoi tässäkin. Agentti pääsi tietylle ruudulle tunnetussa tilassa, otti kuvakaappauksen ja vertasi sitä alkuperäiseen. Sen jälkeen se korjasi erot ja otti uuden kuvan.

Tämä on helppo aliarvioida agenttien kanssa kehittäessä. Agenteille on annettava mahdollisuus myös katsoa ja tarkistaa, miltä lopputulos näyttää. Testi voi tarkistaa, että oikea ruutu on auki, mutta se ei kerro napin tekstin huonosta rivityksestä tai kömpelöstä asettelusta suuremmalla tekstikoolla.
Testejä, joista on agenteille hyötyä
Lopputulosta tarkistetaan usealla tasolla:
- Nopeat tarkistukset: arkkitehtuurisäännöt, kääntäminen, lint, generoidun koodin tarkistukset sekä domain- ja tilanhallintatestit.
- Toimintaskenaariot: yhteiset testit käyttävät sovellusta ohjausrajapinnan kautta oikean kehitysbackendin kanssa.
- Käyttöliittymän tarkistukset: napautukset ja eleet oikeassa sovelluksessa, saavutettavuus sekä molempien alustojen kuvakaappausten tarkastelu.
Jokaisella on tehtävänsä. Sovelluksen tilasta näkee, onko treeni käynnissä. Kuvakaappaus näyttää asettelun. Käyttöliittymätesti voi raahata liikeriviä ja tarkistaa, että järjestyksen muuttaminen toimii.

Myös virheilmoitusten pitää auttaa korjaamaan ongelma. Kun testin odottama tila ei toteudu ajoissa, ilmoitus kertoo, mitä odotettiin ja mitä sovellus palautti. Agentti saa näin lähtökohdan korjaukselle.
Nopeat tarkistukset ajetaan jokaisen muutoksen yhteydessä. Öisin ajettavat E2E-testit suorittavat yhteiset skenaariot, natiivit käyttöliittymätestit ja Maestro-testit molemmissa sovelluksissa kehitysbackendia vasten.
Tekoälyominaisuuksille on erilliset evalit. Treenigeneroinnissa arvioidaan esimerkiksi liikevalintoja, kuormitusta ja progression sopivuutta käyttäjän historiaan ja tavoitteisiin. Tuonnissa ja pikakirjauksessa verrataan tulkittuja liikkeitä ja sarjoja ennalta määriteltyihin odotuksiin.
iOS ja Android samassa linjassa
Molemmat sovellukset käyttävät samoja Alliumilla kirjoitettuja toimintaspeksejä, generoituja backend-tyyppejä, ohjausprotokollaa, testidataa, skenaarioita, käännöksiä ja grafiikoita. Swift- ja Kotlin-toteutusten moduulirakenteet vastaavat toisiaan. Agentit löytävät helposti vastaavan toteutuksen ja testit toiselta alustalta.
Sama skenaariotiedosto ajetaan molemmissa sovelluksissa. Testit tarkistavat, että sovellusten palauttamat tilatiedot käyttävät samoja JSON-kenttiä ja tietotyyppejä. Työnkulkujen testeillä varmistetaan toiminta. Alustakohtaiset erot kirjataan erikseen.

Jokainen sovellusmuutos tehdään molemmille alustoille samalla kertaa. HealthKitillä ja Health Connectilla on omat natiivit toteutuksensa. Agenttien ohjauspalvelin on mukana vain kehitysversioissa.
Toteutustyötä on jonkin verran enemmän, koska muutokset tehdään sekä Swiftillä että Kotlinilla. Yhteisten speksien ja agenttien ansiosta käsityötä tulee kuitenkin hyvin vähän lisää. React Nativekaan ei toiminut identtisesti iOS:llä ja Androidilla, joten molemmat piti tarkistaa jo silloin. QA-työn määrä on suunnilleen sama.
Mitäs hiton järkeä tässä sitten on?
SmartRepsillä on jo yli 1 700 käyttäjää, ja määrä kasvaa. Halusin tarjota heille parhaan mahdollisen käyttökokemuksen ja saavutettavuuden ilman, että tuotteesta pitää tinkiä frameworkin takia. Natiivit sovellukset kaipaavat vielä hienosäätöä, mutta konkreettisia eroja näkyy jo.
Käynnistysaika
Mittasin jokaiselle sovellukselle viisi käynnistystä iOS-simulaattorissa ja Android-emulaattorissa samalla demotilillä. Käyttäjä oli valmiiksi kirjautunut sisään, ja sovellusprosessi suljettiin ennen jokaista käynnistystä. Natiivisovellusten aloitusruutu tuli näkyviin nopeammin molemmilla alustoilla:

Latauskoko ja muistinkäyttö
Google Play ilmoittaa React Native -sovelluksen latauskooksi 42,3 Mt ja natiivin Android-sovelluksen kooksi 10,8 Mt. Noin 74 % pienempi.

Muistinkäyttökin väheni simulaattori- ja emulaattoritesteissä, kun sovellus oli sisäänkirjautuneena aloitusruudulla. iOS:n physical footprint laski 258,9 MiB:stä 70,0 MiB:iin ja Androidin PSS 322,9 MiB:stä 232,6 MiB:iin. Mittarit ovat alustakohtaisia: kumpaakin verrataan saman alustan React Native -versioon.

Alustaintegraatiot
Live Activities -ominaisuuksiin käyttämäni React Native -riippuvuudet olivat bugisia. Natiivina haluamani toiminta oli helppo toteuttaa: aktiviteetti pysyy näkyvissä koko treenin ajan, näyttää nykyisen liikkeen ja laskee lepoaikaa ylöspäin. Androidilla samat treenitiedot näkyvät jatkuvassa ilmoituksessa, joka voidaan näyttää Live Update -ilmoituksena sitä tukevilla laitteilla.
Myös Health Connectin integrointi Androidilla oli helpompaa. Painon synkronointi ja treenien vienti käyttävät nyt suoraan natiiveja rajapintoja. Toteutus huomioi erikseen Health Connectin saatavuuden ja tarvittavat käyttöoikeudet.
Vakaus
Ensimmäiset Sentry-luvutkin näyttävät lupaavilta:

Ero näkyy selvemmin ilman kaatumisia selvinneiden käyttäjien osuudessa kuin käyttökerroissa. Samalla ihmisellä voi olla paljon onnistuneita käyttökertoja ja silti yksi kaatuminen. Nämä ovat ensimmäisiä lukuja Sentrystä. Seuraan niitä, kun natiiviversiot saavat lisää käyttäjiä.
Ensimmäiset pari viikkoa näyttävät lupaavilta myös RevenueCatissa: yhä useampi kokeilee Prota.
Mikä teki migraation mahdolliseksi
Minulla oli jo toimiva sovellus, backend, ulkoasu ja tekstit mallina. Varsinainen iOS-toteutus syntyi noin neljässä päivässä ja Android-toteutus noin kahdessa. Olin tehnyt alustavan kokeilun jo aiemmin. Julkaisun valmistelu ja korjaukset jatkuivat tämän jälkeen.
Muutama päivä tarkoittaa siis varsinaista toteutusta. Arkkitehtuuri, speksit, käyttöliittymän hiominen, alustaintegraatiot ja sovelluskauppojen valmistelut vaativat silti työtä. Backendin piti myös tukea käyttäjien puhelimiin jo asennettuja React Native -versioita.
Hyödyllisintä oli mielestäni antaa agenteille keinot tarkistaa työnsä. Ne pystyivät toteuttamaan ominaisuuden, ajamaan tarkistukset, katsomaan tulosta sovelluksessa ja jatkamaan korjaamista. Itse pystyin keskittymään enemmän arkkitehtuuriin, tuotepäätöksiin ja käyttöliittymän laatuun.
Jos kehität sovellusta koodausagenttien kanssa, tähän kannattaa panostaa aikaisin. Tee sovelluksesta helppo ohjata, anna agentin lukea sen tila ja tarjoa konkreettiset keinot tarkistaa lopputulos. SmartRepsissä tämä teki kahden natiivisovelluksen ylläpidosta paljon käytännöllisemmän vaihtoehdon.
Jos haluat nähdä, miltä sovellus tuntuu käytössä, kokeile SmartRepsiä. Alkuun pääsee ilmaiseksi sekä iOS:llä että Androidilla.
Haluatko kuulla lisää? Ota yhteyttä!





