Przejdź do głównej zawartości

Wymuszona aktualizacja aplikacji iOS

Aplikacja iOS sprawdza przy starcie, czy użytkownik ma najnowszą wersję z App Store. Jeżeli nie — zamiast ekranu aplikacji pokazuje się komunikat „Zaktualizuj aplikację" z przyciskiem prowadzącym prosto do App Store. Nie da się go pominąć: dopuszczalna jest wyłącznie wersja aktualna.

👤 Instrukcja dla użytkownika

Co widzi klient

Po wypuszczeniu nowej wersji do App Store klient, który jej jeszcze nie pobrał, przy najbliższym uruchomieniu aplikacji zobaczy pełnoekranowy komunikat:

  • tytuł Zaktualizuj aplikację i wyjaśnienie, że jego wersja jest nieaktualna,
  • numery wersji: zainstalowana i najnowsza,
  • sekcja Co nowego — dokładnie ta sama lista zmian, którą wpisujemy w App Store Connect przy wydaniu,
  • przycisk Aktualizuj w App Store, który otwiera kartę aplikacji w App Store.

Po aktualizacji i ponownym uruchomieniu aplikacja działa normalnie — komunikat znika sam, nie trzeba nic klikać ani się przelogowywać.

Kogo to dotyczy

GdzieCzy blokuje
Aplikacja iOS (App Store)Tak — poniżej najnowszej wersji ekran jest zablokowany.
TestFlightNie — buildy testowe mają numer wyższy lub równy sklepowemu.
Android (aplikacja z Play)Nie — to powłoka TWA, treść jest webowa i aktualizuje się sama.
Przeglądarka (klient, panel)Nie — sprawdzenie w ogóle się nie uruchamia.

Co zrobić po wypuszczeniu wydania

  1. Wypuść wersję do App Store (yarn ios:release, potem zatwierdzenie wydania w App Store Connect — patrz Wydanie aplikacji iOS).
  2. Odczekaj, aż wydanie faktycznie pojawi się w sklepie. Blokada włącza się samoczynnie w ciągu ok. godziny od tego momentu (tyle trzyma się cache).
  3. Nie trzeba nic wdrażać ani przełączać w panelu — numer najnowszej wersji pobierany jest z App Store.

:::warning Blokada dotyczy wszystkich naraz Od chwili, gdy App Store poda nową wersję, każdy klient ze starszą aplikacją zobaczy ekran aktualizacji. Nie wypuszczaj wydania tuż przed zajęciami, na które klienci muszą się zapisać, jeżeli nie masz pewności co do stabilności buildu. Awaryjny wyłącznik opisany jest niżej. :::

🔧 Dokumentacja techniczna

Przepływ

Elementy

PlikRola
lib/app-update/version.tsPorównanie wersji segment po segmencie (1.10 > 1.9, 1.1 == 1.1.0).
lib/app-update/app-store.tsOdpytanie iTunes Lookup API, cache revalidate: 3600, timeout 8 s.
lib/app-update/types.tsKontrakt odpowiedzi AppVersionResponse.
app/api/public/app-version/route.tsPubliczny endpoint GET, Cache-Control: public, max-age=900.
components/app-update-gate.tsxPełnoekranowa blokada, montowana w obu gałęziach app/layout.tsx.
messages/pl.json, messages/en.jsonPrzestrzeń appUpdate (w ALWAYS_NAMESPACES, więc dostępna na każdej stronie).

Skąd bierze się numer wersji

Publiczne iTunes Lookup API (https://itunes.apple.com/lookup?bundleId=...) zwraca dla identyfikatora pakietu dev.workers.dtmsoftwares.dmt-cms.twa wersję aktualnie opublikowaną w App Store, jej opis zmian, datę wydania i link do karty aplikacji. Nie wymaga klucza ani podpisu JWT — w odróżnieniu od App Store Connect API, którego klucz mamy tylko do wysyłki buildów.

Wersję zainstalowaną podaje App.getInfo() z @capacitor/app, czyli CFBundleShortVersionString (MARKETING_VERSION z projektu Xcode).

Wersji minimalnej nie konfigurujemy nigdzie — wymagana jest zawsze ta ze sklepu.

Zachowanie awaryjne

Każdy błąd — brak sieci, timeout, nieoczekiwana odpowiedź Apple, pusty wynik lookupu — kończy się { enforced: false } i brakiem blokady. Niedostępność Apple nie może zamknąć klientom aplikacji.

Awaryjny wyłącznik: zmienna środowiskowa workera klienta

IOS_FORCE_UPDATE_ENABLED=false

wyłącza mechanizm całkowicie (endpoint zwraca wtedy enforced: false bez odpytywania Apple). Domyślnie mechanizm jest włączony.

Konfiguracja

ZmiennaDomyślnie
NEXT_PUBLIC_IOS_APP_BUNDLE_IDdev.workers.dtmsoftwares.dmt-cms.twa
NEXT_PUBLIC_IOS_APP_STORE_COUNTRYpl
NEXT_PUBLIC_IOS_APP_STORE_URLhttps://apps.apple.com/pl/app/acepark/id6757109661
IOS_FORCE_UPDATE_ENABLEDtrue

Uwagi wdrożeniowe

  • Endpoint leży pod app/api/public, więc trafia wyłącznie do buildu client (patrz Dwa workery) — aplikacja iOS ładuje domenę klienta, więc to wystarcza.
  • Ścieżka /api/public/app-version jest dopisana do publicRoutes w middleware.ts: komunikat musi pokazać się także przed zalogowaniem.
  • Link do App Store otwiera się poza WKWebView, bo apps.apple.com nie znajduje się na liście allowNavigation w capacitor.config.ts — Capacitor przekazuje taki adres systemowi.