diff --git a/.github/workflows/flutter.yml b/.github/workflows/flutter.yml
index 02d9a1f012..7a4ee5df84 100644
--- a/.github/workflows/flutter.yml
+++ b/.github/workflows/flutter.yml
@@ -188,7 +188,7 @@ jobs:
- uses: actions/checkout@v4
- name: ktlint
- uses: ScaCap/action-ktlint@0ff81efa49425bd0df46caabd8005aafdc8f2cf2 # pin@1.8.0
+ uses: ScaCap/action-ktlint@38262d0fb8bff43ddafc8b3c04bce6e6c7263319 # pin@1.8.1
with:
github_token: ${{ secrets.github_token }}
reporter: github-pr-review
diff --git a/.github/workflows/testflight.yml b/.github/workflows/testflight.yml
new file mode 100644
index 0000000000..3e05b6afbb
--- /dev/null
+++ b/.github/workflows/testflight.yml
@@ -0,0 +1,57 @@
+name: Upload to Testflight
+on:
+ push:
+ branches:
+ - main
+ - release/**
+ pull_request:
+ paths:
+ - '.github/workflows/testflight.yml'
+
+jobs:
+ upload_to_testflight:
+ name: Build and Upload to Testflight
+ runs-on: macos-13
+ steps:
+ - uses: actions/checkout@v4
+ - uses: subosito/flutter-action@48cafc24713cca54bbe03cdc3a423187d413aafa # pin@v2.10.0
+ - run: xcodes select 15.0.1
+ - uses: ruby/setup-ruby@250fcd6a742febb1123a77a841497ccaa8b9e939 # pin@v1.152.0
+ with:
+ ruby-version: '2.7.5'
+ bundler-cache: true
+
+ - name: flutter
+ working-directory: ./flutter/example
+ run: |
+ flutter upgrade
+ flutter pub get
+ flutter build ios --no-codesign
+
+ - name: Install Fastlane
+ working-directory: ./flutter/example/ios
+ run: bundle install
+
+ - name: Bump, Build & Upload App to TestFlight
+ working-directory: ./flutter/example/ios
+ env:
+ APP_STORE_CONNECT_KEY_ID: ${{ secrets.APP_STORE_CONNECT_KEY_ID }}
+ APP_STORE_CONNECT_ISSUER_ID: ${{ secrets.APP_STORE_CONNECT_ISSUER_ID }}
+ APP_STORE_CONNECT_KEY: ${{ secrets.APP_STORE_CONNECT_KEY }}
+ FASTLANE_BUNDLE_VERSION: ${{ github.run_number }}
+ FASTLANE_KEYCHAIN_PASSWORD: ${{ secrets.FASTLANE_KEYCHAIN_PASSWORD }}
+ MATCH_GIT_PRIVATE_KEY: ${{ secrets.MATCH_GIT_PRIVATE_KEY }}
+ MATCH_PASSWORD: ${{ secrets.MATCH_PASSWORD }}
+ MATCH_USERNAME: ${{ secrets.MATCH_USERNAME }}
+ SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
+ SENTRY_LOG_LEVEL: DEBUG
+ run: |
+ bundle exec fastlane bump_build_number
+ bundle exec fastlane build_release
+ bundle exec fastlane upload_testflight
+
+ - name: Upload Symbols to Sentry
+ working-directory: ./flutter/example
+ env:
+ SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
+ run: flutter packages pub run sentry_dart_plugin
\ No newline at end of file
diff --git a/flutter/example/ios/Flutter/AppFrameworkInfo.plist b/flutter/example/ios/Flutter/AppFrameworkInfo.plist
index 1bdf525f9f..b7506dd05b 100644
--- a/flutter/example/ios/Flutter/AppFrameworkInfo.plist
+++ b/flutter/example/ios/Flutter/AppFrameworkInfo.plist
@@ -21,6 +21,6 @@
CFBundleVersion
1.0
MinimumOSVersion
- 11.0
+ 12.0
diff --git a/flutter/example/ios/Gemfile b/flutter/example/ios/Gemfile
new file mode 100644
index 0000000000..7a118b49be
--- /dev/null
+++ b/flutter/example/ios/Gemfile
@@ -0,0 +1,3 @@
+source "https://rubygems.org"
+
+gem "fastlane"
diff --git a/flutter/example/ios/Podfile b/flutter/example/ios/Podfile
index 55ddd63416..8cb4ddb285 100644
--- a/flutter/example/ios/Podfile
+++ b/flutter/example/ios/Podfile
@@ -44,7 +44,7 @@ post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
target.build_configurations.each do |config|
- config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '11.0'
+ config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '12.0'
end
end
end
diff --git a/flutter/example/ios/Runner.xcodeproj/project.pbxproj b/flutter/example/ios/Runner.xcodeproj/project.pbxproj
index 29c58327af..ac52d1dcc4 100644
--- a/flutter/example/ios/Runner.xcodeproj/project.pbxproj
+++ b/flutter/example/ios/Runner.xcodeproj/project.pbxproj
@@ -460,7 +460,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@@ -483,6 +483,7 @@
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -491,7 +492,7 @@
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
- PRODUCT_BUNDLE_IDENTIFIER = io.sentry.flutter.example;
+ PRODUCT_BUNDLE_IDENTIFIER = io.sentry.flutter.sample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
@@ -518,7 +519,7 @@
MARKETING_VERSION = 1.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
- PRODUCT_BUNDLE_IDENTIFIER = io.sentry.flutter.example.RunnerTests;
+ PRODUCT_BUNDLE_IDENTIFIER = io.sentry.flutter.sample.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_EMIT_LOC_STRINGS = NO;
@@ -547,7 +548,7 @@
IPHONEOS_DEPLOYMENT_TARGET = 16.2;
MARKETING_VERSION = 1.0;
MTL_FAST_MATH = YES;
- PRODUCT_BUNDLE_IDENTIFIER = io.sentry.flutter.example.RunnerTests;
+ PRODUCT_BUNDLE_IDENTIFIER = io.sentry.flutter.sample.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_VERSION = 5.0;
@@ -574,7 +575,7 @@
IPHONEOS_DEPLOYMENT_TARGET = 16.2;
MARKETING_VERSION = 1.0;
MTL_FAST_MATH = YES;
- PRODUCT_BUNDLE_IDENTIFIER = io.sentry.flutter.example.RunnerTests;
+ PRODUCT_BUNDLE_IDENTIFIER = io.sentry.flutter.sample.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_VERSION = 5.0;
@@ -631,7 +632,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@@ -681,7 +682,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 11.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@@ -706,6 +707,7 @@
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -714,7 +716,7 @@
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
- PRODUCT_BUNDLE_IDENTIFIER = io.sentry.flutter.example;
+ PRODUCT_BUNDLE_IDENTIFIER = io.sentry.flutter.sample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -737,6 +739,7 @@
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
@@ -745,7 +748,7 @@
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
- PRODUCT_BUNDLE_IDENTIFIER = io.sentry.flutter.example;
+ PRODUCT_BUNDLE_IDENTIFIER = io.sentry.flutter.sample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
diff --git a/flutter/example/ios/Runner/Info.plist b/flutter/example/ios/Runner/Info.plist
index 91d89f84d6..1023c8c85e 100644
--- a/flutter/example/ios/Runner/Info.plist
+++ b/flutter/example/ios/Runner/Info.plist
@@ -2,6 +2,10 @@
+ ITSAppUsesNonExemptEncryption
+
+ CADisableMinimumFrameDurationOnPhone
+
CFBundleDevelopmentRegion
$(DEVELOPMENT_LANGUAGE)
CFBundleExecutable
@@ -22,6 +26,8 @@
$(FLUTTER_BUILD_NUMBER)
LSRequiresIPhoneOS
+ UIApplicationSupportsIndirectInputEvents
+
UILaunchStoryboardName
LaunchScreen
UIMainStoryboardFile
@@ -41,9 +47,5 @@
UIViewControllerBasedStatusBarAppearance
- CADisableMinimumFrameDurationOnPhone
-
- UIApplicationSupportsIndirectInputEvents
-
diff --git a/flutter/example/ios/fastlane/Appfile b/flutter/example/ios/fastlane/Appfile
new file mode 100644
index 0000000000..1f85333eac
--- /dev/null
+++ b/flutter/example/ios/fastlane/Appfile
@@ -0,0 +1,7 @@
+app_identifier("io.sentry.flutter.sample") # The bundle identifier of your app
+
+itc_team_id("96157806") # App Store Connect Team ID
+team_id("97JCY7859U") # Developer Portal Team ID
+
+# For more information about the Appfile, see:
+# https://docs.fastlane.tools/advanced/#appfile
diff --git a/flutter/example/ios/fastlane/Fastfile b/flutter/example/ios/fastlane/Fastfile
new file mode 100644
index 0000000000..e14ca13d3c
--- /dev/null
+++ b/flutter/example/ios/fastlane/Fastfile
@@ -0,0 +1,95 @@
+default_platform(:ios)
+
+platform :ios do
+
+ desc "Bump Build Number"
+ lane :bump_build_number do
+ fetch_api_key()
+
+ version_string = get_version_number(xcodeproj: "./Runner.xcodeproj")
+ version_parts = version_string.split(".")
+
+ # Remove last digit if necessary
+ if version_parts.length > 3
+ version_parts.pop
+ end
+
+ new_version = version_parts.join(".")
+
+ increment_version_number(
+ version_number: new_version,
+ xcodeproj: "./Runner.xcodeproj"
+ )
+ increment_build_number(
+ build_number: latest_testflight_build_number + 1,
+ xcodeproj: "./Runner.xcodeproj"
+ )
+ end
+
+ desc "Build Release"
+ lane :build_release do
+
+ setup_ci
+
+ disable_automatic_code_signing
+
+ sync_code_signing(
+ type: "development",
+ readonly: true,
+ app_identifier: ["io.sentry.flutter.sample"]
+ )
+
+ sync_code_signing(
+ type: "appstore",
+ readonly: true,
+ app_identifier: ["io.sentry.flutter.sample"]
+ )
+
+ update_project_provisioning(
+ xcodeproj: "Runner.xcodeproj",
+ target_filter: "Runner",
+ profile: ENV["sigh_io.sentry.flutter.sample_appstore_profile-path"],
+ build_configuration: "Release"
+ )
+
+ build_app(
+ workspace: "Runner.xcworkspace",
+ scheme: "Runner",
+ configuration: "Release",
+ clean: true,
+ include_symbols: true,
+ export_method: "app-store",
+ output_directory:"./build/",
+ export_options: {
+ method: "app-store",
+ provisioningProfiles: {
+ "io.sentry.flutter.sample" => ENV["sigh_io.sentry.flutter.sample_appstore_profile-name"]
+ }
+ },
+ codesigning_identity: ENV["sigh_io.sentry.flutter.sample_appstore_certificate-name"],
+ output_name: "sentry_flutter_sample.ipa"
+ )
+
+ delete_keychain(
+ name: "fastlane_tmp_keychain"
+ ) unless is_ci
+ end
+
+ desc "Upload to TestFlight"
+ lane :upload_testflight do
+ fetch_api_key()
+ testflight(
+ skip_waiting_for_build_processing: true,
+ ipa: "./build/sentry_flutter_sample.ipa"
+ )
+ end
+
+ desc "Fetch ASC API Key"
+ lane :fetch_api_key do
+ app_store_connect_api_key(
+ key_id: ENV["APP_STORE_CONNECT_KEY_ID"],
+ issuer_id: ENV["APP_STORE_CONNECT_ISSUER_ID"],
+ key_content: ENV["APP_STORE_CONNECT_KEY"]
+ )
+ end
+end
diff --git a/flutter/example/ios/fastlane/Matchfile b/flutter/example/ios/fastlane/Matchfile
new file mode 100644
index 0000000000..054fef1c37
--- /dev/null
+++ b/flutter/example/ios/fastlane/Matchfile
@@ -0,0 +1,5 @@
+git_url("git@github.com:getsentry/codesigning.git")
+storage_mode("git")
+username("bot@getsentry.com") # Your Apple Developer Portal username
+
+# The docs are available on https://docs.fastlane.tools/actions/match
diff --git a/flutter/example/lib/main.dart b/flutter/example/lib/main.dart
index 26bbdce6d8..64bfba90e3 100644
--- a/flutter/example/lib/main.dart
+++ b/flutter/example/lib/main.dart
@@ -478,12 +478,7 @@ class MainScaffold extends StatelessWidget {
TooltipButton(
onPressed: () async {
final id = await Sentry.captureMessage('UserFeedback');
- // ignore: use_build_context_synchronously
- if (!context.isMounted) {
- return;
- }
-
- // ignore: use_build_context_synchronously
+ if (!context.mounted) return;
await showDialog(
context: context,
builder: (context) {
@@ -906,12 +901,7 @@ Future makeWebRequest(BuildContext context) async {
await transaction.finish(status: const SpanStatus.ok());
- // ignore: use_build_context_synchronously
- if (!context.isMounted) {
- return;
- }
-
- // ignore: use_build_context_synchronously
+ if (!context.mounted) return;
await showDialog(
context: context,
builder: (context) {
@@ -957,12 +947,7 @@ Future makeWebRequestWithDio(BuildContext context) async {
await span.finish();
}
- // ignore: use_build_context_synchronously
- if (!context.isMounted) {
- return;
- }
-
- // ignore: use_build_context_synchronously
+ if (!context.mounted) return;
await showDialog(
context: context,
builder: (context) {
@@ -992,12 +977,7 @@ Future showDialogWithTextAndImage(BuildContext context) async {
final text =
await DefaultAssetBundle.of(context).loadString('assets/lorem-ipsum.txt');
- // ignore: use_build_context_synchronously
- if (!context.isMounted) {
- return;
- }
-
- // ignore: use_build_context_synchronously
+ if (!context.mounted) return;
await showDialog(
context: context,
// gets tracked if using SentryNavigatorObserver