Site icon AppTractor

10 SwiftUI-хаков для более чистых приложений

Если вы уже какое-то время разрабатываете приложения на SwiftUI, вы, вероятно, испытывали раздражение, когда одни и те же строки кода проникают в каждый проект. SwiftUI — мощный инструмент, да, но иногда он заставляет писать всё снова и снова: настраивать модификаторы, обрабатывать ошибки, управлять опциональными параметрами, форматировать даты.

В чём секрет? Вам не нужно мириться с повторениями. С помощью нескольких умных расширений и шаблонов вы можете сделать код SwiftUI чище, короче и при этом таким же мощным. Вот десять небольших трюков, которые незаметно избавят вас от ненужных элементов, придав коду вашего приложения элегантный вид.

1. Более безопасный способ развертывания Optional в представлениях

Вместо бесконечных операторов if let создайте простое расширение, которое сделает обработку опциональных параметров естественной:

extension View {
    @ViewBuilder
    func ifLet<T, Content: View>(_ value: T?, content: (T) -> Content) -> some View {
        if let value {
            content(value)
        } else {
            self
        }
    }
}

Теперь вы можете просто писать:

Text("Hello")
    .ifLet(username) { Text("Welcome, \($0)") }

2. Условные модификаторы без лишних сложностей

Мы все пишем .padding() или .opacity() с условиями. Вместо того, чтобы заключать целые представления в блоки if, используйте это:

extension View {
    @ViewBuilder
    func apply(_ condition: Bool, modifier: (Self) -> some View) -> some View {
        if condition {
            modifier(self)
        } else {
            self
        }
    }
}

Использование:

Text("Premium")
    .apply(isPro) { $0.foregroundColor(.yellow) }

дна строка, ничего лишнего.

3. Многоразовый AsyncImage с плейсхолдером

Загрузка изображений — обычное дело, но повторение одной и той же логики плейсхолдера утомляет. Сделайте один раз:

struct RemoteImage: View {
    let url: URL
    var body: some View {
        AsyncImage(url: url) { phase in
            switch phase {
            case .success(let image): image.resizable().scaledToFit()
            case .failure(_): Color.gray
            case .empty: ProgressView()
            @unknown default: EmptyView()
            }
        }
    }
}

Теперь вам больше не придётся переписывать загрузчики изображений.

4. Форматирование даты в одну строку

Вместо того, чтобы создавать DateFormatter везде, добавьте расширение:

extension Date {
    func formatted(_ style: DateFormatter.Style = .medium) -> String {
        let formatter = DateFormatter()
        formatter.dateStyle = style
        return formatter.string(from: self)
    }
}

Использование:

Text(Date().formatted(.long))

Легко читаемый и многоразовый код.

5. Автоматические вставки SafeArea

Не любите бороться с .ignoresSafeArea()? Добавьте небольшой вспомогательный метод:

extension View {
    func fillScreen() -> some View {
        self.frame(maxWidth: .infinity, maxHeight: .infinity)
            .ignoresSafeArea()
    }
}

Теперь для каждого полноэкранного представления требуется всего один модификатор.

6. Простая навигация без шаблонных NavigationLinks

Вместо того, чтобы разбрасывать NavigationLinks по всему коду, вы можете упростить навигацию с помощью вспомогательного метода:

extension View {
    func navigate<Destination: View>(to destination: Destination, when binding: Binding<Bool>) -> some View {
        background(
            NavigationLink(destination: destination, isActive: binding) { EmptyView() }
        )
    }
}

Использование:

Button("Go") { showDetails = true }
    .navigate(to: DetailView(), when: $showDetails)

Внезапно навигация снова кажется понятной.

7. Многоразовые разделители

Вместо того, чтобы вводить .frame(height: 20) для задания отступа, создайте ощущение, будто SwiftUI сам предоставил вам этот инструмент:

struct Gap: View {
    let height: CGFloat
    var body: some View { Spacer().frame(height: height) }
}

Использование:

VStack {
    Text("Title")
    Gap(height: 16)
    Text("Subtitle")
}

Более чистый и семантический код.

8. Унифицированная обработка алертов

Надоело писать оповещения снова и снова? Централизуйте их:

struct AlertItem: Identifiable {
    var id = UUID()
    var title: String
    var message: String
}

extension View {
    func alert(item: Binding<AlertItem?>) -> some View {
        self.alert(item.wrappedValue?.title ?? "",
                   isPresented: Binding(value: item.isNotNil())) {
        } message: {
            Text(item.wrappedValue?.message ?? "")
        }
    }
}

Теперь отображение предупреждений выглядит так:

.alert(item: $errorItem)

9. Быстрые помощники предварительного просмотра

Избавьтесь от повторения тестовых данных в предпросмотрах. Добавьте помощников:

extension PreviewProvider {
    static var sampleUser: User { User(id: 1, name: "Alice") }
}

Затем в ваших превью:

ContentView(user: sampleUser)

Больше никаких лишних данных.

10. Однострочное представление загрузки

Вместо того, чтобы каждый раз переписывать состояния загрузки, запрограммируйте их:

extension View {
    func loading(_ isLoading: Bool) -> some View {
        ZStack {
            self
            if isLoading {
                ProgressView().scaleEffect(1.5)
            }
        }
    }
}

Использование:

List(items) { item in
    Text(item.title)
}
.loading(isFetching)

Каждое представление теперь поддерживает состояние загрузки.

Подведение итогов

SwiftUI даёт вам свободу, но вместе со свободой приходит и повторение. Создавая собственные небольшие строительные блоки — расширения, вспомогательные функции и шаблоны — вы не только экономите время, но и создаёте персональный инструментарий SwiftUI, который растёт с каждым проектом.

Меньше кода. Та же мощь. Больше удовольствия. Вот как должен выглядеть SwiftUI.

Источник

Exit mobile version