Guia básico para uma entrevista técnica de iOS

Alcides Junior
11 min readFeb 20, 2021
O mundo é um grande quebra-cabeças e vamos encontrando as peças aos poucos, nem sempre tudo faz sentido, mas precisamos montar.

Já pensou em aplicar para a tão sonhada vaga de iOS, conseguiu a entrevista, fez o projeto teste mas na hora da entrevista técnica se enrolou ou acha que não teve um bom desempenho? Calma!!! isso é normal e na maioria das vezes, por ansiedade e nervosismo, você acaba esquecendo alguns conceitos sobre iOS e talvez até o seu nome, brincadeira haha.
Nesse artigo, vou te ajudar a relembrar e até aprender sobre alguns conceitos que são comumente cobrados em uma entrevista técnica vagas para desenvolvedor iOS baseados na trajetória que já tive em outros momentos.

Vale ressaltar que aqui não trago verdades absolutas sobre os conceitos, mas boa parte da minha visão e entendimento sobre o que vai ser abordado.

Ao final deste artigo você saberá sobre:

  • Estados de uma aplicação iOS
  • Ciclo de vida de uma UIViewController
  • GCD(Grand Central Dispatch)
  • ARC(Automatic Reference Counting), Retain Cycle, Memory Leak
  • Strong, Weak e Unowned
  • Design Patterns(MVC, MVVM, etc)
  • View code vs Storyboard
  • Bounds vs Frame
  • Básico sobre Swift(Struct vs Class, guard, let, var, lazy)
  • Filter, Map, Reduce

Antes de mais nada, saiba que as pessoas que estarão te entrevistando também são seres humanos e já passaram pelo mesmo momento que você e sabem o quanto é tenso estar em um papo técnico. Tente sempre ser objetivo nas suas respostas e caso não saiba seja honesto e diga: eu realmente não sei, mas procurarei saber. É muito importante que as pessoas que estão do outro lado saibam que você não é uma pessoa acomodada e que fica de braços cruzados. No mundo do desenvolvimento de Software, você vai se deparar com muitas situações em que você não tem conhecimento e vai precisar buscar, então isso já pode te dar alguns pontos na tua entrevista.

Estados de uma aplicação iOS

Not running: quando a aplicação ainda não foi lançada(launched) ou foi interrompida pelo sistema por algum motivo.

Inactive: geralmente, quando a aplicação está executando e por algum motivo, tem seu funcionamento momentaneamente tomado por uma outra aplicação. Um exemplo claro é quando você recebe uma chamada telefônica ou quando você bloqueia sua tela, sem fechar a aplicação.

Active: quando a aplicação está rodando em primeiro plano e recebendo eventos.

Background: quando a aplicação está em segundo plano e executando algum código. Um exemplo bem comum são aplicações que lidam com geolocalização e, de alguma forma, ficam executando em background.

Suspended: quando um aplicativo permanece na memória, mas não executa nenhum código. Pode acontecer de o sistema remover estes aplicativos da memória quando houver pouco espaço.

Ciclo de vida de uma UIViewController

Ciclo de vida de uma UIViewController no iOS
Imagem pertencente ao site da Apple.

Vamos ver um pouco sobre o ciclo de vida de uma UIViewController.
Para cada comportamento visto acima, a UIViewController executa um dos métodos que veremos abaixo.

loadView: esse método é chamado sempre que a controller detecta, na inicialização da controller, que sua view está nil, permitindo que você passe para a propriedade view seja carregada com a view criada programaticamente. Isso geralmente acontece quando você está usando view code.

viewDidLoad: esse método é carregado uma vez e é chamado quando a view é carregada.

viewWillAppear: esse método é chamado sempre que antes que a view esteja visível e antes de qualquer animação. Você pode usar esse método, por exemplo, para esconder campos ou desabilitar algumas ações antes que a view esteja visível.

viewWillDisappear: esse método é chamado antes que a view seja removida da hierarquia de views.

