Перейти до вмісту
Усі статті
8 хв читання

Store fallback: що робити, коли застосунку немає

Ланцюжок deep link, що завершується або на сторінці стору, або на fallback-сторінці сайту

Ланцюжок intent вичерпався. Кожен package, який ви пробували, повернувся порожнім, і тепер відвідувач без застосунку стоїть на маршруті, якому нікуди більше його відправити, крім стору — або вашої власної веб-сторінки натомість. Помиліться на цьому останньому кроці, і це буде невідрізнимо від будь-якого іншого типу поломки: відвідувач іде геть, і ніщо у вашій воронці не підкаже, чи застосунку справді немає, чи ваше посилання на стор побудоване з помилкою, чи воно веде на лістинг, якого більше не існує — той самий діагностичний туман, що й постбек, який так і не прийшов.

Помилитись тут можна трьома окремими способами, і жоден із них не виглядає як помилка, поки ви цілеспрямовано її не шукаєте.

Дві форми

Store fallback на Android та iOS — це не одне й те саме значення в різному синтаксисі. Одне виводиться з того, що у вас уже є; інше треба знайти й зберегти окремо.

ПлатформаURL store fallback
Androidhttps://play.google.com/store/apps/details?id=<package>
iOShttps://apps.apple.com/app/id<numeric id>

Fallback на Android детермінований. Це той самий рядок package, який ви вже використали для побудови ланцюжка intent://, просто переформульований як query-параметр — якщо ви знаєте com.instagram.android достатньо, щоб таргетувати його, ви вже знаєте і URL його сторінки в Play Store, без окремого пошуку.

iOS не дає такого скорочення. Числовий id — це окреме, непрозоре значення, яке App Store призначає кожному лістингу, і його не можна вивести з назви застосунку, bundle identifier чи будь-чого іншого, що у вас, імовірно, уже є під рукою. Числові id App Store неможливо вгадати. Ви шукаєте кожен вручну один раз і зберігаєте його поруч із назвою package — і, на відміну від package, немає жодної fallback-логіки, яка відновить його, якщо пошук так і не було зроблено.

Цю асиметрію легко проґавити, поки ви будуєте android-частину ланцюжка, бо fallback для Android дістається безкоштовно в момент, коли ви вже обрали package для таргетингу. Fallback для iOS не дістається безкоштовно ні з чим. Це окремий, незалежний етап збору даних по кожному застосунку, і пропустити його — не означає гучно провалитись: це просто означає, що fallback для iOS тихо відсутній саме тоді, коли ланцюжок його потребує.

Коли відсутній id означає «зник», а не «невідомо»

Порожнє поле id для iOS зазвичай означає лише, що його ще ніхто не шукав. Іноді це означає щось інше: шукати нема чого, бо самого лістингу більше не існує.

Microsoft закрила споживчий Skype у травні 2025 року. Застосунок і досі можна встановити на Android — його лістинг у Play Store і назва package не постраждали, — але лістингу в App Store, до якого можна прив’язати id на iOS, більше немає. Це не тимчасова прогалина, яка чекає, поки розробник внесе правильне числове значення. Це постійний стан саме для цієї платформи, тоді як android-частина того самого fallback-ланцюжка лишається настільки ж валідною, якою була завжди.

Ця відмінність має практичне значення. Відсутній package на Android зазвичай означає «знайти його», і виправлення — просто піти й знайти рядок. Відсутній id на iOS може означати те саме, а може означати «цей лістинг зник і не повернеться», і поводитись із другим випадком як із першим — залишаючи його як TODO до наступного спринту — просто означає, що той самий мертвий пошук повторять пізніше знову. Щойно ви підтвердили, що лістинг закрито, а не просто не зафіксовано, приберіть fallback для iOS для цього застосунку повністю і дайте ланцюжку завершитися на вашій власній веб-сторінці саме на iOS, залишивши запис для Android недоторканим, оскільки Android жодного разу не постраждав від того, що прибрало лістинг на iOS.

Shopee: коли відсутність fallback краща за fallback

У більшості застосунків — один глобальний лістинг у сторі на кожну платформу, і саме це робить таблицю з двома формами вище придатною як шаблон. У Shopee інакше. Вона постачає окремий Android package на кожну країнуcom.shopee. плюс код країни для кожного з одинадцяти ринків, від Індонезії та Бразилії до Чилі й Аргентини, — тому що працює як одинадцять окремих національних вітрин, а не як один продукт з одним лістингом.

Ця структура означає, що немає єдиної глобальної сторінки Shopee, на яку можна fallback-нути. Оберіть лістинг будь-якої однієї країни як «той самий» store fallback для Shopee — і ви влучите правильно рівно для одного з тих одинадцяти ринків, а решту десятьох відвідувачів відправите на лістинг не тією мовою, не в тій валюті і, цілком можливо, навіть не доступний для встановлення в їхньому регіоні.

