Современные приложения часто сталкиваются с проблемами, связанными с сохранением и восстановлением состояния пользовательского интерфейса. Особенно это актуально для сложных оконных менеджеров, которые позволяют пользователям разбивать экран на настройки с несколькими областями, или так называемыми сплитами. Одним из таких проектов является Ghostty - инновационный терминальный клиент с открытым исходным кодом для macOS. Недавно в разработке Ghostty была обнаружена критическая ошибка, касающаяся сохранения состояния, из-за которой масштабированные (zoomed) сплиты могли "застревать" в определённом состоянии и переставали отвечать на попытки изменить их размер, закрыть или разбить ещё дальше. Разбор и исправление этой ошибки не только решило проблему, но и предложило интеллигентный подход к сериализации сложных иерархий данных, который может быть полезен разработчикам, работающим с деревьями пользовательских интерфейсов и связанными структурами в macOS и других системах.
Проблема заключалась в том, что в классической реализации объекта SplitTree - структуры, управляющей иерархией сплитов, - при сохранении состояния сериализовался не путь к масштабированному узлу, а сам узел целиком. Это приводило к тому, что при последующей загрузке состояния система получала отвалившуюся ссылку на узел, который уже не соответствовал текущей структуре дерева. В результате масштабированный сплит становился "застрявшим", поскольку логика реставрации не могла корректно сопоставить сохраненную информацию с фактической структурой интерфейса. Для решения этой проблемы была предпринята попытка отказаться от автоматического механизма Codable (образа кодирования/декодирования) для SplitTree и вместо этого реализовать собственный обработчик, сохраняющий не узел, а путь до него. В структуре SplitTree уже существовала вспомогательная структура Path, описывающая путь к узлу как последовательность компонентов (Component), указывающих направления (лево или право) в дереве.
Тем не менее, до начала работы Path и его компоненты не поддерживали протокол Codable, что требовало доработки. Внедрив Codable в Path и его компоненты, стало возможным безопасно сохранять путь к масштабированному узлу в сериализуемом виде. Это кардинально изменило логику сохранения состояния, поскольку теперь сериализовалась не сама структура узла, а путь к нему, что гарантирует целостность данных при последующем восстановлении. Вторым важным шагом стала реализация версии кодирования в SplitTree, заимствованная из другого компонента Ghostty - TerminalRestorable. Внедрение статической константы version и соответствующий механизм проверки версии при декодировании позволило контролировать совместимость формата сохраненных данных.
Если версия не соответствует ожидаемой, декодирование прерывается с ошибкой. Это предотвращает некорректное считывание устаревших или несовместимых данных, что является важным аспектом поддержки долгосрочной надежности кода и облегчает миграцию на новые версии. Следует отметить, что во время интеграции версии была выявлена частая ошибка Swift - запрет на использование статических сохраненных свойств (static stored properties) в обобщенных типах. Решением стало замена статического свойства на вычисляемое, что позволило успешно компилировать проект без утраты функциональности версии. После внесения всех изменений была проведена сборка проекта, которая первоначально завершилась с ошибками.
Исследование логов сборки выявило именно проблему со статическим свойством. Исправление данной ошибки обеспечило успешный билд, что подтвердило корректность внесенных правок. Помимо технической составляющей, подобные изменения значительно улучшают опыт использования приложения. Пользователи Ghostty перестанут сталкиваться с разочаровывающими багами, когда масштабированные сплиты оказываются негибкими, а интерфейс - нестабильным. Более того, улучшенная система сохранения состояния с поддержкой версий представляет собой прочный фундамент для будущих доработок и дополнений функционала, которые смогут работать без риска повредить или потерять пользовательские настройки из-за ошибок сериализации.
Данный кейс также служит хорошим примером продуманного взаимодействия между разработчиком и автоматизированным агентом. В ходе работы над ошибкой был использован подход agent-generated bug fixes, где ассистент помогал анализировать проблему, выявлять слабые места и предложить структурированное решение. Это демонстрирует, как сочетание человеческого опыта и машинной поддержки может ускорить отладку и повысить качество программного продукта. В целом, описанный кейс с SplitTree в Ghostty является важным напоминанием о том, насколько важно правильно подходить к сериализации деревьев объектов в пользовательских интерфейсах. Прямое сохранение ссылки на узел может привести к серьезным проблемам при изменениях или реструктуризации данных, тогда как сохранение пути обеспечивает гибкость и устойчивость.
Дополнительно, применение механизма версионирования позволяет безопасно эволюционировать формат сохранения без потери обратной совместимости. В перспективе такие методы могут быть полезны не только для терминальных клиентов, но и для любых приложений с динамическими интерфейсами, которые требуют точного восстановления состояния. Интеграция подобных решений повышает надежность, улучшает восприятие пользователем и облегчает дальнейшую поддержку продукта. Подводя итог, устранение ошибки с застреванием масштабированных сплитов Ghostty через внедрение собственного Codable с сохранением путей к узлам и поддержкой версионирования - это пример грамотного инженерного подхода, усиливающего стабильность и удобство работы с приложением. Такой уровень проработки деталей обязательно должен становиться нормой для проектов с глубоким управлением пользовательскими интерфейсами и их состояниями.
.