¿Qué hace swiftui-view-refactor?
Refactoriza y revisa archivos de vista SwiftUI con estándares sólidos para subvistas pequeñas y dedicadas, flujo de datos MV sobre MVVM, árboles de vista estables, inyección de dependencias explícita y uso correcto de Observation. Útil al limpiar una vista SwiftUI, dividir cuerpos largos, eliminar acciones o efectos secundarios en línea, reducir ayudantes computados de some View, o estandarizar @Observable y patrones de inicialización de modelos de vista.
Principios
- Subvistas pequeñas y dedicadas: Cada vista debe tener una sola responsabilidad. Divide las vistas grandes en subvistas más pequeñas que manejen una parte específica de la UI.
- MV sobre MVVM: Prefiere el patrón Model-View (MV) sobre Model-View-ViewModel (MVVM) para evitar la sobreingeniería y mantener el flujo de datos simple. Los modelos pueden ser observables directamente usando
@Observable. - Árboles de vista estables: Minimiza las recreaciones de vistas usando identificadores estables y evitando cambios innecesarios en la estructura del árbol de vistas.
- Inyección de dependencias explícita: Pasa las dependencias de manera explícita a las vistas a través de inicializadores o propiedades, en lugar de usar Singletons o variables globales.
- Uso correcto de Observation: Utiliza el macro
@Observablepara modelos y@Bindablepara enlaces bidireccionales, asegurando que las vistas se actualicen correctamente cuando cambien los datos.
Cuándo usar
- Al limpiar una vista SwiftUI que se ha vuelto larga o compleja.
- Cuando necesitas dividir el cuerpo de una vista en subvistas más manejables.
- Para eliminar acciones o efectos secundarios que están definidos en línea dentro del cuerpo de la vista.
- Al reducir el número de propiedades computadas
some Viewque pueden afectar el rendimiento. - Para estandarizar el uso de
@Observabley los patrones de inicialización de modelos de vista en todo el proyecto.
Ejemplo de refactorización
Antes:
struct ContentView: View {
@State private var items: [Item] = []
@State private var isLoading = false
var body: some View {
List {
if isLoading {
ProgressView()
} else {
ForEach(items) { item in
HStack {
Text(item.name)
Spacer()
Button("Eliminar") {
deleteItem(item)
}
}
}
}
}
.onAppear {
loadItems()
}
}
private func loadItems() {
// carga elementos
}
private func deleteItem(_ item: Item) {
// elimina elemento
}
}
Después:
struct ContentView: View {
@State private var viewModel = ContentViewModel()
var body: some View {
List {
if viewModel.isLoading {
LoadingView()
} else {
ItemsListView(items: viewModel.items, onDelete: viewModel.deleteItem)
}
}
.task {
await viewModel.loadItems()
}
}
}
@Observable
final class ContentViewModel {
var items: [Item] = []
var isLoading = false
func loadItems() async {
isLoading = true
// carga elementos de forma asíncrona
isLoading = false
}
func deleteItem(_ item: Item) {
items.removeAll { $0.id == item.id }
}
}
struct LoadingView: View {
var body: some View {
ProgressView()
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
struct ItemsListView: View {
let items: [Item]
let onDelete: (Item) -> Void
var body: some View {
ForEach(items) { item in
ItemRowView(item: item, onDelete: onDelete)
}
}
}
struct ItemRowView: View {
let item: Item
let onDelete: (Item) -> Void
var body: some View {
HStack {
Text(item.name)
Spacer()
Button("Eliminar") {
onDelete(item)
}
}
}
}
Notas
- Asegúrate de que todas las dependencias sean inyectadas explícitamente en las vistas o modelos de vista.
- Usa
@Environmentpara valores proporcionados por el entorno de SwiftUI, comodismissoopenURL. - Evita el uso de
@StateObjecty@ObservedObjecta favor de@Statecon@Observable. - Si necesitas que una vista modifique el estado de un modelo observable, usa
@Bindablepara crear un enlace.