Flutter接入Apple的WeatherKit
title: Flutter接入Apple的WeatherKit date: 2023-01-01 10:33:56 tags:
為什麼選擇WeatherKit
最近個人在開發一個Flutter項目,是釣魚相關的,提供水文查詢,釣點記錄,釣魚記錄等功能。其中釣魚記錄需要關聯到天氣相關的內容,主要是要用到歷史天氣數據。網上提供天氣API的服務商很多,我查了一些資料,最終發現visualcrossing這家的接口設計和數據比較符合我的要求,但是這些天氣API的免費額度都很低,如果只是個人玩玩是夠用,發佈出去商用肯定是需要充值的。後面查資料發現Apple在WWDC22
新推出了WeatherKit
,除了系統原生庫WeatherKit
外(僅支持Apple平台,iOS 16.0+,iPadOS 16.0+,macOS 13.0+,Mac Catalyst 16.0+,tvOS 16.0+,watchOS 9.0+),還提供了Weather Kit REST API,而這個是全平台支持的,同時Apple開發者會員資格提供50萬次調用/月
的額度,並且支持歷史天氣數據的查詢,剛好滿足我Flutter項目的要求。
WeatherKit也是收費的,會員資格提供
50萬次調用/月
的額度,超過需要額外付費,詳細內容見:開始使用 WeatherKit
如何接入WeatherKit
1. 添加WeatherKit權限
在開發者中心網站Certificates, Identifiers & Profiles
頁面中選擇Identifiers
欄目,然後選擇需要開啟WeatherKit權限的項目:
進入後,選擇App Service
欄目並勾選WeatherKit
選項:
注意這裏AppID Prefix
就是你賬户的Team ID
。
2. 註冊WeatherKit
密鑰
在Certificates, Identifiers, and Profiles
頁面選擇Keys
選項,然後選擇Keys右邊的加號,進入新建頁面,勾選WeatherKit 選項,註冊一個Key。
這裏特別需要注意的是,系統會提示下載一個密鑰文件,是簽名WeatherKit
服務的私鑰,只能下載一次,一定要妥善保管到安全的位置且不能泄露。註冊後我們需要拿到Key ID
,在後續簽名時需要用到:
完成以上操作後會在Services選項裏面看到WeatherKit
,點擊View
後滑到頁面底部,可以看到WeatherKit service
的identifier
:
完成上述操作後,我們得到以下內容: 1. Team ID 2. 密鑰 3. WeatherKit Service identifier 4. WeatherKit Key ID
Weather Kit REST API簽名
Weather Kit REST API
使用JWT(JSON Web Token)
和ES256
算法作為請求授權認證,通常這一步應該在後端服務器上完成。具體內容可以在官方文檔Request authentication for WeatherKit REST API中看到。由於現在開發的項目還沒有做後台開發,為了方便調用,我先用Dart語言編寫簽名代碼。
首先我們需要引入dart_jsonwebtoken框架,它提供了JWT的封裝。
```yaml
在 pubspec.yaml中引入dart_jsonwebtoken框架
dependencies: flutter: sdk: flutter
dart_jsonwebtoken: ^2.6.2
簽名部分的代碼封裝如下:
Dart
import 'package:dart_jsonwebtoken/dart_jsonwebtoken.dart';
class AppleWeatherKitSign { final String secretKey; final String iss; final String sub; final String kid;
AppleWeatherKitSign( {required this.secretKey, required this.iss, required this.sub, required this.kid});
String sign(DateTime expirationDate) { final id = "$iss.$sub"; final jwt = JWT({ "iss": iss, "iat": DateTime.now().millisecondsSinceEpoch ~/ 1000, "exp": expirationDate.millisecondsSinceEpoch ~/ 1000, "sub": sub }, header: { "alg": "ES256", "kid": kid, "id": id });
final key = ECPrivateKey(secretKey);
final token = jwt.sign(key, algorithm: JWTAlgorithm.ES256);
return token;
}
factory AppleWeatherKitSign.fromDefaultConstants() => AppleWeatherKitSign( secretKey: "填入:註冊Key時下載的密鑰", iss: "填入:Team ID", sub: "填入:WeatherKit Service identifier", kid: "填入:WeatherKit Key ID"); } ```
生成簽名:
Dart
final token = AppleWeatherKitSign.fromDefaultConstants()
.sign(DateTime.now().add(const Duration(days: 1)));
print('Signed token: $token');
// Signed token: eyJhbGciOiJFUzI1NiIsImtpZCI6IjY1UDk5NExBTlAiLCJpZCI6IlBHTFI4U1hQUVAuY29tLmRldmx4eC5maXNoaW5nV2VhdGhlclNlcnZpY2UiLCJ0eXAiOiJKV1QifQ.eyJpc3MiOiJQR0xSOFNYUFFQIiwiaWF0IjoxNjcyNTgzMTY3LCJleHAiOjE2NzEyOTY5ODYsInN1YiI6ImNvbS5kZXZseHguZmlzaGluZ1dlYXRoZXJTZXJ2aWNlIn0.Ca7BY43zbtmSZTPD6zDBKIjiS8w45txCYBK3zOmgSwgUI-u8Z2-UlXH-7Xo1aQ0MUipvj8uYjekSdxB2FOI6eB
Weather Kit REST API
調用
總共提供了兩個接口,第一個是查詢某地的天氣數據集的可用狀態,具體使用方式如下:
```Dart import 'package:tuple/tuple.dart'; import 'package:dart_jsonwebtoken/dart_jsonwebtoken.dart';
test("request weather kit availability", () async { try { final token = AppleWeatherKitSign.fromDefaultConstants() .sign(DateTime.now().add(const Duration(days: 1))); const coordinate = Tuple2(28.212151, 112.955606); var dio = Dio(); final response1 = await dio.get( 'https://weatherkit.apple.com/api/v1/availability/${coordinate.item1}/${coordinate.item2}', options: Options(headers: {"Authorization": "Bearer $token"})); // 這裏的country參數需要符合規範 ISO Alpha-2 country code: https://www.iban.com/country-codes final response2 = await dio.get( 'https://weatherkit.apple.com/api/v1/availability/${coordinate.item1}/${coordinate.item2}?country=CN', options: Options(headers: {"Authorization": "Bearer $token"})); print(response1.data); // [currentWeather, forecastDaily, forecastHourly] print(response2.data); // [currentWeather, forecastDaily, forecastHourly] expect(response1.data, response2.data); } catch (e) { print(e); expect(true, false); } }); ```
極端天氣警報和空氣質量等數據集不是所有國家可用,因此需要額外提供國家代碼進行查詢。目前測試中國(CN)返回currentWeather/forecastDaily/forecastHourly
, 美國(US)會多返回 weatherAlerts
。
第二個是查詢某地的天氣數據,具體調用樣例如下:
```Dart import 'package:intl/intl.dart'; import 'package:intl/date_symbol_data_local.dart'; import 'package:tuple/tuple.dart'; import 'package:dart_jsonwebtoken/dart_jsonwebtoken.dart';
test("request weather data", () async { try { initializeDateFormatting(); final token = AppleWeatherKitSign.fromDefaultConstants() .sign(DateTime.now().add(const Duration(days: 1))); const language = "zh-CN"; const coordinate = Tuple2(28.212151, 112.955606);
final dateFormat = DateFormat("yyyy-MM-dd'T'HH:mm:ss", "zh-CN");
Map
var dio = Dio(); final response1 = await dio.get( 'https://weatherkit.apple.com/api/v1/weather/$language/${coordinate.item1}/${coordinate.item2}', queryParameters: parameters, options: Options(headers: {"Authorization": "Bearer $token"})); print(response1.data); expect(response1.data["forecastHourly"] != null, true); } catch (e) { print(e); expect(true, false); } }); ```
上述接口調用後,返回的數據樣例如下:
JSON
{
forecastHourly:
{
name: HourlyForecast,
metadata:
{
attributionURL: https://weatherkit.apple.com/legal-attribution.html,
expireTime: 2023-01-01T16:02:18Z,
latitude: 28.212,
longitude: 112.956,
readTime: 2023-01-01T15:02:18Z,
reportedTime: 2023-01-01T14:00:00Z,
units: m,
version: 1,
},
hours:
[
{
forecastStart: 2023-01-01T22:00:00Z,
cloudCover: 0.98,
conditionCode: Cloudy,
daylight: false,
humidity: 0.92,
precipitationAmount: 0.0,
precipitationIntensity: 0.0,
precipitationChance: 0.0,
precipitationType: clear,
pressure: 1031.96,
pressureTrend: steady,
snowfallIntensity: 0.0,
snowfallAmount: 0.0,
temperature: 4.93,
temperatureApparent: 1.97,
temperatureDewPoint: 3.71,
uvIndex: 0,
visibility: 14391.75,
windDirection: 339,
windGust: 25.43,
windSpeed: 13.11,
},
...
],
},
}
上述內容只提供了訪問歷史數據的樣例,當然WeatherKit
也提供了獲取未來天氣數據的功能,具體可以查看API文檔,根據文檔內容依葫蘆畫瓢填入相應參數即可,這裏不做特別説明。
歸因要求
無論是App或者Web網站,只要用到了WeatherKit
,都必須清晰地展示 Apple“天氣”商標 (天氣) 以及指向其他數據來源的法律鏈接。具體查看:Apple“天氣”App 和第三方歸因
問題
對於Weather Kit
現在還沒有過多使用,畢竟項目還沒有正式上線,但是測試時有發現它存在數據不準確的問題。我在獲取當前定位位置的歷史天氣數據時,發現與實際有4攝氏度左右的差別,REST API
現在還是Beta
狀態,期望後續能修復這個問題。另外就是Weather Kit
只能提供2021-08-01
以後的數據,更早的歷史記錄是無法獲取的。
如果您知道更多的Weather Kit
問題,歡迎在評論區告知。
參考資料:
- Apple WeatherKit REST API 上手
- 開始使用 WeatherKit
- Request authentication for WeatherKit REST API
- weatherkitrestapi
- Hourly history with WeatherKit REST API
- Apple“天氣”App 和第三方歸因
by 星的天空