Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion .github/workflows/ios.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,21 @@ name: Build iOS
# Pull requests only. A push to `main` is handled by release.yml,
# which builds the signed, uploadable artifact — running this as well would
# spend a second runner producing an unsigned one nobody installs.
#
# Every pull request, not only the ones targeting `main`. A stacked branch
# opens its pull request against the layer below it, so `branches: [main]`
# silently excluded exactly the reviews that most needed a compiler: the ones
# where only the top of the stack is ever read against `main`. Four iOS-only
# commits reached review that way without Swift being built once.
on:
pull_request:
branches: ["main"]

# One in-flight run per ref; a new push cancels the previous run. This matters
# more now that the trigger is every pull request: restacking a five-deep chain
# pushes five branches at once, and each one wants a macOS runner.
concurrency:
group: ios-${{ github.ref }}
cancel-in-progress: true

jobs:
changes:
Expand Down Expand Up @@ -66,6 +78,14 @@ jobs:
bash tool/dev/deps.sh
bash tool/dev/codegen.sh

# Before the device build, because it is the step that can fail on
# something a reviewer would have had to catch by eye. It builds the
# Runner scheme, which depends on DPIPWidgetsExtension, so the widget
# target and its SwiftUI views are compiled here as well — nothing else
# in this repository compiles them.
- name: Test (ios/RunnerTests)
run: bash tool/dev/test_ios.sh

- name: Build iOS App and create IPA
run: |
bash tool/dev/build.sh ios --debug
Expand Down
6 changes: 6 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ tool/dev/analyze.sh
|---|---|
| Start the app | `tool/run.sh` (see [Running](#running)) |
| Run the tests | `tool/dev/test.sh` |
| Run the Swift tests (macOS only) | `tool/dev/test_ios.sh` — `ios/RunnerTests`, which `tool/dev/test.sh` cannot reach |
| Test coverage, into `coverage/` | `tool/dev/coverage.sh` (see [Coverage](#coverage)) |
| Format + analyze | `tool/dev/analyze.sh` |
| Reformat in place | `tool/dev/format.sh` |
Expand Down Expand Up @@ -223,13 +224,18 @@ tool/check/tooling.sh
tool/dev/analyze.sh
tool/dev/codegen.sh # then git diff --exit-code
tool/dev/test.sh
tool/dev/test_ios.sh # only if you touched ios/ — macOS, and slow
```

The bash gates need only bash and python3, so they fail fast without the
toolchain. `.github/workflows/ci.yml` must stay green;
`android.yml` / `ios.yml` build artifacts and `review.yml` adds an automated PR
review.

`ios.yml` also runs `ios/RunnerTests`, and it is the only thing that compiles
the widget extension — so a change under `ios/` is unverified until that job
goes green, whatever `ci.yml` says about the Dart side.

Safety-critical seismic maths is pinned by golden tests
(`test/features/earthquake/eew_estimator_test.dart`). If you change the EEW
estimator, update those goldens deliberately.
Expand Down
20 changes: 20 additions & 0 deletions ios/DPIPWidgets/CurrentApparentTemperature.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import Foundation

/// CWA outdoor, ventilated, shaded apparent temperature in degrees Celsius.
func currentApparentTemperature(
temperature: Double?,
humidity: Int?,
windSpeed: Double?
) -> Double? {
guard let temperature, let humidity, let windSpeed,
temperature.isFinite, windSpeed.isFinite,
(0...100).contains(humidity), windSpeed >= 0 else {
return nil
}

let vapourPressure = Double(humidity) / 100 * 6.105
* exp(17.27 * temperature / (237.7 + temperature))
let apparentTemperature = 1.04 * temperature + 0.2 * vapourPressure
- 0.65 * windSpeed - 2.7
return apparentTemperature.isFinite ? apparentTemperature : nil
}
35 changes: 35 additions & 0 deletions ios/DPIPWidgets/CurrentWeatherRemoteDTO.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ struct CurrentWeatherRemoteDTO: Decodable, Sendable {
let temperature: Double?
let humidity: Int?
let rain: Double?
let windDirection: String?
let windSpeed: Double?

var condition: CurrentWeatherWidgetCondition {
currentWeatherWidgetCondition(for: weatherCode)
Expand All @@ -31,13 +33,15 @@ struct CurrentWeatherRemoteDTO: Decodable, Sendable {
let temperature: Double?
let humidity: Int?
let rain: Double?
let wind: Wind

private enum CodingKeys: String, CodingKey {
case weather
case weatherCode
case temperature
case humidity
case rain
case wind
}

init(from decoder: Decoder) throws {
Expand All @@ -57,6 +61,7 @@ struct CurrentWeatherRemoteDTO: Decodable, Sendable {
from: container,
forKey: .rain
)
wind = try container.decode(Wind.self, forKey: .wind)
}

private static func decodeNullableDouble(
Expand Down Expand Up @@ -88,6 +93,34 @@ struct CurrentWeatherRemoteDTO: Decodable, Sendable {
}
}

private struct Wind: Decodable, Sendable {
let direction: String?
let speed: Double?

private enum CodingKeys: String, CodingKey {
case direction
case speed
}

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
direction = try container.decodeIfPresent(
String.self,
forKey: .direction
)

guard let value = try container.decodeIfPresent(
Double.self,
forKey: .speed
) else {
speed = nil
return
}

speed = value == -99 ? nil : value
}
}

init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
let station = try container.decode(Station.self, forKey: .station)
Expand All @@ -100,6 +133,8 @@ struct CurrentWeatherRemoteDTO: Decodable, Sendable {
temperature = data.temperature
humidity = data.humidity
rain = data.rain
windDirection = data.wind.direction
windSpeed = data.wind.speed
}
}

Expand Down
Loading
Loading