Animações fornecidas pelo servidor em aplicativos iOS



Olá pessoal! Há cerca de seis meses, lançamos um dos recursos mais interessantes do Badoo: transmissão ao vivo . Uma de suas principais funcionalidades é que os espectadores podem enviar presentes para seus streamers favoritos para expressar sua gratidão. Queríamos tornar os presentes os mais chiques e atraentes possíveis, por isso foi decidido deixar alguns deles realmente animados, e com isso quero dizer animados. E para envolver ainda mais as pessoas, nós, a equipe do Badoo, planejamos atualizar esses presentes e animações a cada poucas semanas.

Como engenheiro do iOS, você já deve ter adivinhado o desafio que enfrentamos aqui: a necessidade de adicionar novas animações e remover as antigas exigia uma quantidade considerável de trabalho do lado do cliente. Precisávamos das equipes de desenvolvimento do Android e do iOS para cada versão - o que, combinado com o tempo que as revisões e a aprovação da App Store costumam levar, levaria alguns dias para que cada atualização fosse lançada. Mas resolvemos o problema e vou explicar como.

Visão geral da solução


Nesta fase, já sabíamos como exportar animações do Adobe After Effects (AAE) para o formato legível pelo nosso aplicativo iOS usando a biblioteca Lottie. Desta vez, porém, fomos um pouco mais longe: decidimos criar um tipo de serviço de armazenamento de animação, disponível via internet. Em outras palavras, armazenaríamos todas as animações reais no servidor e as entregaríamos aos aplicativos clientes sob demanda:



Aqui está a aparência da solução final no simulador iOS na máquina do desenvolvedor:


No entanto, neste post, o exemplo que vou usar é uma animação muito simples que eu mesmo criei. Não é tão sofisticado quanto o do Badoo, mas é bom o suficiente para demonstrar o potencial da abordagem descrita.

Exportando animações


O projeto de animações do Adobe After Effects (AAE) que estou usando aqui pode ser encontrado junto com outros arquivos de origem no github . Portanto, depois de abrir o projeto de animação AAE localizado em _raw/animations/Fancy/Fancy.aep , você deverá ver uma janela como esta:



Neste ponto, não vou explicar como as animações estão sendo criadas no AEE, mas o que vou explicar é como importar animações já existentes do AAE para um formato legível por aplicativo iOS usando o plug-in Bodymovin .

Depois de verificar se o plug-in está instalado, abra-o selecionando a opção Window / Extensions / Bodymovin no menu:



Agora você deve ver a janela Bodymovin, onde pode selecionar a animação que deseja exportar, especificar o caminho do arquivo de saída e abrir as configurações de exportação:



Após selecionar e abrir as configurações de animação, agora podemos pedir ao Bodymovin para incorporar os ativos no arquivo JSON resultante, marcando a opção Assets / Include in json :



Por fim, a composição de animação selecionada é exportada e salva no arquivo especificado, clicando no botão Renderizar .

Armazenando animações no servidor


Vamos supor que movemos nossos arquivos JSON de animações renderizadas para o servidor da Web preferido via Internet. No nosso caso, por uma questão de simplicidade, eu os carreguei no repositório github deste projeto. As animações estão disponíveis aqui:


URL base: https://raw.githubusercontent.com/chupakabr/server-provided-animations/master/_raw/rendered-animations/

IDs específicos da animação:

  • clouds.json
  • fireworks.json

Nota: Procurando um servidor Web de provedor de animações escrito em Swift? Encontre a solução aqui no github e uma explicação detalhada neste artigo .
Neste ponto, temos um servidor provedor de animações totalmente funcional, então é hora de passar para a parte mais interessante: apresentar as animações aos nossos usuários.

Buscando e apresentando animações


Neste ponto, recomendo fortemente abrir nosso exemplo de projeto de aplicativo para iOS localizado em Client/ServerProvidedAnimation.xcworkspace pois ele já possui todo o código e configurações necessários.

Carregando dados de animações


