Um clone do Instagram usando SwiftUI e GraphQL

Um clone do Instagram usando SwiftUI e GraphQL

Hoje estamos iniciando uma série de postagens no blog que ensinarão você a usar muitas ferramentas interessantes para criar sua própria rede social: um aplicativo que se assemelha ao Instagram.

Não economizaremos tecnologia e usaremos o melhor e o mais recente: Parse, GraphQL, um pouco de NodeJS e especialmente o framework SwiftUI da Apple (ainda a ser lançado). 

Isso levará algumas postagens para ser totalmente funcional, mas quando chegarmos lá, você perceberá como pode ser simples colocar suas ideias em funcionamento com muito pouco esforço aqui no Back4App.

Então, parece que é hora de…

Uma breve introdução

Bem, você provavelmente já conhece os enormes benefícios que o Parse traz para o seu processo de desenvolvimento, especialmente se hospedado no Back4App, pois nosso conjunto de ferramentas aumenta a produtividade em grandes quantidades.

E nossas publicações mais recentes sobre o  GraphQL  mostraram como é fácil manter o desenvolvimento de APIs ao longo do tempo, se você o usar. Se você perdeu essas postagens, sugiro fortemente que você reserve um tempo e as leia, pois elas melhorarão muito seu entendimento à medida que avançamos.

Você pode ler mais sobre o GraphQL  neste tutorial  e também temos um post completo sobre como usá-lo  neste artigo .

Você também pode ver alguma integração com o NodeJS em Cloud Code Functions  neste artigo .

Isso nos deixa com SwiftUI.

O SwiftUI é, de acordo com a Apple, “uma maneira inovadora e excepcionalmente simples de criar interfaces de usuário em todas as plataformas da Apple com o poder do Swift”.

Eu próprio gosto de pensar que o SwiftUI é mais do que isso.

Se você, como eu, está desenvolvendo software há algum tempo, provavelmente sabe que a comunidade de desenvolvedores sempre esteve dividida entre a conveniência de usar Storyboards, a interface de criação de UI de arrastar e soltar da Apple ou o poder da interface de usuário programática.

O SwiftUI vem para trazer o melhor dos dois mundos: é fácil o suficiente para os iniciantes aprenderem, enquanto mantém a capacidade de manutenção de uma interface do usuário codificada.

Combine isso com a renderização em tempo real, para que os desenvolvedores possam ver facilmente a saída visual do que eles estão codificando, e o façam de duas maneiras. Se você alterar o código, ele refletirá na interface do usuário e alterará graficamente a interface do usuário. no código, e o que você obtém é uma das maneiras mais poderosas e fáceis de fornecer interfaces de usuário com design bonito e manutenção ao longo do tempo.

Quer começar sem esforço?

Clone esta aplicação a partir do nosso Hub e saia usando! 

Então, o que você vai precisar?

No momento da redação deste post, a Apple ainda está testando beta do novo macOS Catalina, XCode 11, iOS 13 e alguns outros produtos que devem chegar às prateleiras em breve.

Enquanto você pode usar o XCode 11 com o macOS Mojave, precisará do Catalina para renderizar as visualizações do SwiftUI em tempo real.

screen-shot-2019-08-26-at-09-07-25

Sem Catalina? Sem pré-visualizações

Esta é uma decisão que você deve tomar, pois descobri que alterar a alteração do Bash para o ZSH, pois o shell oficial do macOS na Catalina quebrou muitas das minhas ferramentas e scripts que uso no meu fluxo de trabalho diário. Decidi ficar no Mojave por um tempo. Cabe a você decidir se deseja atualizar ou não.

Além disso, você precisará de uma conta Back4app com um aplicativo criado. Você pode aprender como criar seu primeiro aplicativo no Back4app  neste tutorial .

Por último, mas não menos importante, usaremos o  Apollo Client  para integrar o GraphQL ao nosso projeto. Se você não tem experiência com isso, escrevi um post muito detalhado sobre como configurá-lo e usá-lo  neste artigo .

Portanto, antes de começarmos, você precisará de:

  • XCode 11 instalado (com o macOS Catalina, se você quiser ver as Visualizações)
  • Seu novo aplicativo criado no Back4app
  • Cliente Apollo instalado e configurado

Meu aplicativo será chamado de Back4Gram , pois será semelhante ao Instagram.

Nossa primeira classe

Eu sempre digo a vocês sobre como o Parse cria a classe User, para que você possa inscrever novos usuários e autenticar os existentes, e a classe Role, para poder agrupar usuários com privilégios semelhantes. Isso é ótimo porque, pelo menos para autenticação, não precisamos criar nenhuma classe.

