Иногда даже самые заядлые геймеры подолгу не могут пройти уровни в видеоиграх. Помните дремлющего дракона Сина из Dark Souls, уровень Chamber 15 из Portal или миссию Demolition Man — она же легендарная «миссия с вертолётиком» — из GTA Vice City? Вы пытаетесь и пытаетесь их пройти, но у вас не получается. Вы не понимаете, что упускаете и упускаете ли вообще.
Тогда вы идёте проходить другие доступные вам миссии, исследовать открытый мир, выполнять сайд-квесты. А потом, может быть даже не через один год, возвращаетесь к игре и проходите заветный уровень с первой-второй попытки.
Такое бывает и когда вы боретесь с багами. Привет! Меня зовут Лёша Берёзка. Я iOS техлид в Додо Пицце. Сегодня я расскажу вам историю о том, как внимательность и упорство творят чудеса, и помогают решать задачи, на которые другие бы просто забили.
Раньше у нас в приложении был таббар. В нём было несколько элементов, в том числе и кнопка перехода в корзину:
А потом случился редизайн: мы убрали таббар, а для корзины сделали отдельную кнопку в меню. Если в корзине пусто, то кнопка не видна, но как только вы что-то добавляете — кнопка тут же анимированно появляется:
Когда мы заменили таббар на плавающую кнопку, к нам в поддержку стали поступать жалобы на неработающую корзину. Выяснилось, что клиенты просто не видели кнопку корзины. Жалоб было не очень много и вроде бы некритично, но регулярный фон был.
Со временем мы заметили, что все обратившиеся клиенты пользовались устройствами на iOS 14. У наших QA аппарат с такой версией ОС был. Они попробовали воспроизвести баг, но ни разу не смогли этого сделать. В итоге поступили просто и радикально: включили постоянное отображение кнопки корзины на всех устройствах с iOS 14, независимо от наполненности корзины.
Прошло 3-4 месяца и я случайно узнал, что тикет с проблемой всё ещё открыт и в него всё ещё добавляются новые кейсы. Некоторые клиенты присылали видео с багом: кнопка корзины видна, но стоимость продуктов на ней не отображается, и по нажатию на неё ничего не происходит:
Я внимательно изучил кейсы, пообщался с QA и выяснил, что проблема стреляет только на iOS 14.0-14.4.2. У наших же QA аппарат был на iOS 14.6. Это объяснило, почему мы не могли воспроизвести баг.
Установить симулятор iOS 14.0 мы не смогли — у всех уже самые последние версии макосей и икскодов, с них её скачать нельзя. Если ставить подходящие старые икскоды, то они не запускаются — ОС сильно свежая (не шучу):
Если вы знаете, как установить симулятор iOS 14.0 на macOS 14, напишите в коментах.
Ситуация: симулятора нет, подходящего айфона у меня и у моих знакомых нет, а неработающая корзина у пользователей есть. Значит, чтобы починить баг, надо купить подходящий айфон.
В итоге я пошёл на Авито. Там я неделю искал нужный мне айфон, убеждая продавцов в том, что аппарат мне нужен именно на iOS 14, а я ничего не перепутал.
В конце концов я вышел на объявление от забытого привокзального сервисного центра формата «киоск в углу ТЦ». Там я и купил iPhone 6S на iOS 14.4.2 за 5500 ₽.
Пришёл домой, подключил айфон к маку и позвал на совместный дебаг всю iOS-команду Додо Пиццы — ребятам тоже было интересно, в чём там дело. Спустя полчаса выяснили причину:
Кнопку корзины мы сверстали на SwiftUI.
Экран меню у нас ещё на UIKit.
Кнопку корзины в меню мы встраиваем через
UIHostingController
.На этом
UIHostingController
не вызывается методviewDidLoad()
, в котором мы как раз подписываем кнопку на все нужные изменения и настраиваем на ней экшены. Все остальные методы ЖЦ вызываются.
Проверил другие подобные места с UIHostingController
— проблема есть везде. Осталось понять, точно ли проблема только на iOS 14.0-14.4.2, чтобы в фиксе ифчик по версии ОС правильный написать.
Открыл аналитику, собрал цифры, на каких именно версиях iOS 14 нет событий открытия корзины, а они везде есть. Даже события оформления заказов прилетают. Пу-пу-пум.
Я решил посмотреть детальную аналитику по всем сессиям. Чекал только те, где события есть, но быть их там не должно.
Оказалось, что клиенты нашли воркэраунды. Всего их было два:
Воркэраунд 1:
Положить товары в корзину.
Открыть историю заказов.
Повторить какой-то случайный заказ. Так корзина откроется автоматически.
Удалить из корзины товары из повторенного заказа, оставив только те продукты, которые хотели заказать изначально.
Оформить заказ.
Воркэраунд 2:
Положить товары в корзину.
Открыть коинстор. В нём можно купить продукты за додокоины.
Купить продукт за додокоины — всплывёт другая кнопка перехода в корзину.
Нажать на кнопку и открыть корзину.
Удалить из корзины товары, купленные в коинсторе, и оставить только те продукты, которые хотели заказать изначально.
Оформить заказ.
Отсекаю все такие кейсы и подтверждаю, что проблема только на iOS 14.0-14.4.2. Ура!
Пора чинить. Варианты такие:
Вручную дёргать
viewDidLoad()
сразу после создания инстанса вьюхи.Перенести настройку кнопки во
viewWillAppear()
, который вызывается корректно. Ну и спрятать за флажок, чтобы не больше раза отрабатывало.
Мы решили пойти первым путём, сократив время рефакторинга и уменьшив вероятность что-то сломать. Для этого везде заюзали UIHostingController
не напрямую, а через его сабкласс. В нём из конструктора вручную дёрнули viewDidLoad()
. Сам фикс уместился в пару строк — кайф, люблю такое:
open class DHostingController<Content>: UIHostingController<Content> where Content: View {
public override init(rootView: Content) {
super.init(rootView: rootView)
// Fix for iOS 14.0-14.4.2
//
// iOS doesn't call viewDidLoad despite view being loaded
// Other lifecycle methods, like viewWillAppear/viewWillDisappear are called as expected
if #unavailable(iOS 14.5) {
viewDidLoad()
}
}
var viewDidLoadAlready = false
open override func viewDidLoad() {
guard !viewDidLoadAlready else { return }
defer { viewDidLoadAlready = true }
super.viewDidLoad()
}
}
Когда выйдет версия с фиксом, мы отключим поддержку iOS 14. А пока расскажите нам вы, были ли у вас похожие кейсы? Покупали ли вы старые устройства, чтобы посмотреть, как появляется баг и как можно его поправить?
Предлагаю вам ещё поучаствовать в нашей голосовалке. Интересно, какую минимальную версию iOS поддерживаете вы!