tidligere i år introducerede vi React Native til iOS. React Native bringer, hvad udviklere er vant til fra React på nettet — deklarative selvstændige UI-komponenter og hurtige udviklingscyklusser — til den mobile platform, samtidig med at hastigheden, troskab, og fornemmelsen af native applikationer bevares. I dag er vi glade for at frigive React Native til Android.
på Facebook har vi brugt React Native i produktion i over et år nu. For næsten nøjagtigt et år siden satte vores team sig for at udvikle Ads Manager-appen. Vores mål var at oprette en ny app for at lade de millioner af mennesker, der annoncerer på Facebook, administrere deres konti og oprette nye annoncer på farten. Det endte med at blive ikke kun Facebook ‘ s første fuldt reagere indfødte app, men også den første cross-platform en. I dette indlæg vil vi gerne dele med dig, hvordan vi byggede denne app, Hvordan React Native gjorde det muligt for os at bevæge os hurtigere og de erfaringer, vi lærte.
valg af React Native
for ikke længe siden var React Native stadig en ny teknologi, der ikke var bevist i produktionen. Mens udviklingen af en ny app baseret på denne teknologi medførte en vis risiko, det blev opvejet af de potentielle ulemper.
for det første var vores første team af tre produktingeniører allerede bekendt med React. For det andet skulle appen indeholde en masse kompleks forretningslogik for nøjagtigt at håndtere forskelle i annonceformater, tidsområder, datoformater, valutaer, valutakonventioner og så videre. Meget af dette var allerede skrevet i JavaScript. Udsigten til at skrive al den kode i Objective-C kun for senere at skrive den i Java til Android — versionen af appen var ikke tiltalende-og det ville heller ikke være effektivt. For det tredje ville det i React Native være let at implementere de fleste af de UI — overflader, vi ønskede at bygge-vise masser af data i form af lister, tabeller eller grafer. Produkt ingeniører kunne være umiddelbart produktive gennemføre disse synspunkter, så længe de vidste reagere.nogle funktioner var naturligvis en udfordring for denne nye platform — f.eks. billededitoren, som lader annoncører forstørre og beskære et foto, og kortvisningen, som lader annoncører målrette mod personer inden for en bestemt radius af en placering. Et andet eksempel er brødkrummenavigationen, som hjælper annoncører med at visualisere hierarkiet af annoncer på deres konti. Disse gav os muligheder for at skubbe platformen videre.
Building Ads Manager til iOS first
vores team besluttede at udvikle en iOS-version af appen først, som var meget godt tilpasset React Native, der også blev udviklet først til iOS. Vi voksede holdet fra tre til otte ingeniører i løbet af de følgende måneder. De nye rekrutter var ikke fortrolige med React — og nogle af dem var ikke fortrolige med JavaScript — men de var ivrige efter at opbygge en fantastisk mobiloplevelse for vores annoncører, og de steg hurtigt op.
erfarne iOS-ingeniører på React Native-teamet hjalp os med at bygge bro over funktioner, der endnu ikke var tilgængelige i React Native, såsom at give adgang til telefonens kamerarulle. De hjalp os også med at samle appen med nogle af Facebooks eksisterende iOS-biblioteker, der allerede blev brugt i andre Facebook-Facebook-apps til at udføre godkendelse, analyse, crashrapportering, netværk og push-meddelelser. Det lader vores team fokusere på at opbygge netop produktet.
som nævnt ovenfor var vi i stand til at genbruge mange af vores allerede eksisterende JavaScript-biblioteker. Et sådant bibliotek er Relay, Facebooks ramme for levering af data til React-applikationer via Facebook. Et andet sæt biblioteker beskæftigede sig med internationalisering og lokalisering, hvilket kan være vanskeligt, når tidsområder og valutaer er involveret. Normalt indlæser disse biblioteker den rigtige konfiguration fra et JSON-slutpunkt på hjemmesiden. Vi skrev scripts for at eksportere JSON-filerne til alle understøttede lokaliteter, inkluderede filerne med appen ved hjælp af iOS ‘ s lokaliserede bundter og udsatte derefter JSON-dataene for JavaScript med et par linjer med indbygget kode. Dette gjorde det muligt for vores biblioteker at arbejde næsten uændret.
en af de største udfordringer, vi stod overfor, var navigationsstrømmene. For at navigere i en annoncørs eksisterende annoncer og kampagner ønskede vi en brødkrummenavigationslinje. Til annonceoprettelsesstrømmen havde vi brug for en navigationslinje i guiden-stil. Oven i købet, det var også afgørende at få overgangen animationer og touch gestus højre, ellers app ville have følt mere som en glorificeret mobil hjemmeside end en indfødt app.
vores løsning var Navigator komponent, som blev stillet til rådighed sammen med React Native under CustomComponents mappe. I det væsentlige er det en React-komponent, der holder styr på et sæt andre React-komponenter i en stak. Det kan vise en af disse komponenter og animere mellem dem baseret på knaptryk eller berøringsbevægelser. Det har også en pluggbar navigationslinjekomponent, som lader os implementere en iOS-lignende navigationslinje til de fleste regelmæssige visninger, brødkrummer til navigering af annoncer og kampagner og en guiden-lignende trin til oprettelsesstrømmen. Navigationslinjen komponent er underrettet om animation fremskridt og kan udføre den nødvendige animation tilvækst til at matche. Dette betyder, at alle animationer, både for visningerne og for navigationslinjerne, beregnes i JavaScript, men test viste, at vi stadig var i stand til at udføre dem ved 60 fps.
der er kun en måde, hvorpå navigationsanimationer kunne stamme, og det var da JavaScript-tråden blev blokeret under en stor operation. Da vi stødte på dette scenario, skyldtes det næsten udelukkende behandling af store mængder nyligt hentede data. Det giver selvfølgelig mening, at når du navigerer til en ny visning, skal flere data indlæses og behandles. På et tilstrækkeligt hurtigt netværk kunne denne proces let forstyrre en navigationsanimation, der stadig er i gang. Vores løsning her var at eksplicit udsætte databehandlingen, indtil animationerne var færdige, ved hjælp af Interaktionmanager-komponenten, som også sendes som en del af React Native. Vi vil først animere til en visning, der indeholdt pladsholdere og derefter lade Relay udføre databehandlingen, hvilket automatisk fik de nødvendige React-komponenter til at gengive igen.
forsendelse af en Android-version
da Ads Manager til iOS var tæt på forsendelse, begyndte vi at se på at opbygge en Android-version af den samme app. En React Native port til Android virkede som den bedste måde at få det til at fungere. Heldigvis var React Native-teamet allerede hårdt på arbejde med at skabe netop det. Naturligvis ønskede vi at genbruge så meget appkode som muligt. Ikke kun forretningslogikken, men også UI-koden, fordi de fleste af visningerne stort set var de samme, bortset fra noget styling. Selvfølgelig, der var steder, hvor Android-versionen skulle se anderledes ud end iOS-versionen, for eksempel, med hensyn til navigation eller brug af native UI-elementer til datovælgere, kontakter, etc.
heldigvis hjalp React Native packager ‘ s block list-funktionen og reacts abstraktionsmekanisme os meget med at maksimere genbrug af kode på tværs af de to platforme og minimere behovet for eksplicit platformkontrol. På iOS bad vi pakkeren om at ignorere alle filer, der sluttede på .Android.js. Til Android-udvikling ignorerede den alle filer, der sluttede på .ios.js. Nu kunne vi implementere den samme komponent en gang til Android og en gang til iOS, mens den forbrugende kode ville være uvidende om platformen. Så i stedet for at introducere eksplicit if/else-kontrol for platformen, forsøgte vi at refactor platformspecifikke dele af brugergrænsefladen i separate komponenter, der ville have en Android-og iOS-implementering. På tidspunktet for shipping Ads Manager til Android gav denne tilgang omkring 85 procent genbrug af appkode.
en større udfordring, som vi stod overfor, var, hvordan man styrer kildekoden. Android-og iOS-kodebaser blev administreret i to forskellige arkiver på Facebook. Kildekoden til Ads Manager til iOS boede naturligvis i iOS-depotet, mens koden til Android-versionen skulle leve i Android-depotet af forskellige årsager. For eksempel, ligesom med iOS-versionen, ønskede vi at gøre brug af et par af Facebook ‘ s Android-biblioteker, som boede i Android-depotet. Derudover blev alle byggeværktøjer, automatisering og kontinuerlig integration til Android-apps tilsluttet Android-arkivet. I betragtning af at appens Android-port krævede refactoring af eksisterende iOS-kode til abstrakte platformspecifikke komponenter i deres egne filer, ville vi i det væsentlige konstant forkere og flette to versioner af den samme kodebase. Det virkede som en uacceptabel situation for os.
i sidste ende besluttede vi at udpege iOS-depotet som sandhedskilden, mest fordi det allerede var der, og iOS-versionen af appen var den mest modne. Vi oprettede en cronjob, der synkroniserede Al JavaScript-kode fra iOS til Android-depotet mange gange om dagen. At forpligte JavaScript til Android-depotet blev afskrækket og var kun tilladt, hvis det blev fulgt op med en ledsagende forpligtelse til iOS-depotet. Hvis synkroniseringsskriptet opdagede en uoverensstemmelse, indgav det en opgave til yderligere undersøgelse.
vi gjorde det også muligt for JavaScript packager-serveren at køre Android-kode fra iOS-depotet. På den måde kunne vores produktudviklere, der for det meste rørte ved JavaScript og ingen indbygget kode, udvikle og teste deres ændringer på både iOS og Android direkte fra iOS-depotet. Men det krævede stadig, at de havde bygget de oprindelige dele af Android — appen fra Android-depotet, og det samme for iOS-appen-en enorm skat, når man tester ændringer på to platforme. For at fremskynde strømmen for udviklere, der kun er JavaScript, byggede vi også et script, der hentede den relevante native binære fra vores kontinuerlige integrationsservere. Dette gjorde det unødvendigt at endda holde en klon af Android — depotet for de fleste udviklere-de kunne gøre al deres JavaScript-udvikling fra sandhedskilden i iOS-depotet og gentage så hurtigt som eller hurtigere end på Facebook ‘ s internetstabel.
hvad vi lærte
React Native-teamet udviklede platformen sammen med vores app og udsatte de native komponenter og API ‘ er, som vi havde brug for for at få det til at ske. Disse komponenter vil gavne alle, der bygger en app i fremtiden. Selv hvis vi selv havde opbygget et par komponenter, ville det stadig have været det værd at bruge React Native over pure native. Vi ville alligevel have været nødt til at skrive disse komponenter, og de ville sandsynligvis ikke have været genanvendelige af andre hold nede ad vejen.
en lektion, vi lærte, var, at det er svært at arbejde på tværs af separate iOS-og Android-kodelagre, selv med masser af værktøjer og automatisering. Da vi byggede appen, brugte Facebook denne model, og alle vores build-automatiserings-og udviklerprocesser blev oprettet omkring den. Det fungerer dog ikke godt for et produkt, der for det meste har en enkelt delt JavaScript-kodebase. Heldigvis flytter Facebook til et samlet lager for begge platforme — kun en kopi af almindelig JavaScript-kode er nødvendig, og synkroniseringer hører fortiden til.
en anden lektion, vi lærte, vedrørte test. Når der foretages ændringer, skal enhver ingeniør være omhyggelig med at teste på begge platforme, og processen er tilbøjelig til menneskelig fejl. Men det er bare en uundgåelig konsekvens af at udvikle en cross-platform app fra samme kodebase. Når det er sagt, opvejes omkostningerne ved et lejlighedsvis uheld på grund af utilstrækkelig test langt af den udviklingseffektivitet, der opnås ved at bruge React Native og være i stand til at genbruge kode på tværs af begge platforme i første omgang. Husk, denne lektion gælder ikke kun for produktingeniører; det gælder også for React Native platform ingeniører, der arbejder i Objective-C og Java. Meget af det arbejde, disse ingeniører udfører, er ikke udelukkende begrænset til de respektive modersmål. Det kan også påvirke JavaScript-for eksempel komponent API ‘ er eller delvist delte implementeringer. Indfødte iOS-ingeniører er typisk ikke vant til at skulle teste ændringer på Android, og det modsatte gælder for Android-ingeniører. Dette er hovedsageligt et kulturelt hul, der tog tid og kræfter at lukke, og som et resultat er vores stabilitet over tid steget.
vi behandlede også problemet ved at opbygge integrationstest, der ville køre på hver revision. Mens dette fungerede ud af kassen for at fange iOS-problemer på iOS og ligeledes til Android, blev vores kontinuerlige integrationssystemer ikke konfigureret til at køre Android-test på iOS-revisioner og omvendt. Dette krævede teknisk indsats for at løse, og der er stadig en stor nok fejlmargin til lejlighedsvis at bryde appen.
når alt blev sagt og gjort, betalte vores indsats sig — vi var i stand til at sende Facebook ‘ s første fuldt React Native app på to platforme med indbygget udseende, bygget af det samme team af JavaScript-ingeniører. Ikke alle ingeniører var bekendt med React, da de sluttede sig til holdet, men de byggede en iOS-app med indbygget udseende på bare fem måneder. Og efter yderligere tre måneder frigav vi Android-versionen af appen.
i et forsøg på at være mere inkluderende på vores sprog har vi redigeret dette indlæg for at erstatte sort liste med blokliste.