viewDidAppear: esse método é chamado depois que a view é apresentada da tela.

viewDidDisappear: esse método é chamado depois que a view é removida da hierarquia de views.

GCD(Grand Central Dispatch)

O GCD é uma LIB que lida com concorrência criada pela Apple. É uma camada de abstração para trabalhar com concorrência, trabalhando com o conceito de filas que são elas: Main Queue e a Global Queue, podendo criar outra caso seja necessário.

ARC(Automatic Reference Counting)

Swift e Objective-C utilizam o Contador de Referências Automática para rastrear e gerenciar o uso de memória no seu app, onde o ARC libera o uso de memória usada por instâncias de classes quando essas instâncias não são mais necessárias. O ARC libera memória para objetos que não tem referência forte(Strong) para eles.

Para mais informações sobre o assunto veja esse artigo.

Retain Cycle

É uma condição onde 2 objetos mantém referência um ao outro, criando um ciclo de retenção e impossibilitando a liberação de um ao outro.

O artigo citado acima também trata desse assunto um pouco mais a fundo.

Memory Leak

Um memory leak(vazamento de memória) acontece quando uma quantidade de espaço alocado na memória não pode ser desalocado devido a retain cycles.

Strong, Weak e Unowned

Esses são recursos usados pelo ARC para se referir a objetos criados na memória que, por sua vez, tem seus significados descritos abaixo.

Strong: significa que um objeto tem uma referência forte. No Swift, por padrão, todos os objetos e variáveis são declarados por padrão como sendo Strong e indica para o ARC que ele deverá contar a referência a este objeto, como no exemplo abaixo:

exemplo de uma declaração Strong.

Weak: significa que um objeto tem uma referência fraca, garantindo pro ARC que ele não deverá contar esta referência, ajudando a evitar retain cycles e memory leaks.

exemplo de declaração Weak.

Unowned: faz a mesma coisa que o Weak, ou seja, diz pro ARC não contar objetos que são declarados com essas palavras-chave, porém com a diferença que Unowned sempre espera um valor, não podendo ser optional.

— Ok! entendi que Weak e Unowned são semelhantes com essa diferença citada acima, mas… quando deve usar quem?
— é aqui que mora o PERIGO porque o Weak nos dá a segurança de ter um objeto como optional, onde podemos tratar facilmente o conteúdo usando um guard ou if let da vida e evitar problemas, mas como o Unowned sempre espera um valor, o que acontece é que, quando o objeto Unowned é desalocado da memória, ele não vai receber um nil como valor padrão e consequentemente, tomaremos um CRASH com um fatal error, então muito cuidado ao usar Unowned.

Design Patterns

Design patterns são um conjunto de técnicas para resolver determinados problemas comuns enfrentados por desenvolvedores e vamos explorar alguns como MVC, MVVM, MVP, The Composable Architecture etc. Nesse artigo, focaremos apenas em MVC e MVVM.

MVC: O MVC(Model-View-Controller) é um dos mais famosos design — patterns, onde seu fluxo é descrito na imagem abaixo:

https://arquivo.devmedia.com.br/artigos/Joel_Rodrigues/Guias/guia-aspnet-mvc-1.png

O usuário realiza alguma ação na view que dispara para a controller, a controller por sua vez, sabe o que usuário precisa e pede para a model, a model devolve os dados para a controller e a controller devolve para a view, que por sua vez apresenta ao usuário.
Note que cada uma dessas camadas tem uma responsabilidade.

Model: tem a responsabilidade de modelar as entidades e lidar com os dados da aplicação.

View: renderizar informações para o usuário e captar interações afim de enviar para a controller.

Controller: receber ações da view, e comunica para a model, permitindo a comunicação entre a view e a model.

MVVM: O MVVM(Model-View View-Model) é um padrão que vem com uma proposta melhor de estabelecer uma separação mais clara das responsabilidades e permitir uma melhor testabilidade da nossa aplicação.
Vamos ver o seu fluxo abaixo:

