Construindo uma aplicação Full Stack com Swift
Revelei recentemente no Twitter algo que pode ter sido uma surpreendente para muitos dos meus membros da comunidade Swift/iOS: eu nunca tinha escrito um aplicativo para iOS antes! Venho escrevendo Swift há alguns anos, mas me concentrei inteiramente no desenvolvimento de bibliotecas e no Swift do lado do servidor.
Um recurso altamente chamativo do Swift é que ele permite que você escreva um aplicativo para iOS e um backend correspondente – um aplicativo completo e de ponta a ponta – tudo na mesma linguagem. É semelhante a como usar o Node.js para o backend de um aplicativo da web permite escrever JavaScript em qualquer lugar.
Para testar isso e aprender sobre o desenvolvimento para iOS, decidi criar um aplicativo full-stack totalmente em Swift. Optei por um aplicativo CRUD conhecido, para o qual já criei uma versão web, um aplicativo que permite ao usuário gerenciar uma lista de gatinhos e informações sobre eles.
Optar por construir o aplicativo utilizando os seguintes componentes:
- Um servidor de backend, escrito usando a popular estrutura web do Swift Vapor e usando o driver do MongoDB Swift via MongoDBVapor para armazenar dados no MongoDB
- Um aplicativo iOS criado com a SwiftUI e usando oSwiftBSON para dar suporte à serialização/deserialização de dados de/para JSON estendido, uma versão do JSON com extensões específicas do MongoDB para simplificar a preservação do tipo
Consegui combinar tudo isso em uma única base de código com uma estrutura de pastas como a seguir:
1 FullStackSwiftExample/ 2 ├── Models/ 3 │ ├── Package.swift 4 │ └── Sources/ 5 │ └── Models/ 6 │ └── Models.swift 7 ├── Backend/ 8 │ ├── Package.swift 9 │ └── Sources/ 10 │ ├── App/ 11 │ │ ├── configure.swift 12 │ │ └── routes.swift 13 │ └── Run/ 14 │ └── main.swift 15 └── iOSApp/ 16 └── Kittens/ 17 ├── KittensApp.swift 18 ├── Utilities.swift 19 ├── ViewModels/ 20 │ ├── AddKittenViewModel.swift 21 │ ├── KittenListViewModel.swift 22 │ └── ViewUpdateDeleteKittenViewModel.swift 23 └── Views/ 24 ├── AddKitten.swift 25 ├── KittenList.swift 26 └── ViewUpdateDeleteKitten.swift
No geral, foi uma ótima experiência de aprendizado para eu e, embora o aplicativo seja bem básico, estou honrado com o que eu criei! Aqui está o aplicativo finalizado, as instruções para executá-lo e a documentação de cada componente.
No restante deste post, discutirei algumas de minhas conclusões sobre essa experiência.
Como mencionei acima, criei um pacote SwiftPM compartilhado para qualquer código que eu quisesse usar no frontend e no backend do meu aplicativo. Nesse pacote, defini os tipos
Codable
modelando os dados em meu aplicativo, por exemplo:1 /** 2 * Represents a kitten. 3 * This type conforms to `Codable` to allow us to serialize it to and deserialize it from extended JSON and BSON. 4 * This type conforms to `Identifiable` so that SwiftUI is able to uniquely identify instances of this type when they 5 * are used in the iOS interface. 6 */ 7 public struct Kitten: Identifiable, Codable { 8 /// Unique identifier. 9 public let id: BSONObjectID 10 11 /// Name. 12 public let name: String 13 14 /// Fur color. 15 public let color: String 16 17 /// Favorite food. 18 public let favoriteFood: CatFood 19 20 /// Last updated time. 21 public let lastUpdateTime: Date 22 23 private enum CodingKeys: String, CodingKey { 24 // We store the identifier under the name `id` on the struct to satisfy the requirements of the `Identifiable` 25 // protocol, which this type conforms to in order to allow usage with certain SwiftUI features. However, 26 // MongoDB uses the name `_id` for unique identifiers, so we need to use `_id` in the extended JSON 27 // representation of this type. 28 case id = "_id", name, color, favoriteFood, lastUpdateTime 29 } 30 }
Quando você usa linguagens de código/programação separadas para representar dados no frontend e no backend de um aplicativo, é fácil que as implementações fiquem fora de sincronia. Mas nesse aplicativo, como o mesmo tipo exato de modelo é usado para as representações defront-ende back-end de gatinhos, não pode haver nenhuma inconsistência.
Como esse tipo está em conformidade com o protocolo
Codable
, também obtemos uma definição única e consistente para a representação de um gatinho em formatos de dados externos. Os formatos usados neste aplicativo são:- JSON estendido, que o frontend e o backend usam para se comunicar via HTTP, e
- BSON, que o backend e o MongoDB usam para se comunicar
Para um exemplo concreto do uso de um tipo de modelo em toda a pilha, quando um usuário adiciona um novo gatinho por meio da interface do usuário, os dados fluem pelo aplicativo da seguinte forma:
- O aplicativo iOS cria uma nova instância
Kitten
contendo os dados fornecidos pelo usuário - A instância do
Kitten
é serializada para JSON estendido viaExtendedJSONEncoder
e enviada em uma solicitação POST para o backend - O backend Vapor desserializa uma nova instância do
Kitten
a partir dos dados JSON estendidos utilizandoExtendedJSONDecoder
- O
Kitten
é passado para o método do driver MongoDBMongoCollection<Kitten>.insertOne()
- O driver do MongoDB usa seu
BSONEncoder
integrado para serializar oKitten
para o BSON e enviá-lo por meio do protocolo de fiodo MongoDB para o banco de dados
Com todas essas transformações, pode ser complicado garantir que o frontend e o backend permaneçam sincronizados em termos de forma de modelar, serializar e desserializar dados. Usar o Swift em todos os lugares e compartilhar esses
Codable
tipos de dados me permitiram evitar esses problemas completamente neste aplicativo.Apesar de nunca ter criado um aplicativo para iOS antes, verifiquei que minha experiência com o Swift tornava surpreendentemente fácil aprender os conceitos de que eu precisava para implementar a parte para iOS do meu aplicativo. Suspeito que é mais comum que alguém Go na direção oposta, mas acha que a experiência do iOS também se traduziria bem em escrever um backend Swift!
Usei vários recursos da linguagem Swift , como protocolos, encerramentos finaise propriedades computadas no código do iOS e do backend. Também aproveitei os novos recursos integrados do Swift para simultaneidade em toda a pilha. Usei as APIs
async
noURLSession
para enviar solicitações HTTP do frontend e usei o Vapor e as APIsasync
do driver do MongoDB para lidar com solicitações no backend. Foi muito mais fácil usar um modelo e uma sintaxe consistentes para programação simultânea e assíncrona em todo o aplicativo do que tentar manter os modelos de simultaneidade para duas linguagens diferentes ao mesmo tempo.Em geral, usar a mesma linguagem realmente fez com que eu parecesse que eu estava criando um único aplicativo em vez de dois aplicativos distintos, e reduziu muito a quantidade de troca de contexto que eu precisava fazer ao alternar entre trabalhar no front-end e no back-end.
Muitas das minhas experiências anteriores tentando construir um frontend para projetos escolares ou pessoais usando HTML e JavaScript foram chatas. Desta vez, a combinação de usar minha linguagem de programação favorita e uma estrutura elegante e declarativa tornou a escrita do front-end muito fácil. De forma mais geral, foi ótimo finalmente aprender um pouco sobre o desenvolvimento do iOS e o que a maioria das pessoas que escrevem Swift e que eu reconheço da comunidade Swift faz!
Concluindo, minha primeira incursão no desenvolvimento para iOS criando esse aplicativo Swift completo foi muito divertida e uma ótima experiência de aprendizado. Isso me demonstrou fortemente os benefícios de usar uma única linguagem para criar um aplicativo inteiro e usar uma linguagem com a qual você já está familiarizado ao se aventurar na programação em um novo domínio.
Incluí uma lista de referências abaixo, incluindo um link para o aplicativo de exemplo. Sinta-se à vontade para entrar em contato com quaisquer perguntas ou sugestões sobre o aplicativo ou as bibliotecas MongoDB listadas abaixo – a melhor maneira de entrar em contato eu e minha equipe é registrando um problema no GitHub ou umtíquete no Jira!
Relacionado
Tutorial
Construindo e hospedando continuamente nossa documentação do Swift DocC usando ações do Github e Netlify
Sep 17, 2024 | 6 min read
exemplo de código
Crie uma ferramenta de linha de comando com Swift e MongoDB
Sep 11, 2024 | 13 min read