0%

【文章內容使用 Gemini 1.5 Pro 自動產生】

大規模提供作業系統相容性

使用 Flutter 編寫的應用程式可以在 Android、iOS、網頁和桌面作業系統上運行。由於我們在支援多個平台方面的投入,Flutter 應用程式可以在任何具有應用程式編寫時所用作業系統版本(或更高版本)的設備上運行,而無需進行任何修改,這擴展了 Flutter 的跨平台吸引力。

在本文中,Flutter 的核心開發團隊(我們)想要分享我們為何投資於支援多個平台,我們如何才能繼續支援多個平台,以及在需要新增對新平台的支援或停止支援舊平台時,我們如何做出決定。

展望未來:Android 和 iOS 的版本

Flutter 致力於為 Android 和 iOS 的最新功能提供完全支援。我們始終監控著 Apple 和 Google 發布的有關其平台 API、工具使用和授權條款變更的定期指南。對新作業系統版本的支援是我們定期產品規劃的一部分,我們會盡量將我們的版本與 Google 和 Apple 的最新指南保持一致,以確保我們的穩定版本始終與兩者的最新指南相容。

我們承諾對您提供多個平台和 Android 和 iOS 的軟體版本(例如 Android 11 或 iOS 14)以及其小版本更新的支援。對於目標平台的主要修訂,我們會在了解到所需內容後立即開始開發必要的功能和更改。這通常發生在春季後期,Google 公佈了其即將發佈的 Android 版本的計劃,而 Apple 在其全球開發者大會 (WWDC) 上發布了最新公告。在發布公告之後,我們會評估 Flutter 程式碼庫中必要的更改,例如由於可能已棄用的 API 導致的引擎更改,工具更改如何影響 flutter 工具和開發人員體驗,以及兩者的設計語言更改。我們還會評估工作範圍,預期這兩個平台都将在秋季初提供給客戶。

我們會針對 Android 和 iOS 的測試版進行這項工作,並會透過我們的 master、dev 和 beta 頻道定期提供這些更改。master 頻道的版本是持續性的。我們計劃每週發佈兩到三個 dev 頻道版本(針對 Google 內部應用程式和測試套件進行測試的 master 頻道版本)。beta 頻道通常在每月的第一週發佈。我們會使用 GitHub 問題追蹤我們的進度。若要查看問題清單,您可以根據 platform-iosplatform-android 搜尋。

將我們的穩定版本發佈時間表與 iOS 保持一致尤其具有挑戰性,因為從新聞發佈到我們獲得有關作業系統更改的指南之間的時間通常非常短。我們會透過在秋季規劃我們的其中一個版本,並準備好針對 iOS 或甚至 Android 上出現的任何阻礙性錯誤對我們目前的穩定版本進行熱修復來減輕這一挑戰。(這發生在 Flutter 1.20 和 iOS 14 上,我們在 iOS 14 發佈的同一天發布了 熱修復)。

無論支援新平台版本需要做多少工作,我們的目標都是支援目標平台的新版本,而不棄用舊版本。

向後相容性和 Flutter 的價值主張

Flutter 是 Google 的可移植 UI 工具包,用於從單一程式碼庫建立針對行動、網頁和桌面的精美的原生編譯應用程式。為此,我們希望支援我們可以支援的最廣泛的設備集合,而不仅仅是特定類型的平台。我們還希望支援最多數量的平台版本。

以 Android 為例:根據 statcounter,截至 2020 年 6 月,市面上的 Android 版本如下所示:

從數字來看,Android KitKat (Android 4.x) 的市場佔有率不到 2%。絕大多數 Android 設備 (54%) 正在運行 Android Pie (Android 9.0) 或 Android 10。支援每個版本的 Android 都需要投資。您知道我們為何要投資於像 KitKat 這樣市場佔有率低於 2% 的平台嗎?

答案很簡單:淨數字。由於超過 10 億台 Android 設備沒有運行 Android Pi 或更高版本,我們希望確保 Flutter 尽可能广泛地得到使用。我們還希望支援多元的全球使用者群。而且,由於 Android 和 iOS 的舊版本經常被開發中國家的使用者使用,我們希望在尽可能長的時間內避免停止支援舊版本。

透過 Flutter 的核心引擎、框架和 Plugin 中的工作,Flutter 為作業系統平台和作業系統版本的各種問題提供了抽象。例如,Material 應用程式可以在舊版本的 iOS 或 Android 上運行,而无需進行任何修改。事實上,開發人員已發佈在與第一個版本同時推出的硬體上運行的商業 Material 應用程式,而使用這些設備附帶的 Widget 集,則無法在這些設備上執行 。Plugin 也有類似的案例。Plugin 會抽象化平台和版本差異,讓您可以專注於重要的部分:您的應用程式。

Flutter 如何支援如此多的目標

Flutter 為多目標支援帶來了兩大優勢。首先,Flutter 對底層作業系統的依賴性很小。Flutter 的引擎直接與圖形 API 互動,Plugin 為其他功能提供了大部分的設備特定實作。

我們的持續整合 (CI) 系統會為 macOS、Linux 和 Windows 建立 Flutter 及其工具,並部署到一系列 Android 和 iOS 設備(實體設備和模擬設備)。部分 CI 是使用 Google 辦公室中的實驗室進行的;其他 CI 測試是在 Firebase 測試實驗室 上進行的,這是 Google 為 Google 和第三方開發人員運營的雲端託管測試解決方案。

以下是一張我們測試實驗室中其中一個機架的照片:

我們在設備實驗室中運行相對低端的行動設備,因為我們會在那裡進行 效能測試。在測試時,我們希望從代表大多數使用者擁有的設備中獲取效能指標。這樣做可以讓我們獲得最糟糕的情況、真實世界的效能。這種理念也適用於核心工程團隊使用的設備。核心工程團隊的大部分成員並沒有在最新的 Google Pixel 或 Apple iPhone 上進行 Flutter 除錯。

由於我們想要測試許多類型的行動設備,因此我們會保留一個設備庫,核心工程團隊的成員可以在其中檢出特定設備以進行測試和除錯。(在新冠肺炎疫情期間,我們充分利用了美國郵政服務!)

由於實驗室只能測試少數幾種設備類型和版本,我們如何確保我們正在提供我們聲稱提供的支援?請繼續閱讀以瞭解。

對平台版本支援做出一般性斷言

首先也是最重要的是,我們會針對每個程式碼提交在各種平台上進行測試。

在我們目前的實驗室中,我們正在以下 Android 平台上進行測試:

  • Android API 24 (Android N)
  • Android API 28 (Android P)
  • Android API 29 (Android 10)

這涵蓋了已部署 Android 版本市場的 57%;稍後,我們會說明如何透過使用 Firebase 測試實驗室來涵蓋剩餘的設備。

我們的實驗室還會針對每個提交在運行以下作業系統的設備上測試 iOS:

  • iOS 9.3.6
  • iOS 12.4
  • iOS 12.4.1
  • iOS 13.1.3
  • iOS 13.2

最後,對於桌面和網頁,我們會針對每個提交在以下設備上進行測試:

  • Chrome 84
  • Firefox 72.0
  • Catalina 上的 Safari
  • Edge 1.2.0.0
  • Windows 10
  • macOS El Capitan
  • Debian 10

很明顯,Android 方面需要進行更多測試。我們依賴於 Firebase 測試實驗室 完成這項工作,測試以下額外的 Android 和 iOS 組態:

  • Android API 19 (Android K)
  • Android API 21 和 22 (Android L)
  • Android API 23 (Android M)
  • Android API 26 和 27 (Android O)
  • Android API 30 (Android 11)
  • iOS 11
  • iOS 13.0
  • iOS 13.1
  • iOS 13.3–13.7
  • iOS 14

總體而言,我們對 Android 和 iOS 平台的測試涵蓋了目前發佈版本中的 95% 以上。

對於其他作業系統版本,我們透過 您的 支援,也就是 Flutter 開發社群,提供向後相容性。這對特定的設備/作業系統組合尤其適用。你們中許多人使用我們無法使用到的設備:可能是因為你們的作業系統版本我們沒有,或是因為這些設備在美國無法取得。在某些情況下,我們可以減輕這種情況。例如,我們可以在一些 Google Pixel 硬體上刷入較舊的 Android 版本,購買較舊的翻新 iOS 設備,或在模擬器中運行 Android 版本。在許多情況下,你們中的一個人提交的報告會觸發我們對特定平台問題的調查。

這對許多 Android 設備的原始設備製造商 (OEM) 尤其適用。OEM 通常會對 Android 的某些方面進行自訂,例如鍵盤支援。過去,由於你們的幫助,我們能夠修復許多僅在某些市場的某些設備上發生的文字輸入問題。

我們感謝您的幫助,因為我們不可能自己找到所有問題,但我們會努力降低注入問題的風險。在進行程式碼更改時,我們會謹慎行事,並注意作業系統的最早版本支援哪些功能。在需要支援不連貫的組態時,我們會提供選項。這就是為什麼您可以使用 OpenGL 或 Metal 建立 iOS 應用程式,Flutter 會在運行時自動選擇其中之一。

由於這些原因,儘管我們會盡力支援作業系統版本上的測試,但我們支援的一些設備是基於「盡力而為」的基礎支援的。我們會嘗試撰寫支援我們聲稱支援的平台的程式碼,我們會依賴團隊對設備多樣性的隨機使用承諾,然後會尋求社群的回饋,以了解我們在實際使用中表現如何。

我們需要您幫助測試屬於「盡力而為」類別的設備。此類別中有數萬(甚至更多!)個 SKU。我們會盡可能地進行測試,有時候我們會從拍賣網站取得較舊的設備,以方便我們測試和修復在無法複製問題時通報的設備問題。如果您發現特定設備的問題,請在 GitHub 上提交問題。

