Connect with us

Программирование

Стриминг изменений с помощью Observations

Появление типа Observations знаменует собой значительное улучшение возможностей Swift по наблюдению за данными, особенно для разработчиков, использующих Swift Concurrency.

Опубликовано

/

     
     

Несколько лет назад Apple представила фреймворк Observation. Он стал основным инструментом для построения наблюдаемых моделей, заменив Combine. На этой неделе мы расскажем о новой части фреймворка под названием Observations.

Основным недостатком фреймворка Observation была его неспособность преобразовывать наблюдаемые типы в асинхронные потоки, что позволяло бы нам наблюдать за ними так же, как мы это делаем с Publisher в Combine.

func startObservation() {
    withObservationTracking {
        render(store.state)
    } onChange: {
        Task { startObservation() }
    }
}

Хотя фреймворк Observation предлагает функцию withObservationTracking, которая позволяет нам вручную отслеживать изменения в наблюдаемом типе, он все еще имеет некоторые ограничения.

Во-первых, рекурсивное наблюдение приходится инициировать вручную, поскольку оно срабатывает только при первом изменении. Во-вторых, оно не вписывается в мир Swift Concurrency, поскольку его нельзя использовать как асинхронный поток внутри асинхронного цикла for. К счастью, Apple исправила все эти недостатки, представив новый тип Observations. Он предназначен для работы в паре с макросом Observable.

let store = Store() // Observable

struct State {
    let items: [String]
    let isLoading: Bool
}

let streamOfStates = Observations {
    let state = State(
        items: store.items,
        isLoading: store.isLoading
    )
    return state
}

for await state in streamOfStates {
    render(state)
}

Тип Observations соответствует протоколу AsyncSequence, что позволяет использовать экземпляры этого типа внутри асинхронного цикла for. Замыкание, которое мы используем для инициализации экземпляра типа Observations, неявно отслеживает все свойства наблюдаемых экземпляров, с которыми вы работаете.

Как видно из примера ниже, мы создаём новое состояние внутри замыкания. Мы работаем со свойствами items и isLoading типа Store, соответствующего протоколу Observable.

Мы наблюдаем за создаваемыми экземплярами типа State внутри асинхронного цикла for. При каждом изменении свойств items и isLoading он создаёт новый экземпляр типа State, который мы асинхронно извлекаем внутри цикла.

Тип Observations достаточно интеллектуален для использования транзакционных обновлений, что означает, что он не генерирует значение для каждого изменения. Он может группировать обновления, когда изменения происходят одновременно в свойствах items и isLoading.

extension Observable {
    func stream<Value: Sendable>(
        of keyPath: KeyPath<Self, Value>
    ) -> any AsyncSequence<Value, Never> {
        Observations {
            self[keyPath: keyPath]
        }
    }
}

Вот небольшое расширение протокола Observable, позволяющее вам легко создавать асинхронные последовательности для KeyPath для наблюдаемого типа.

for await items in store.stream(of: \.items) {
    print(items)
}

Появление типа Observations знаменует собой значительное улучшение возможностей Swift по наблюдению за данными, особенно для разработчиков, использующих Swift Concurrency. Он стирает разрыв между реактивными обновлениями и современными асинхронными шаблонами, упрощая создание понятных, эффективных и отзывчивых пользовательских интерфейсов. Надеюсь, вам понравится эта публикация. Подписывайтесь на автора в Твиттере и задавайте вопросы по теме. Спасибо за прочтение, увидимся на следующей неделе!

Источник

Если вы нашли опечатку - выделите ее и нажмите Ctrl + Enter! Для связи с нами вы можете использовать info@apptractor.ru.
Telegram

Популярное

Сообщить об опечатке

Текст, который будет отправлен нашим редакторам: