Dart 2.18 釋出,Objective-C 和 Swift interop
theme: smartblue
Dart 2.18 版本開始提供與 Objective-C 和 Swift 互動的能力預覽,以及在這基礎上構建的新 iOS / macOS 包支援。
Dart 2.18 還包含對通用函式的型別推斷改進、非同步程式碼的效能改進、新的 pub.dev 功能支援以及對工具和核心庫的整理。
最後,還有最新的 null safety 遷移狀態解析,以及通往完全 null safety 的重要路線圖更新。
Dart 支援與 Objective-C 和 Swift 互動的能力
在 2020 年的時候我們預覽了用於呼叫原生 C API 的 Dart 外函式介面(FFI),並於 2021 年 3 月在 Dart 2.12 中釋出了它。
自該版本釋出以來,大量軟體包利用此功能與現有的原生C API整合,例如: file_picker
、printing
、win32
、objectbox
、realm
、isar
、tflite_flutter
和 dbus
等。
Dart 團隊希望支援所執行平臺上所有主要語言的互動能力,而 Dart 2.18達到了實現這一目標的下一個里程碑。
在 2.18, Dart 程式碼可以呼叫 Objective-C 和 Swift 程式碼,這通常用於呼叫 macOS 和 iOS 平臺上的API,Dart在任何應用中都支援這種互操作機制,從CLI 應用到後端程式碼和 Flutter UI。
這種新機制其實是利用了 Objective-C 和 Swift 程式碼可以基於 API 繫結 C 程式碼公開,Dart API 包裝了生成工具 ffigen
,可以從 API 標頭建立這些繫結。
使用Objective-C的時區示例
macOS 有一個 API 可用於查詢 NSTimeZone
上公開的時區資訊,開發者可以查詢該 API 以瞭解使用者為其裝置配置的時區和 UTC 時區偏移量。
以下示例中 Objective-C 使用此時區 API 獲取系統時區和GMT偏移量:
```
import
int main(int argc, const char * argv[]) { @autoreleasepool { NSTimeZone *timezone = [NSTimeZone systemTimeZone]; // Get current time zone. NSLog(@"Timezone name: %@", timezone.name); NSLog(@"Timezone offset GMT: %ld hours", timezone.secondsFromGMT/60/60); } return 0; } ```
這裡匯入了 Foundation.h
,其中包含 Apple Foundation 庫的 API headers。
接下來,在 main
方法中,它從 NSTimeZone
類呼叫了 systemTimeZone
方法,此方法返回裝置上選定時區的 NSTimeZone
例項。
最後,應用向控制檯輸出兩行結果,其中包含時區名稱和UTC偏移量(以小時為單位)。
如果執行此程式,它應該會返回類似於以下內容的東西,具體取決於開發者的位置:
Timezone name: Europe/Copenhagen
Timezone offset GMT: 2 hours
使用 Dart 的時區示例
讓我們使用新的 Dart 與 Objective-C 一起重新實現上面的結果。
首先建立一個新的 Dart CLI :
$ dart create timezones
然後編輯 pubspec
檔案以包含 ffigen
配置,配置指向標頭檔案,並列出了哪些 Objective-C 介面應該生成包裝器:
ffigen:
name: TimeZoneLibrary
language: objc
output: "foundation_bindings.dart"
exclude-all-by-default: true
objc-interfaces:
include:
- "NSTimeZone"
headers:
entry-points:
- "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/Library/Frameworks/Foundation.framework/
Headers/NSTimeZone.h"
這就為 NSTimeZone.h
中的 headers 選擇 Objective-C 繫結,並僅包括NSTimeZone
介面中的API,要生成 wrappers, 可以允行 ffigen
:
$ dart run ffigen
該命令會建立一個新檔案 foundation_bindings.dart
,其中包含一堆生成的API繫結,使用該繫結檔案,就可以編寫 Dart main
方法,此方法映象Objective-C 程式碼:
void main(List<String> args) async {
const dylibPath =
'/System/Library/Frameworks/Foundation.framework/Versions/Current/Foundation';
final lib = TimeZoneLibrary(DynamicLibrary.open(dylibPath));
final timeZone = NSTimeZone.getLocalTimeZone(lib);
if (timeZone != null) {
print('Timezone name: ${timeZone.name}');
print('Offset from GMT: ${timeZone.secondsFromGMT / 60 / 60} hours');
}
}
就這樣,從 Dart 2.18 開始,這種新的支援在實驗狀態下可用,該能力增強了Dart 的互動支援,以直接呼叫 macOS 和 iOS API 支援。
並且這也反向補充了 Flutter 的外掛,提供了允許開發者直接從 Dart 程式碼呼叫macOS 和 iOS API 的能力。
要了解有關這種互操作性的更多資訊,請參閱 Objective-C 和 Swift 互動指南。
特定於平臺的http庫
Dart 裡包括一個通用的多平臺http
庫,該庫允許開著編寫程式碼而無需考慮平臺細節,但是有時候開發者可能希望編寫特定於特定 native 平臺的 網路 API的程式碼,例如:蘋果的網路 庫NSURLSession
允許指定僅限 WiFi 或 VPN的網路。
為了支援這些用例,我們為 macOS 和 iOS 平臺建立了一個新的網路包 cupertino_http
,該能力建立在上一節中提到的 Objective-C 直接互動的基礎上。
Cupertino http library 示例
以下示例將 Flutter 的 http 客戶端設定為在其他平臺上使用 cupertino_http
庫,以及 dart:io
下的 http 庫:
late Client client;
if (Platform.isIOS || Platform.isMacOS) {
final config = URLSessionConfiguration.ephemeralSessionConfiguration()
..allowsCellularAccess = false
..allowsExpensiveNetworkAccess = false;
client = CupertinoClient.fromSessionConfiguration(config);
} else {
client = Client(); // Uses an HTTP client based on dart:io
}
初始配置後,應用會對特定客戶端進行後續網路呼叫,例如 http get()
請求現在類似於以下內容:
final response = await get(
Uri.https(
'www.googleapis.com',
'/books/v1/volumes',
{'q': 'HTTP', 'maxResults': '40', 'printType': 'books'},
),
);
當開發者無法使用通用客戶端介面時,就可以直接使用 cupertino_http
庫呼叫蘋果的網路API:
``` final session = URLSession.sessionWithConfiguration( URLSessionConfiguration.backgroundSession('com.example.bgdownload'), onFinishedDownloading: (s, t, fileUri) { actualContent = File.fromUri(fileUri).readAsStringSync(); });
final task = session.downloadTaskWithRequest( URLRequest.fromUrl(Uri.https(...)) ..resume(); ```
多平臺應用程式中特定於平臺的網路
在設計該功能時,目標仍然是使應用盡支援更多的平臺,為了實現這個目標,我們為基本的 http 操作保留了通用的多平臺 http
API 集,並允許為每個平臺配置要使用的網路庫。
package:http
將需要編寫的特定於平臺的程式碼量降至最低,此 API 可以按平臺配置,但以獨立於平臺的方式使用。
Dart 2.18 提供了對兩個對於 package:http
特定於平臺的 http 庫的實驗性支援:
cupertino_http
基於NSURLSession
的 macOS/iOS 支援。cronet_http
基於 Cronet,Android 上流行的網路庫支援。
將一個通用客戶端 API 與多個 HTTP 實現相結合,以獲得特定於平臺的行為,同時仍然從所有平臺的一組共享源中維護應用。
改進的型別推斷
Dart 使用了許多通用函式,例如 fold
方法,它將元素集合減少為單個值,如計算整數列表的總和:
List<int> numbers = [1, 2, 3];
final sum = numbers.fold(0, (x, y) => x + y);
print(‘The sum of $numbers is $sum’);
對於 Dart 2.17 或更早版本,這個方法返回型別錯誤:
line 2 • The operator ‘+’ can’t be unconditionally invoked because the receiver can be ‘null’.
Dart 2.18 改進了型別推斷,前面的示例通過了靜態分析,可以推斷出 x 和 y 都是不可為空的整數,此更改允許開發者編寫更簡潔的 Dart 程式碼,同時保留強推斷型別的完整可靠性屬性。
非同步效能改進
此版本的 Dart 改進了 Dart VM 應用 async
方法和 async*
/sync*
生成器功能的方式。
這減少了程式碼大小,在兩個大型內部 Google 應用程式中,我們看到 AOT 快照大小減少了約 10%,還可以看到微基準測試的效能有所提高。
這些變化包括額外的小行為變化;要了解更多資訊,請參閱更改日誌。
pub.dev 改進
結合 2.18 版本,我們對 pub.dev
包 儲存庫進行了兩項更改。
個人業餘時間通過 pub.dev
維護和釋出的可能會產生一些時間上的投入,為了促進贊助,我們現在在 中支援一個新 funding
標籤,pubspec
包釋出者可以使用該標籤列出指向一種或多種贊助包的方式的連結。然後這些連結顯示pub.dev
在側邊欄中:
要了解更多資訊,請參閱
pubspec
文件。
此外,我們希望鼓勵豐富的開源軟體包生態系統,為了突出這一點,自動包評分對使用 OSI 批准的許可證 的 pub.dev
包額外獎勵 10 分。
一些重大變化
Dart 非常注重簡單和易學的能力,在新增新功能時,我們一直在努力保持謹慎的平衡。
保持簡單的一種方法是刪除歷史功能和 API,Dart 2.18 清理了此類別中的專案,包括一些較小的重大更改:
- 我們早在 2020 年 10 月就添加了統一的
dart
CLI 開發人員工具,在 2.18 中我們完成了過渡。此版本刪除了最後兩個已棄用的工具dart2js
(usedart compile js
) 和dartanalyzer
(usedart analyze
)。 - 隨著語言版本控制的引入,
pub
生成了一個新的解析檔案:.dart_tool/package_config.json
。 之前的.packages
檔案使用了一種不能包含版本的格式,而現在我們停止使用.packages
檔案,如果你有任何.packages
檔案,現在可以刪除它們了。 - 不能使用未擴充套件的類的混合
Object
(重大更改#48167)。 dart:io
的RedirectException
的uri
屬性已更改為可為空(重大更改#49045)。dart:io
遵循 SCREAMING_SNAKE 約定的網路 API 中的常量已被刪除(重大更改# 34218;以前已棄用),請改用相應的 lowerCamelCase 常量。- Dart VM 在退出時不再恢復初始終端設定,更改
Stdin
設定lineMode
的echoMode
現在負責在程式退出時恢復設定(重大更改#45630)。
空安全更新
自 2020 年 11 月釋出測試版和 2021 年 3 月釋出 Dart 2.12 以來,我們很高興看到 null 安全性的廣泛使用。
首先,大多數流行包的開發人員都在 pub.dev
遷移到了零安全性,分析表明,100% 的前 250 個和 98% 的前 1000 個最常用的包支援零安全。
其次,大多數應用開發人員在具有完全空安全遷移的程式碼庫中工作,這是至關重要的條件,在遷移所有程式碼和所有依賴項(包括傳遞性)之前, Dart 健全的 null safety 不會發揮作用。
下圖顯示了 flutter run
在引入零安全和沒有引起之間的對比,隨著應用開始遷移到零安全,開發人員進行了部分遷移,但仍存在部分內容未遷移到 null safety。
隨著時間的推移可以看到, null safety 使用在健康地增長。到上月底,與不使用 null safety 相比, null safety 多出四倍,所以我們希望,在接下來的幾個季度中,我們將看到 100% 的可靠零安全方法。
重要的零安全路線圖更新
同時支援空安全和非空安全會增加開銷和複雜性。
首先,Dart 開發者需要學習和理解這兩種模式,每當閱讀一段 Dart 程式碼時,檢查語言版本以檢視型別是否預設為非空(Dart 2.12 及更高版本)或預設可空(Dart 2.11 及更早版本)。
其次,在我們的編譯器和執行時同時支援這兩種模式會減慢 Dart SDK 的發展以支援新功能。
基於非空安全的開銷和上一節中提到的非常積極的採用數字,我們的目標是過渡到僅支援可靠的空值安全,並停止非空值安全和不健全的空值安全模式,我們暫時將其定於 2023 年年中釋出。
這將意味著停止對 Dart 2.11 及更早版本的支援,具有低於 2.12 的 SDK 約束的 Pubspec 檔案將不再在 Dart 3 及更高版本中解析。
在包含語言標記的原始碼中,如果設定為小於 2.12(例如
// @dart=2.9
)也會失敗。
如果已遷移到可靠的 null 安全性,那麼你的程式碼將在 Dart 3 中以完全的 null 安全性工作,如果還沒有,請立即遷移!
要了解有關這些更改的更多資訊,請參閱此 GitHub 問題。
- 面向 ChatGPT 開發 ,我是如何被 AI 從 “逼瘋” 到 “覺悟” ,未來又如何落地
- 維護高 Star Github 專案,會遇到什麼有趣的問題 2023 版
- Flutter - Dart 3α 新特性 Record 和 Patterns 的提前預覽講解
- 2023 年第一彈, Flutter 3.7 釋出啦,快來看看有什麼新特性
- 2023 Flutter Forward 大會回顧,快來看看 Flutter 的未來會有什麼
- Flutter 的下一步, Dart 3 重大變更即將在 2023 到來
- Flutter 小技巧之快速理解手勢邏輯
- 一文快速帶你瞭解 KMM 、 Compose 和 Flutter 的現狀
- Flutter 工程化框架選擇 — 混合開發的摸爬滾打
- 如何利用 Flutter 實現炫酷的 3D 卡片和帥氣的 360° 展示效果
- Flutter 工程化框架選擇——搞定 Flutter 動畫
- Flutter 實現 “真” 3D 動畫效果,用純程式碼實現立體 Dash 和 3D 掘金 Logo
- Android Studio Dolphin | 2021.3.1 釋出,快來看看有什麼更新吧~
- 掘金 XDC 2022 - 普通技術人的彎道超車指南
- Flutter 工程化框架選擇 — 搞定 UI 生產力
- Dart 2.18 釋出,Objective-C 和 Swift interop
- Flutter 工程化框架選擇 — 搞定資料儲存選型
- 2022 年 App 上架稽核問題集錦,全面踩坑上線不迷路
- React Native 0.70 版本釋出,Hermes 終於成為預設 Engine
- Flutter 3.3 之 SelectionArea 好不好用?用 “Bug” 帶你全面瞭解它