最後,一些平台,例如 iOS 7,已被棄用。也許它們可以正常工作,但我們不會在已被棄用的平台上進行測試,也不提供任何保證。但是,如果我們可以在修補程式生效後測試時充分支援這些平台,我們會根據具體情況考慮針對已被棄用的平台的修補程式。

Flutter 會維護一個 網頁,列出我們會在其中進行測試的設備以及我們會尋求社群幫助的設備。

決定停止支援

儘管我們致力於在尽可能長的時間內支援每個平台版本,但支援舊平台(或平台組態,例如 Apple A7 上的 OpenGL)需要付出代價,並且會遵循我們上游合作夥伴(包括 Skia)的指南。Flutter 的核心工程團隊規模相對較小,我們會不斷做出權衡,以確保我們所做的事情對使用者來說最有利。在某些時候,工程方面、CI 實驗室中測試的頻寬以及隨機測試的頻寬,以及技術限制(例如作業系統供應商停止支援平台版本中的工具)的成本可能會讓繼續支援目標平台變得困難或不可能。

在我們決定停止支援之前,我們會考慮以下一些問題:

  • 這將會減少我們開發人員的多少終端使用者?
  • 這對 Flutter 開發人員有何影響?是否有主要合作夥伴的業務運營會受到重大影響?
  • 我們可能會節省多少工程成本?例如,透過支援多個作業系統版本,使用 Flutter 建立的應用程式數量是否會增加?(例如,在 iOS 版本中支援 OpenGL 和 Metal 就是這樣的案例。)
  • 這個決定是否符合我們廣泛採用和包容性的目標?
  • 停止支援是否是由外部原因引起的,例如平台供應商在其建置工具中停止支援平台版本?如果是,是否有可能減輕這種停止支援的影響?

在評估是否停止支援時,我們會先透過在 GitHub 中開啟一個議題來詢問您的意見,並發布一份相關的 Flutter 設計文件,該文件會提出停止支援的建議,說明我們為何考慮停止支援,以及可能可用的緩解措施。這是一個向社群徵求意見的請求 (RFC);公共設計文件會透過我們的討論清單 ([email protected]) 和我們的 Discord 推廣。

若要開始停止支援,我們會在發佈穩定版本時,作為穩定版本公告的一部分,啟動 RFC 流程。然後,我們會留出時間供公眾發表意見,然後在後續平台版本中減少對平台或功能的支援。RFC 是您提供回饋的機會。根據我們收到的回饋,我們將繼續停止支援,或對提議進行修改,將平台版本的支援時間延長。我們會認真对待這些前瞻性決定的回饋意见,因為我們認識到我們對您和您的產品使用者負有責任。

徵求意見:停止支援 iOS 8

我們正在考慮停止支援 iOS 8。所有 iOS 8 設備都可以升級到 iOS 9。根據 Google 和第三方分析,運行 iOS 8 的設備數量非常少(一些報告顯示「0.0%」),而 Apple 正在逐步停止支援其工具中的 iOS 8。

我們已發布了 有關停止支援 iOS 8 的徵求意見請求,我們請求您審閱並參與討論。我們的目的是使 Flutter 1.22(我們最新的穩定版本)成為最後一個支援 iOS 8 的穩定版本。RFC 包含理由以及如果您受到影響,該如何操作。

徵求意見:停止支援 Android Jelly Bean

我們正在考慮停止支援 Android Jelly Bean,因為我們不再在實驗室中測試運行該版本的 Android 設備,而為實驗室取得可靠的設備也越來越困難。目前運行 Android Jelly Bean 的設備約佔 Android 使用者群體的 0.46%。

我們已發布了 有關停止支援 Android Jelly Bean 的徵求意見請求,我們請求您審閱並參與討論。我們的目的是使 Flutter 1.22(我們最新的穩定版本)成為最後一個支援 Android Jelly Bean 的穩定版本。RFC 包含理由以及如果您受到影響,該如何操作。

您可以如何幫助

最簡單的幫助方式是參與!我們感謝您提交有關我們尚未遇到的各種設備的邊緣案例的問題。在許多情況下,這些問題出現在我們從未擁有的硬體上,即使我們將硬體測試的支出增加一倍或四倍。報告這些問題,並提供可重現的情況和有關軟體版本和硬體型號的詳細資訊,對我們追蹤問題的根源至關重要。

此外,我們希望您在需要減少對特定平台的支援時,參與決策過程。當我們考慮 Flutter 的未來時,您的見解非常寶貴。請在您看到 RFC 時參與我們公開的 RFC。您是您特定市場的專家,也是離您的客戶最近的人。

我們非常感謝您對我們使用者和讓我們成為您產品一部分的信任。我們仍然致力於幫助您將您的願景呈現在尽可能多的螢幕上。


大規模提供作業系統相容性 最初發佈在 Flutter 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

\n

【文章內容使用 Gemini 1.5 Pro 自動產生】

更新整合測試,支援行動端的 Firebase Test Lab;同時也支援網頁和桌面端的獨立支援!

Flutter 的目標是提供一個開放的框架,用於在多個平台上快速構建精美的原生應用程式。實現這個目標的很大一部分是能夠輕鬆地測試您的 Flutter 應用程式,以確保它們按您的意願工作,並在您所針對的平台上以您想要的方式呈現。一些測試可以透過使用 內建於 Dart 的單元測試框架 編寫的單元測試來處理。Dart 單元測試對於非 UI 測試非常有效;它在您的開發機器上執行,並且不依賴於 Flutter 應用程式的 GUI。

整合測試(也稱為端到端測試或 GUI 測試)比單元測試更進一步,因為整合測試嘗試透過按下按鈕、選擇項目和在鍵盤上輸入等方式模擬使用者與您的應用程式互動。這種測試是自動的,以避免讓人類做這種重複性的工作,因為坦白說,我們不擅長這種事情。我們最初避免人類互動的解決方案是一種編寫 Flutter 測試的特殊方式。這些測試從主機執行,就像 Dart 單元測試一樣,並驅動在真實或虛擬設備上運行的應用程式,就像一個人一樣。這些測試稱為 Flutter 驅動測試,因為您使用 flutter_driver 套件和 flutter drive 命令列來驅動應用程式的 GUI。

不幸的是,Flutter 驅動測試存在一些問題。一個問題是測試從開發機器執行並與設備上的應用程式通訊,這意味著測試不適合在像 Firebase Test Lab 這樣的設備池上執行。另一個問題是,為您的測試單獨使用一個程序會讓您很難檢查應用程式的狀態。您可以檢查輸出,但是您如何知道,例如,應用程式的內部狀態。最後,flutter_driver API 比它需要的要複雜,特別是在編寫程式碼以找到螢幕上適當的 Widget 來進行測試時。

因此,為了解決這些問題並繼續改進 Flutter 測試體驗,使其涵蓋越來越多目標,我們很高興宣布 integration_test Plugin 的 1.0 版,這是一種更簡單的方式來測試您的 Flutter 應用程式,並且也支援 Firebase Test Lab。

開始使用 integration_test

使用 integration_test Plugin 需要兩個步驟。首先,將 Plugin 添加到您的 pubspec.yaml 檔案中,作為開發相依,並使用 flutter pub get 將 plugin 拉到您的專案中:

1
2
3
4
5
6
7
# pubspec.yaml

dev_dependencies:
flutter_test:
sdk: flutter
integration_test: ^1.0.0

然後,在測試程式碼中使用 integration_test 套件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:my_app/main.dart' as app;

void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();

group('end-to-end', () {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
// Build our app and trigger a frame.
app.main();
await tester.pumpAndSettle();

// Verify that our counter starts at 0.
expect(find.text('0'), findsOneWidget);

// Tap the '+' icon and trigger a frame.
await tester.tap(find.byIcon(Icons.add));
await tester.pumpAndSettle();

// Verify the counter increments by 1.
expect(find.text('1'), findsOneWidget);
});
});
}

請注意 integration_test 套件的匯入和對 IntegrationTestWidgetsFlutterBinding.ensureInitialized() 的呼叫,這將確保套件正確初始化。還要注意標準 WidgetTester 測試程式碼。這是在 flutter create 期間由預設的 Counter 應用程式範本生成的相同測試程式碼。

雖然 integration_test 使您的測試能夠以獨立的方式捆绑到您的應用程式中(這是像 Firebase Test Lab 這樣的設備池的要求),但它需要一些我們將在下面介紹的構建技巧。但是,在測試開發過程中,能夠透過命令列互動式執行您的測試非常方便,为此,您需要一個新的入口點:

1
2
3
4
5
6
import 'package:integration_test/integration_test.dart';

void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
integrationDriver();
}

integrationDriver 的呼叫是 integrationDriver Plugin 的便捷包裝函數,使您能夠使用 flutter drive 命令執行新的測試:

1
2
3
$ flutter drive 
--driver=test_driver/integration_test.dart
--target=test/widget_integration_test.dart

此命令會將您的應用程式部署到模擬器,執行您的測試,並向您顯示結果。

Flutter integration_test in action

此特定測試在 iOS 上執行,但 integration_test Plugin 也適用於 Android,只需更改 --device-id 選項,就像適用於其他平台一樣。此外,您也可以針對網頁和桌面目標執行整合測試,但此功能仍處於預發佈階段。

在 Firebase Test Lab 上執行

當您知道測試可以使用虛擬或實體硬體在本地執行後,就可以將應用程式在 Firebase Test Lab 提供的大量設備上執行。

Firebase Test Lab console

