雲端 Mac 上的 iOS 溫度換算契約:絕對溫度、溫差與顯示驗證
天氣或感測器應用程式若把「升溫十攝氏度」顯示成「升溫五十華氏度」,通常不是格式設定出了問題,而是把溫差當成絕對溫度來換算。在雲端 Mac 上,可以先用獨立的 Swift 測試套件確立計算契約,再接入 iOS 專案;不必連接感測器,也不必啟動模擬器。
先區分讀值與變化量
絕對溫度換算同時涉及比例與原點偏移:攝氏零度對應華氏三十二度。溫差則只有比例變化:上升十攝氏度對應上升十八華氏度。Foundation 的 Measurement<UnitTemperature> 表示溫度讀值,不會根據變數名稱判斷它是否為變化量。
建議在模型中明確區分 reading 與 delta,並在介面文件中註明單位。業務邏輯層一律保留未經格式化的數值,到顯示時才決定小數位數。不要將「二十三點五度」這類介面文字重新解析成計算輸入。
溫差可以是負值;低於絕對零度的絕對溫度則應拒絕。這兩個欄位不能共用同一套範圍驗證規則。
以下驗證針對物理溫度。若產品處理的是數學外推資料,應另外聲明適用範圍,而不是悄悄放寬輸入限制。
建立不依賴介面的測試套件
先在獨立目錄中確認工具鏈並建立套件,避免更動現有專案設定。執行這些指令前,必須已安裝可用的 Xcode 命令列工具;測試會使用目前選定的 Swift 工具鏈。
xcrun swift --version
workdir="$(mktemp -d)"
cd "$workdir"
swift package init --type library --name TemperatureChecks
保留產生的檔案,再將下一節程式碼儲存為 Tests/TemperatureChecksTests/TemperatureContractTests.swift。程式碼只依賴 Foundation 與 XCTest,不需要加入外部套件。
獨立套件驗證的是換算規則,不代表 iOS 顯示已經通過驗收。移入專案後,應讓測試呼叫正式實作,避免兩份函式各自修改,測試卻始終只檢查舊副本。
用基準點與往返換算確立規則
絕對溫度先檢查是否為有限值,再換算成克耳文,檢查是否低於物理下限。溫差則以「換算後的端點減去換算後的零點」消除偏移。這種寫法只適用於此處的溫度線性換算,不要直接套用到任意單位模型。
import Foundation
import XCTest
private enum TemperatureError: Error {
case invalidReading
}
private func kelvin(
_ value: Double,
from unit: UnitTemperature
) throws -> Double {
guard value.isFinite else {
throw TemperatureError.invalidReading
}
let result = Measurement(value: value, unit: unit)
.converted(to: .kelvin).value
guard result.isFinite, result >= 0 else {
throw TemperatureError.invalidReading
}
return result
}
private func temperatureDelta(
_ value: Double,
from source: UnitTemperature,
to target: UnitTemperature
) -> Double {
let endpoint = Measurement(value: value, unit: source)
.converted(to: target).value
let origin = Measurement(value: 0, unit: source)
.converted(to: target).value
return endpoint - origin
}
final class TemperatureContractTests: XCTestCase {
func testReferencePoints() throws {
let freezing = Measurement(
value: 0,
unit: UnitTemperature.celsius
).converted(to: .fahrenheit).value
XCTAssertEqual(freezing, 32, accuracy: 1e-9)
XCTAssertEqual(
try kelvin(0, from: .celsius),
273.15,
accuracy: 1e-9
)
XCTAssertEqual(
try kelvin(-273.15, from: .celsius),
0,
accuracy: 1e-9
)
}
func testTemperatureDeltas() {
for value in [-10.0, 0.0, 10.0] {
XCTAssertEqual(
temperatureDelta(
value,
from: .celsius,
to: .fahrenheit
),
value * 1.8,
accuracy: 1e-9
)
}
}
func testRoundTrip() {
for value in [-40.0, 0.0, 37.0, 100.0] {
let original = Measurement(
value: value,
unit: UnitTemperature.celsius
)
let restored = original.converted(to: .fahrenheit)
.converted(to: .celsius)
XCTAssertEqual(
restored.value,
value,
accuracy: 1e-9
)
}
}
func testInvalidReadings() {
for value in [
Double.nan,
Double.infinity,
-Double.infinity,
-274.0
] {
XCTAssertThrowsError(
try kelvin(value, from: .celsius)
)
}
}
}
在套件根目錄執行 swift test。容差是用來吸收浮點運算誤差,不是業務上可接受的誤差。感測器精度應另行定義,不能靠放寬斷言容差來掩蓋錯誤。
往返測試也不能取代基準點:如果兩個方向的錯誤恰好互相抵消,往返測試仍可能通過。至少應保留零點、負值、已知參考值與無效輸入這四類樣本。溫差輸入端也應自行拒絕非有限值;上面的輔助函式只示範換算,不負責輸入驗證。
分開驗收在地化與數值精度
將以下程式碼儲存為套件目錄中的 format-temperature.swift,並以 swift format-temperature.swift 執行。此處固定使用攝氏單位,避免地區偏好自動改變單位,讓檢查範圍限於顯示規則。
import Foundation
for identifier in ["zh_CN", "en_US", "fr_FR"] {
let locale = Locale(identifier: identifier)
let formatter = MeasurementFormatter()
formatter.locale = locale
formatter.unitOptions = .providedUnit
formatter.unitStyle = .short
formatter.numberFormatter.locale = locale
formatter.numberFormatter.minimumFractionDigits = 1
formatter.numberFormatter.maximumFractionDigits = 1
let reading = Measurement(
value: 23.45,
unit: UnitTemperature.celsius
)
print(identifier, formatter.string(from: reading))
}
檢查小數分隔符號、單位符號與間距,但不要假設不同系統版本輸出的完整字串會逐字相同。需要使用快照斷言時,應一併記錄系統版本與地區識別碼。
顯示溫差時,不要直接交由絕對溫度格式器隱式處理。先算出目標單位的溫差,再透過數字格式器與在地化資源明確標示「溫差」。負零也應有產品規則:若四捨五入後顯示為零,是否移除負號,應在顯示層統一處理。
接入專案時守住三個邊界
第一,原始資料必須帶有單位。遇到未知單位時,不能預設為攝氏,否則錯誤資料仍可能落在合理範圍內。通訊協定中的未知列舉值應回傳明確錯誤。
第二,閾值比較應使用已統一單位、尚未四捨五入的數值。兩筆顯示為相同數字的讀值,不一定會得到相同的警報判定結果。加入一筆「數值不同但顯示相同」的測試樣本,可以防止介面重構影響業務判斷。
第三,雲端 Mac 上的 Foundation 測試通過後,仍須在目標 iOS 執行環境中檢查格式與版面配置。小工具、圖表座標軸標籤與語音朗讀應分別驗收,不能只檢查主畫面。
最後,保留測試結果、工具鏈版本與格式化輸出。發生回歸問題時,先判斷是換算、輸入契約還是顯示有所變動,再修改對應層;不要只用一項介面字串斷言,就試圖涵蓋所有正確性驗證。
常見問題
為什麼溫差不能直接使用絕對溫度的換算方法?
攝氏與華氏之間的絕對溫度換算包含偏移量,溫差只能套用比例。可分別換算溫差值與來源單位的零點,再相減;十攝氏度的溫差對應十八華氏度的溫差。
在地化溫度字串適合直接做固定文字斷言嗎?
只有同時固定系統版本、語言地區、單位選項及精度時才適合。一般門禁應先驗證數值契約,顯示層再檢查指定單位、四捨五入及版面。
將下一次建置交給獨享雲端 Mac
MacFTP 提供搭載原生 macOS 的 Apple Silicon 獨享實體主機,可選擇日租、週租、月租或季租。先確認規格,再將現有工具鏈連接至節點;實際可用狀態請以控制台即時資訊為準。