no início deste ano, nós introduzimos Reat Native para iOS. Reat Native traz o que os desenvolvedores são usados para Desde Reat on the web-declarative self-contained UI componentes e ciclos de desenvolvimento rápido-para a plataforma móvel, mantendo a velocidade, fidelidade e sensação de aplicações nativas. Hoje, estamos felizes em lançar Reat Native para Android.
no Facebook estamos usando Reat Native em produção há mais de um ano. Há quase exactamente um ano, a nossa equipa partiu para desenvolver a aplicação de gestão de anúncios. Nosso objetivo era criar um novo aplicativo para deixar os milhões de pessoas que anunciam no Facebook gerenciar suas contas e criar novos anúncios em movimento. Acabou por ser não só o primeiro aplicativo nativo totalmente React do Facebook, mas também o primeiro cross-platform one. Neste post, gostaríamos de compartilhar com vocês como construímos este aplicativo, como Reat Native nos permitiu mover mais rápido, e as lições que aprendemos.
escolher Reat Native
não há muito tempo, Reat Native era ainda uma nova tecnologia que não tinha sido comprovada na produção. Embora o desenvolvimento de um novo aplicativo baseado nesta tecnologia tenha acarretado algum risco, ele foi superado pelas potenciais vantagens.em primeiro lugar, a nossa equipa inicial de três engenheiros de produtos já estava familiarizada com a React. Em segundo lugar, o aplicativo precisava conter um monte de complexa lógica de negócios para lidar com as diferenças de formatos ad, fusos horários, formatos de data, moedas, Convenções de moeda, e assim por diante. Muito disso já foi escrito em JavaScript. A perspectiva de escrever todo esse código no Objective-C apenas para depois escrevê — lo em Java para a versão Android do aplicativo não era atraente-nem seria eficiente. Em terceiro lugar, em Reat Native seria fácil implementar a maioria das superfícies UI que queríamos construir — exibindo muitos dados na forma de listas, tabelas, ou gráficos. Os engenheiros de produtos poderiam ser imediatamente produtivos implementando essas visões, desde que soubessem reagir.
claro, algumas características apresenta um desafio para esta nova plataforma, por exemplo, o editor de imagens, que permite aos anunciantes ampliar e cortar uma foto, e a exibição do mapa, que permite aos anunciantes alvo pessoas dentro de um determinado raio de um local. Outro exemplo é a navegação de migalhas, que ajuda os anunciantes a visualizar a hierarquia de anúncios em suas contas. Estes proporcionaram-nos oportunidades para empurrarmos ainda mais a plataforma.
Building Ads Manager for iOS first
A equipe passou de três para oito engenheiros nos meses seguintes. Os novos recrutas, não estavam familiarizados com a Reagir — e alguns deles não estavam familiarizados com JavaScript, mas eles estavam ansiosos para construir uma grande experiência móvel para os nossos anunciantes, e eles cresceram rapidamente.engenheiros experientes do iOS na equipe React Native ajudaram-nos a fazer a ponte de recursos que ainda não estavam disponíveis no Reat Native, como fornecer acesso ao rolo de câmera do telefone. Eles também nos ajudaram a empacotar o aplicativo com algumas das bibliotecas iOS existentes do Facebook que já estavam sendo usadas em outros aplicativos do Facebook para realizar autenticação, análise, relatórios de acidentes, redes e notificações push. Isso deixou nossa equipe se concentrar em construir apenas o produto.
Como mencionado acima, nós fomos capazes de reutilizar um monte de nossas bibliotecas JavaScript pré-existentes. Uma dessas bibliotecas é Relay, o framework do Facebook para fornecer dados para reagir aplicativos via GraphQL. Outro conjunto de bibliotecas lidou com a internacionalização e localização, o que pode ser complicado quando os fusos horários e as moedas estão envolvidos. Normalmente, essas bibliotecas carregam a configuração correta a partir de um endpoint JSON no site. Nós escrevemos scripts para exportar os arquivos JSON para todos os locais suportados, incluindo os arquivos com o aplicativo usando pacotes localizados do iOS, e, em seguida, expôs os dados JSON para JavaScript com algumas linhas de código nativo. Isso permitiu que nossas bibliotecas funcionassem praticamente inalteradas.um dos maiores desafios que enfrentámos foi os fluxos de navegação. Para navegar nos anúncios e campanhas de um anunciante, queríamos uma barra de navegação. Para o fluxo de criação de anúncios, precisávamos de uma barra de navegação estilo feiticeiro. Além disso, foi também crucial para obter as animações de transição e gestos de toque certo, caso contrário o aplicativo teria se sentido mais como um site móvel glorificado do que um aplicativo nativo.
a nossa solução era o componente Navigator, que foi disponibilizado juntamente com o Reat Native no directório CustomComponents. Em essência, é um componente de reação que mantém o controle de um conjunto de outros componentes de reação em uma pilha. Ele pode exibir um destes componentes e animar entre eles com base em apertos de botão ou gestos de toque. Ele também tem um componente de barra de navegação pluggable, que nos permite implementar uma barra de navegação iOS-like Para vistas mais regulares, migalhas de pão para a navegação anúncios e campanhas, e um wizard-like stepper para o fluxo de criação. O componente de barra de navegação é notificado do progresso de animação e pode executar o incremento de animação necessário para corresponder. Isto significa que todas as animações, tanto para as vistas como para as barras de navegação, são computadas em JavaScript, mas testes mostraram que ainda éramos capazes de realizá-las em 60 fps.
só há uma maneira de as animações de navegação poderem gaguejar, e é quando o tópico JavaScript foi bloqueado durante uma grande operação. Quando encontramos este cenário, foi quase exclusivamente devido ao processamento de grandes quantidades de dados recém obtidos. Claro, faz sentido que quando você navega para uma nova visão, mais dados têm que ser carregados e processados. Em uma rede suficientemente rápida, esse processo poderia facilmente interferir com uma animação de navegação ainda em andamento. A nossa solução aqui era atrasar explicitamente o processamento de dados até que as animações fossem completas, usando o componente InteractionManager, que também faz parte da Reat Native. Nós primeiro animaríamos a uma visão que contivesse espaços livres e, em seguida, deixar Relay fazer o processamento de dados, o que automaticamente fez com que os componentes de reação necessários para re-renderizar.
envio de uma versão Android
Quando o Gerente de anúncios para iOS estava perto de transporte, começamos a olhar para a construção de uma versão Android do mesmo app. Um porto nativo de reação para Android parecia a melhor maneira de fazer isso funcionar. Felizmente, a equipe nativa React já estava trabalhando duro para criar apenas isso. Naturalmente, queríamos reutilizar o máximo de código de Aplicação possível. Não apenas a lógica de negócios, mas também o código UI, porque a maioria dos pontos de vista eram em grande parte os mesmos, exceto para algum estilo. Claro, havia lugares onde a versão Android precisava olhar e se sentir diferente da versão iOS, por exemplo, em termos de navegação ou usando elementos UI nativos para picadores de data, switches, etc.
felizmente, a funcionalidade de Lista de blocos do React Native packager e o mecanismo de abstração do React ajudaram-nos muito com a maximização da reutilização de código nas duas plataformas e minimizando a necessidade de verificações explícitas de plataformas. No iOS, dissemos ao empacotador para ignorar todos os ficheiros que acabassem .androide.js. Para o desenvolvimento do Android, ele ignorou todos os arquivos terminando em .s.js. Agora nós poderíamos implementar o mesmo componente uma vez para Android e uma vez para iOS, enquanto o código consumidor seria alheio à plataforma. Então, em vez de introduzir verificações explícitas se/else para a plataforma, nós tentamos refaturar partes específicas da plataforma UI em componentes separados que teriam uma implementação Android e iOS. Na época do Gerenciador de Anúncios de envio para o Android, essa abordagem rendeu cerca de 85 por cento de reutilização do Código app.
um desafio maior que enfrentamos foi como gerir o código fonte. Codebases Android e iOS foram gerenciados em dois repositórios diferentes no Facebook. O código fonte para o Gerenciador de anúncios para iOS viveu no repositório iOS, é claro, enquanto o código para a versão Android teria que viver no repositório Android por várias razões. Por exemplo, muito parecido com a versão iOS, nós queríamos fazer uso de algumas das bibliotecas Android do Facebook, que viviam no repositório Android. Além disso, todas as ferramentas de construção, automação e integração contínua para aplicativos Android foram conectados ao repositório Android. Dado que a porta Android do app necessitava de refactorar o código iOS existente para componentes abstratos específicos da plataforma em seus próprios arquivos, nós teríamos essencialmente sido constantemente bifurcando e fundindo duas versões da mesma base de código. Pareceu-nos uma situação inaceitável.
no final, decidimos designar o repositório iOS como a fonte da verdade, principalmente porque já estava lá e a versão iOS do app era a mais madura. Montamos um cronjob que sincronizou todo o código JavaScript do iOS para o repositório Android muitas vezes por dia. O envio de JavaScript para o repositório Android foi desencorajado e só foi permitido se ele foi seguido com um commit acompanhante para o repositório iOS. Se o script de sincronização detectou uma discrepância, ele preencheu uma tarefa para mais investigação.
Nós também tornamos possível para o servidor de packager JavaScript executar o Código Android do repositório iOS. Dessa forma, nossos desenvolvedores de produtos, que tocaram principalmente JavaScript e nenhum código nativo, poderiam desenvolver e testar suas alterações em ambos iOS e Android diretamente do repositório iOS. Mas isso ainda exigia que eles tivessem construído as partes nativas do aplicativo Android a partir do repositório Android, e o mesmo para o aplicativo iOS — um enorme imposto ao testar mudanças em duas plataformas. Para acelerar o fluxo para desenvolvedores somente JavaScript, nós também construímos um script que descarregou o binário nativo apropriado de nossos servidores de integração contínua. Isso tornou desnecessário até mesmo manter um clone do repositório Android para a maioria dos desenvolvedores — eles poderiam fazer todo o seu desenvolvimento JavaScript a partir da fonte da verdade no repositório iOS e iterate tão rápido quanto ou mais rápido do que na pilha web do Facebook.
o que aprendemos
a equipa nativa React desenvolveu a plataforma ao lado do nosso aplicativo, e expôs os componentes nativos e APIs que precisávamos para que isso acontecesse. Esses componentes irão beneficiar toda a gente a construir uma aplicação no futuro. Mesmo que tivéssemos que construir alguns componentes nós mesmos, usando Reat Native sobre puro native ainda teria valido a pena. Teríamos de escrever esses componentes de qualquer maneira, e provavelmente não seriam reutilizáveis por outras equipas ao longo da estrada.
uma lição que aprendemos foi que trabalhar através de repositórios de código iOS separados e Android é difícil, mesmo com muitas ferramentas e automação. Quando estávamos construindo o aplicativo, o Facebook usou este modelo, e todos os nossos processos de automação de construção e desenvolvimento foram configurados em torno dele. No entanto, não funciona bem para um produto que, na maioria das vezes, tem uma única base de código JavaScript compartilhada. Felizmente, o Facebook está se movendo para um repositório unificado para ambas as plataformas — apenas uma cópia do código JavaScript comum será necessária, e os syncs serão uma coisa do passado.outra lição que aprendemos foi o teste. Ao fazer mudanças, cada engenheiro deve ter o cuidado de testar em ambas as plataformas, e o processo é propenso ao erro humano. Mas isso é apenas uma consequência inevitável do desenvolvimento de uma aplicação multi-plataforma a partir da mesma base de código. Dito isso, o custo de um percalço ocasional devido a testes insuficientes é superado pela eficiência de desenvolvimento obtida usando Reat Native e ser capaz de reutilizar código em ambas as plataformas, em primeiro lugar. Tenha em mente, esta lição não se aplica apenas aos engenheiros de produtos; também se aplica aos engenheiros de plataforma Reat Native que trabalham em Objective-C e Java. Grande parte do trabalho que estes engenheiros fazem não se limita apenas às respectivas línguas nativas. Ele também pode afetar JavaScript-por exemplo, APIs de componentes ou implementações parcialmente compartilhadas. Engenheiros nativos do iOS normalmente não estão acostumados a ter que testar mudanças no Android, e o inverso é verdadeiro para os engenheiros do Android. Trata-se, sobretudo, de uma lacuna cultural que levou tempo e esforços para colmatar e, consequentemente, ao longo do tempo, a nossa estabilidade aumentou.também abordamos o problema construindo testes de integração que seriam executados em cada revisão. Enquanto isso funcionou fora da caixa para capturar questões de iOS em iOS e também para Android, nossos sistemas de integração contínua não foram configurados para executar testes Android em revisões de iOS e vice-versa. Isso levou um esforço de engenharia para resolver, e ainda há uma grande margem de erro suficiente para ocasionalmente quebrar o aplicativo.
Quando tudo foi dito e feito, a nossa aposta valeu a pena — fomos capazes de enviar o primeiro aplicativo nativo totalmente React do Facebook em duas plataformas, com aparência e sensação nativa, construído pela mesma equipe de engenheiros JavaScript. Nem todos os engenheiros estavam familiarizados com a React quando se juntaram à equipe, mas eles construíram um aplicativo iOS com visual nativo e sentir em apenas cinco meses. E depois de mais três meses, lançamos a versão Android do aplicativo.
em um esforço para ser mais inclusivo em nosso idioma, editamos este post para substituir a lista negra por lista de blocos.