| layout | default |
|---|---|
| title | Weather app example |
The weather example is a useful end-to-end reference because it combines a native data table, asynchronous HTTP, loading state, selection, an XML detail view, and a graceful offline/error path.
Run it with:
make run-weather
# or
./lua-objc apps/weatherThe service is wttr.in; when the request fails, the app keeps its native table
usable and shows -- or unreachable values instead of crashing.
The condition artwork lives in apps/weather/assets/ as four original SVG
assets created for this example (sunny.svg, cloudy.svg, rain.svg, and
snow.svg). The requested Orca checkout was not present at the supplied local
path, so these are not copied or attributed as Orca assets.
apps/weather/
init.lua returns the controller class
Model.lua city list and wttr.in response parsing
Controller.lua loading, rows, selection, and detail mounting
views/Window.etlua window configuration and Refresh toolbar item
views/CityDetail.etlua selected-city detail and forecast layout
Model.lua owns the network boundary. It returns a normalized city record with
stable keys such as temp, cond, humid, wind, and forecast, so the
controller and template do not need to understand the upstream JSON shape.
Controller.lua owns the native list and detail pane. It calls
showLoading() before the asynchronous fetch and hideLoading() afterward,
then replaces all rows in one operation. Selection calls showDetail() and
mounts the selected city into the detail container.
The stable visual structure belongs in views/CityDetail.etlua:
<VStack flexGrow="1" padding="24" spacing="16" alignment="leading">
<Label text="<%= city.city %>" size="24" weight="bold" />
<HStack spacing="24" alignment="top">
<Label text="<%= tostring(city.temp or "--") %>°C" size="32" />
<Label text="<%= city.cond or "unreachable" %>" size="16" />
</HStack>
</VStack>
The dynamic location list remains in Controller.lua because the current XML
registry can declare List and Column, but it cannot yet declare a row schema
with cell formatters, selection callbacks, or a runtime data source. That is a
framework documentation/API gap, not an application mistake. Until a schema
API exists, use this pattern:
The seven-day forecast is different: it is stable structure and therefore lives
in etlua. Its seven 100-point cards occupy 772 points, so the native
ScrollView keeps the cards readable and reveals a horizontal scroller when a
window is narrower than the forecast.
Open-Meteo supplies daily high/low, precipitation probability, wind, and UV values for the full week.
local list = ns.List {
style = "sourceList",
flexGrow = 1,
columns = {
{ id = "city", title = "City", width = 150 },
{ id = "temp", title = "Temperature", width = 110, alignment = "right" },
},
}
list:onRowSelect(function(_, _, row)
controller:showDetail(controller.weatherData[row._id], row._id)
end)
list:replaceRows(rows)- Keep the city list and response parsing in a headless model.
- Normalize incomplete API responses before rendering.
- Use
ns.asyncfor network work; do not block the window or add sleeps. - Show the table's native loading spinner during fetches.
- Use
replaceRows()for a completed batch of results. - Keep primary data in a
fullWidthorplaintable. - Use semantic system colors and native controls; do not use emoji weather icons
as a substitute for a missing native image API. The example's local SVGs are
explicit image assets loaded through the native
Imageview. - Provide a selected, empty, loading, and unreachable state.
- Test with a stubbed
ns.fetch_jsonso tests never depend on the network.
- Declarative table row schemas: XML can describe columns, but not typed row fields, cell secondary text, formatters, or selection behavior.
- Declarative async state: templates do not currently bind directly to a loading/error/result state. Controllers own the state transition.
- Semantic weather/icon assets: the XML registry has
ImageandSystemImage, but no weather-condition symbol mapping or remote image policy. The example intentionally uses text conditions. - Cross-platform HTTP contract:
fetch_jsonis shared by the platform layers, but this example is currently demonstrated and tested on AppKit.
These gaps are documented so contributors can improve the framework without making every application invent a different pattern.
make test
./lua-objc tests/weather_controller.test.luaThe controller test stubs both ns.async and ns.fetch_json, verifies loading
lifecycle and row formatting, and runs without opening a window.
