SwiftHTML & CSSSolidityDesenvolvimento de JogosSolana/Rust
01.12.2024

Lição 197: Arquitetura Limpa em Swift

A Arquitetura Limpa é uma filosofia de design de software que enfatiza a separação de responsabilidades, tornando seu código mais manutenível, testável e adaptável a alterações. Nesta lição, exploraremos como implementar a Arquitetura Limpa em uma aplicação Swift.

Conceitos Chave da Arquitetura Limpa

  1. Entidades: Modelos de negócios centrais que encapsulam as regras de negócio.
  2. Casos de Uso: Regras de negócios específicas da aplicação que definem interações entre as entidades.
  3. Adaptadores de Interface: Código que transforma dados do formato mais conveniente para os casos de uso e entidades para o formato mais conveniente para agentes externos como a interface do usuário ou bancos de dados.
  4. Frameworks e Acessos: Bibliotecas externas, frameworks de interface do usuário e acesso a banco de dados que interagem com o sistema.

Estrutura do Projeto

Aqui está uma estrutura de projeto sugerida para uma aplicação Swift seguindo os princípios da Arquitetura Limpa:

MeuAppArquiteturaLimpa/
├── Entidades/
│   └── Usuario.swift
├── CasosDeUso/
│   └── ObterUsuarioCasoDeUso.swift
├── AdaptadoresDeInterface/
│   ├── UsuarioViewModel.swift
│   └── UsuarioRepositorio.swift
└── FrameworksEAcessos/
    ├── ViewControllers/
    │   └── UsuarioViewController.swift
    └── Servicos/
        └── ServicoDeRede.swift

Implementando a Arquitetura Limpa em Swift

Etapa 1: Definir Entidades

Começamos com o modelo de negócios central. Para este exemplo, vamos definir uma entidade Usuario.

// Entidades/Usuario.swift
struct Usuario {
    let id: Int
    let nome: String
    let email: String
}

Etapa 2: Criar Casos de Uso

Em seguida, implementaremos um caso de uso que buscará um usuário. Este caso de uso interage com a entidade.

// CasosDeUso/ObterUsuarioCasoDeUso.swift
protocol UsuarioRepositorio {
    func obterUsuario(by id: Int) -> Usuario?
}

class ObterUsuarioCasoDeUso {
    private let usuarioRepositorio: UsuarioRepositorio

    init(usuarioRepositorio: UsuarioRepositorio) {
        self.usuarioRepositorio = usuarioRepositorio
    }

    func executar(usuarioId: Int) -> Usuario? {
        return usuarioRepositorio.obterUsuario(by: usuarioId)
    }
}

Etapa 3: Implementar Adaptadores de Interface

Aqui, criamos um UsuarioViewModel para adaptar a entidade Usuario para a interface do usuário e implementamos um repositório que simula a recuperação de dados.

// AdaptadoresDeInterface/UsuarioViewModel.swift
class UsuarioViewModel {
    private let obterUsuarioCasoDeUso: ObterUsuarioCasoDeUso

    var usuario: Usuario?

    init(obterUsuarioCasoDeUso: ObterUsuarioCasoDeUso) {
        self.obterUsuarioCasoDeUso = obterUsuarioCasoDeUso
    }

    func buscarUsuario(by id: Int) {
        self.usuario = obterUsuarioCasoDeUso.executar(usuarioId: id)
    }
}

// AdaptadoresDeInterface/UsuarioRepositorio.swift
class UsuarioRepositorioImpl: UsuarioRepositorio {
    private var usuarios: [Usuario] = [
        Usuario(id: 1, nome: "João Silva", email: "joao.silva@exemplo.com"),
        Usuario(id: 2, nome: "Maria Silva", email: "maria.silva@exemplo.com")
    ]

    func obterUsuario(by id: Int) -> Usuario? {
        return usuarios.first { $0.id == id }
    }
}

Etapa 4: Criar a Interface do Usuário

Agora vamos implementar uma interface de usuário simples para exibir os detalhes do usuário.

// FrameworksEAcessos/ViewControllers/UsuarioViewController.swift
import UIKit

class UsuarioViewController: UIViewController {
    private let viewModel: UsuarioViewModel
    private let usuarioId: Int

    init(viewModel: UsuarioViewModel, usuarioId: Int) {
        self.viewModel = viewModel
        self.usuarioId = usuarioId
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) não foi implementado")
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        viewModel.buscarUsuario(by: usuarioId)
        exibirInformacoesDoUsuario()
    }

    private func exibirInformacoesDoUsuario() {
        guard let usuario = viewModel.usuario else { return }
        print("Informações do Usuário: \(usuario.nome) - \(usuario.email)")
    }
}

Etapa 5: Configurar Injeção de Dependência

Finalmente, configuraremos a injeção de dependência para criar instâncias de nossas classes e conectar tudo.

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        let usuarioRepositorio = UsuarioRepositorioImpl()
        let obterUsuarioCasoDeUso = ObterUsuarioCasoDeUso(usuarioRepositorio: usuarioRepositorio)
        let usuarioViewModel = UsuarioViewModel(obterUsuarioCasoDeUso: obterUsuarioCasoDeUso)

        window = UIWindow(frame: UIScreen.main.bounds)
        let usuarioViewController = UsuarioViewController(viewModel: usuarioViewModel, usuarioId: 1)
        window?.rootViewController = usuarioViewController
        window?.makeKeyAndVisible()

        return true
    }
}

Conclusão

Ao estruturar sua aplicação Swift de acordo com os princípios da Arquitetura Limpa, você cria uma clara separação de responsabilidades que melhora a manutenibilidade e a testabilidade. Essa estrutura facilita a adaptação a mudanças e a adição de novas funcionalidades sem afetar significativamente a base de código existente. À medida que você continua a desenvolver seu aplicativo, mantenha esses princípios em mente para um projeto mais organizado e escalável.

Video

Did you like this article? Rate it from 1 to 5:

Thank you for voting!