Как стать автором
Обновить

Комментарии 9

Как решаете проблему поиска topController, если в момент поиска может идти анимация показа другого экрана?
Сам поиск будет работать корректно, потому что presentedViewController или кастомное поле экрана-контейнера будет обновлено сразу, до начала анимации. Если же вы имеете в виду выполнение перехода во время анимации показа другого экрана, то тут есть два случая:
1) Автоматическая навигация выполняется как шаг в единой серии переходов на какой-то экран (например серия «переключение вкладки + push в navigation controller» как части единого перехода в конкретный чат). В этом случае очередной шаг будет выполняться после окончания анимации предыдущего.
2) Переход выполняется, когда уже начал показываться другой экран и эти действия происходит независимо. Тогда можно выяснять состояние активного экрана, возможно ли прямо сейчас показать новый экран сверху него и, возможно, подписываться на изменение этого состояния. Как именно это сделать — часто зависит от типа перехода и это уже детали реализации.
В демо проекте в ChatViewController почему-то self.chatId = "" вместо self.chatId = chatId. Из-за этого экран Contact 1 создается несколько раз, если тапать на экране Profile по Go to Contact 1.

Спасибо за замечание! Если будет дополнительные предложения по доработке демо-приложения, то советую использовать issues или pull requests в самом GitHub, чтобы работать прямо в контексте проблемы.

Спасибо за ссылку, на первый взгляд действительно похоже

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

Судя по демо-проекту у вас транзишн привязан к конкретному контексту, соответственно ViewControllerContextTransitionProvider выглядит спорно, в большом проекте switch станет огромным. Похожая ситуация с ViewControllersByContextFactory, только тут switch будет еще больше.

Возможно имеет смысл создавать отдельную реализацию для каждого ViewControllerContext, которая будет хранить в себе всю необходимую информацию о переходе. Количество файлов в проекте вырастет, но зато мы получим тайп-сейф инициализаторы контекста, конфигурация контроллеров будет инкапсулирована внутри контекста, плюс отпадет необходимость в реализации ViewControllerContextInfo, энума ScreenType, который в реальном проекте может может иметь 50+ значений; исчезнет огромный switch из ViewControllersByContextFactory.

Что-то вроде этого:
protocol ViewControllerContext {
    
    var transition: ViewControllerContextTransition { get }
    func prepareController(using resolver: ContextResolver) -> UIViewController
}

struct ChatViewControllerContext: ViewControllerContext {
    
    let chatId: String
    let transition: ViewControllerContextTransition = NavigationControllerTransition()
    
    func prepareController(using resolver: ContextResolver) -> UIViewController {
        // ContextResolver – абстракция над вашим DI-контейнером etc.        
        return ChatViewController(chatId: chatId)
    }
}


А методы свитчера и холдера будут иметь generic-спецификацию:
protocol ViewControllerContextHolder {
    func hasSameContext<C: ViewControllerContext>(as other: C) -> Bool
}

protocol ViewControllerContextSwitcher {
    func canSwitch<C: ViewControllerContext>(to context: C) -> Bool
    func switchContext<C: ViewControllerContext>(to context: C, animated: Bool)
}


+1

Зарегистрируйтесь на Хабре, чтобы оставить комментарий