A classe User possui três propriedades criadas automaticamente, duas obrigatórias e uma opcional:

  • nome de usuário (obrigatório)
  • senha (obrigatória)
  • e-mail opcional)

Isso é suficiente para o nosso aplicativo registrar e gerenciar usuários.

Como você provavelmente sabe, a política da Apple sobre o armazenamento de e-mails dos usuários é muito rigorosa, por isso recomendo não pedir ou manter essas informações ou você poderá não ser aprovado.

Como este é apenas um tutorial, solicitarei ao usuário que insira essas informações, apenas para que você possa ver como elas funcionam.

screen-shot-2019-08-26-at-09-30-18

Classes User e Role são criadas automaticamente

Como se isso não fosse útil o suficiente, seu aplicativo recém-criado já possui consultas e mutações do GraphQL criadas automaticamente para nós. Isso vale para as classes criadas automaticamente e também para qualquer outra classe criada ao longo do tempo. Também cobrimos a documentação!

screen-shot-2019-08-26-at-09-39-46

Eu já disse que também temos o preenchimento automático? Nós temos!

Portanto, minha primeira mutation será como acima, e use nossa mutation específica signUp () para criar um novo usuário:

As queries e mutations vão variar de acordo com a versão de Parse que você escolheu:

Parse 3.7.2

mutation signUpUser($username: String!, $password: String!, $email: String) {
  users {
    signUp(
      fields: { username: $username, password: $password, email: $email }
    ) {
      objectId
    }
  }
}

Parse 3.8

mutation signUpUser($username: String!, $password: String!, $email: String) {
    signUp(
      fields: { username: $username, password: $password, email: $email }
    ) {
      objectId
    }
}

Parse 3.9

mutation signUpUser($username: String!, $password: String!, $email: String) {
    signUp(
      fields: { username: $username, password: $password, email: $email }
    ) {
      id
    }
}

Observe que minhas variáveis obrigatoriamente precisam de “username” e “password” ao ter um “!” no final de seus tipos, enquanto o e-mail é opcional, ao não ter o “!” Após o tipo.

No final, estou retornando o objectId do usuário recém-criado.

E como estamos aqui, vamos também codificar nossa mutation GraphQL para efetuar login em um usuário já existente.

Nesse caso, o username e password são obrigatórios e eu recuperarei o sessionToken para o usuário:

Parse 3.7.2:

mutation logInUser($username: String!, $password: String!){
  users{
    logIn(username: $username, password: $password){
      sessionToken
    }
  }
}

Parse 3.8:

mutation logInUser($username: String!, $password: String!){
    logIn(username: $username, password: $password){
      sessionToken
    }
}

Parse 3.9:

mutation logInUser($username: String!, $password: String!){
    logIn(username: $username, password: $password){
      sessionToken
    }
}

Por último, mas não menos importante, uma mutation para o logout do usuário, que não requer nenhum parâmetro e retorna apenas um booleano:

Parse 3.7.2:

mutation logOutUser{
  users{
    logOut
  }
}

Parse 3.8:

mutation logOutUser{
    logOut
}

Parse 3.9:

mutation logOutUser{
    logOut
}

Adicione todos esses itens a um novo arquivo chamado UserMutations.graphql e salve o  arquivo schema.graphql que você aprendeu a baixar no nosso artigo anterior.

Mantenha esses arquivos em segurança, pois os adicionaremos ao nosso…

Projeto SwiftUI

Vamos criar nosso projeto SwiftUI.

Inicie o XCode 11 e clique em “Criar um novo projeto XCode” na janela Bem-vindo ao XCode.

Se não aparecer essa janela, vá em Arquivo -> Novo -> Projeto.

Escolha Aplicativo para iOS na parte superior e depois Aplicativo para exibição única. Dê um bom nome e lembre-se de escolher Swift como Language e SwiftUI como User Interface:

screen-shot-2019-08-26-at-10-15-21

Não se esqueça de selecionar SwiftUI como user interface

Agora, instale o Apollo client como você aprendeu no nosso  artigo , e depois disso, adicione os arquivos UserMutations.graphql e schema.graphql em seu projeto.

Compile e adicione o arquivo API.swift recém-gerado ao projeto.

Neste ponto, o que eu espero que você tenha é:

  • XCode 11 instalado e em execução
  • Apollo client instalado e integrado ao seu projeto XCode
  • Arquivos UserMutations.graphql e schema.graphql adicionados ao seu projeto XCode
  • Arquivo gerado API.swift adicionado ao seu projeto XCode