https://www.raywenderlich.com/34-design-patterns-by-tutorials-mvvm

Cada View tem sua ViewModel onde toda mudança que acontecer no seu Model irá refletir na ViewModel e por sua vez, na View, permitindo que a Controller tenha menos responsabilidades com os dados e acione as informações necessárias para apresentar para a View. Na hora de testar nossa aplicação, focamos na parte da ViewModel que é onde está o comportamento que é apresentado na nossa View. Desta forma, vemos claramente que cada camada está bem desacoplada(Serviço, Model, View, ViewModel, Controller etc) permitindo facilmente readequações em camadas específicas sem muitos side-effects.

— Tá, eu entendi o que são os padrões MVC e MVVM, mas qual devo utilizar?

— a resposta é: depende! cada projeto tem sua peculiaridade e a equipe técnica deve pesar os fatores que levarão a decisão de qual padrão utilizar.

Vou apresentar algumas vantagens de se usar MVVM ao invés de MVC:

MVC vs MVVM
Diferente do MVC, o MVVM nos livra de um problema chamado massive controller, que não significa apenas que escreveremos uma controller gigantesca em nossa aplicação, mas que uma controller terá muitas responsabilidades de lidar com delegates, apresentação dos dados, interações com a model dentre outras coisas.

View code vs Storyboards

Existe uma grande discussão na internet sobre esse assunto e digamos que não tem o jeito certo ou errado nesse caso, mas sim o caso que mais se adequa ao seu projeto e ao seu time.
Trabalhar com view code(escrever interfaces gráficas de forma programática) tem uma série de vantagens e uma das que mais são levadas em conta quando o assunto é o time, podemos citar a resolução de conflitos de git. Quando isso acontece, fica mais fácil resolver o problema porque é código, quando isso acontece e você está usando Storyboard se torna um pesadelo. Em contra partida, usar Storyboard te permite ter uma certa preview de como sua tela vai ficar e te permite alinhar coisas, ter uma prototipação bem mais rápida, só que aparece um outro problema: quando a aplicação tende a escalar, ter várias telas teremos um problema de tempo de build. Projetos que utilizam Storyboard, tendem a ter uma build mais demorada porque o Xcode está renderizando as telas e ao mesmo tempo te gerando um build para rodar no device(físico ou simulado). Existem uma série de outros fatores, mas o que vejo mais ser discutido entre devs e que merecem uma certa atenção foram esses que citei aqui.

Bounds vs Frame

Outra coisa que é bastante recorrente em entrevistas técnicas de iOS é justamente a diferença entre Bounds e Frame.
Vamos entender um pouco sobre o comportamento de ambas.

Bounds: significa limites e está associado as coordenadas do próprio espaço da atual view, como se o resto da hierarquia de views não existissem.

Frame: significa quadro, ou seja, está associado as coordenadas de onde determinada view está inserida, seu espaço pai.

Básico sobre Swift

Struct vs Class

Struct e Class tem muitas coisas em comum e devem ser usadas em determinados contextos, vamos explorar um pouco?!

Características em comum

  • Possuem métodos e atributos
  • Possuem construtor
  • Permitem implementar protocolos
  • Permitem o uso de extension
  • Permitem uso de subscripts
  • Permitem trabalhar com generics

Características que diferem

  • Classes podem herdar de outras classes, Struct não.
  • Classes podem definir um método destrutor, que são chamadas antes da instância da classe ser desalocada da memória, Struct não.
  • Classes permitem ter mais de uma referência uma única instância, Struct não.

Tomando como base alguns desses argumentos, vamos agora entender mais um pouco sobre Classes e Structs em Swift e vamos entender quando usar cada um.

