SwiftUI é um framework declarativo da Apple para construir interfaces de utilizador em todas as plataformas do ecossistema. Em vez de descrever passos de forma imperativa, o programador declara como a interface deve ser e o SwiftUI gere a sua renderização e atualização. De acordo com a Apple Developer Documentation (2025), o SwiftUI suporta iOS 15+, iPadOS 15+, macOS 12+, watchOS 8+ e tvOS 15+ e utiliza o View Protocol como bloco de construção base para todos os componentes de interface.
Principais conclusões
body é a base de qualquer componente UI do SwiftUI, devolvendo uma descrição do ecrã através de composição de vistas.SwiftUI é um framework declarativo apresentado pela Apple em 2019 para substituir o UIKit em novos projetos. Em vez de criar manualmente instâncias de UIView e adicioná-las à hierarquia, o programador descreve a interface através de estruturas que cumprem o protocolo View. O SwiftUI calcula automaticamente a diferença entre o estado atual e o novo e redesenha apenas as partes alteradas usando o seu próprio motor de renderização.
O framework é escrito em Swift usando value semantics (estruturas, não classes), tornando os componentes UI leves e thread-safe. Ao contrário do UIKit, onde UIViewController pode pesar 200+ bytes devido ao runtime Objective-C, uma SwiftUI View é simplesmente uma estrutura de poucos bytes. Isto é especialmente importante para o watchOS com a sua memória limitada.
A mesma descrição de View funciona no iPhone, iPad, Mac, Apple Watch, Apple TV e Apple Vision Pro. O SwiftUI adapta a interface à plataforma: gestos táteis no iOS, combinações de teclado no macOS, deslocamento com Digital Crown no watchOS. Isto reduz o tempo de desenvolvimento para empresas que lançam aplicações em múltiplas plataformas Apple, mas requer configuração adicional para elementos específicos de cada plataforma.
Em SwiftUI, cada ecrã é uma estrutura que implementa o protocolo View com um único requisito: uma propriedade computada body do tipo some View. A palavra-chave some (opaque type) oculta o tipo concreto da vista, permitindo ao SwiftUI otimizar a renderização. Dentro de body, o programador combina componentes prontos — Text, Image, Button, List — usando ViewBuilder, que monta várias vistas numa só.
struct GreetingView: View {
let name: String
var var body: some View {
VStack {
Text("Olá, \(name)!")
.font(.title)
.foregroundColor(.blue)
Image(systemName: "hand.wave")
.imageScale(.large)
}
.padding()
}
}
No exemplo, VStack (stack vertical) contém Text e Image. O valor de name é passado através do inicializador da estrutura — é assim que o DI (Dependency Injection) funciona no SwiftUI sem contentores DI externos. Cada modificador devolve uma nova vista com a alteração aplicada, sem mutar o original. Isto é possível graças à imutabilidade dos value types.
ViewBuilder é um result builder anotado com @resultBuilder que monta até 10 vistas numa só. Dentro de body podem usar-se if/else, switch e ForEach sem invólucros adicionais. ForEach trabalha com elementos Identifiable — a cada vista é atribuído um id único para uma animação correta durante inserção/remoção.
Em SwiftUI, o estado determina que conteúdo é exibido no ecrã. Quando o estado muda, o SwiftUI recria o body da vista dependente e compara o resultado com o anterior usando um algoritmo de diff. Para armazenar o estado, usam-se property wrappers — cada um resolve a sua tarefa específica: estado local, ligação com uma vista filha ou modelo de dados externo.
struct CounterView: View {
@State private var count = 0
var var body: some View {
VStack {
Text("Contador: \(count)")
Button("Incrementar") {
count += 1
}
}
}
}
class UserViewModel: ObservableObject {
@Published var name = ""
@Published var age = 0
}
@State armazena um valor local simples (Int, String, Bool) dentro da estrutura View. O SwiftUI move a memória da estrutura para um armazenamento separado — portanto, uma propriedade com @State pode ser mutada mesmo que View seja um value type. @ObservableObject é para classes com propriedades @Published, cujas alterações notificam automaticamente o SwiftUI da necessidade de redesenhar.
@Binding cria uma ligação bidirecional a uma fonte de dados localizada na vista pai. O pai passa $variable (projected value), e o filho lê e escreve o valor através do binding. Isto permite mover a entrada de texto ou um interruptor para um componente separado mantendo o estado no pai. Sem @Binding, cada alteração exigiria um closure de callback para passar o novo valor para cima.
Antes do iOS 16, a navegação em SwiftUI era construída sobre NavigationView — uma API legada com comportamento complexo no iPad (split view, coluna dupla). A partir do iOS 16, a Apple recomenda NavigationStack — uma alternativa simplificada com rotas type-safe. O programador define um enum de rotas possíveis, e o NavigationStack gere automaticamente a pilha de ecrãs com suporte para deep links e retorno à raiz.
enum Route: Hashable {
case detail(id: Int)
case settings
}
struct ContentView: View {
var var body: some View {
NavigationStack {
List {
NavigationLink("Ecrã de detalhe",
value: Route.detail(id: 42))
NavigationLink("Definições",
value: Route.settings)
}
.navigationDestination(for: Route.self) { route in
switch route {
case .detail(let id): DetailView(id: id)
case .settings: SettingsView()
}
}
}
}
}
Rotas em conformidade com Hashable permitem usar qualquer tipo de dados para passar parâmetros. navigationDestination(for:destination:) associa o tipo de rota à vista de destino. A vantagem sobre a navegação UIKit é que não é necessário redesenhar ao adicionar uma nova rota: basta adicionar um case ao enum e um handler no switch. Os deep links são tratados através de processDeepLink no NavigationStack.
Para navegação programática (após login, temporizador ou resposta do servidor), usa-se @State com o inicializador NavigationLink: NavigationLink(isActive: $isActive). Quando isActive = true, a transição ocorre sem toque do utilizador. Uma alternativa é o binding do array $path no NavigationStack: $path.append(Route.detail(id: 1)).
Modifier é um método que devolve uma cópia modificada da vista. Ao contrário do UIKit, onde a configuração de propriedades é feita através de mutação de uma vista existente, o SwiftUI cria um novo valor com a alteração aplicada. O encadeamento de modificadores constrói a interface final a partir de transformações sequenciais: fonte → padding → cor → sombra → gesto.
A Apple fornece mais de 200 modificadores integrados. Os mais comuns: .font(), .foregroundColor(), .padding(), .background(), .cornerRadius(), .shadow(), .opacity(), .offset(). A ordem dos modificadores é importante: .padding() antes de .background() preenche a área com padding, depois — apenas a área interior. Modificadores personalizados são criados através do protocolo ViewModifier.
Os modificadores podem ser aplicados condicionalmente através do operador ternário: .foregroundColor(isError ? .red : .primary). Para animação, usa-se .animation(.easeInOut, value: state) — o modificador de animação é vinculado a uma propriedade de estado específica. Quando esta propriedade muda, o SwiftUI anima a transição entre o valor antigo e o novo. A animação funciona com opacity, offset, scale, rotation, tamanho e cor — cada propriedade tem um AnimatableParameter correspondente.
Para animações personalizadas, estão disponíveis .transition (aparecimento/desaparecimento) e .matchedGeometryEffect (transição suave de um elemento entre dois contentores). O último é usado para hero animation em listas: um ícone numa célula de lista transforma-se suavemente numa imagem grande no ecrã de detalhe.
A escolha entre SwiftUI e UIKit é um dos primeiros dilemas do programador iOS. Ambos os frameworks são suportados pela Apple, mas resolvem o problema de construção de interfaces de formas fundamentalmente diferentes: SwiftUI declarativamente, UIKit imperativamente. A diferença manifesta-se na gestão de estado, navegação, desempenho e compatibilidade.
| Aspeto | SwiftUI | UIKit |
|---|---|---|
| Abordagem | Declarativa: o que mostrar | Imperativa: como construir |
| Estado | Property Wrappers, redesenho automático | Manual: reloadData, setNeedsLayout |
| Código UI | Compacto, cadeias de modificadores | Verboso, NSCoder/Storyboard/restrições |
| Desempenho | Alto no iOS 17+, algoritmo de diff | Pico no iOS 12–16, controlo direto |
| Versão mínima | iOS 15+ (suporte completo) | iOS 2+ (todas as versões) |
Para novos projetos com versão mínima iOS 17, a Apple recomenda SwiftUI como framework principal. O UIKit continua necessário para interfaces que requerem controlo fino sobre a renderização (UICollectionViewLayout personalizado, cenas CAAnimation complexas) ou suporte para iOS 12–14. Muitos projetos usam uma abordagem híbrida: o SwiftUI através de UIHostingController é incorporado numa app UIKit, e UIViewRepresentable permite usar componentes UIKit dentro da hierarquia SwiftUI.
Perguntas frequentes
Sim, através de UIHostingController (SwiftUI no UIKit) e UIViewRepresentable (UIKit no SwiftUI). É uma abordagem híbrida, popular durante a migração.
O iOS 17 oferece funcionalidade completa: NavigationStack, Observation framework, Swift Charts. O iOS 15 é o limiar mínimo para produção.
A causa mais frequente é alterar uma propriedade @Published numa thread secundária. O ObservableObject deve enviar as alterações no main actor: @MainActor class ViewModel.
Use .debounce através do Combine: Button.publisher(for: .tap) .debounce(for: .seconds(0.3), scheduler: RunLoop.main).
Sim, através dos modificadores Gesture: DragGesture, LongPressGesture, MagnificationGesture, RotationGesture. Combine-os usando .simultaneousGesture() e .sequenced().
Resumo
Vamos desenvolver um aplicativo móvel chave na mão
A IT Sectr cria aplicativos para iOS e Android para startups e empresas desde 2017. Nós vamos aconselhá-lo e propor a melhor solução.
Leia também