Seu projeto deve ser semelhante a este:

screen-shot-2019-08-26-at-10-32-00

Tudo integrado ao XCode 11

Agora estamos prontos para iniciar algumas SwiftUI’ing.

Inscreva-se agora no Back4App  e comece a criar seu aplicativo Instagram Clone.

Decidindo nossos Controles de Entrada

Antes de podermos conectar ou desconectar um usuário, precisamos criar esse usuário em primeiro lugar. Então, vamos codificar nossa tela de inscrição primeiro.

Geralmente, podemos ter de 4 a 5 controles nessa tela:

  • Algum tipo de texto para informar ao usuário qual é a tela (texto)
  • Nome de usuário (controle de entrada de texto)
  • Senha (controle seguro de entrada de texto)
  • Confirmar senha (opcional, depende da plataforma, controle seguro de entrada de texto)
  • E-mail (controle de entrada de texto formatado)
  • Botão Inscrever-se (botão)

Como esse será um aplicativo móvel, decidi não colocar o controle de entrada de texto Confirmar senha, pois a maioria das plataformas móveis permite ao usuário ver o último caractere digitado e, na maioria dos casos, é suficiente para o usuário determinar se a senha está sendo usada. digitado corretamente.

Esses controles aparecerão alinhados verticalmente, um abaixo do outro, na ordem acima, cada um com sua própria cor.

 

Construindo realmente a interface do usuário

Como mencionei anteriormente, não estou usando o macOS Catalina, por isso não terei as Pré-visualizações ativadas, mas compilarei e mostrarei os resultados juntamente com o meu código como ele estava.

Crie uma nova SwiftUI View, vá em Arquivo -> Novo -> Arquivo e, em seguida, escolha SwiftUI View na seção Interface do usuário.

screen-shot-2019-08-26-at-11-08-58

Nomeie esse arquivo como SignUpView.swift e ele aparecerá integrado ao seu projeto.

screen-shot-2019-08-26-at-11-14-13

Você notará que a exibição recém-criada já possui um controle Text com a string “Hello World!”.

screen-shot-2019-08-26-at-11-18-31

A vantagem do Swift é que você pode criar várias views simples, contendo apenas os controles necessários para essa exibição e, em seguida, mesclar várias views em uma mais complexa. É a Orientação a Objetos no seu melhor.

Decidimos alinhar nossos controles verticalmente e o SwiftUI possui um controle específico para o VStack (pilha vertical): tudo o que estiver dentro de um VStack será automaticamente empilhado verticalmente; portanto, apenas para teste, vamos adicionar nosso controle VStack ao nosso ContentView. rápido e dentro dele, adicione duas vezes o SignUpView:

screen-shot-2019-08-26-at-11-39-39

Adicionado SignUpView duas vezes no VStack: mostra duas vezes alinhados verticalmente

Agora, como isso é legal! Se precisarmos de mais controles, podemos continuar adicionando-os!

Vamos remover o VStack e a duplicidade e ter apenas um SignUpView () nessa view e voltar ao nosso SignUpView e começar a alterá-lo, como já vimos os resultados do nosso teste. Seu ContentView deve ser semelhante a isso agora:

struct ContentView: View { 
    var body: some View { 
        SignUpView () 
    } 
}

Já sabemos que precisaremos desse VStack com texto na parte superior da tela, então vamos adicionar o VStack ao SignUpView e alterar a string “Hello World!” Para algo útil, como “Sign Up”. Nosso SignUpView deve ficar assim:

struct SignUpView: View {
    var body: some View {
        VStack{
            Text("Sign Up")
        }
    }
}

E como já sabemos quais controles devemos adicionar abaixo, podemos adicionar:

  • um TextField para nome de usuário
  • um SecureField for Password
  • um TextField para Email
  • um botão para se inscrever

O TextFields e o SecureField são bem parecidos com o antigo UITextField, mas precisam depender da ligação ao estado, portanto, declararemos como:

Control ( Placeholder , text: stateValueToBindTo )

e o primeiro passo é declarar o estado vinculado a:

@State var username: String = ""
@State var password: String = ""
@State var email: String = ""

Com isso fora do caminho, podemos começar a adicionar nossos controles:

Text("Sign Up")
TextField("Username", text: $username)
SecureField("Password", text: $password)
TextField("Email (optional)", text: $email)

Por último, mas não menos importante, podemos adicionar nosso Button, que tem essa estrutura:

Button(action: {
//Code to run when triggered
}){
//Content
}