Ao pensar em classe, temos que ter em mente que classes são reference type, ou seja, são passadas por referência e permitem o que citamos acima: ter mais de uma referência a instância de uma mesma classe, já Structs são value types, ou seja, a cada nova instância de uma struct, seu valor é copiado da memória e alocado em outro espaço, criando assim, uma "vida" independente de sua cópia.

Tá meio confuso né? calma! vou te propor uma analogia bem bacana!

Pensa que tu tem um trabalho da faculdade para fazer com tua equipe e ambos precisam estar editando um mesmo documento, certo? ok. É assim que o conceito de reference type funciona, onde cada um da tua equipe é uma copia da instância e o documento é a classe e todas mudanças que vocês fizeram vai refletir diretamente no mesmo documento.

Agora, imagina que tu baixa esse documento pra tua máquina e passa a editar ele localmente. É exatamente assim que vai se comportar a value type, os dados que você(cópia da instância) editar, vai refletir apenas no seu documento, deixando o original intacto das tuas modificações. É assim que Struct vai funcionar.

beleza, agora fica o questionamento: quando devo usar Class ou Struct?

Uma boa pergunta que você tem a se fazer é: vou precisar herdar de outro? se sim, opte por usar Classe, apenas Classe possui essa habilidade de herdar de outras classes. Um bom exemplo é você precisar criar um botão customizado.
Você vai criar uma view que herdar de UIButton.

Agora, se você quiser fazer uso de programação orientada a protocolo, faz mais sentido usar struct, visto que você não vai precisar herdar de outra struct.

Outro caso bem comum do uso de Structs é na escrita de Models, onde você vai decodar dados de uma requisição http e não tem a necessidade de herança.

Filter, Map e Reduce

É muito importante e útil saber quando usar cada uma das funções de alta ordem. Ajuda demais a resolver nossos problemas e simplificar nossa vida.
Para isso, vamos entender como funcionam cada uma dessa funções.

Filter
Vamos analisar o seguinte caso, onde precisamos guardar apenas as baterias cujo preço seja R$ 2500,00:

O filter gera um novo array a partir de um filtro que aplicamos, no nosso caso, os que tem preço igual a 2500.

Map
Agora, vamos a um caso onde precisamos exibir o aumento de preços de cada instrumento.

Utilizamos o map quando precisamos gerar um outro array transformado. No nosso caso, precisamos gerar um array com um aumento nos preços.

Reduce
Agora, vamos para um caso onde precisamos exibir o total dos preços dos instrumentos.

Note que o reduce possui dois parâmetros, onde o primeiro significa o valor inicial e o segundo qual operação matemática vamos utilizar.

Conclusão

Esse é o básico que você precisa dominar para ir bem em uma entrevista técnica. Tentei compilar nesse artigo, baseado nas entrevistas que já tive até hoje, para que você possa tomar como base. Cada entrevista pode variar e alguns conceitos presentes aqui podem estar pela metade, ou seja, faltando algum complemento, mas isso não significa que é tão ruim assim. Numa entrevista técnica, o que é esperado de um candidato é que ele saiba, no mínimo, explicar sobre os conceitos propostos a cada rodada de perguntas. Você não precisa, necessariamente, explicar de forma bem detalhada, o que é esperado nesse momento é entender como você pensa em relação ao conteúdo em si. Saiba se expressar e caso não saiba de um conceito, seja honesto e sincero e diga: eu não sei.
Para finalizar, meu conselho é que você pesquise muito sobre entrevistas técnicas de iOS e pratique algumas coisas. É bem importante saber esses conceitos para o dia-a-dia.
Espero ter contribuído de alguma forma para a experiência de você, leitor e mais uma vez: o conteúdo desse artigo não é uma verdade absoluta, a proposta aqui é te dar uma base do que você poderá precisar.

Boa sorte!!!

No final, tudo dá certo.

--

--

Alcides Junior

iOS Software Engineer @ iFood —  Developer Academy 2018/19 — WWDC Scholarship 2019, 20 and 21 — Graduando em Engenharia de Computação IFCE