Firebase Test Lab 是一個基於雲端的應用程式測試基礎設施。透過一項操作,您可以在各種設備和設備配置上測試您的 Android 或 iOS 應用程式,並在 Firebase 主控台中查看結果 - 包括日誌、影片和螢幕截圖。integration_test Plugin 的一大進步是能夠在 Firebase Test Lab 上執行針對 Android 和 iOS 的 Flutter 應用程式,讓您能夠在發佈應用程式之前,在數百台設備上同時測試,以查找平台、外觀因素或設備特定的問題。

若要在 Firebase Test Lab 上執行測試,您需要進行一些配置,並使用 Gradle 命令為 Android 和 iOS 構建測試工具,這取決於您選擇的平台。有關這些詳細資訊,請查看 flutter.dev 上最新的整合測試文件

從 Flutter 驅動測試遷移

對於使用現有的 flutter_driver 測試的使用者,遷移到新的 API 並不困難。除了前面提到的適當初始化之外,您還需要遷移到新的 WidgetTester API。

flutter_driver API (left) vs. WidgetTesting API (right)

flutter_driver API(左邊顯示)和 WidgetTester API(右邊顯示)在概念上非常相似,但您可以看到許多細節是不同的。例如,您不是在 flutter_driver 上呼叫 waitFor 方法,而是在 WidgetTester 上呼叫 pumpAndSettle 方法。前者等待特定 Widget 出現,後者等待應用程式 UI 渲染階段穩定。獲得特定 Widget 後,您可以使用兩個 API 類似地對其進行操作,但您使用的是不同的物件。WidgetTest API 更符合您在 Dart 單元測試中看到的內容。如您所見,expect 方法用於確保 Widget 的內容符合您的預期。

有關如何將測試从 flutter_driver 遷移到 integration_test 和 WidgetTester 的詳細資訊,請查看 flutter.dev 上的遷移文件

原生 UI 測試

如果您有一個現有的 Android 或 iOS 應用程式,您使用 Add-to-App 為其添加 Flutter,那麼您可能已經為這些原生應用程式有了現有的整合測試,您想要利用這些測試。在這種情況下,將 Flutter 螢幕的測試添加到這些現有的測試中。對於 Android,如果您使用 Espresso 框架編寫測試,可以使用 espresso Plugin,它為 Espresso 對 Flutter Android 應用程式的測試提供了綁定。我們很快就會有類似的 Plugin 來支援使用 Earl Grey 建立的原生 iOS 測試。

總結

本公告介紹了使用適用於 Flutter 的新 integration_test Plugin 的整合測試的新基礎。不僅 API 更簡單,而且更一致,使用 integration_test 編寫的測試可以使用 Firebase Test Lab 在數百台不同的設備上執行。Flutter 團隊計劃透過將 flutter create 範本來使用 integration_test 作為預設值,更新測試輸出以支援使用 JUnit 格式的現有測試工具,添加在測試期間為黃金測試比較截取螢幕截圖的功能等等,來進一步發展這個新基礎。有關針對 Flutter 應用程式和 Plugin 的整合測試新建議的完整詳細資訊,請查看 flutter.dev 上的測試文件


Flutter 測試更新 最初發佈在 Flutter 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

【文章內容使用 Gemini 1.5 Pro 自動翻譯產生】

為什麼要使用可空類型?

幾週前,我們宣布了 Dart 空安全測試版,這是一項主要的生產力功能,旨在幫助您避免空錯誤。說到空值,在 /r/dart_lang subreddit 中,一位使用者最近問道

但是為什麼我們仍然擁有/想要空值?為什麼不完全擺脫它呢?我目前也在玩 Rust,它根本沒有空值。所以似乎沒有它也能活下去。

我喜歡這個問題。為什麼完全擺脫空值?本文是我在該討論串中回答內容的擴展版本。

簡短的答案是,是的,完全有可能在沒有空值的情況下生存,像 Rust 這樣的語言就是這樣做的。但是程式設計師確實會使用空值,所以在我們可以將其移除之前,我們需要了解為什麼要使用它。當我們在確實擁有它的語言中使用它時,空值通常什麼?

事實證明,空值通常用於表示值的缺失,這非常有用。有些人沒有中間名。有些郵寄地址沒有公寓號碼。有些怪物在你殺死它們時不會掉落任何寶藏。

在這種情況下,我們想要一種表達方式:「這個變數可以具有 X 類型的值,或者它可能根本沒有值。」那麼問題是如何建模呢?

一種選擇是說一個變數可以包含預期類型的值,或者它可以包含魔術值 null。如果我們在值為 null 時嘗試使用它,就會出現執行時錯誤。這就是 Dart 在空安全之前所做的,SQL 所做的,Java 對非基本類型所做的,以及 C# 對類類型所做的。

但是執行時失敗很糟糕。這意味著我們的使用者會遇到這個錯誤。我們程式設計師寧願在他們遇到之前就發現這些錯誤。事實上,如果我們能夠在我們執行程式之前就發現錯誤,我們會很高興。那麼我們如何以類型系統理解的方式對值的缺失進行建模呢?換句話說,我們如何給「可能缺失」的值和「肯定存在」的值不同的靜態類型?

主要有兩種解決方案:

  1. 使用選項或 maybe 類型
  2. 使用可空類型

解決方案 1:選項類型

這就是 ML 和大多數源自 ML 的函數式語言(包括 Rust、Scala 和 Swift)所做的。當我們知道我們肯定會有一個值時,我們只使用底層類型。如果我們寫 int,則表示「這裡肯定有一個整數」。

為了表示一個可能缺失的值,我們將底層類型包裝在一個 選項類型 中。所以 Option<int> 表示一個值,它可能是一個整數,也可能什麼都不是。它就像一個可以包含零個或一個項目的集合類型。

從類型系統的角度來看,intOption<int> 之間沒有直接關係。將它們視為不同的類型意味著我們不能意外地將可能缺失的 Option<int> 傳遞給預期接收真實 int 的東西。我們也不能意外地嘗試使用 Option<int> 好像它是一個整數一樣,因為它不支援任何這些操作。我們不能對 Option<int> 執行算術運算,就像我們不能對 List<int> 執行算術運算一樣。

要從底層類型的現有值(例如 3)建立選項類型的值,您可以像 Some(3) 一樣構造選項。要在值缺失時建立選項類型,您可以寫類似 None() 的內容。

為了使用儲存在 Option<int> 中的可能缺失的整數,我們必須首先檢查並查看值是否存在。如果存在,我們可以從選項中提取整數並使用它,就像從集合中讀取值一樣。具有選項類型的語言通常也具有良好的 模式匹配 語法,這為我們提供了一種優雅的方式來檢查值是否存在,如果存在則使用它。

解決方案 2:可空類型

另一種選擇 (heh) 是 Kotlin、TypeScript 和現在的 Dart 所做的。可空類型聯集類型 的一種特殊情況。

(題外話:這裡的命名非常令人困惑。選項類型——ML 和它的朋友們上面所做的——是 代數資料類型 的一種特殊情況。代數資料類型的另一個名稱是「區分聯集」。但是,儘管名稱中有「聯集」,但「區分聯集」與「聯集類型」卻大不相同。正如 Phil Karlton 所說,電腦科學中只有兩個難題:快取失效和命名。)

與選項類型方法類似,我們使用底層類型來表示一個肯定存在的值。所以 int 仍然表示我們絕對有一個整數。如果我們想要一個可能缺失的整數,我們可以使用 int? 可空類型。這個小小的問號是 int | Null 之類的聯集類型的語法糖。

就像選項類型一樣,可空類型不支援與底層類型相同的操作。類型系統不允許我們嘗試對可空 int 執行算術運算,因為這是不安全的。同樣,我們不能將可空整數傳遞給需要實際整數的東西。

然而,類型系統比選項類型更靈活一些。類型系統理解聯集類型是其分支的超類型。換句話說,intint? 的子類型。這意味著我們可以將肯定存在的整數傳遞給預期接收可能存在的整數的東西,因為這樣做是安全的。這是一個向上轉換,就像我們可以將 String 傳遞給接收 Object 的函數一樣。Dart 只禁止我們反過來——從可空到不可空——因為那將是一個向下轉換,而這些可能會失敗。

當我們有一個可空類型的值,並且我們想要查看是否存在實際值或 null 時,我們會像在 C 或 Java 中自然地那樣以命令式方式檢查該值:

1
2
3
4
5
foo(int? i) {
if (i != null) {
print(i + 1);
}
}

然後,語言使用 流程分析 來確定程式的哪些部分受到這些檢查的保護。分析確定只有在變數不為 null 時才能到達程式碼,因此在這些區域內,類型系統會將變數的類型收緊為不可空。因此,在這裡,它將 i 視為在 if 語句內具有 int 類型。

語言應該採取哪種解決方案?

因此,當我們 Dart 團隊決定讓語言以更安全的方式處理 null 時,我們應該如何選擇解決方案 1 或 2?我們可以從觀察我們的使用者開始。他們想要如何編寫檢查缺失值的程式碼?在函數式語言中,模式匹配是主要的控制流程結構之一,那裡的使用者對它非常熟悉。使用選項類型和模式匹配在這種風格中是很自然的。

在源自 C 的命令式語言中,像我之前的範例這樣的程式碼是檢查 null 的慣用方法。使用流程分析和可空類型使熟悉的程式碼能夠正確安全地工作。事實上,在 Dart 中,我們發現大多數現有程式碼在新類型系統下已經是靜態空安全的,因為新的流程分析可以正確地分析已經編寫的程式碼。

(這在某種程度上並不令人驚訝。大多數程式碼在處理 null 方面已經是動態正確的。如果沒有,它會一直崩潰。大部分工作只是使類型系統足夠聰明,以便看到該程式碼已經正確,從而使用者的注意力集中在少數不正確的部分。)