Como você provavelmente notou, ele funciona tanto como um UIButton, mas sua estrutura é muito mais flexível, pois podemos definir seu conteúdo programaticamente, adicionando praticamente tudo o que há: um texto, uma imagem. Você escolhe.

A estrutura antiga de destino / ação desapareceu, substituída por uma nova ação: {} estrutura para lidar com o comportamento quando tocado.

Nosso botão terá um texto dizendo “Sign Up!!”, Então vamos adicioná-lo:

Button(action: {

}){
    Text("Sign Up!")
}

E seu código final deve ficar assim:

struct SignUpView: View {
    @State var username: String = ""
    @State var password: String = ""
    @State var email: String = ""
    
    var body: some View {
        VStack{
            Text("Sign Up")
            TextField("Username", text: $username)
            SecureField("Password", text: $password)
            TextField("Email (optional)", text: $email)
            Button(action: {
                
            }){
                Text("Sign Up!")
            }
        }
    }
}

E a nossa Pré-visualização?

screen-shot-2019-08-26-at-16-11-48

Parece promissor, mas podemos melhorar muito definindo algumas propriedades visuais.

E se há algo super legal no SwiftUI é que você pode adicionar propriedades visuais ao código e ver as mudanças em tempo real também!

Vamos tentar!

Primeiro, vamos declarar as cores que usaremos

let lightGreyColor = Color(red: 239.0/255.0, green: 243.0/255.0, blue: 244.0/255.0, opacity: 1.0)

    let lightBlueColor = Color(red: 36.0/255.0, green: 158.0/255.0, blue: 235.0/255.0, opacity: 1.0)

então vamos adicionar alguns recursos interessantes:

Algum preenchimento para termos algum espaço entre esses elementos.

Defina as propriedades background e foregroundColor para ter controles consistentes com a nossa marca.

Alguns cornerRadius para que possamos ter cantos arredondados.

E definindo fonte e peso da fonte para tornar as coisas mais agradáveis.

Voilà!

screen-shot-2019-08-26-at-16-30-55

Isso é legal ou o quê?

Apenas para acompanhar nosso código neste momento:

struct SignUpView: View {
    @State var username: String = ""
    @State var password: String = ""
    @State var email: String = ""
    
    let lightGreyColor = Color(red: 239.0/255.0, green: 243.0/255.0, blue: 244.0/255.0, opacity: 1.0)
    let lightBlueColor = Color(red: 36.0/255.0, green: 158.0/255.0, blue: 235.0/255.0, opacity: 1.0)

    
    var body: some View {
        VStack{
            Text("Sign Up")
                .font(.largeTitle)
                .foregroundColor(lightBlueColor)
                .fontWeight(.semibold)
                .padding(.bottom, 20)
            TextField("Username", text: $username)
                .padding()
                .background(lightGreyColor)
                .cornerRadius(5.0)
                .padding(.bottom, 20)
            SecureField("Password", text: $password)
                .padding()
                .background(lightGreyColor)
                .cornerRadius(5.0)
                .padding(.bottom, 20)
            TextField("Email (optional)", text: $email)
                .padding()
                .background(lightGreyColor)
                .cornerRadius(5.0)
                .padding(.bottom, 20)
            Button(action: {
                
            }){
                Text("Sign Up!")
                 .font(.headline)
                 .foregroundColor(.white)
                 .padding()
                 .frame(width: 220, height: 60)
                 .background(lightBlueColor)
                 .cornerRadius(15.0)
            }
        }.padding()
    }
}

Então, nós temos nossa bela tela brilhante quase pronta.

E se…

 

Mais Funcionalidade

Agora que preparamos nossa interface do usuário, é hora de adicionar nossas chamadas Apollo aos nossos métodos GraphQL.

Como vamos usar essas chamadas em quase todas as Views, devemos criar um cliente Apollo em um local onde todas as views possam acessá-lo. Esse lugar é o AppDelegate.swift .

Declare seu Apollo cliente lá abaixo do

import UIKit

e o início do bloco de código

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
...

assim como mostramos no  artigo anterior , e a partir de agora todas as Views podem realizar consultas e mutações no GraphQL.

Você primeiro precisa importar a estrutura Apollo e instanciar seu cliente assim:

import Apollo

// Apollo Client initialization.
// More about it here: https://www.back4app.com/docs/ios/swift-graphql
let apollo: ApolloClient = {
    let configuration = URLSessionConfiguration.default
    configuration.httpAdditionalHeaders = [
        "X-Parse-Application-Id": "YourAppIdHere",
        "X-Parse-Client-Key": "YourClientKeyHere"
    ]
    
    let url = URL(string: "https://parseapi.back4app.com/graphql")!
    
    return ApolloClient(
        networkTransport: HTTPNetworkTransport(
            url: url,
            configuration: configuration
        )
    )
}()