Це саме те рішення, яке варто приймати свідомо, а не за замовчуванням: для Shopee відсутність store fallback краща за неправильний. Відвідувач, який потрапляє на вашу власну веб-сторінку після невдалої спроби відкрити застосунок, принаймні потрапляє кудись правильно, незалежно від того, з якого з одинадцяти ринків він прийшов. Відвідувач, спрямований на лістинг не тієї країни в App Store чи Play Store, не може відрізнити «застосунку немає» від «мене спрямували не в ту країну», а сторінка стору іноземною мовою виглядає як поломка так, як ваша власна веб-сторінка не виглядає. Пропустіть крок зі стором саме для Shopee й дайте ланцюжку завершитися там, де він насправді правильний скрізь: на вашій сторінці, а не на здогадці, до якого з одинадцяти сторів належить відвідувач.

Та сама структура з одинадцятьма package, яка змушує вас пробувати com.shopee.id, com.shopee.br, com.shopee.vn та решту по черзі на Android, — це саме те, що робить єдиний store fallback неможливим на жодній платформі. Немає єдиного Shopee, на яку можна fallback-нути, бо немає єдиної Shopee — їх одинадцять, і питання fallback має чітку відповідь лише для застосунків, де це не так.

Де насправді місце fallback

Store fallback — це не лише вибір URL, а й питання часу, і помилка з часом тихо ламає кандидатів, які йдуть перед ним.

S.browser_fallback_url у посиланні intent:// приймає лише значення http і https; значення market://, поставлене туди, Android просто відкидає. А Chrome вимагає справжнього жесту користувача, щоб узагалі запустити навігацію intent:// — без нього Chrome повністю пропускає застосунок і переходить прямо на S.browser_fallback_urlте саме тихе приземлення в браузері, яке виникає щоразу, як зникає жест користувача, — що зовні сторінки виглядає ідентично до «застосунку немає».

Поєднайте ці дві поведінки — і наслідок легко проґавити, поки він не коштує вам кандидата. Якщо ви прикріпите fallback URL до першого package у ланцюжку з кількома package, відвідувач, у якого просто немає цього першого package — але встановлений другий, — буде виштовхнутий у браузер до того, як другого кандидата взагалі спробують. Fallback спрацьовує зарано і з’їдає конверсію, яка мала б відбутися. Це стосується кожного кроку в ланцюжку, включно з останнім: жоден кандидат не несе власного fallback URL.

Fallback належить після того, як вичерпано всіх кандидатів, а не вбудований в окремий крок, і саме ваша власна сторінка — а не ланцюжок intent — вирішує, коли цей момент настав. Спочатку спробуйте кожен package по черзі; лише коли останній провалився, відправляйте відвідувача на посилання стору, і лише якщо це посилання справді існує та веде в правильну країну.

Перед тим як запускати fallback

ПеревіркаГотово
Жоден кандидат у ланцюжку intent не несе власний browser_fallback_url, включно з останнім
Кожен числовий id iOS знайдено й збережено по кожному застосунку, ніколи не виведено чи вгадано
Закритий лістинг iOS прибрано з ланцюжка саме для нього, запис для Android лишено без змін
Застосунки без єдиної глобальної сторінки стору повністю пропускають store fallback, а не обирають один ринок
Посилання на стор спрацьовує лише після того, як усі package-кандидати спробувано й вони провалились

FAQ

Чи можна просто вставити market:// у S.browser_fallback_url і покластися на Android?

Ні. S.browser_fallback_url приймає лише значення http і https — URL market://, поставлений туди, відкидається, а не конвертується, тож параметр поводиться так, ніби ви його взагалі не задавали.

Що станеться, якщо відвідувач тапне посилання без справжнього жесту користувача?

Chrome вимагає жест користувача, щоб запустити навігацію intent://. Без нього Chrome взагалі не намагається відкрити застосунок — він одразу переходить на S.browser_fallback_url, і зовні сторінки це невідрізнимо від того, що застосунку справді немає.

Чи безпечно завжди трактувати відсутній id App Store як «ще не шукали»?

Лише поки ви не перевірили. За замовчуванням трактуйте його як невідомий, але підтвердіть це, перш ніж будувати автоматизацію навколо повторного пошуку — деякі id відсутні тому, що самого лістингу більше немає, і закритому застосунку нема чого шукати.

Де живе це правило

Тримайте правило порядку в одному місці, а не повторюйте його в кожному ланцюжку intent, який ви будуєте: спочатку спробуйте кожного package-кандидата, без fallback URL на жодному з них, і лише передавайте відвідувача на посилання стору — або нікуди, як для Shopee — коли весь список вичерпано. Streams від DarkCore застосовує цей порядок за замовчуванням, окремо по кожній платформі, тож закритий лістинг iOS чи застосунок із прив’язкою до країни без глобальної сторінки стору — це правило маршрутизації, яке ви налаштовуєте один раз, а не збій, який знаходите вже в продакшні.

  • app not installed fallback
  • play store deep link fallback
  • app store id link
  • deep link fallback url