Dado que os pontos de extremidade da API REST para obter dados de animação agora estão em funcionamento, é hora de introduzir o protocolo do provedor de dados e adicionar a implementação do servidor:

 import Lottie protocol AnimationsProviderProtocol { typealias Completion = (_ animation: LOTComposition?) -> Void func loadAnimation(byId id: String, completion: @escaping Completion) } final class ServerAnimationProvider: AnimationsProviderProtocol { private let endpoint: URL init(endpoint: URL) { self.endpoint = endpoint } func loadAnimation(byId id: String, completion: @escaping Completion) { let path = "/\(id).json" guard let animationUrl = URL(string: path, relativeTo: self.endpoint) else { completion(nil) return } URLSession.shared.invalidateAndCancel() let task = URLSession.shared.dataTask(with: animationUrl) { (data, response, error) in guard error == nil, let data = data, let json = self.parseJson(from: data) else { completion(nil) return } let animation = LOTComposition(json: json) completion(animation) } task.resume() } private func parseJson(from data: Data?) -> [AnyHashable : Any]? { guard let data = data else { return nil } do { let json = try JSONSerialization.jsonObject(with: data, options: []) as? [AnyHashable : Any] return json } catch { return nil } } } 

Essa classe de provedor de dados nos permite carregar animações do servidor no formato JSON sob demanda e mantê-las na memória para renderização na interface do usuário. Supondo que estamos seguindo o padrão MVVM, ele pode ser facilmente usado na entidade ViewModel da seguinte maneira:

  // ... private let animationProvider: AnimationsProviderProtocol private(set) var animationModel: LOTComposition? // … func loadAnimation(byId animationId: String) {     self.animationProvider.loadAnimation(byId: animationId) { [weak self] (animationModel) in         self?.animationModel = animationModel     } } // ... 

O ViewModel atualiza a propriedade de dados de animação selecionada quando recebe uma resposta HTTP válida do servidor com um objeto JSON não vazio dentro. Esses dados são usados ​​pela camada de apresentação para agendar a renderização da animação.

Camada de apresentação


Agora podemos usar nosso ViewModel para acessar os dados da animação e apresentá-los através da interface do usuário no manipulador de ações "no toque" anexado ao botão:

 class ViewController: UIViewController {   // ...   @IBOutlet weak var animationContainer: UIView!   override func viewDidLoad() {       super.viewDidLoad()       // ...       self.animationView = {           let view = LOTAnimationView(frame: self.animationContainer.bounds)           self.animationContainer.addSubview(view)           return view       }()   }   @IBAction func onPlayAnimationAction(_ sender: Any) {       self.animationView.stop()       self.animationView.sceneModel = self.viewModel.animationModel       self.animationView.play()   } } 

Basicamente, o que temos aqui é um manipulador de botão que aciona uma atualização da instância LOTAnimationView com os dados de animação mais recentes provenientes do ViewModel .

Veja como é o resultado final:


É isso mesmo. Agora, as animações estão sendo carregadas do terminal de API REST preparado e renderizadas no cliente sob demanda.

Dicas e Limitações


Dicas e truques:

  • O AAE permite o uso da maioria dos tipos de ativos, incluindo gráficos de varredura e vetoriais;
  • O Bodymovin torna possível incorporar todos os ativos em um arquivo de animação JSON de saída (usando a codificação base64) - isso significa que podemos evitar o carregamento de ativos separados no lado do cliente;
  • Para as animações, você pode optar entre desenhar diretamente no vetor no AAE ou simplesmente importar gráficos vetoriais do Adobe Illustrator.

Infelizmente, ainda não é possível importar gráficos vetoriais SVG para o AAE (tentei!).

Mais truques e possíveis problemas são descritos neste incrível artigo escrito pelo meu colega Radoslaw Cieciwa .

Conclusões


Então, o que o uso de animações fornecidas pelo servidor nos fornece? O benefício mais óbvio dessa abordagem é a capacidade de dissociar todos os envolvidos no fluxo de atualização das animações. Em outras palavras, para lançar uma nova animação sofisticada, todos os designers precisam fornecer a representação JSON da animação à equipe do servidor. E para remover uma, a equipe do servidor apenas precisa remover essa animação específica do serviço de descoberta. Sem tempo perdido!

Outra coisa interessante é que a mesma funcionalidade pode ser implementada em todas as plataformas de clientes suportadas (iOS, Android, Web, etc.) sem precisar ajustar a funcionalidade do servidor ou animações brutas existentes.

É isso por hoje! Obrigado pela leitura

Recursos


Source: https://habr.com/ru/post/pt439370/


All Articles