Apenas substitua as seqüências YourAppIdHere e YourClientKeyHere pelos seus valores atuais e podemos continuar.

Como você provavelmente adivinhou, devemos executar nossa Mutation para SignUp quando clicarmos no botão SignUp, portanto, no bloco de ação, vamos chamá-lo:

// Perform the SignUpUser mutation, passing the parameters we just got from our TextFields
            apollo.perform(mutation: SignUpUserMutation(username: self.username, password: self.password, email: self.email)){ result in
                // Let's switch the result so we can separate a successful one from an error
                switch result {
                    // In case of success
                    case .success(let graphQLResult):
                        // We try to parse our result
                        if let objId = graphQLResult.data?.users?.signUp.objectId {
                            print ("User created with ObjectId: " + objId)
                        }
                        // but in case of any GraphQL errors we present that message
                        else if let errors = graphQLResult.errors {
                            // GraphQL errors
                            print(errors)
                            
                        }
                    // In case of failure, we present that message
                    case .failure(let error):
                      // Network or response format errors
                      print(error)
                }
            }

Isso funcionará, mas esses métodos print () serão impressos apenas no console. Precisamos apresentar um alerta ao nosso usuário, então vamos alterá-lo para um alert, com a seguinte estrutura:

Alert(title: Text(“Title”), message: Text(“Message”), dismissButton: .default(Text(“TextOfButton”)))

Como precisaremos alterar o título e a mensagem no tempo de execução, devemos definir um Struct para lidar com esses valores, pois as variáveis ​​independentes não podem ser alteradas no tempo de execução:

struct Message {
    var alertTitle: String = ""
    var alertText: String = ""
}

var myMessage = Message()

E também crie um estado para que o View possa saber quando mostrar o alerta:

Button(action: {
            // Perform the SignUpUser mutation, passing the parameters we just got from our TextFields
            apollo.perform(mutation: SignUpUserMutation(username: self.username, password: self.password, email: self.email)){ result in
                // Let's switch the result so we can separate a successful one from an error
                switch result {
                    // In case of success
                    case .success(let graphQLResult):
                        // We try to parse our result
                        if let objId = graphQLResult.data?.users?.signUp.objectId {
                            myMessage.alertTitle = "Yay!"
                            myMessage.alertText = "User signed up!"
                            
                            self.showingAlert = true

                            print ("User created with ObjectId: " + objId)
                        }
                        // but in case of any GraphQL errors we present that message
                        else if let errors = graphQLResult.errors {
                            // GraphQL errors

                            myMessage.alertTitle = "Oops!"
                            myMessage.alertText = "We've got a GraphQL error: " + errors.description
                            self.showingAlert = true

                            print(errors)
                        }
                    // In case of failure, we present that message
                    case .failure(let error):
                        // Network or response format errors
                        myMessage.alertTitle = "Oops!"
                        myMessage.alertText = "We've got an error: " + error.localizedDescription
                        self.showingAlert = true
                        
                        print(error)
                }
            }
           }){
               Text("Sign Up!")
                .font(.headline)
                .foregroundColor(.white)
                .padding()
                .frame(width: 220, height: 60)
                .background(lightBlueColor)
                .cornerRadius(15.0)
           }
           .alert(isPresented: $showingAlert) {
                Alert(title: Text(myMessage.alertTitle), message: Text(myMessage.alertText), dismissButton: .default(Text("OK")))
           }

Parece promissor, não é?

Chegou a hora!

Compile e execute. Tente inscrever um novo usuário e você deve obter…

screen-shot-2019-08-27-at-15-40-59

Vamos ver se o usuário é criado em nosso Parse Dashboard!

screen-shot-2019-08-27-at-15-42-30

Isto é! 

Conclusão

Você criou uma visualização SwiftUI totalmente funcional que executa mutations no GraphQL usando o Apollo Client. Quão incrível é isso!

Continuaremos trabalhando em nosso aplicativo clone do Instagram! Os próximos passos são LogIn e logOut e o artigo está perto de ser publicado. Fique ligado!

Inscreva-se agora no Back4App  e comece a criar seu aplicativo Instagram Clone.

Gostou deste artigo? Abaixo mais um exemplo de como se criar um app igual ao Uber.

Como fazer um aplicativo igual ao Uber?


Leave a reply

Your email address will not be published.