Site icon AppTractor

Типобезопасный EventBus на Swift

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

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

Реализация фокусируется на типобезопасности, потокобезопасном хранении, автоматической очистке при деаллокации владельца, явной отмене отдельных подписок, доставке через MainActor для UI-кода и поддержке AsyncStream для потребителей, использующих async/await.

Каждое событие, отправляемое через шину, соответствует простому протоколу-маркеру EventBusEvent, который также требует соответствия протоколу Sendable. Это важно, поскольку события могут пересекать границы параллелизма, и требование Sendable стимулирует проектирование полезных данных событий как небольших, неизменяемых значений.

Основной тип EventBus является финальным классом и помечен как @unchecked Sendable, поскольку потокобезопасность обеспечивается вручную с помощью NSLock. Шина хранит подписки в словаре, где ключом является ObjectIdentifier типа события, а значением — массив записей подписок. Это позволяет разделять подписки по типам событий, используя единое внутреннее хранилище.

API для публикации событий намеренно минималистичен. При публикации создается ключ из типа события, затем шина блокирует внутреннее хранилище, находит подписки для этого типа, удаляет неактивные или отмененные подписки и создает снимок обработчиков получения. Обработчики вызываются вне блокировки, что изолирует пользовательский код от механизма синхронизации и обеспечивает семантику доставки снимка.

Основной API подписки принимает владельца (Owner: AnyObject) и обработчик. Подписка ассоциируется с объектом, который хранится слабо. Когда владелец деаллоцируется, подписка становится неактивной и очищается позже. Это делает токен подписки опциональным для управления жизненным циклом, так как подписка следует жизненному циклу владельца.

API также предоставляет перегрузку, принимающую только событие, которая полезна для простых реакций, где владелец используется только как якорь жизненного цикла. Форма обработчика, ориентированная на владельца, сохраняет явность модели владения на месте вызова, избегая распространенного паттерна с [weak self].

Читать оригинал

Exit mobile version