Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

iOS server-rendered home widgets can't follow light/dark mode (`theme` param is always "light")

Aperta
#337 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub

I maintainer di solito rispondono entro 1 giorno

Nessuno ha ancora preso questa issue.

Valutazione

Difficoltà
5/5
Tempo stimato
Più di una settimana
Idoneità per principianti
35/100
Tipo di issue
Funzionalità
Chiarezza
Abbastanza chiara
Stato di attività
Attiva
Stack tecnologico
ios, react-native, swift, typescript

Direzione di ricerca

Start with ios/shared/WidgetServer/VoltraWidgetAppearance.swift and VoltraWidgetServerFetcher.swift:84 to understand how the theme parameter is set, then read ios/target/VoltraHomeWidget.swift:107,289 for entry parsing and rendering. Compare the suggested payload-variant and dynamic-color approaches before choosing a direction; done means server-rendered and app-pushed widgets render correctly in both appearances and respond to appearance changes, with tests covering selection and fallback behavior.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Descrizione

platform:iOS

What happens

A server-rendered home screen widget (serverUpdate on a widget with no entry) stays in its light colors when the device is in dark mode. It also doesn't change when the user switches appearance, until something renders the payload again.

Reproduced on 2.2.0. The code involved is unchanged in 2.3.2.

Repro

  1. Configure a home screen widget with serverUpdate pointing at an endpoint that returns renderWidgetToString(variants) and picks its colors from the theme query param.
  2. Put the device (or simulator) in dark mode and add the widget.
  3. The server receives theme=light on every timeline fetch, so the widget renders light.
  4. Switch the device appearance either way. The widget keeps whatever colors were in the last payload.

Cause

There are two separate problems.

1. The theme sent with server requests isn't the system appearance. VoltraWidgetAppearance.currentTheme() (ios/shared/WidgetServer/VoltraWidgetAppearance.swift:9) reads UITraitCollection.current.userInterfaceStyle. It runs inside the timeline provider's fetch (VoltraWidgetServerFetcher.swift:84), which isn't drawing anything, so there is no meaningful current trait collection. In our testing it came back "light" while the system was dark. A TimelineProvider can't know the appearance at all: WidgetKit asks for the timeline once and draws the same entry in both light and dark.

2. Payloads can only describe one appearance. Colors are serialized as fixed strings (JSColorParser.parse has no light/dark color), and VoltraHomeWidgetEntry holds a single rootNode that VoltraHomeWidgetView draws without reading @Environment(\.colorScheme) (ios/target/VoltraHomeWidget.swift:107, :289). So even with a correct theme, a payload rendered for one appearance is wrong as soon as the user switches. Fixing that means re-rendering from the app or waiting for the next server refresh.

Dynamic Widgets solve this through env.colorScheme. #319 covers light/dark colors for Android Dynamic Widgets and scopes server-rendered payloads out. Server-rendered and app-pushed (updateWidget) widgets on iOS have no equivalent.

Suggested fix

Some options, roughly smallest first:

  • A. Per-appearance variants in the payload. Allow optional <family>@light / <family>@dark keys next to the existing family keys. The provider parses them, and VoltraHomeWidgetView picks one with @Environment(\.colorScheme), falling back to the plain family key. Older clients ignore unknown keys, so the payload stays backward compatible. renderWidgetToJson already accepts arbitrary root names, so both variants share one stylesheet. We run this as a local patch (about 60 lines, all in VoltraHomeWidget.swift) and can open a PR if this direction works for you.
  • B. Light/dark color pairs in color values, e.g. { light: '#FFFFFF', dark: '#1C1C1E' }, resolved when drawing through a dynamic UIColor. This makes payloads smaller and covers every widget kind, and it matches what #319 proposes for Android.
  • C. Either way, make theme reliable or document it. If the appearance can't be known while fetching, it may be better to drop theme or mark it as best-effort. That would tell server authors to send both appearances (A or B) instead of trusting it.

Workaround

Until then, we render both appearances and let the widget pick one with the local patch from option A. On the server we ignore theme and read the appearance the app last reported from a custom credential header (setWidgetServerCredentials({ headers })). Unpatched builds fall back to that.

Environment

  • @use-voltra/ios-client 2.2.0 (VoltraWidgetAppearance / VoltraHomeWidget checked in 2.3.2)
  • Xcode 27, iOS 26.5 and iOS 27 simulators
  • Expo SDK 58, server-rendered widget with serverUpdate, app also pushing with updateWidget
Lingua principale
TypeScript
Stelle
844
Fork
46
Merge medio
2g 14h
PR unite (30g)
27

Preparare l'ambiente

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di callstackincubator/voltra

Tutte le issue di callstackincubator/voltra

Issue simili

Altre issue su TypeScript

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.