因此,如果我們的目標是最大限度地提高熟悉度和使用者舒適度(這語言設計中的重要標準),我們應該遵循我們語言的控制流程結構為我們設定的路徑。

表示缺失和存在

有一種更深層次的方法來處理這個問題,基於選項類型和可空類型的表示方式之間的差異。這種表示方式的差異迫使我們做出一些關鍵的取捨,而這些取捨可能會使我們傾向於某個方向。

在第一種方法中,選項類型的值具有與底層值不同的執行時表示。假設我們在 Dart 中選擇了選項類型,您建立了一個選項類型,然後將其向上轉換為 Object

1
2
3
var optionalInt = Some(3);
Object obj = optionalInt;
print(obj is int); // false

請注意最後一行。Option<int> 值,即使存在,也不像底層類型的值。Some(3)3 是不同的、可區分的

【文章內容使用 Gemini 1.5 Pro 自動產生】

開始將套件遷移到更健全、更安全的狀態

此文章同時發布於 Dart 部落格 上。

今天,我們宣佈 Dart 和 Flutter 的健全空安全功能已進入 Beta 階段。空安全是我們最新的主要生產力功能,旨在 幫助您避免空錯誤,這是一種通常難以發現的錯誤。如果您想快速了解為什麼我們對空安全感到兴奋,請查看這部新影片:

隨著空安全功能升級到 Beta 階段, 是時候開始對數千個在 pub.dev 上提供的套件進行社群遷移了。我們已經遷移了 Dart 核心函式庫、Flutter 框架以及 超過 40 個 Dart 和 Flutter 套件。我們希望看到社群透過遷移他們的套件來擁抱空安全性。

Timeline of Dart sound null safety support, from Technical Preview 1 to Stable in early 2021

隨著 Beta 版本的发布,我們也開始了在發佈穩定版空安全功能之前的最後衝刺。我們希望您能使用此功能,並告訴我們哪些部分可以改進,哪些 UI 訊息可以變得更容易理解,或者哪些文件可以變得更清晰。我們 **非常期待 您的回饋**。

選擇加入空安全功能

在我們討論空安全功能的遷移之前,重要的是要重申(如我们在空安全原则 中所述),您可以控制何时开始采用空安全功能。應用程式和套件只有在它们的最小 Dart SDK 约束 至少为 Dart 2.12 预览版本时才会使用空安全功能:

1
2
environment:
sdk: ">=2.12.0-0 <3.0.0"

要体验这一点,请尝试创建一个小的空安全版 hello 应用程序(例如,使用 dart create),其中包含如下所示的代码。然后,您可以尝试在更改SDK约束之前和之后运行该应用程序,以及运行 dart pub get,并体验程序行为的變化。(请确保使用在 dart –version 中报告 2.12 的 SDK。)

1
2
3
4
5
6
7
8
9
**bin/hello.dart:**
...
void main() {
var hello = 'Hello Dart developers';
if (someCondition) {
hello = null;
}
print(hello);
}
1
2
**在更改 SDK 约束之前:**
$ dart run
1
null
1
2
**在更改 SDK 约束之后(并运行 dart pub get):**
$ dart run
1
2
3
bin/hello.dart:6:13: Error: Null can't be assigned to a variable of type 'String' because 'String' is not nullable.
hello = null;
^

遷移到空安全功能

要將套件(或簡單的應用程式)遷移到空安全功能,請遵循以下五个步骤,这些步骤在 dart.dev 上的 遷移指南 中有完整的說明。

第 1 步:檢查您的相依性是否已準備好

我們強烈建議您按照順序遷移程式碼,先遷移相依性圖的葉子节点。例如,如果 C 相依於 B,而 B 相依於 A,請先將 A 遷移到空安全功能,然后是 B,最后是 C。此顺序适用于 A、B 和 C 是函式庫、套件还是应用程序。

Illustration of dependency order vs. migration order

為什麼順序很重要?雖然您可以在相依性遷移之前取得一些遷移程式碼的進展,但如果您在相依性遷移期間更改其 API,您可能會冒著需要進行第二次遷移的風險。如果您的幾個相依性不是空安全的,請考慮使用在 pub.dev 上列出的每個套件的聯絡資訊來聯繫套件發佈者。

驗證相依性是否已準備好

若要驗證您的應用程式或套件是否已準備好開始遷移,您可以在空安全模式下使用 dart pub outdated。以下範例顯示,如果此應用程式將其相依性升級到如 可解析 欄位中列出的路徑、進程和 pedantic 的預覽版本,則它已準備好進行遷移。

Screenshot of `dart pub outdated` output

如果空安全支援在次要新版本中可用,您將在 可升級 欄位中看到它們。通常,空安全支援將在主要新版本中可用;在这种情况下,您将看到在 outdated 输出的 可解析 下列出的版本。若要升級到這些版本,請編輯您的 pubspec.yaml 檔案以允許這些主要版本。例如,您可能會將 process: ^3.0.13 更改為 process: ^4.0.0-nullsafety。

您還可以在 pub.dev 上使用套件頁面上的新 空安全 標籤(例如 collection 1.15)和新的 進階搜尋 空安全搜尋選項 來查找具有空安全支援的套件。

Screenshot of pub.dev search

第 2 步:使用遷移工具進行遷移

如果您的相依性已準備好,您可以繼續使用遷移工具 dart migrate 來遷移您的應用程式或套件。

遷移工具是互動式的,因此您可以審查工具推斷出的可空性屬性。如果您不同意工具的任何結論,您可以加入可空性提示以更改推斷。加入幾個遷移提示可能會對遷移品質產生重大影響。

Screenshot of the migration tool UI

少數 Dart 套件作者已經使用空安全功能的早期預覽版本測試了遷移,他們的回饋令人鼓舞。遷移指南中提供有關如何使用遷移工具的額外提示。

第 3 步:靜態分析您的遷移程式碼

在您的 IDE 或命令列中使用 pub get 更新您的套件。然後使用您的 IDE 或命令列對您的 Dart 程式碼執行靜態分析:

1
2
$ dart pub get
$ dart analyze

或在您的 Flutter 程式碼上:

1
2
$ flutter pub get
$ flutter analyze

第 4 步:確保測試通過

運行您的測試,并确保它们通过。您可能需要更新預期為空值的測試,以防您更改了套件程式碼,使其不再允許為空。

第 5 步:發佈您的空安全套件

遷移完成且測試通過後,您可以將套件作為預覽版本發佈。以下是最佳實務的簡要摘要:

  • 將您的版本號碼增量到下一個主要版本(例如,從 2.3.x 到 3.0.0)。此最佳實務可確保您的套件使用者不會在他們準備好使用空安全功能本身之前升級到它,並且它讓您能夠自由地重構 API 以最佳地利用空安全功能。
  • 將套件的版本設定為 pub.dev 上的預覽版本。(例如,使用 3.0.0-nullsafety.0,而不是 3.0.0。)

有關遷移和版本控制的完整詳細資訊,請參閱 遷移指南

健全空安全的優點

我們之前關於空安全功能技術預覽的部落格文章 在 Dart 中在 Flutter 中 已經透過許多範例討論了空安全的優點。現在,空安全功能即將完成,我們看到了這種好处的几个实际例子。

更安全的程式碼

就在最近,我們 發現了 Flutter 主頻道中的一个錯誤,其中各種 flutter 工具命令在某些機器設定上會因空錯誤而崩潰:方法 ‘>=’ 被調用於 null 上。潜在问题是最近的一个添加了对检测 Android Studio 4.1 的支持的拉取请求。该 PR 添加了类似于以下内容的代码:

1
2
3
4
5
6
final int major = version?.major;
final int minor = version?.minor;
if (globals.platform.isMacOS) {
/// Android Studio 的插件路径在 4.1 版本之后发生了变化。
if (major >= 4 && minor >= 1) {
...

你能找到错误吗?由于 version 可能会为 null,major 和 minor 也可能会为 null。这个错误在孤立情况下似乎很容易发现,但在实践中,即使是像 Flutter 存储库中使用的严格代码审查流程,也会经常出现类似的代码。使用空安全功能,静态分析 会立即发现这个问题

Screenshot of analysis output in an IDE

这是一个非常简单的错误。在 Google 内部早期使用空安全功能的代码中,我们已经看到更多复杂的错误被发现,然后通过空安全功能解决。以下是几个例子:

  • 一个内部团队发现,他们经常检查代码中的空值,而空安全功能知道这些空值永远不会为 null。这个问题最常出现在使用 protobuf 的代码中,其中可选字段在未设置时返回默认值,而不会返回 null。这导致代码错误地检查默认条件,因为混淆了默认值和 null 值。
  • Google Pay 团队发现他们的 Flutter 代码中的错误,这些错误在尝试在 Widget 上下文之外访问 Flutter State 对象时会导致失败。在空安全功能之前,它们会返回 null 并隐藏错误;使用空安全功能,健全的分析确定这些属性永远不会为空,并抛出一个分析错误。
  • Flutter 团队发现一个错误,如果将 null 传递给 Window.render() 中的 scene 参数,Flutter 引擎可能会崩溃。在空安全功能迁移期间,他们添加了一个提示,以 将 Scene 标记为不可为空,并且能够轻松地防止潜在的应用程序崩溃,如果传递了 null,这些崩溃可能会被触发。

在編譯過程中利用健全的空安全功能

Dart 空安全功能的健全性還有一個令人歡迎的含義:這意味著 Dart 編譯器可以利用可空性資訊。這可能會使您的程式更小、更快。我們還沒有很多實際應用程式完全遷移到健全的空安全功能(因為我們現在才開始進行這些應用程式相依的套件的生態系統遷移),但我們從核心框架中看到了非常令人鼓舞的結果。

我們最近對 hello_world 範例進行了測試重新編譯,以測量空安全功能對應用程式大小的影響。這是一個最簡單的範例,它只會顯示「hello world」。在 比較 編譯後程式碼的總體大小時,未壓縮的(安裝在設備上的)程式碼大小縮小了 3.5%,而沒有做任何其他事情,只是使用健全的空安全功能重新編譯。這有可能實現,儘管這個應用程式只有 10 行程式碼,因為所有包含的函式庫的程式碼大小都縮小了;例如,Flutter 框架本身(套件:flutter)縮小了 3.9%。

至於程式碼速度,強制執行健全的類型系統可能會增加開銷。但是,減少空檢查也可能會使程式碼執行得更快。我們對基准測試的初步分析表明,效能與以前的版本相當,並且新的額外類型資訊為我們在未來進行新的效能改進提供了潛力。我們計劃在未來的部落格文章中更多地介紹我們的效能工作。

在某些情况下,我们已经看到空安全功能带来了性能提升,通常是在迁移到空安全功能时发现了代码逻辑中的缺陷。例如,我们在 Flutter 网页文字布局缓存中发现了一个问题。这个缓存使用了一个可为空的键,然后使用了一些逻辑来在为空时使用 TextAlign.start。这种逻辑导致缓存出现缺陷,即使元素仍然具有默认值,它们看起来也会像改变了一样。因此,经常出现缓存未命中。添加一个不可为空的 textAlign getter 有助于修复缓存缺陷,从而在文字被缓存的情况下,使文字渲染性能 提高了 14 倍

立即開始吧!

包含空安全功能的 Dart 和 Flutter 的 Beta 版本今天已準備就緒。如果您使用 Flutter 開發,您可以使用 flutter channel beta 然後 flutter upgrade 切換到 Beta 版本。否則,您可以從 Dart SDK 檔案 中獲取獨立的 Dart SDK。

如果您開發套件,我們鼓勵您閱讀我們的 遷移指南 並規劃您的遷移。請 告知我們 您遇到的任何問題或建議。

如果您是應用程式開發人員,您可能更希望延遲遷移,直到該功能出现在我們的穩定通道中。我們計劃快速解决 Beta 版本的回饋,修复任何剩余的问题。很难确定空安全功能将在稳定版中发布的确切时间,但我们预计将在明年年初。

感謝您在我們努力使 Dart 變得更加健壯,以及使 Flutter 變得更加强大的過程中提供支持和回饋!


宣佈 Dart 空安全功能 Beta 版本 最初發佈在 Flutter 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

【文章內容使用 Gemini 1.5 Pro 自動翻譯產生】

宣佈 Dart 空安全 Beta 版

此文章交叉發佈於此處和 Flutter 部落格

今天我們宣佈 Dart 和 Flutter 的 Beta 版已提供完善的空安全功能。空安全是我們最新的主要生產力功能,旨在幫助您避免空值錯誤,這是一類通常難以發現的錯誤。如果您正在尋找我們為何對空安全感到興奮的快速摘要,請查看這部新影片:

隨著空安全進入 Beta 版,是時候開始社群遷移 pub.dev 上可用的數千個套件了。我們已經遷移了 Dart 核心程式庫、Flutter 架構,以及超過 40 個 Dart 和 Flutter 套件。有了這些,我們希望看到社群透過遷移他們的套件來擁抱空安全。

Timeline of Dart sound null safety support, from Technical Preview 1 to Stable in early 2021

隨著 Beta 版的推出,我們也開始在發佈空安全功能的穩定版本之前的最後階段。我們希望您能使用此功能,並讓我們知道它的哪些部分可以改進,UI 訊息是否可以更容易理解,或者文件是否可以更清晰。我們**非常期待收到您的回饋**。

選擇加入空安全

在我們討論空安全遷移之前,需要重複的是(如我們的空安全原則中所述),您可以控制何時開始採用空安全。只有當應用程式和套件的最低 Dart SDK 限制 至少是 Dart 2.12 預發佈版時,它們才會以空安全模式執行:

1
2
environment:
sdk: ">=2.12.0-0 <3.0.0"

要體驗這一點,請嘗試建立一個小的空安全 hello 應用程式(例如,使用 dart create),其中包含如下所示的程式碼。然後,您可以嘗試在更改 SDK 限制並執行 dart pub get 之前和之後執行應用程式,並體驗程式行為的變化。(確保使用在 dart --version 中報告 2.12 的 SDK。)

1
2
3
4
5
6
7
8
9
// bin/hello.dart:
...
void main() {
var hello = 'Hello Dart developers';
if (someCondition) {
hello = null;
}
print(hello);
}
1
2
# 更改 SDK 限制之前:
$ dart run
1
null
1
2
# 更改 SDK 限制之後(並執行 dart pub get):
$ dart run
1
2
3
4
bin/hello.dart:6:13: Error: Null can't be assigned to a variable of type 'String' because 'String' is not nullable.

hello = null;
^

遷移至空安全

要將套件(或簡單的應用程式)遷移至空安全,請按照以下五個步驟操作,這些步驟在 dart.dev 的遷移指南中已完整記錄。

步驟 1:檢查您的依賴項是否已準備就緒

我們強烈建議您按順序遷移程式碼,先遷移依賴圖的葉子。例如,如果 C 依賴於 B,而 B 依賴於 A,則先將 A 遷移至空安全,然後是 B,然後是 C。此順序適用於 A、B 和 C 是程式庫、套件還是應用程式。

Illustration of dependency order vs. migration order

為什麼順序很重要?儘管您可以在依賴項遷移之前在遷移程式碼方面取得一些進展,但如果您的依賴項在其遷移期間更改其 API,則您可能會冒險進行第二次遷移。如果您的某些依賴項不是空安全的,請考慮使用 pub.dev 上列出的每個套件的聯絡方式與套件發佈者聯繫。

驗證依賴項是否已準備就緒

要驗證您的應用程式或套件是否已準備好開始遷移,您可以使用空安全模式下的 dart pub outdated。以下範例顯示,如果此應用程式將其依賴項升級到 可解析 欄中列出的 pathprocesspedantic 的預發佈版本,則它已準備好遷移。

Screenshot of `dart pub outdated` output

如果在新的小版本中提供空安全支援,您將在 可升級 欄中看到它們。通常,在新的大版本中會提供空安全支援;在這種情況下,您將在過時輸出的 可解析 下看到列出的版本。要升級到這些版本,請編輯您的 pubspec.yaml 檔案以允許這些主要版本。例如,您可以將 process: ^3.0.13 更改為 process: ^4.0.0-nullsafety

您還可以使用套件頁面上的新 空安全 標籤(例如 collection 1.15)和新的進階搜尋 空安全搜尋選項 在 pub.dev 上找到具有空安全支援的套件。

Screenshot of pub.dev search

步驟 2:使用遷移工具進行遷移

如果您的依賴項已準備就緒,則可以使用遷移工具 dart migrate 繼續遷移您的應用程式或套件。

遷移工具是互動式的,因此您可以查看工具推斷出的可空性屬性。如果您不同意工具的任何結論,您可以加入可空性提示以更改推斷。加入一些遷移提示可以對遷移品質產生巨大影響。

Screenshot of the migration tool UI

我們已經讓少數 Dart 套件作者使用空安全的早期預覽版本測試遷移,他們的回饋令人鼓舞。遷移指南中包含有關如何使用遷移工具的更多提示。

步驟 3:靜態分析您遷移的程式碼

在您的 IDE 或命令列中使用 pub get 更新您的套件。然後使用您的 IDE 或命令列對您的 Dart 程式碼執行靜態分析:

1
2
$ dart pub get
$ dart analyze

或者在您的 Flutter 程式碼上:

1
2
$ flutter pub get
$ flutter analyze

步驟 4:確保測試通過

執行您的測試並確保它們通過。您可能需要更新預期空值的測試,以防您更改了套件程式碼以不再允許空值。

步驟 5:發佈您的空安全套件

當遷移完成且測試通過後,您可以將您的套件作為預發佈版本發佈。以下是最佳實務的簡要摘要:

  • 將您的版本號遞增到下一個主要版本(例如,從 2.3.x 到 3.0.0)。此最佳實務可確保您的套件的使用者在準備好自己使用空安全之前不會升級到它,並且讓您可以自由地重構 API 以最佳地利用空安全。
  • 在 pub.dev 上將您的套件作為預發佈版本進行版本控制和發佈。(例如,使用 3.0.0-nullsafety.0,而不是 3.0.0。)

有關遷移和版本控制的完整詳細資訊,請參閱遷移指南

完善空安全的好處

我們之前關於 Dart 和 Flutter 中空安全的技術預覽的部落格文章透過許多範例討論了空安全的好處。現在,隨著空安全即將完成,我們看到了此好處的一些真實範例。

更安全的程式碼

就在最近,我們在 Flutter master channel 中發現了一個錯誤,其中各種 flutter tool 命令會在某些機器配置上因空值錯誤而崩潰:在空值上呼叫了方法 '>='。根本原因是最近的一個拉取請求,用於加入對偵測 Android Studio 4.1 的支援。該 PR 加入了如下程式碼:

1
2
3
4
5
6
final int major = version?.major;
final int minor = version?.minor;
if (globals.platform.isMacOS) {
/// Android Studio 4.1 版之後的 Plugin 路徑已更改。
if (major >= 4 && minor >= 1) {
...

您能發現錯誤嗎?因為 version 可能為空,majorminor 也可能為空。這個錯誤在這裡單獨看來似乎很容易發現,但在實務中,這樣的程式碼總是會溜走,即使是像 Flutter 儲存庫中使用的嚴格程式碼審查流程也是如此。使用空安全,靜態分析立即捕獲此問題

Screenshot of analysis output in an IDE

那是一個相當簡單的錯誤。在我們在 Google 內部程式碼中早期使用空安全的過程中,我們已經看到更多複雜的錯誤被捕獲,然後透過空安全解決。以下是一些範例:

  • 一個內部團隊發現,他們經常在空安全知道永遠不會為空的程式碼中檢查空值。此問題最常見於使用 protobuf 的程式碼中,其中可選欄位在未設定時返回預設值,而不是空值。這會導致程式碼透過混淆預設值和空值來錯誤地檢查預設條件。
  • Google Pay 團隊在他們的 Flutter 程式碼中發現了錯誤,在嘗試在 Widget 上下文之外存取 Flutter State 物件時,它們會失敗。在空安全之前,這些會返回空值並掩蓋錯誤;使用空安全,完善的分析確定這些屬性永遠不會為空,並拋出分析錯誤。
  • Flutter 團隊發現了一個錯誤,如果將空值傳遞給 Window.render() 中的 scene 參數,Flutter 引擎可能會崩潰。在空安全遷移期間,他們加入了一個提示以將 Scene 標記為不可為空,然後能夠輕鬆地防止如果傳遞空值會觸發的潛在應用程式崩潰。

在編譯期間利用完善的空安全

Dart 空安全的完善性還有另一個令人歡迎的含義:這意味著 Dart 編譯器可以利用可空性資訊。這可能會使您的程式更小、更快。我們還沒有很多真實世界的應用程式完全遷移到完善的空安全(因為我們現在才剛剛開始這些應用程式所依賴的套件的生態系統遷移以實現完善性),但我們從核心架構中看到了非常令人鼓舞的結果。

我們最近對 hello_world 範例進行了測試重新編譯,以測量空安全對應用程式大小的影響。這是一個最小化的範例,只顯示「hello world」。在比較編譯程式碼的總大小時,未壓縮的(安裝在設備上的)程式碼大小縮小了 3.5%,除了使用完善的空安全重新編譯之外沒有做任何事情。儘管這個應用程式只有 10 行程式碼,但這也是可能的,因為所有包含的程式庫的程式碼大小都縮小了;例如 Flutter 架構本身(package:flutter)縮小了 3.9%。

至於程式碼速度,必須強制執行完善的型別系統可能會增加開銷。但是,進行更少的空值檢查也可能會使程式碼更快。我們對基準測試的初步分析顯示,效能與以前的版本相當,並且新的額外型別資訊為我們未來進行新型別的效能改進創造了潛力。我們計畫在未來的部落格文章中詳細介紹我們的效能工作。

在某些情況下,我們已經看到空安全會帶來效能提升,通常是在遷移至空安全時發現了程式碼邏輯中的缺陷。例如,我們在 Flutter 網頁文字佈局快取中發現了一個問題。此快取使用的鍵是可為空的,然後是一些在為空時使用 TextAlign.start 的邏輯。此邏輯導致快取中的缺陷,即使元素仍然具有預設值,它們看起來也像已更改。因此,經常發生快取未命中。加入一個不可為空的 textAlign getter 有助於修復快取缺陷,從而在文字被快取的情況下使文字渲染效能提高 14 倍

立即開始!

包含空安全的 Dart 和 Flutter 的 Beta 版本今天已經準備好了。如果您使用 Flutter 進行開發,您可以使用 flutter channel beta 切換到 Beta,然後使用 flutter upgrade。否則,您可以從 Dart SDK 存檔 中獲取獨立的 Dart SDK。

如果您開發套件,我們鼓勵您閱讀我們的遷移指南並規劃您的遷移。如果您有任何問題或建議,請告知我們

如果您是應用程式開發人員,您可能希望延遲遷移,直到該功能進入我們的穩定頻道。我們計畫快速處理 Beta 版的回饋,修復任何剩餘的問題。很難說明空安全何時會在穩定版本中發佈的具體時間表,但我們認為是明年年初。

感謝您的支援和回饋,我們正在努力使 Dart 成為更強大的語言,使 Flutter 成為更強大的架構!


宣佈 Dart 空安全 Beta 版 最初發佈在 Dart 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

【文章內容使用 Gemini 1.5 Pro 自動產生】

Flutter 中的棄用生命週期

新的一年即將到來,Flutter 團隊正在為 2021 年的全新開始做好準備。

為此,我們將從 Flutter 架構中移除一些已棄用的 API。

在 Flutter 架構中移除棄用並非新鮮事。我過去也曾經 自己處理過快速搜尋 結果顯示,過去移除棄用沒有定義的時間軸或排程,因為我們的 棄用策略 並沒有規定何時移除它們。由於我們希望為使用者提供一致且可靠的體驗,因此我們決定為棄用建立標準生命週期。

本文將討論我們目前針對重大變更和棄用的策略,以及這些策略如何付諸實行,以及如何發展以為使用者提供更好的體驗。

Flutter 中的重大變更

Photo by CHUTTERSNAP on Unsplash

在 Flutter 中棄用程式碼的流程始於我們的 重大變更策略。棄用被視為重大變更,這意味著它們必須遵循 公開設計文件 指南,該指南要求一段時間用於討論。(此提案的設計文件為 實作棄用生命週期。) 除了提供設計文件外,我們還會在 Flutter 貢獻者 Discord 和受歡迎的社群,例如 flutter-announceflutter-dev 上發佈訊息,所有這些都在進行任何變更之前。這是我作為工程師最喜歡的流程之一:我能夠聽到您的意見。我們很棒的社群在這個流程中非常活躍,這也讓我們變得更好。在多次場合中,我看到一個功能的設計在透過這些管道發佈後有了很大的改進,這全都是因為社群的回饋。

重大變更的作者會遷移受重大變更影響的客戶程式碼。在我們重大變更策略的最後一次更新中,我們 邀請 您將測試提交到我們的 客戶測試儲存庫。這些測試是我們標誌著變更會造成中斷的訊號,我們承諾會在進行變更之前與測試作者合作遷移他們的程式碼。

我們還為我們的重大變更提供 遷移指南。這些指南旨在幫助使用者在更新到最新版本的 Flutter 後遷移他們的程式碼。如果您看過我們的 發行公告 之一,我們也會在特定版本中指出中斷,並引導使用者到有用的資源,幫助他們更新應用程式。

過去的調查 中,我們詢問使用者他們對重大變更的偏好。我們了解到使用者認為重大變更使程式碼更乾淨,對他們很有價值,這就是為什麼我們通常更喜歡重大變更而不是棄用的原因。根據研究:

總體而言,78% 的人表示他們更喜歡導致最乾淨的 API 的重大變更;這也使 API 更易於學習和使用。

我們的重大變更策略基於此研究。與所有規則一樣,總是會有例外。我最近 棄用了一些非常受歡迎的方法,改用新的 Widget。如果我們知道特定變更會造成重大中斷,影響許多使用者,我們就會棄用。棄用允許遷移緩衝期,同時仍然提供重大變更策略所產生所有工具。

日漸老舊的棄用

Photo by Dilyara Garifullina on Unsplash

遷移緩衝期從棄用在 穩定頻道 上發佈時開始。緩衝期結束的時間軸為 1 個日曆年或 4 個穩定版本,取較長者。為了制定出我們認為舒適的時間軸,我們查看了我們的成長率和採用率,並評估了一些已公開發佈的 Flutter 應用程式和套件。

我們希望使用者能夠預期這個排程,而不是擔心下一個版本是否會因為棄用消失而帶來意外的升級障礙。我們還希望確保我們提前通知這些變更,並且我們承諾在開始移除棄用之前,會宣布棄用即將結束生命週期。本著這種精神,我想要與您分享 第一批即將結束生命週期的棄用,現在 Flutter 1.22 已推出。

由於這是我們第一次實施這項策略,因此有很多棄用,其中有些甚至早於 Flutter 1.0!其中一些棄用也早於我們的重大變更策略。我們將會為所有這些棄用努力建立遷移工具和指南。預計在我們下一個穩定版本推出之前,這些棄用將從 Flutter 架構中移除,並會在移除時透過我們通常的管道發布公告。

在我們開始實施這些變更的同時,我們希望聽到您的意見!我們始終歡迎您將測試提交到我們的客戶測試儲存庫,我們會在進行中斷之前與您合作遷移您的程式碼。若要進一步了解測試儲存庫以及如何提交測試,請查看 Flutter 測試 README。在進行這些變更時,我們將透過 Flutter 貢獻者 Discordflutter-announceflutter-dev 頻道通知您並徵求您的回饋。因此,如果您尚未加入其中一個社群,請務必加入!我們期待收到您的訊息,並與您合作。


Flutter 中的棄用生命週期 最初發佈在 Flutter 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

http://creativecommons.org/licenses/by/4.0/

【文章內容使用 Gemini 1.5 Pro 自動產生】

為 Flutter 行動廣告 Plugin 開放嵌入式橫幅和原生廣告的 Beta 程式

有許多方法可以讓行動應用程式開發人員獲得工作的報酬。透過廣告變現應用程式是許多行動開發人員(包括使用 Flutter 的開發人員)的熱門選擇之一。

原生廣告格式支援,特別是 Flutter 的前五名熱門功能請求之一。Flutter 團隊先前提供的 Google 廣告 Plugin(也稱為 firebase_admob Plugin)支援覆蓋橫幅、插頁式和獎勵影片,但無法渲染嵌入式橫幅或原生廣告等格式。

嵌入式橫幅和原生廣告

顧名思義,嵌入式橫幅廣告讓您將廣告單元整合為 Flutter 視圖的一部分,而不是將橫幅覆蓋在 UI 上。同樣地,原生廣告允許您設計一種感覺像是您應用程式內容自然一部分的廣告體驗。一些開發人員告訴我們,缺少嵌入式橫幅和原生廣告會阻礙他們建立美麗自然的廣告體驗的能力,而另一些開發人員則表示,沒有這些功能可能會阻止他們將 Flutter 採用到他們的應用程式中。

為了響應您的請求,我們一直在開發對這些格式的支援。一年前,我們開始與 Google Ads 密切合作,為 Flutter 實作健全且值得信賴的嵌入式廣告解決方案。我們還完全重構了 Plugin 程式碼,以提高效能和穩定性。今天,在完成實作和一系列 Alpha 測試後,我們很高兴地宣布,此更新的 Plugin 的 beta 版本將提供僅限邀請的存取權。

藉由這個 beta 測試,我們在現有 Plugin 上進行了建置,並為 AdMob 和 Ad Manager 新增了對新格式的支援。與所有 beta 版本一樣,仍然存在一些需要解決的問題,而且功能支援可能會在未經通知的情況下發生變化。如果被選為參與 beta 測試,我們強烈建議您在將應用程式發佈給使用者之前,在您的應用程式中徹底測試整合。

立即申請

若要申請參與我們的 beta 測試,請填寫 我們的申請表。我們特別尋找已有應用程式上架且願意與我們團隊合作,在他們的生產應用程式中使用我們的 beta 版本的客戶。如果您被選為參與 beta 測試,您將收到一封包含更多說明的電子郵件。

根據此 beta 測試的結果,我們預計在 2021 年初向更廣泛的受眾開放使用。

我們希望嵌入式橫幅和原生廣告能幫助您為您的 Flutter 應用程式解鎖更多收入增長。

祝您程式設計和廣告愉快!


Flutter 的新廣告格式 最初發佈在 Flutter 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

http://creativecommons.org/licenses/by/4.0/

【文章內容使用 Gemini 1.5 Pro 自動產生】

使用 Material Icons 和 Flutter 打造可存取的表達力

Material Design 和 Flutter 都能幫助開發人員打造出靈活、可存取且富於表現力的體驗,同時提供出色的效能和效率。現在,我們很高興為您提供更多方法來使用改進的 Material Icons 支援在 Flutter 中建立獨特的 UI。

Material Icons 是為常見動作和項目精心設計的圖示,包括從用於導航的簡單箭頭和指示器,到代表可存取性、錯誤回報,甚至洗手等概念的圖示。

Flutter 現在支援所有四種圖示樣式——填滿、描邊、銳利和圓形——幫助您讓您的應用程式或網站對使用者來說熟悉且易於存取,同時讓您以獨特的方式傳達您的品牌。而且,透過對樹狀搖動的支援,Flutter 會優化您的應用程式建構,只包含您正在使用的圖示,從而使載入時間和記憶體使用更加有效率。

以下是一些展示您在 Material 目錄中找到的選項範圍的圖示。您可以在 Material.io 上瀏覽完整的圖示集並下載位圖或向量版本以供在您喜愛的設計工具中使用,或在 Icons API 頁面上查看 Flutter 版本。

Sample of 4 icons: shopping_cart, chat, masks, wash shown in the 4 supported styles: filled, outlined, rounded, sharp.
Sample of 4 icons: shopping_cart, chat, masks, wash. Displayed in the 4 supported styles: filled, outlined, rounded, sharp.

入門

若要開始使用 Icon 類別,請確保在您專案的 pubspec.yaml 檔案中設定 uses-material-design: true。這會告訴 Flutter 將圖示資產包含在您的應用程式中。

Flutter 中的圖示透過圖示字體以向量形式提供,因此您可以無限調整大小和顏色,而無需擔心影像品質下降。在 GitHub 上查看用於生成上述圖示集的程式碼。

不要跳過語義標籤!

為了讓使用螢幕閱讀器等輔助科技的使用者能夠有效地導航您的應用程式或網站,提供有意義且上下文適當的語義標籤非常重要。

預設情況下,螢幕閱讀器會朗讀螢幕上顯示的任何文字。為了將圖示等視覺元素準確地轉換為基於文字的 UI,您需要仔細標記這些元素。

例如,當圖示與動作元素(如按鈕)結合使用時,您應該始終將語義標籤設定為描述使用者點擊該圖示時會發生什麼的字串。

Material.io 提供指南 來指示透過動作來表示 UI 元素。常見的錯誤是將語義標籤預設為圖示的名稱,而不是選擇該圖示時執行的動作。

an example of what to do: a Pencil icon with the label “edit” and what not to do: a Pencil icon with the label “Pencil”

若要進一步了解建立可存取的數位體驗的重要性,請參閱 flutter.dev 上的 可存取性

只發佈您使用的內容!

當您為發佈建構應用程式時,Flutter 編譯器會執行「樹狀搖動」,移除未使用的程式碼和資產(包括圖示),以優化應用程式的佔用空間,並幫助最大限度地減少下載和載入時間。可以使用您想要的任何圖示!在編譯期間,會生成一個自訂圖示字體,其中只包含專案中使用的圖示子集。這可以大幅減少二進制檔案的大小——所有人都能受益!

除了樹狀搖動之外,這裡還有一篇關於 縮減 Flutter 應用程式大小的最佳實務 的很棒社群文章。

向我們展示您的傑作!

全世界的開發人員都在使用 Material Icons 來確保在大型和小尺寸條件下都能保持可讀性和清晰度;這些圖示已針對在所有 Flutter 支援的平台和顯示解析度上美麗顯示而進行優化。

在 @MaterialDesign 和 @@FlutterDev 上關注我們,並展示您如何使用 Material Design 和 Flutter 打造出美麗、可存取的數位體驗!


使用 Material Icons 和 Flutter 打造可存取的表達力 最初發佈在 Flutter 上的 Medium,人們在那裡透過突出顯示和回應這個故事來繼續討論。

本篇文章主要介紹在2020年 Goolge 改版後的 Play Store Console 如何上架 Android App

首先到 Play Store Console 右邊點擊新增應用程式

填寫以下應用程式詳細資訊

  • 應用程式名稱:會顯示在商店的App名稱
  • 預設語言:預設App使用的語言,作為商店一開始提供的預設語言
  • 應用程式類型:分類只有遊戲與應用程式,目的是要把遊戲區分出來,日後可更改
  • 是否收費:設定App是否下載時需要費用,勾選收費後,需要至 付費應用程式 修改設定

最後同意 開發人員計畫政策美國出口法律 後,即可完成初始設定

接著進入到 資訊主頁

第一次設定時會出現 初始設定 提示,可依序點擊設定,該步驟都是必須完成的步驟,否則無法完成審查上架

應用程式存取權

設定 App 是否開放給全部使用者

或是部分功能有使用限制,需設定:

  • 名稱
  • 使用者名稱/電話號碼
  • 密碼
  • 任何操作說明

透過此可限制使用者下載應用程式

廣告

設定App中是否有廣告,若有勾選廣告則會在Play商店上顯示 含廣告內容 的標籤

內容分級

根據國際年齡分級聯盟(英語:International Age Rating Coalition,縮寫IARC)設計的簡化各國分級的內容分級問卷,降低產品評比的過程,參考資料

填寫內容分級問卷,讓使用者了解App的分類,及是否會有不宜兒童的內容

首先填寫電子郵件,問卷完成會寄送一份結果至該信箱

選擇 App 的類別,以 文藻校務通 為例,選擇 參考資訊、新聞或教育內容

根據選擇的類型,需填寫相關內容是否有未成年暴力色情訊息

完成後會顯示基本的報表,點擊右下角提交完成問卷

完成後,若日後想在修改問卷內容,需至右上角 點擊 Start new questionaire 重新提交問卷

目標對象

填寫詳細的App發布目標對象

首先點選 目標年齡層,主要是確認目標對象是不是兒童,若對象未滿13歲則需要新增隱私權政策

勾選 是否會引起兒童興趣,若上者點擊13歲以下,可勾選 宣稱適合兒童

最後點擊 儲存 完成目標對象和內容

接著就完成一半了~~

應用程式類別及詳細資料

應用程式分類,區分應用程式或遊戲的類別,在Play 商店中,也會有類別排名

接著設定商店的聯絡詳細資訊,分別為

  • 電子郵件地址 (必填)
  • 電話號碼
  • 網站

以上訊息皆會在Play商店上顯示

並勾選是否要在Play商店外行銷,讓外部網站可搜尋到你的App

完成後就只剩下最後一個步驟

商店資訊

首先會根據一開始設定的主要語言,設定 App 在 Play 商店的資訊,可根據不同語言,設定不同的商店資訊,可點擊 管理其他語言版本的翻譯內容 管理其他語言的內容

應用程式詳細資料

首先可設定

  • 應用程式名稱:App名稱,作為可供搜尋的關鍵字,上限50字
  • 簡短說明:可在App頁面首要看到簡短說明,上限80字
  • 完整說明:在點擊關於這個應用程式後顯示的完整說明

可參照 Play 商店對應位置

接著設定 應用程式圖示 會顯示在 Play 商店的圖示,限定尺寸為 512*512 的解析度,上傳後都會以橢圓裁剪顯示

主要圖片顯示於商店資訊的最頂端,可用於宣傳應用程式,大小限制 1024*500 解析度的圖片

螢幕截圖主要分為

  • 手機
  • 七吋平板電腦
  • 十吋平板電腦

基本上,對應類型的裝置截圖都適用,也可自行製作符合規定的尺寸的圖片,皆為使用 JPEG 或 24 位元 PNG 圖片,長寬比建議16:9

加入影片也會顯示於商店上

商店資訊設定完成後,若未來要修改都可直接修改,但修改後都需要等商店部署時間,通常都會為半天左右時間

上傳App至商店

發佈 的目錄下,選擇App目前要發布的方式,有分成

  • 正式版:會發布給商店中所有設置的地區
  • 公開測試:任何使用者可至 Play 商店點擊測試計畫,即可使用此版本
  • 封閉測試:由開發人員建立電子郵件清單,或是可透過連結加入測試計畫 https://play.google.com/apps/testing/{app id}
  • 內部測試:由開發人員建立電子郵件清單,或是可透過內部邀請測試連結:https://play.google.com/apps/internaltest/{test group id} 加入
  • 搶先註冊:若還沒發佈正式版時,可利用此功能,在Play商店中顯示搶先體驗的字樣,並提供測試人員特殊獎勵

不管利用哪種測試方式,接下來上傳App的方式都會相同,例如選擇 正式版 發布,並點擊右上方的 建立新版本

接著第一次上架時需點擊 同意使用 Google Play 應用程式簽署,Google 會管理你簽署所使用的金鑰,並且該金鑰只能提供給該 App 使用

若今天金鑰遺失,可請帳戶擁有者聯絡支援小組重新上傳金鑰

將利用 Android Studio 等等的 Android 編譯工具,將原生Android 的 Apk 或是 App Bundle 上傳至此頁面

每次新上傳的 版本號碼(version code) 皆需大於先前上傳的

關於金鑰使用詳細 可參考

版本詳細資訊

版本名稱會根據上傳的 ApkApp Bundle 命名

版本資訊會根據商店可提供的語言,以 XML 格式撰寫,將這次更新內容寫至 語言碼(language code)

完成後點擊儲存,並點擊檢查版本

接著會發現沒有設定提供地區

返回至上一頁的最上方,選擇 國家與地區 編輯針對正式版的發布國家/地區

若沒勾選,Play商店就不會發佈至此國家/地區

接著回到剛剛編輯的版本資訊,點擊 開始發布(正式版)

最後會跳回正式版的頁面,並顯示審查中

自 2019 年開始,Play 商店在第一次審查時,最久大約會至七天,爾後提交大約都是一下子就完成審查,並都是半天會完全部署至商店(所有使用者都可以看到更新)

本篇文章主要介紹在2020年 Apple 改版後的 App Store Connect 如何上架 iOS App

新增 App

App Store Connect 點擊 我的App

點擊 App 旁邊邊的 + 選擇 新的 App

註冊Identifier

憑證、識別碼及描述檔 註冊App的 Identifier

選擇 App IDs

選擇 App

Discription 填寫可以識別的名稱

App ID Prefix 選擇 Explicit 並填上與 Xcode 中的 Bundle Indentifier 相同的 ID

App ID Prefix 的 Bundle ID 上架後就不可修改

最後點擊 Continue 然後 Register 完成註冊

或是

懶人方法

使用 XcodeTarget 中的 Runner 選擇 Signing & Capabilities

選擇想要上架的Apple開發者帳號

並點擊 + Capability 隨意新增一個 Capability

此動作 Xcode 會自動註冊 Indentifier 至 App Store Connect

完成後再將其取消

沒取消的話審查時會納入審查範圍 要特別注意

接著填寫App基本資訊

  • 平台:選擇你的App會在哪些平台提供,若沒勾選可事後新增
  • 名稱:在Apple Store的名稱,無法跟已上架App相同,命名上需要先搶先贏,除非對方把App刪除或修改名稱,也是使用者搜尋時能找到你的App的首要途徑
  • 主要語言:首先可在商店提供的語言,若有發佈不同國家語系,可事後新增
  • 套件識別碼:選擇剛才新增的 Indentifier,選擇後就不能修改,對應到原生的 Bundle Indentifier
  • SKU:App ID 為獨一無二,可任意命名,填寫後不能修改,但不會公開顯示

App 資訊

新增 App 後,可先來 一般資訊 -> App 資訊,填寫 App 基本資訊,這邊會分兩個區塊,一個是可本地化資訊及一般資訊

可本地化資訊

  • 名稱:必填,為一開始輸入的 App 名稱
  • 副標題:必填,商店中會位於 App 名稱下方,建議使用簡短語句說明 App
  • 隱私權政策:必填,聲明你的 App 隱私權政策的網址,不限制格式

一般資訊

  • Apple ID:由 Apple 產生,作為網頁上的編號,可至 https://apps.apple.com/tw/app/id`${你的 Apple ID}`,找到你的App
  • 內容版權:聲明你的 App 是否有第三方內容,像是以校務通來說,資料來源於學校,就可以視為第三方內容
  • 年齡分級:必填,根據填寫 年齡分級問卷 的結果
  • 許可協議:必填,預設使用 《Apple 標準終端使用者許可協議》(EULA),可點擊自訂許可協議
  • 類別:必填,在商店的分類,可選擇兩個,也作為與同類型App排名的分類

定價與供應狀況

  • 價格排程:選擇你的App定價方式,最低免費到最高 32900元,使用者付費購買後90天內都可以反悔
  • 預定:第一次上架前會顯示,自訂日期在App成功發布後,會通知預定的使用者
  • 供應狀況:需先填寫供應國家與地區,若選擇停止供應,會從商店下架,直到重新供應為止
  • Mac 上的 iOS App:如果你的iOS App有使用到 Mac Catalyst (Flutter 目前不支援),會將你的iOS App發佈至 Mac App Store,若有發佈macOS App,則優先發布 macOS App
  • App發布方式:根據你的開發者帳號,若個人開發者帳號只能選擇公開在Apple Store,若是企業開發者帳號,則可以發布到私人的商店發布

App版本資訊

進入 App 版本資訊

首先設定 App預覽與截圖 可參考以下規格 每個至少三張

12.9 吋 iPad Pro 第三代 及 第二代 尺寸是可以相容的 故截圖第三代 可直接上傳到第二代

建議使用模擬器截圖 上傳完可用滑鼠拖曳改變顯示順序

螢幕尺寸說明 平台 截圖尺寸
iPhone 6.5 吋 iPhone 11 Pro Max、iPhone 11、iPhone X S Max、iPhone XR 1242 x 2688 像素(直向) 2688 x 1242 像素(橫向)
iPhone 5.5 吋 iPhone 8 Plus、iPhone 7 Plus、iPhone 6s Plus 1242 x 2208 像素(直向) 2208 x 1242 像素(橫向)
12.9 吋 iPad Pro(第三代) iPad Pro 2018、iPad Pro 2020 2048 x 2732 像素(直向) 2732 x 2048 像素(橫向)
12.9 吋 iPad Pro(第二代) iPad Pro 2017 2048 x 2732 像素(直向) 2732 x 2048 像素(橫向)

更多詳細資訊 可參考

接著填寫

  • 行銷宣傳文字:作為 App 行銷宣傳標語,建議簡短的文字,也要符合App的性質
  • 關鍵字:在商店搜尋時,透過關鍵字加強找到App,並使用逗點隔開
  • 描述:詳細介紹App的功能與特色
  • 支援URL:對應商店開發者網頁
  • 行銷URL:待補充
  • 版本:可參考 語意化版本,不限定兩碼或三碼,不能與先前定義的相同
  • 版權:宣告App版權的註解
  • 年齡分級:根據填寫 年齡分級問卷 的結果(需點擊編輯填寫)

對應在商店的位置可參考

年齡分級問卷

針對App的內容填寫是否有兒童不宜的

App審查資訊

蘋果在審查時都是採用 人工審查,審查過程都須填寫完整的資料

  • 登入資訊:若你的App有設計登入功能,需提供測試帳號給審查人員
  • 聯絡人資訊:當審查有問題時的聯絡人資訊,電話號碼需要加入國際冠碼
  • 備註:資訊則提供其他資訊供審查人員了解你的App,像是之前就被問過這幾個問題,爾後我都在新的App加入這些回答
    • Who is the target audience?
    • How do users obtain an account?
    • Is this app meant for internal distribution in your own company, in the company of one target client, or in multiple target clients’ companies?
    • In which countries will this app primarily be distributed?
    • If this app is meant for internal distribution, will the app be accessible by both internal and external partners? Or will it be exclusive to in-house employees?
      • 附件:可附上App操作影片,若你的App無法提供帳號測試,或是有些功能需要提供範例影片操作(ex.何時會使用到讀取wifi連線資訊的功能),都需要附上影片

建制版本

接著選擇要送至審查的App版本,需先透過Xcode Archive 後上傳至App Store Connect,點擊藍色按鈕

選擇對應想提交的版本,通常上傳完會需要一段時間處理,當完成處理都會email通知

屆時才會出現在這邊

接著選擇這次提交的出口合規資訊,點擊是否有使用加密功能,若點擊是的話,需要另外填寫出口法律資訊

廣告識別碼

最後是填寫是否有使用到 廣告識別碼,像是 Firebase Analytics 或是 Ad Mod 等第三方的廣告或分析工具,就需要聲明有使用到,若審查後發現錯誤,則需要重新上傳新的App版本

最後回到最上方點擊完成,並點擊審查,結著就會進入正在等待審查

通常審查時間已台灣都是晚上開始到半夜,對應到美國的白天時間,第一次審查都會比較久,大約2~3天,要耐心等待

但有時候會審查失敗,也會寄送 email 通知,可至連結查詢問題,並回覆審查人員

iOS Resolve Center : https://appstoreconnect.apple.com/apps/{apple id}/appstore/platform/ios/resolutioncenter?m=

macOS Resolve Center : https://appstoreconnect.apple.com/apps/{apple id}/appstore/platform/osx/resolutioncenter?m=

若審查成功,則會通知 Ready for Sale,完成這次版本審查