diff --git a/.gitignore b/.gitignore
index e0b115cb7..9caa9528e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -125,3 +125,4 @@ packages/app_usage/example/.flutter-plugins-dependencies
packages/app_usage/example/.flutter-plugins-dependencies
.sdkmanrc
+**/.cxx/
diff --git a/packages/health/CHANGELOG.md b/packages/health/CHANGELOG.md
index 704d1cd24..fafe282ee 100644
--- a/packages/health/CHANGELOG.md
+++ b/packages/health/CHANGELOG.md
@@ -1,3 +1,20 @@
+## 12.1.0
+
+* Add delete record by UUID method. See function `deleteByUUID(required String uuid, HealthDataType? type)`
+* iOS: Parse metadata to remove unsupported types - PR [#1120](https://github.com/cph-cachet/flutter-plugins/pull/1120)
+* iOS: Add UV Index Types
+* Android: Add request access to historic data [#1126](https://github.com/cph-cachet/flutter-plugins/issues/1126) - PR [#1127](https://github.com/cph-cachet/flutter-plugins/pull/1127)
+```XML
+
+
+```
+* Android:
+ * Update `androidx.compose:compose-bom` to `2025.02.00`
+ * Update `androidx.health.connect:connect-client` to `1.1.0-alpha11`
+ * Update `androidx.fragment:fragment-ktx` to `1.8.6`
+ * Update to Java 11
+* Update example apps
+
## 12.0.1
* Update of API and README doc
@@ -18,12 +35,10 @@
* Fix [#984](https://github.com/cph-cachet/flutter-plugins/issues/984) - PR [#1055](https://github.com/cph-cachet/flutter-plugins/pull/1055)
* Add `LEAN_BODY_MASS` data type [#1078](https://github.com/cph-cachet/flutter-plugins/issues/1078) - PR [#1097](https://github.com/cph-cachet/flutter-plugins/pull/1097)
* The following AndroidManifest values are required to READ/WRITE `LEAN_BODY_MASS`:
-
```XML
```
-
* iOS: Add `WATER_TEMPERATURE` and `UNDERWATER_DEPTH` health values [#1096](https://github.com/cph-cachet/flutter-plugins/issues/1096)
* iOS: Add support for `Underwater Diving` workout [#1096](https://github.com/cph-cachet/flutter-plugins/issues/1096)
* Fix [#1072](https://github.com/cph-cachet/flutter-plugins/issues/1072) and [#1074](https://github.com/cph-cachet/flutter-plugins/issues/1074)
diff --git a/packages/health/README.md b/packages/health/README.md
index ffa41fc89..559ec09c4 100644
--- a/packages/health/README.md
+++ b/packages/health/README.md
@@ -74,6 +74,16 @@ An example of asking for permission to read and write heart rate data is shown b
```
+By default, Health Connect restricts read data to 30 days from when permission has been granted.
+
+You can check and request access to historical data using the `isHealthDataHistoryAuthorized` and `requestHealthDataHistoryAuthorization` methods, respectively.
+
+The above methods require the following permission to be declared:
+
+```xml
+
+```
+
Accessing fitness data (e.g. Steps) requires permission to access the "Activity Recognition" API. To set it add the following line to your `AndroidManifest.xml` file.
```xml
@@ -362,6 +372,7 @@ The plugin supports the following [`HealthDataType`](https://pub.dev/documentati
| MENSTRUATION_FLOW | NO_UNIT | yes | yes | |
| WATER_TEMPERATURE | DEGREE_CELSIUS | yes | | Related to/Requires Apple Watch Ultra's Underwater Diving Workout |
| UNDERWATER_DEPTH | METER | yes | | Related to/Requires Apple Watch Ultra's Underwater Diving Workout |
+| UV_INDEX | COUNT | yes | | |
| LEAN_BODY_MASS | KILOGRAMS | yes | yes | |
## Workout Types
diff --git a/packages/health/android/build.gradle b/packages/health/android/build.gradle
index 1f96a1fb1..45c7b67d8 100644
--- a/packages/health/android/build.gradle
+++ b/packages/health/android/build.gradle
@@ -25,15 +25,15 @@ apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
android {
- compileSdkVersion 34
+ compileSdk 34
compileOptions {
- sourceCompatibility JavaVersion.VERSION_1_8
- targetCompatibility JavaVersion.VERSION_1_8
+ sourceCompatibility JavaVersion.VERSION_11
+ targetCompatibility JavaVersion.VERSION_11
}
kotlinOptions {
- jvmTarget = '1.8'
+ jvmTarget = '11'
}
sourceSets {
@@ -51,12 +51,12 @@ android {
}
dependencies {
- def composeBom = platform('androidx.compose:compose-bom:2022.10.00')
+ def composeBom = platform('androidx.compose:compose-bom:2025.02.00')
implementation(composeBom)
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
- implementation("androidx.health.connect:connect-client:1.1.0-alpha07")
- def fragment_version = "1.6.2"
+ implementation("androidx.health.connect:connect-client:1.1.0-alpha11")
+ def fragment_version = "1.8.6"
implementation "androidx.fragment:fragment-ktx:$fragment_version"
}
diff --git a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt
index 1d6ee83dc..a9c23af2c 100644
--- a/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt
+++ b/packages/health/android/src/main/kotlin/cachet/plugins/health/HealthPlugin.kt
@@ -9,9 +9,12 @@ import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.result.ActivityResultLauncher
import androidx.annotation.NonNull
+import androidx.health.connect.client.feature.ExperimentalFeatureAvailabilityApi
import androidx.health.connect.client.HealthConnectClient
+import androidx.health.connect.client.HealthConnectFeatures
import androidx.health.connect.client.PermissionController
import androidx.health.connect.client.permission.HealthPermission
+import androidx.health.connect.client.permission.HealthPermission.Companion.PERMISSION_READ_HEALTH_DATA_HISTORY
import androidx.health.connect.client.records.*
import androidx.health.connect.client.records.MealType.MEAL_TYPE_BREAKFAST
import androidx.health.connect.client.records.MealType.MEAL_TYPE_DINNER
@@ -147,6 +150,9 @@ class HealthPlugin(private var channel: MethodChannel? = null) :
when (call.method) {
"installHealthConnect" -> installHealthConnect(call, result)
"getHealthConnectSdkStatus" -> getHealthConnectSdkStatus(call, result)
+ "isHealthDataHistoryAvailable" -> isHealthDataHistoryAvailable(call, result)
+ "isHealthDataHistoryAuthorized" -> isHealthDataHistoryAuthorized(call, result)
+ "requestHealthDataHistoryAuthorization" -> requestHealthDataHistoryAuthorization(call, result)
"hasPermissions" -> hasPermissions(call, result)
"requestAuthorization" -> requestAuthorization(call, result)
"revokePermissions" -> revokePermissions(call, result)
@@ -161,6 +167,7 @@ class HealthPlugin(private var channel: MethodChannel? = null) :
"writeBloodOxygen" -> writeBloodOxygen(call, result)
"writeMenstruationFlow" -> writeMenstruationFlow(call, result)
"writeMeal" -> writeMeal(call, result)
+ "deleteByUUID" -> deleteByUUID(call, result)
else -> result.notImplemented()
}
}
@@ -512,6 +519,55 @@ class HealthPlugin(private var channel: MethodChannel? = null) :
}
}
+ /**
+ * Checks if the health data history feature is available on this device
+ */
+ @OptIn(ExperimentalFeatureAvailabilityApi::class)
+ private fun isHealthDataHistoryAvailable(call: MethodCall, result: Result) {
+ scope.launch {
+ result.success(
+ healthConnectClient
+ .features
+ .getFeatureStatus(HealthConnectFeatures.FEATURE_READ_HEALTH_DATA_HISTORY) ==
+ HealthConnectFeatures.FEATURE_STATUS_AVAILABLE)
+ }
+ }
+
+ /**
+ * Checks if PERMISSION_READ_HEALTH_DATA_HISTORY has been granted
+ */
+ private fun isHealthDataHistoryAuthorized(call: MethodCall, result: Result) {
+ scope.launch {
+ result.success(
+ healthConnectClient
+ .permissionController
+ .getGrantedPermissions()
+ .containsAll(listOf(PERMISSION_READ_HEALTH_DATA_HISTORY)),
+ )
+ }
+ }
+
+ /**
+ * Requests authorization for PERMISSION_READ_HEALTH_DATA_HISTORY
+ */
+ private fun requestHealthDataHistoryAuthorization(call: MethodCall, result: Result) {
+ if (context == null) {
+ result.success(false)
+ return
+ }
+
+ if (healthConnectRequestPermissionsLauncher == null) {
+ result.success(false)
+ Log.i("FLUTTER_HEALTH", "Permission launcher not found")
+ return
+ }
+
+ // Store the result to be called in [onHealthConnectPermissionCallback]
+ mResult = result
+ isReplySubmitted = false
+ healthConnectRequestPermissionsLauncher!!.launch(setOf(PERMISSION_READ_HEALTH_DATA_HISTORY))
+ }
+
private fun hasPermissions(call: MethodCall, result: Result) {
val args = call.arguments as HashMap<*, *>
val types = (args["types"] as? ArrayList<*>)?.filterIsInstance()!!
@@ -2340,6 +2396,43 @@ class HealthPlugin(private var channel: MethodChannel? = null) :
}
}
+ /** Delete a specific record by UUID and type */
+ private fun deleteByUUID(call: MethodCall, result: Result) {
+ val arguments = call.arguments as? HashMap<*, *>
+ val dataTypeKey = (arguments?.get("dataTypeKey") as? String)!!
+ val uuid = (arguments?.get("uuid") as? String)!!
+
+ if (!mapToType.containsKey(dataTypeKey)) {
+ Log.w("FLUTTER_HEALTH::ERROR", "Datatype $dataTypeKey not found in HC")
+ result.success(false)
+ return
+ }
+
+ val classType = mapToType[dataTypeKey]!!
+
+ scope.launch {
+ try {
+ healthConnectClient.deleteRecords(
+ recordType = classType,
+ recordIdsList = listOf(uuid),
+ clientRecordIdsList = emptyList()
+ )
+ result.success(true)
+ Log.i(
+ "FLUTTER_HEALTH::SUCCESS",
+ "[Health Connect] Record with UUID $uuid was successfully deleted!"
+ )
+ } catch (e: Exception) {
+ Log.e("FLUTTER_HEALTH::ERROR", "Error deleting record with UUID: $uuid")
+ Log.e("FLUTTER_HEALTH::ERROR", e.message ?: "unknown error")
+ Log.e("FLUTTER_HEALTH::ERROR", e.stackTraceToString())
+ result.success(false)
+ }
+ }
+ }
+
+
+
private val mapSleepStageToType =
hashMapOf(
0 to SLEEP_UNKNOWN,
diff --git a/packages/health/example/.gitignore b/packages/health/example/.gitignore
new file mode 100644
index 000000000..79c113f9b
--- /dev/null
+++ b/packages/health/example/.gitignore
@@ -0,0 +1,45 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.build/
+.buildlog/
+.history
+.svn/
+.swiftpm/
+migrate_working_dir/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+**/doc/api/
+**/ios/Flutter/.last_build_id
+.dart_tool/
+.flutter-plugins
+.flutter-plugins-dependencies
+.pub-cache/
+.pub/
+/build/
+
+# Symbolication related
+app.*.symbols
+
+# Obfuscation related
+app.*.map.json
+
+# Android Studio will place build artifacts here
+/android/app/debug
+/android/app/profile
+/android/app/release
diff --git a/packages/health/example/.metadata b/packages/health/example/.metadata
index 959f67f99..9a674c613 100644
--- a/packages/health/example/.metadata
+++ b/packages/health/example/.metadata
@@ -4,7 +4,42 @@
# This file should be version controlled and should not be manually edited.
version:
- revision: cc949a8e8b9cf394b9290a8e80f87af3e207dce5
- channel: beta
+ revision: "35c388afb57ef061d06a39b537336c87e0e3d1b1"
+ channel: "stable"
project_type: app
+
+# Tracks metadata for the flutter migrate command
+migration:
+ platforms:
+ - platform: root
+ create_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1
+ base_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1
+ - platform: android
+ create_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1
+ base_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1
+ - platform: ios
+ create_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1
+ base_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1
+ - platform: linux
+ create_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1
+ base_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1
+ - platform: macos
+ create_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1
+ base_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1
+ - platform: web
+ create_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1
+ base_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1
+ - platform: windows
+ create_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1
+ base_revision: 35c388afb57ef061d06a39b537336c87e0e3d1b1
+
+ # User provided section
+
+ # List of Local paths (relative to this file) that should be
+ # ignored by the migrate tool.
+ #
+ # Files that are not part of the templates will be ignored by default.
+ unmanaged_files:
+ - 'lib/main.dart'
+ - 'ios/Runner.xcodeproj/project.pbxproj'
diff --git a/packages/health/example/analysis_options.yaml b/packages/health/example/analysis_options.yaml
new file mode 100644
index 000000000..0d2902135
--- /dev/null
+++ b/packages/health/example/analysis_options.yaml
@@ -0,0 +1,28 @@
+# This file configures the analyzer, which statically analyzes Dart code to
+# check for errors, warnings, and lints.
+#
+# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
+# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
+# invoked from the command line by running `flutter analyze`.
+
+# The following line activates a set of recommended lints for Flutter apps,
+# packages, and plugins designed to encourage good coding practices.
+include: package:flutter_lints/flutter.yaml
+
+linter:
+ # The lint rules applied to this project can be customized in the
+ # section below to disable rules from the `package:flutter_lints/flutter.yaml`
+ # included above or to enable additional rules. A list of all available lints
+ # and their documentation is published at https://dart.dev/lints.
+ #
+ # Instead of disabling a lint rule for the entire project in the
+ # section below, it can also be suppressed for a single line of code
+ # or a specific dart file by using the `// ignore: name_of_lint` and
+ # `// ignore_for_file: name_of_lint` syntax on the line or in the file
+ # producing the lint.
+ rules:
+ # avoid_print: false # Uncomment to disable the `avoid_print` rule
+ # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
+
+# Additional information about this file can be found at
+# https://dart.dev/guides/language/analysis-options
diff --git a/packages/health/example/android/.gitignore b/packages/health/example/android/.gitignore
new file mode 100644
index 000000000..be3943c96
--- /dev/null
+++ b/packages/health/example/android/.gitignore
@@ -0,0 +1,14 @@
+gradle-wrapper.jar
+/.gradle
+/captures/
+/gradlew
+/gradlew.bat
+/local.properties
+GeneratedPluginRegistrant.java
+.cxx/
+
+# Remember to never publicly share your keystore.
+# See https://flutter.dev/to/reference-keystore
+key.properties
+**/*.keystore
+**/*.jks
diff --git a/packages/health/example/android/app/build.gradle b/packages/health/example/android/app/build.gradle
deleted file mode 100644
index 63e07ba82..000000000
--- a/packages/health/example/android/app/build.gradle
+++ /dev/null
@@ -1,69 +0,0 @@
-def localProperties = new Properties()
-def localPropertiesFile = rootProject.file('local.properties')
-if (localPropertiesFile.exists()) {
- localPropertiesFile.withReader('UTF-8') { reader ->
- localProperties.load(reader)
- }
-}
-
-def flutterRoot = localProperties.getProperty('flutter.sdk')
-if (flutterRoot == null) {
- throw new FileNotFoundException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
-}
-
-def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
-if (flutterVersionCode == null) {
- flutterVersionCode = '1'
-}
-
-def flutterVersionName = localProperties.getProperty('flutter.versionName')
-if (flutterVersionName == null) {
- flutterVersionName = '1.0'
-}
-
-apply plugin: 'com.android.application'
-apply plugin: 'kotlin-android'
-apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
-
-android {
- compileSdkVersion 34
-
- sourceSets {
- main.java.srcDirs += 'src/main/kotlin'
- }
-
-
- defaultConfig {
- // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
- applicationId "cachet.plugins.example_app"
- minSdkVersion 26
- targetSdkVersion 34
- versionCode flutterVersionCode.toInteger()
- versionName flutterVersionName
- testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
- }
-
- buildTypes {
- release {
- // TODO: Add your own signing config for the release build.
- // Signing with the debug keys for now, so `flutter run --release` works.
- signingConfig signingConfigs.debug
- }
- }
- lint {
- disable 'InvalidPackage'
- }
-}
-
-flutter {
- source '../..'
-}
-
-dependencies {
- def composeBom = platform('androidx.compose:compose-bom:2022.10.00')
- implementation(composeBom)
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
- testImplementation 'junit:junit:4.12'
- androidTestImplementation 'androidx.test:runner:1.1.1'
- androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
-}
diff --git a/packages/health/example/android/app/build.gradle.kts b/packages/health/example/android/app/build.gradle.kts
new file mode 100644
index 000000000..b5cd45fa6
--- /dev/null
+++ b/packages/health/example/android/app/build.gradle.kts
@@ -0,0 +1,44 @@
+plugins {
+ id("com.android.application")
+ id("kotlin-android")
+ // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
+ id("dev.flutter.flutter-gradle-plugin")
+}
+
+android {
+ namespace = "cachet.plugins.health.health_example"
+ compileSdk = flutter.compileSdkVersion
+ // ndkVersion = flutter.ndkVersion
+
+ compileOptions {
+ sourceCompatibility = JavaVersion.VERSION_11
+ targetCompatibility = JavaVersion.VERSION_11
+ }
+
+ kotlinOptions {
+ jvmTarget = JavaVersion.VERSION_11.toString()
+ }
+
+ defaultConfig {
+ // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
+ applicationId = "cachet.plugins.health.health_example"
+ // You can update the following values to match your application needs.
+ // For more information, see: https://flutter.dev/to/review-gradle-config.
+ minSdk = 26
+ targetSdk = flutter.targetSdkVersion
+ versionCode = flutter.versionCode
+ versionName = flutter.versionName
+ }
+
+ buildTypes {
+ release {
+ // TODO: Add your own signing config for the release build.
+ // Signing with the debug keys for now, so `flutter run --release` works.
+ signingConfig = signingConfigs.getByName("debug")
+ }
+ }
+}
+
+flutter {
+ source = "../.."
+}
diff --git a/packages/health/example/android/app/src/debug/AndroidManifest.xml b/packages/health/example/android/app/src/debug/AndroidManifest.xml
index b4e67ed6e..399f6981d 100644
--- a/packages/health/example/android/app/src/debug/AndroidManifest.xml
+++ b/packages/health/example/android/app/src/debug/AndroidManifest.xml
@@ -1,6 +1,6 @@
-
-
diff --git a/packages/health/example/android/app/src/main/AndroidManifest.xml b/packages/health/example/android/app/src/main/AndroidManifest.xml
index 996adaf97..1b559506e 100644
--- a/packages/health/example/android/app/src/main/AndroidManifest.xml
+++ b/packages/health/example/android/app/src/main/AndroidManifest.xml
@@ -1,19 +1,5 @@
-
+
-
-
-
-
-
-
-
-
-
@@ -68,18 +54,27 @@
-
+
+
+
-
-
+
@@ -100,7 +95,25 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/health/example/android/app/src/main/kotlin/cachet/plugins/example_app/MainActivity.kt b/packages/health/example/android/app/src/main/kotlin/cachet/plugins/health/health_example/MainActivity.kt
similarity index 75%
rename from packages/health/example/android/app/src/main/kotlin/cachet/plugins/example_app/MainActivity.kt
rename to packages/health/example/android/app/src/main/kotlin/cachet/plugins/health/health_example/MainActivity.kt
index 720eb0f47..4e511d0a4 100644
--- a/packages/health/example/android/app/src/main/kotlin/cachet/plugins/example_app/MainActivity.kt
+++ b/packages/health/example/android/app/src/main/kotlin/cachet/plugins/health/health_example/MainActivity.kt
@@ -1,4 +1,4 @@
-package cachet.plugins.example_app
+package cachet.plugins.health.health_example
import android.os.Bundle
diff --git a/packages/health/example/android/app/src/main/res/drawable-v21/launch_background.xml b/packages/health/example/android/app/src/main/res/drawable-v21/launch_background.xml
new file mode 100644
index 000000000..f74085f3f
--- /dev/null
+++ b/packages/health/example/android/app/src/main/res/drawable-v21/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/packages/health/example/android/app/src/main/res/values-night/styles.xml b/packages/health/example/android/app/src/main/res/values-night/styles.xml
new file mode 100644
index 000000000..06952be74
--- /dev/null
+++ b/packages/health/example/android/app/src/main/res/values-night/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/packages/health/example/android/app/src/main/res/values/styles.xml b/packages/health/example/android/app/src/main/res/values/styles.xml
index d460d1e92..cb1ef8805 100644
--- a/packages/health/example/android/app/src/main/res/values/styles.xml
+++ b/packages/health/example/android/app/src/main/res/values/styles.xml
@@ -3,7 +3,7 @@
diff --git a/packages/health/example/android/build.gradle b/packages/health/example/android/build.gradle
deleted file mode 100644
index e306d8ebf..000000000
--- a/packages/health/example/android/build.gradle
+++ /dev/null
@@ -1,31 +0,0 @@
-buildscript {
- ext.kotlin_version = '1.8.0'
- repositories {
- google()
- mavenCentral()
- }
-
- dependencies {
- classpath 'com.android.tools.build:gradle:7.4.0'
- classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
- }
-}
-
-allprojects {
- repositories {
- google()
- mavenCentral()
- }
-}
-
-rootProject.buildDir = '../build'
-subprojects {
- project.buildDir = "${rootProject.buildDir}/${project.name}"
-}
-subprojects {
- project.evaluationDependsOn(':app')
-}
-
-tasks.register("clean", Delete) {
- delete rootProject.buildDir
-}
diff --git a/packages/health/example/android/build.gradle.kts b/packages/health/example/android/build.gradle.kts
new file mode 100644
index 000000000..89176ef44
--- /dev/null
+++ b/packages/health/example/android/build.gradle.kts
@@ -0,0 +1,21 @@
+allprojects {
+ repositories {
+ google()
+ mavenCentral()
+ }
+}
+
+val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get()
+rootProject.layout.buildDirectory.value(newBuildDir)
+
+subprojects {
+ val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
+ project.layout.buildDirectory.value(newSubprojectBuildDir)
+}
+subprojects {
+ project.evaluationDependsOn(":app")
+}
+
+tasks.register("clean") {
+ delete(rootProject.layout.buildDirectory)
+}
diff --git a/packages/health/example/android/gradle.properties b/packages/health/example/android/gradle.properties
index d2032bce8..f018a6181 100644
--- a/packages/health/example/android/gradle.properties
+++ b/packages/health/example/android/gradle.properties
@@ -1,4 +1,3 @@
-org.gradle.jvmargs=-Xmx1536M
-android.enableJetifier=true
+org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
-android.enableR8=true
+android.enableJetifier=true
diff --git a/packages/health/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/health/example/android/gradle/wrapper/gradle-wrapper.properties
index 8049c684f..afa1e8eb0 100644
--- a/packages/health/example/android/gradle/wrapper/gradle-wrapper.properties
+++ b/packages/health/example/android/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
diff --git a/packages/health/example/android/settings.gradle b/packages/health/example/android/settings.gradle
deleted file mode 100644
index 5a2f14fb1..000000000
--- a/packages/health/example/android/settings.gradle
+++ /dev/null
@@ -1,15 +0,0 @@
-include ':app'
-
-def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
-
-def plugins = new Properties()
-def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
-if (pluginsFile.exists()) {
- pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
-}
-
-plugins.each { name, path ->
- def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
- include ":$name"
- project(":$name").projectDir = pluginDirectory
-}
diff --git a/packages/health/example/android/settings.gradle.kts b/packages/health/example/android/settings.gradle.kts
new file mode 100644
index 000000000..a439442c2
--- /dev/null
+++ b/packages/health/example/android/settings.gradle.kts
@@ -0,0 +1,25 @@
+pluginManagement {
+ val flutterSdkPath = run {
+ val properties = java.util.Properties()
+ file("local.properties").inputStream().use { properties.load(it) }
+ val flutterSdkPath = properties.getProperty("flutter.sdk")
+ require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
+ flutterSdkPath
+ }
+
+ includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
+
+ repositories {
+ google()
+ mavenCentral()
+ gradlePluginPortal()
+ }
+}
+
+plugins {
+ id("dev.flutter.flutter-plugin-loader") version "1.0.0"
+ id("com.android.application") version "8.7.0" apply false
+ id("org.jetbrains.kotlin.android") version "1.8.22" apply false
+}
+
+include(":app")
diff --git a/packages/health/example/ios/.gitignore b/packages/health/example/ios/.gitignore
new file mode 100644
index 000000000..7a7f9873a
--- /dev/null
+++ b/packages/health/example/ios/.gitignore
@@ -0,0 +1,34 @@
+**/dgph
+*.mode1v3
+*.mode2v3
+*.moved-aside
+*.pbxuser
+*.perspectivev3
+**/*sync/
+.sconsign.dblite
+.tags*
+**/.vagrant/
+**/DerivedData/
+Icon?
+**/Pods/
+**/.symlinks/
+profile
+xcuserdata
+**/.generated/
+Flutter/App.framework
+Flutter/Flutter.framework
+Flutter/Flutter.podspec
+Flutter/Generated.xcconfig
+Flutter/ephemeral/
+Flutter/app.flx
+Flutter/app.zip
+Flutter/flutter_assets/
+Flutter/flutter_export_environment.sh
+ServiceDefinitions.json
+Runner/GeneratedPluginRegistrant.*
+
+# Exceptions to above rules.
+!default.mode1v3
+!default.mode2v3
+!default.pbxuser
+!default.perspectivev3
diff --git a/packages/health/example/ios/Flutter/AppFrameworkInfo.plist b/packages/health/example/ios/Flutter/AppFrameworkInfo.plist
index 8c6e56146..7c5696400 100644
--- a/packages/health/example/ios/Flutter/AppFrameworkInfo.plist
+++ b/packages/health/example/ios/Flutter/AppFrameworkInfo.plist
@@ -3,7 +3,7 @@
CFBundleDevelopmentRegion
- $(DEVELOPMENT_LANGUAGE)
+ en
CFBundleExecutable
App
CFBundleIdentifier
diff --git a/packages/health/example/ios/Flutter/Debug.xcconfig b/packages/health/example/ios/Flutter/Debug.xcconfig
index e8efba114..ec97fc6f3 100644
--- a/packages/health/example/ios/Flutter/Debug.xcconfig
+++ b/packages/health/example/ios/Flutter/Debug.xcconfig
@@ -1,2 +1,2 @@
-#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"
diff --git a/packages/health/example/ios/Flutter/Release.xcconfig b/packages/health/example/ios/Flutter/Release.xcconfig
index 399e9340e..c4855bfe2 100644
--- a/packages/health/example/ios/Flutter/Release.xcconfig
+++ b/packages/health/example/ios/Flutter/Release.xcconfig
@@ -1,2 +1,2 @@
-#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"
diff --git a/packages/health/example/ios/Podfile b/packages/health/example/ios/Podfile
index 0b62d79ed..c2c1dc543 100644
--- a/packages/health/example/ios/Podfile
+++ b/packages/health/example/ios/Podfile
@@ -29,9 +29,11 @@ flutter_ios_podfile_setup
target 'Runner' do
use_frameworks!
- use_modular_headers!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
+ target 'RunnerTests' do
+ inherit! :search_paths
+ end
end
post_install do |installer|
diff --git a/packages/health/example/ios/Runner.xcodeproj/project.pbxproj b/packages/health/example/ios/Runner.xcodeproj/project.pbxproj
index 77f2ffb80..f9034579c 100644
--- a/packages/health/example/ios/Runner.xcodeproj/project.pbxproj
+++ b/packages/health/example/ios/Runner.xcodeproj/project.pbxproj
@@ -7,16 +7,29 @@
objects = {
/* Begin PBXBuildFile section */
+ 0E73C03C9CE12BC96B79C15D /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 331C3466792E0AE145779DB5 /* Pods_Runner.framework */; };
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
+ 1538537FCF3E6B2D5EE731AB /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7CF05C63DD5841073CB4E39B /* Pods_RunnerTests.framework */; };
+ 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
- 5FC8384EA5BC0E70B579910E /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 87EA13F4837A2485245BF0FD /* Pods_Runner.framework */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
- 82F0E6512375BDAE0022096E /* HealthKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 82F0E6502375BDAE0022096E /* HealthKit.framework */; };
+ 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
+ ABB05D872D6BB16700FA4740 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = ABB05D862D6BB16700FA4740 /* FlutterGeneratedPluginSwiftPackage */; };
/* End PBXBuildFile section */
+/* Begin PBXContainerItemProxy section */
+ 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 97C146E61CF9000F007C117D /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 97C146ED1CF9000F007C117D;
+ remoteInfo = Runner;
+ };
+/* End PBXContainerItemProxy section */
+
/* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
@@ -31,18 +44,20 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
+ 080FC4BE7B2926E42EFB9EF5 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = ""; };
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
- 1C793EC91E7B6FC11F0E5E44 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; };
+ 331C3466792E0AE145779DB5 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; };
+ 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+ 3A090B140475A389FCD6B6D8 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = ""; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
- 3C038B01E9A5A754A9CA72B6 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; };
- 4F5A1AFCB405AB82BD233633 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; };
+ 53E0CC498F9E1686FE7B71EC /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = ""; };
+ 66EFA595EFC367CCF62B5486 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
- 82F0E64E2375BDAE0022096E /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; };
- 82F0E6502375BDAE0022096E /* HealthKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = HealthKit.framework; path = System/Library/Frameworks/HealthKit.framework; sourceTree = SDKROOT; };
- 87EA13F4837A2485245BF0FD /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
+ 7CF05C63DD5841073CB4E39B /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -50,31 +65,50 @@
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
- D6912A28277CD12C0012EA21 /* RunnerDebug.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = RunnerDebug.entitlements; sourceTree = ""; };
+ ABEE568C2D6B9E4300551983 /* Runner.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Runner.entitlements; sourceTree = ""; };
+ F97011D03CBC7A35D177D127 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; };
+ FCEA0A19EEB97E6889BA5240 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
+ 5F1F7F882329D7A1B0D9F8A5 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 1538537FCF3E6B2D5EE731AB /* Pods_RunnerTests.framework in Frameworks */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
- 82F0E6512375BDAE0022096E /* HealthKit.framework in Frameworks */,
- 5FC8384EA5BC0E70B579910E /* Pods_Runner.framework in Frameworks */,
+ 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */,
+ ABB05D872D6BB16700FA4740 /* FlutterGeneratedPluginSwiftPackage in Frameworks */,
+ 0E73C03C9CE12BC96B79C15D /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
- 82F0E64F2375BDAE0022096E /* Frameworks */ = {
+ 0FF3A715528E9DF113CAF119 /* Frameworks */ = {
isa = PBXGroup;
children = (
- 82F0E6502375BDAE0022096E /* HealthKit.framework */,
- 87EA13F4837A2485245BF0FD /* Pods_Runner.framework */,
+ 331C3466792E0AE145779DB5 /* Pods_Runner.framework */,
+ 7CF05C63DD5841073CB4E39B /* Pods_RunnerTests.framework */,
);
name = Frameworks;
sourceTree = "";
};
+ 331C8082294A63A400263BE5 /* RunnerTests */ = {
+ isa = PBXGroup;
+ children = (
+ 331C807B294A618700263BE5 /* RunnerTests.swift */,
+ );
+ path = RunnerTests;
+ sourceTree = "";
+ };
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
@@ -92,8 +126,9 @@
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
- 82F0E64F2375BDAE0022096E /* Frameworks */,
- 9AAFFBA17DFD34F5375C5D92 /* Pods */,
+ 331C8082294A63A400263BE5 /* RunnerTests */,
+ F5F8E5994A43423CF6FC3663 /* Pods */,
+ 0FF3A715528E9DF113CAF119 /* Frameworks */,
);
sourceTree = "";
};
@@ -101,6 +136,7 @@
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
+ 331C8081294A63A400263BE5 /* RunnerTests.xctest */,
);
name = Products;
sourceTree = "";
@@ -108,13 +144,11 @@
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
- D6912A28277CD12C0012EA21 /* RunnerDebug.entitlements */,
- 82F0E64E2375BDAE0022096E /* Runner.entitlements */,
+ ABEE568C2D6B9E4300551983 /* Runner.entitlements */,
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
- 97C146F11CF9000F007C117D /* Supporting Files */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
@@ -123,19 +157,15 @@
path = Runner;
sourceTree = "";
};
- 97C146F11CF9000F007C117D /* Supporting Files */ = {
+ F5F8E5994A43423CF6FC3663 /* Pods */ = {
isa = PBXGroup;
children = (
- );
- name = "Supporting Files";
- sourceTree = "";
- };
- 9AAFFBA17DFD34F5375C5D92 /* Pods */ = {
- isa = PBXGroup;
- children = (
- 1C793EC91E7B6FC11F0E5E44 /* Pods-Runner.debug.xcconfig */,
- 3C038B01E9A5A754A9CA72B6 /* Pods-Runner.release.xcconfig */,
- 4F5A1AFCB405AB82BD233633 /* Pods-Runner.profile.xcconfig */,
+ F97011D03CBC7A35D177D127 /* Pods-Runner.debug.xcconfig */,
+ FCEA0A19EEB97E6889BA5240 /* Pods-Runner.release.xcconfig */,
+ 66EFA595EFC367CCF62B5486 /* Pods-Runner.profile.xcconfig */,
+ 53E0CC498F9E1686FE7B71EC /* Pods-RunnerTests.debug.xcconfig */,
+ 080FC4BE7B2926E42EFB9EF5 /* Pods-RunnerTests.release.xcconfig */,
+ 3A090B140475A389FCD6B6D8 /* Pods-RunnerTests.profile.xcconfig */,
);
path = Pods;
sourceTree = "";
@@ -143,25 +173,48 @@
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
+ 331C8080294A63A400263BE5 /* RunnerTests */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
+ buildPhases = (
+ F39DD7443B254A13543A9E9D /* [CP] Check Pods Manifest.lock */,
+ 331C807D294A63A400263BE5 /* Sources */,
+ 331C807F294A63A400263BE5 /* Resources */,
+ 5F1F7F882329D7A1B0D9F8A5 /* Frameworks */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 331C8086294A63A400263BE5 /* PBXTargetDependency */,
+ );
+ name = RunnerTests;
+ productName = RunnerTests;
+ productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */;
+ productType = "com.apple.product-type.bundle.unit-test";
+ };
97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
- AFF7CCF5217A091E1625CD54 /* [CP] Check Pods Manifest.lock */,
+ 53B344462328A747F09FF208 /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
- 8AB8966E9F27B6C816D51EA9 /* [CP] Embed Pods Frameworks */,
- 99FD4B47838A33EC942FDC35 /* [CP] Copy Pods Resources */,
+ 6FEBDBC7D4FF675303904EA3 /* [CP] Embed Pods Frameworks */,
+ BC3ECE390F66D6EA0F9FC9C4 /* [CP] Copy Pods Resources */,
);
buildRules = (
);
dependencies = (
);
name = Runner;
+ packageProductDependencies = (
+ 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */,
+ ABB05D862D6BB16700FA4740 /* FlutterGeneratedPluginSwiftPackage */,
+ );
productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application";
@@ -172,24 +225,22 @@
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
+ BuildIndependentTargetsInParallel = YES;
LastUpgradeCheck = 1510;
- ORGANIZATIONNAME = "The Chromium Authors";
+ ORGANIZATIONNAME = "";
TargetAttributes = {
+ 331C8080294A63A400263BE5 = {
+ CreatedOnToolsVersion = 14.0;
+ TestTargetID = 97C146ED1CF9000F007C117D;
+ };
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
- DevelopmentTeam = 59TCTNUBMQ;
- LastSwiftMigration = 0910;
- ProvisioningStyle = Automatic;
- SystemCapabilities = {
- com.apple.HealthKit = {
- enabled = 1;
- };
- };
+ LastSwiftMigration = 1100;
};
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
- compatibilityVersion = "Xcode 3.2";
+ compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
@@ -197,16 +248,27 @@
Base,
);
mainGroup = 97C146E51CF9000F007C117D;
+ packageReferences = (
+ ABB05D852D6BB16700FA4740 /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */,
+ );
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
97C146ED1CF9000F007C117D /* Runner */,
+ 331C8080294A63A400263BE5 /* RunnerTests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
+ 331C807F294A63A400263BE5 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@@ -237,20 +299,39 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
- 8AB8966E9F27B6C816D51EA9 /* [CP] Embed Pods Frameworks */ = {
+ 53B344462328A747F09FF208 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
+ inputFileListPaths = (
+ );
inputPaths = (
- "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh",
- "${BUILT_PRODUCTS_DIR}/device_info_plus/device_info_plus.framework",
- "${BUILT_PRODUCTS_DIR}/health/health.framework",
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
);
- name = "[CP] Embed Pods Frameworks";
outputPaths = (
- "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/device_info_plus.framework",
- "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/health.framework",
+ "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+ showEnvVarsInLog = 0;
+ };
+ 6FEBDBC7D4FF675303904EA3 /* [CP] Embed Pods Frameworks */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
+ );
+ name = "[CP] Embed Pods Frameworks";
+ outputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
@@ -272,25 +353,24 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
- 99FD4B47838A33EC942FDC35 /* [CP] Copy Pods Resources */ = {
+ BC3ECE390F66D6EA0F9FC9C4 /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
- inputPaths = (
- "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh",
- "${PODS_CONFIGURATION_BUILD_DIR}/permission_handler_apple/permission_handler_apple_privacy.bundle",
+ inputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
- outputPaths = (
- "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/permission_handler_apple_privacy.bundle",
+ outputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
- AFF7CCF5217A091E1625CD54 /* [CP] Check Pods Manifest.lock */ = {
+ F39DD7443B254A13543A9E9D /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
@@ -305,34 +385,24 @@
outputFileListPaths = (
);
outputPaths = (
- "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
+ "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
- FEE6262278064D703A8D8D21 /* [CP] Copy Pods Resources */ = {
- isa = PBXShellScriptBuildPhase;
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 331C807D294A63A400263BE5 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- );
- inputPaths = (
- "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh",
- "${PODS_CONFIGURATION_BUILD_DIR}/permission_handler_apple/permission_handler_apple_privacy.bundle",
- );
- name = "[CP] Copy Pods Resources";
- outputPaths = (
- "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/permission_handler_apple_privacy.bundle",
+ 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
- shellPath = /bin/sh;
- shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
- showEnvVarsInLog = 0;
};
-/* End PBXShellScriptBuildPhase section */
-
-/* Begin PBXSourcesBuildPhase section */
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@@ -344,6 +414,14 @@
};
/* End PBXSourcesBuildPhase section */
+/* Begin PBXTargetDependency section */
+ 331C8086294A63A400263BE5 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 97C146ED1CF9000F007C117D /* Runner */;
+ targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
@@ -368,6 +446,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
@@ -397,6 +476,7 @@
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@@ -405,9 +485,10 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
+ SUPPORTED_PLATFORMS = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
@@ -420,38 +501,77 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
- CODE_SIGN_IDENTITY = "Apple Development";
- CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 59TCTNUBMQ;
ENABLE_BITCODE = NO;
- FRAMEWORK_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- );
INFOPLIST_FILE = Runner/Info.plist;
- IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
- LIBRARY_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- );
- PRODUCT_BUNDLE_IDENTIFIER = dk.cachet.example;
+ PRODUCT_BUNDLE_IDENTIFIER = cachet.plugins.health.example;
PRODUCT_NAME = "$(TARGET_NAME)";
- PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
- SWIFT_VERSION = 4.0;
+ SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Profile;
};
+ 331C8088294A63A400263BE5 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 53E0CC498F9E1686FE7B71EC /* Pods-RunnerTests.debug.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ GENERATE_INFOPLIST_FILE = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = cachet.plugins.health.healthExample.RunnerTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
+ };
+ name = Debug;
+ };
+ 331C8089294A63A400263BE5 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 080FC4BE7B2926E42EFB9EF5 /* Pods-RunnerTests.release.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ GENERATE_INFOPLIST_FILE = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = cachet.plugins.health.healthExample.RunnerTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 5.0;
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
+ };
+ name = Release;
+ };
+ 331C808A294A63A400263BE5 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 3A090B140475A389FCD6B6D8 /* Pods-RunnerTests.profile.xcconfig */;
+ buildSettings = {
+ BUNDLE_LOADER = "$(TEST_HOST)";
+ CODE_SIGN_STYLE = Automatic;
+ CURRENT_PROJECT_VERSION = 1;
+ GENERATE_INFOPLIST_FILE = YES;
+ MARKETING_VERSION = 1.0;
+ PRODUCT_BUNDLE_IDENTIFIER = cachet.plugins.health.healthExample.RunnerTests;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_VERSION = 5.0;
+ TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
+ };
+ name = Profile;
+ };
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
@@ -481,6 +601,7 @@
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
@@ -495,7 +616,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@@ -507,6 +628,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
@@ -536,6 +658,7 @@
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
@@ -544,10 +667,12 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 12.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
- SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
+ SUPPORTED_PLATFORMS = iphoneos;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
@@ -559,32 +684,20 @@
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
- CODE_SIGN_ENTITLEMENTS = Runner/RunnerDebug.entitlements;
- CODE_SIGN_IDENTITY = "Apple Development";
- CODE_SIGN_STYLE = Automatic;
+ CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 59TCTNUBMQ;
ENABLE_BITCODE = NO;
- FRAMEWORK_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- );
INFOPLIST_FILE = Runner/Info.plist;
- IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
- LIBRARY_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- );
- PRODUCT_BUNDLE_IDENTIFIER = dk.cachet.example;
+ PRODUCT_BUNDLE_IDENTIFIER = cachet.plugins.health.example;
PRODUCT_NAME = "$(TARGET_NAME)";
- PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
- SWIFT_VERSION = 4.0;
+ SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
@@ -596,30 +709,18 @@
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_ENTITLEMENTS = Runner/Runner.entitlements;
- CODE_SIGN_IDENTITY = "Apple Development";
- CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = 59TCTNUBMQ;
ENABLE_BITCODE = NO;
- FRAMEWORK_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- );
INFOPLIST_FILE = Runner/Info.plist;
- IPHONEOS_DEPLOYMENT_TARGET = 13.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
- LIBRARY_SEARCH_PATHS = (
- "$(inherited)",
- "$(PROJECT_DIR)/Flutter",
- );
- PRODUCT_BUNDLE_IDENTIFIER = dk.cachet.example;
+ PRODUCT_BUNDLE_IDENTIFIER = cachet.plugins.health.example;
PRODUCT_NAME = "$(TARGET_NAME)";
- PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
- SWIFT_VERSION = 4.0;
+ SWIFT_VERSION = 5.0;
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
@@ -627,6 +728,16 @@
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
+ 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 331C8088294A63A400263BE5 /* Debug */,
+ 331C8089294A63A400263BE5 /* Release */,
+ 331C808A294A63A400263BE5 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
@@ -648,6 +759,24 @@
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
+
+/* Begin XCLocalSwiftPackageReference section */
+ ABB05D852D6BB16700FA4740 /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = {
+ isa = XCLocalSwiftPackageReference;
+ relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage;
+ };
+/* End XCLocalSwiftPackageReference section */
+
+/* Begin XCSwiftPackageProductDependency section */
+ 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = {
+ isa = XCSwiftPackageProductDependency;
+ productName = FlutterGeneratedPluginSwiftPackage;
+ };
+ ABB05D862D6BB16700FA4740 /* FlutterGeneratedPluginSwiftPackage */ = {
+ isa = XCSwiftPackageProductDependency;
+ productName = FlutterGeneratedPluginSwiftPackage;
+ };
+/* End XCSwiftPackageProductDependency section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}
diff --git a/packages/health/example/ios/Runner/RunnerDebug.entitlements b/packages/health/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
similarity index 63%
rename from packages/health/example/ios/Runner/RunnerDebug.entitlements
rename to packages/health/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
index 2ab14a262..f9b0d7c5e 100644
--- a/packages/health/example/ios/Runner/RunnerDebug.entitlements
+++ b/packages/health/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -2,9 +2,7 @@
- com.apple.developer.healthkit
-
- com.apple.developer.healthkit.access
-
+ PreviewsEnabled
+
diff --git a/packages/health/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/health/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
index 5e31d3d34..b76580770 100644
--- a/packages/health/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
+++ b/packages/health/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -1,10 +1,28 @@
+ version = "1.7">
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/health/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/health/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 000000000..f9b0d7c5e
--- /dev/null
+++ b/packages/health/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+
+
+
+
+ PreviewsEnabled
+
+
+
diff --git a/packages/health/example/ios/Runner/AppDelegate.swift b/packages/health/example/ios/Runner/AppDelegate.swift
index b63630348..626664468 100644
--- a/packages/health/example/ios/Runner/AppDelegate.swift
+++ b/packages/health/example/ios/Runner/AppDelegate.swift
@@ -1,5 +1,5 @@
-import UIKit
import Flutter
+import UIKit
@main
@objc class AppDelegate: FlutterAppDelegate {
diff --git a/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
index 28c6bf030..7353c41ec 100644
Binary files a/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png and b/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ
diff --git a/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
index 2ccbfd967..797d452e4 100644
Binary files a/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png and b/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ
diff --git a/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
index f091b6b0b..6ed2d933e 100644
Binary files a/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png and b/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ
diff --git a/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
index 4cde12118..4cd7b0099 100644
Binary files a/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png and b/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ
diff --git a/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
index d0ef06e7e..fe730945a 100644
Binary files a/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png and b/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ
diff --git a/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
index dcdc2306c..321773cd8 100644
Binary files a/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png and b/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ
diff --git a/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
index 2ccbfd967..797d452e4 100644
Binary files a/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png and b/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ
diff --git a/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
index c8f9ed8f5..502f463a9 100644
Binary files a/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png and b/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ
diff --git a/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
index a6d6b8609..0ec303439 100644
Binary files a/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png and b/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ
diff --git a/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
index a6d6b8609..0ec303439 100644
Binary files a/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png and b/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ
diff --git a/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
index 75b2d164a..e9f5fea27 100644
Binary files a/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png and b/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ
diff --git a/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
index c4df70d39..84ac32ae7 100644
Binary files a/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png and b/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ
diff --git a/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
index 6a84f41e1..8953cba09 100644
Binary files a/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png and b/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ
diff --git a/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
index d0e1f5853..0467bf12a 100644
Binary files a/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png and b/packages/health/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ
diff --git a/packages/health/example/ios/Runner/Info.plist b/packages/health/example/ios/Runner/Info.plist
index b4af43739..59c5529bb 100644
--- a/packages/health/example/ios/Runner/Info.plist
+++ b/packages/health/example/ios/Runner/Info.plist
@@ -6,6 +6,8 @@
CFBundleDevelopmentRegion
$(DEVELOPMENT_LANGUAGE)
+ CFBundleDisplayName
+ Health Example
CFBundleExecutable
$(EXECUTABLE_NAME)
CFBundleIdentifier
@@ -24,10 +26,8 @@
$(FLUTTER_BUILD_NUMBER)
LSRequiresIPhoneOS
- NSHealthShareUsageDescription
- We will sync your data with the Apple Health app to give you better insights
- NSHealthUpdateUsageDescription
- We will sync your data with the Apple Health app to give you better insights
+ UIApplicationSupportsIndirectInputEvents
+
UILaunchStoryboardName
LaunchScreen
UIMainStoryboardFile
@@ -38,6 +38,10 @@
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
+ NSHealthShareUsageDescription
+ We will sync your data with the Apple Health app to give you better insights
+ NSHealthUpdateUsageDescription
+ We will sync your data with the Apple Health app to give you better insights
UISupportedInterfaceOrientations~ipad
UIInterfaceOrientationPortrait
@@ -45,9 +49,5 @@
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
- UIViewControllerBasedStatusBarAppearance
-
- UIApplicationSupportsIndirectInputEvents
-
diff --git a/packages/health/example/ios/Runner/Runner-Bridging-Header.h b/packages/health/example/ios/Runner/Runner-Bridging-Header.h
index 7335fdf90..308a2a560 100644
--- a/packages/health/example/ios/Runner/Runner-Bridging-Header.h
+++ b/packages/health/example/ios/Runner/Runner-Bridging-Header.h
@@ -1 +1 @@
-#import "GeneratedPluginRegistrant.h"
\ No newline at end of file
+#import "GeneratedPluginRegistrant.h"
diff --git a/packages/health/example/ios/Runner/Runner.entitlements b/packages/health/example/ios/Runner/Runner.entitlements
index 2ab14a262..e10f4302d 100644
--- a/packages/health/example/ios/Runner/Runner.entitlements
+++ b/packages/health/example/ios/Runner/Runner.entitlements
@@ -4,7 +4,5 @@
com.apple.developer.healthkit
- com.apple.developer.healthkit.access
-
diff --git a/packages/health/example/ios/RunnerTests/RunnerTests.swift b/packages/health/example/ios/RunnerTests/RunnerTests.swift
new file mode 100644
index 000000000..86a7c3b1b
--- /dev/null
+++ b/packages/health/example/ios/RunnerTests/RunnerTests.swift
@@ -0,0 +1,12 @@
+import Flutter
+import UIKit
+import XCTest
+
+class RunnerTests: XCTestCase {
+
+ func testExample() {
+ // If you add code to the Runner application, consider adding tests here.
+ // See https://developer.apple.com/documentation/xctest for more information about using XCTest.
+ }
+
+}
diff --git a/packages/health/example/lib/main.dart b/packages/health/example/lib/main.dart
index 59c963dac..ccb27e727 100644
--- a/packages/health/example/lib/main.dart
+++ b/packages/health/example/lib/main.dart
@@ -122,6 +122,10 @@ class HealthAppState extends State {
try {
authorized =
await health.requestAuthorization(types, permissions: permissions);
+
+ // request access to read historic data
+ await health.requestHealthDataHistoryAuthorization();
+
} catch (error) {
debugPrint("Exception in authorize: $error");
}
@@ -289,11 +293,10 @@ class HealthAppState extends State {
startTime: earlier,
endTime: now);
success &= await health.writeHealthData(
- value: 22,
- type: HealthDataType.LEAN_BODY_MASS,
- startTime: earlier,
- endTime: now,
- );
+ value: 22,
+ type: HealthDataType.LEAN_BODY_MASS,
+ startTime: earlier,
+ endTime: now);
// specialized write methods
success &= await health.writeBloodOxygen(
@@ -400,6 +403,12 @@ class HealthAppState extends State {
startTime: earlier,
endTime: now,
recordingMethod: RecordingMethod.manual);
+ success &= await health.writeHealthData(
+ value: 4.3,
+ type: HealthDataType.UV_INDEX,
+ startTime: earlier,
+ endTime: now,
+ recordingMethod: RecordingMethod.manual);
}
setState(() {
@@ -421,6 +430,26 @@ class HealthAppState extends State {
);
}
+ // To delete a record by UUID - call the `health.deleteByUUID` method:
+ /**
+ List healthData = await health.getHealthDataFromTypes(
+ types: [HealthDataType.STEPS],
+ startTime: startDate,
+ endTime: endDate,
+ );
+
+ if (healthData.isNotEmpty) {
+ print("DELETING: ${healthData.first.toJson()}");
+ String uuid = healthData.first.uuid;
+
+ success &= await health.deleteByUUID(
+ type: HealthDataType.STEPS,
+ uuid: uuid,
+ );
+
+ }
+ */
+
setState(() {
_state = success ? AppState.DATA_DELETED : AppState.DATA_NOT_DELETED;
});
diff --git a/packages/health/example/lib/util.dart b/packages/health/example/lib/util.dart
index 5c6f70be8..d896c6b02 100644
--- a/packages/health/example/lib/util.dart
+++ b/packages/health/example/lib/util.dart
@@ -62,6 +62,7 @@ const List dataTypesIOS = [
HealthDataType.MENSTRUATION_FLOW,
HealthDataType.WATER_TEMPERATURE,
HealthDataType.UNDERWATER_DEPTH,
+ HealthDataType.UV_INDEX,
];
/// List of data types available on Android.
diff --git a/packages/health/example/pubspec.yaml b/packages/health/example/pubspec.yaml
index 031bcc6c2..19f8b9007 100644
--- a/packages/health/example/pubspec.yaml
+++ b/packages/health/example/pubspec.yaml
@@ -17,6 +17,7 @@ dependencies:
path: ../
dev_dependencies:
+ flutter_lints: any
flutter_test:
sdk: flutter
diff --git a/packages/health/ios/Classes/SwiftHealthPlugin.swift b/packages/health/ios/Classes/SwiftHealthPlugin.swift
index 282ae9ff6..8b4fa1bbf 100644
--- a/packages/health/ios/Classes/SwiftHealthPlugin.swift
+++ b/packages/health/ios/Classes/SwiftHealthPlugin.swift
@@ -164,6 +164,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin {
let MENSTRUATION_FLOW = "MENSTRUATION_FLOW"
let WATER_TEMPERATURE = "WATER_TEMPERATURE"
let UNDERWATER_DEPTH = "UNDERWATER_DEPTH"
+ let UV_INDEX = "UV_INDEX"
// Health Unit types
@@ -300,6 +301,11 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin {
else if call.method.elementsEqual("delete") {
try! delete(call: call, result: result)
}
+
+ /// Handle deleteByUUID data
+ else if call.method.elementsEqual("deleteByUUID") {
+ try! deleteByUUID(call: call, result: result)
+ }
}
func checkIfHealthDataAvailable(call: FlutterMethodCall, result: @escaping FlutterResult) {
@@ -765,6 +771,45 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin {
HKHealthStore().execute(deleteQuery)
}
+
+ func deleteByUUID(call: FlutterMethodCall, result: @escaping FlutterResult) throws {
+ guard let arguments = call.arguments as? NSDictionary,
+ let uuidarg = arguments["uuid"] as? String,
+ let dataTypeKey = arguments["dataTypeKey"] as? String else {
+ throw PluginError(message: "Invalid Arguments - UUID or DataTypeKey invalid")
+ }
+ let dataTypeToRemove = dataTypeLookUp(key: dataTypeKey)
+ guard let uuid = UUID(uuidString: uuidarg) else {
+ result(false)
+ return
+ }
+ let predicate = HKQuery.predicateForObjects(with: [uuid])
+
+ let query = HKSampleQuery(
+ sampleType: dataTypeToRemove,
+ predicate: predicate,
+ limit: 1,
+ sortDescriptors: nil
+ ) { query, samplesOrNil, error in
+ guard let samples = samplesOrNil, !samples.isEmpty else {
+ DispatchQueue.main.async {
+ result(false)
+ }
+ return
+ }
+
+ self.healthStore.delete(samples) { success, error in
+ if let error = error {
+ print("Error deleting sample with UUID \(uuid): \(error.localizedDescription)")
+ }
+ DispatchQueue.main.async {
+ result(success)
+ }
+ }
+ }
+
+ healthStore.execute(query)
+ }
func getData(call: FlutterMethodCall, result: @escaping FlutterResult) {
let arguments = call.arguments as? NSDictionary
@@ -860,8 +905,8 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin {
"recording_method": (sample.metadata?[HKMetadataKeyWasUserEntered] as? Bool == true)
? RecordingMethod.manual.rawValue
: RecordingMethod.automatic.rawValue,
- "metadata": dataTypeKey == INSULIN_DELIVERY ? sample.metadata : nil,
- "dataUnitKey": unit?.unitString
+ "dataUnitKey": unit?.unitString,
+ "metadata": sanitizeMetadata(sample.metadata)
]
}
DispatchQueue.main.async {
@@ -904,14 +949,6 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin {
samplesCategory = samplesCategory.filter { $0.value == 4 }
}
let categories = samplesCategory.map { sample -> NSDictionary in
- var metadata: [String: Any] = [:]
-
- if let sampleMetadata = sample.metadata {
- for (key, value) in sampleMetadata {
- metadata[key] = value
- }
- }
-
return [
"uuid": "\(sample.uuid)",
"value": sample.value,
@@ -920,7 +957,7 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin {
"source_id": sample.sourceRevision.source.bundleIdentifier,
"source_name": sample.sourceRevision.source.name,
"recording_method": (sample.metadata?[HKMetadataKeyWasUserEntered] as? Bool == true) ? RecordingMethod.manual.rawValue : RecordingMethod.automatic.rawValue,
- "metadata": metadata
+ "metadata": sanitizeMetadata(sample.metadata)
]
}
DispatchQueue.main.async {
@@ -1037,6 +1074,54 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin {
HKHealthStore().execute(query)
}
+ private func sanitizeMetadata(_ metadata: [String: Any]?) -> [String: Any] {
+ guard let metadata = metadata else { return [:] }
+
+ var sanitized = [String: Any]()
+
+ for (key, value) in metadata {
+ switch value {
+ case let stringValue as String:
+ sanitized[key] = stringValue
+ case let numberValue as NSNumber:
+ sanitized[key] = numberValue
+ case let boolValue as Bool:
+ sanitized[key] = boolValue
+ case let arrayValue as [Any]:
+ sanitized[key] = sanitizeArray(arrayValue)
+ case let mapValue as [String: Any]:
+ sanitized[key] = sanitizeMetadata(mapValue)
+ default:
+ continue
+ }
+ }
+
+ return sanitized
+ }
+
+ private func sanitizeArray(_ array: [Any]) -> [Any] {
+ var sanitizedArray: [Any] = []
+
+ for value in array {
+ switch value {
+ case let stringValue as String:
+ sanitizedArray.append(stringValue)
+ case let numberValue as NSNumber:
+ sanitizedArray.append(numberValue)
+ case let boolValue as Bool:
+ sanitizedArray.append(boolValue)
+ case let arrayValue as [Any]:
+ sanitizedArray.append(sanitizeArray(arrayValue))
+ case let mapValue as [String: Any]:
+ sanitizedArray.append(sanitizeMetadata(mapValue))
+ default:
+ continue
+ }
+ }
+
+ return sanitizedArray
+ }
+
@available(iOS 14.0, *)
private func fetchEcgMeasurements(_ sample: HKElectrocardiogram) -> NSDictionary {
let semaphore = DispatchSemaphore(value: 0)
@@ -1630,6 +1715,10 @@ public class SwiftHealthPlugin: NSObject, FlutterPlugin {
dataTypesDict[WATER_TEMPERATURE] = HKQuantityType.quantityType(forIdentifier: .waterTemperature)!
dataTypesDict[UNDERWATER_DEPTH] = HKQuantityType.quantityType(forIdentifier: .underwaterDepth)!
+
+ dataTypesDict[UV_INDEX] = HKSampleType.quantityType(forIdentifier: .uvExposure)!
+ dataQuantityTypesDict[UV_INDEX] = HKQuantityType.quantityType(forIdentifier: .uvExposure)!
+
}
// Concatenate heart events, headache and health data types (both may be empty)
diff --git a/packages/health/ios/health.podspec b/packages/health/ios/health.podspec
index 96254602b..aba1806e8 100644
--- a/packages/health/ios/health.podspec
+++ b/packages/health/ios/health.podspec
@@ -3,7 +3,7 @@
#
Pod::Spec.new do |s|
s.name = 'health'
- s.version = '12.0.0'
+ s.version = '12.1.0'
s.summary = 'Wrapper for Apple\'s HealthKit on iOS and Google\'s Health Connect on Android.'
s.description = <<-DESC
Wrapper for Apple's HealthKit on iOS and Google's Health Connect on Android.
@@ -16,7 +16,7 @@ Wrapper for Apple's HealthKit on iOS and Google's Health Connect on Android.
s.public_header_files = 'Classes/**/*.h'
s.dependency 'Flutter'
- s.ios.deployment_target = '13.0'
+ s.ios.deployment_target = '14.0'
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'VALID_ARCHS[sdk=iphonesimulator*]' => 'x86_64' }
end
diff --git a/packages/health/lib/health.g.dart b/packages/health/lib/health.g.dart
index 70feb2de1..d985c19dc 100644
--- a/packages/health/lib/health.g.dart
+++ b/packages/health/lib/health.g.dart
@@ -137,6 +137,7 @@ const _$HealthDataTypeEnumMap = {
HealthDataType.HEADACHE_SEVERE: 'HEADACHE_SEVERE',
HealthDataType.HEADACHE_UNSPECIFIED: 'HEADACHE_UNSPECIFIED',
HealthDataType.NUTRITION: 'NUTRITION',
+ HealthDataType.UV_INDEX: 'UV_INDEX',
HealthDataType.GENDER: 'GENDER',
HealthDataType.BIRTH_DATE: 'BIRTH_DATE',
HealthDataType.BLOOD_TYPE: 'BLOOD_TYPE',
diff --git a/packages/health/lib/src/health_plugin.dart b/packages/health/lib/src/health_plugin.dart
index cc62e3ac7..0c7bae82b 100644
--- a/packages/health/lib/src/health_plugin.dart
+++ b/packages/health/lib/src/health_plugin.dart
@@ -200,6 +200,70 @@ class Health {
}
}
+ /// Checks if the Health Data History feature is available.
+ ///
+ /// See this for more info: https://developer.android.com/reference/androidx/health/connect/client/permission/HealthPermission#PERMISSION_READ_HEALTH_DATA_HISTORY()
+ ///
+ ///
+ /// Android only. Returns false on iOS or if an error occurs.
+ Future isHealthDataHistoryAvailable() async {
+ if (Platform.isIOS) return false;
+
+ try {
+ final status =
+ await _channel.invokeMethod('isHealthDataHistoryAvailable');
+ return status ?? false;
+ } catch (e) {
+ debugPrint(
+ '$runtimeType - Exception in isHealthDataHistoryAvailable(): $e');
+ return false;
+ }
+ }
+
+ /// Checks the current status of the Health Data History permission.
+ /// Make sure to check [isHealthConnectAvailable] before calling this method.
+ ///
+ /// See this for more info: https://developer.android.com/reference/androidx/health/connect/client/permission/HealthPermission#PERMISSION_READ_HEALTH_DATA_HISTORY()
+ ///
+ ///
+ /// Android only. Returns true on iOS or false if an error occurs.
+ Future isHealthDataHistoryAuthorized() async {
+ if (Platform.isIOS) return true;
+
+ try {
+ final status =
+ await _channel.invokeMethod('isHealthDataHistoryAuthorized');
+ return status ?? false;
+ } catch (e) {
+ debugPrint(
+ '$runtimeType - Exception in isHealthDataHistoryAuthorized(): $e');
+ return false;
+ }
+ }
+
+ /// Requests the Health Data History permission.
+ ///
+ /// Returns true if successful, false otherwise.
+ ///
+ /// See this for more info: https://developer.android.com/reference/androidx/health/connect/client/permission/HealthPermission#PERMISSION_READ_HEALTH_DATA_HISTORY()
+ ///
+ ///
+ /// Android only. Returns true on iOS or false if an error occurs.
+ Future requestHealthDataHistoryAuthorization() async {
+ if (Platform.isIOS) return true;
+
+ await _checkIfHealthConnectAvailableOnAndroid();
+ try {
+ final bool? isAuthorized =
+ await _channel.invokeMethod('requestHealthDataHistoryAuthorization');
+ return isAuthorized ?? false;
+ } catch (e) {
+ debugPrint(
+ '$runtimeType - Exception in requestHealthDataHistoryAuthorization(): $e');
+ return false;
+ }
+ }
+
/// Requests permissions to access health data [types].
///
/// Returns true if successful, false otherwise.
@@ -444,6 +508,38 @@ class Health {
return success ?? false;
}
+ /// Deletes a specific health record by its UUID.
+ ///
+ /// Returns true if successful, false otherwise.
+ ///
+ /// Parameters:
+ /// * [uuid] - The UUID of the health record to delete.
+ /// * [type] - The health data type of the record. Required on iOS.
+ ///
+ /// On Android, only the UUID is required. On iOS, both UUID and type are required.
+ Future deleteByUUID({
+ required String uuid,
+ HealthDataType? type,
+ }) async {
+ await _checkIfHealthConnectAvailableOnAndroid();
+
+ if (uuid.isEmpty || uuid == "") {
+ throw ArgumentError("UUID must not be empty.");
+ }
+
+ if (Platform.isIOS && type == null) {
+ throw ArgumentError("On iOS, both UUID and type are required to delete a record.");
+ }
+
+ Map args = {
+ 'uuid': uuid,
+ 'dataTypeKey': type?.name,
+ };
+
+ bool? success = await _channel.invokeMethod('deleteByUUID', args);
+ return success ?? false;
+ }
+
/// Saves a blood pressure record.
///
/// Returns true if successful, false otherwise.
diff --git a/packages/health/lib/src/heath_data_types.dart b/packages/health/lib/src/heath_data_types.dart
index 0a1f8c400..6ab42d8d8 100644
--- a/packages/health/lib/src/heath_data_types.dart
+++ b/packages/health/lib/src/heath_data_types.dart
@@ -91,6 +91,7 @@ enum HealthDataType {
HEADACHE_SEVERE,
HEADACHE_UNSPECIFIED,
NUTRITION,
+ UV_INDEX,
// HealthKit Characteristics
GENDER,
BIRTH_DATE,
@@ -212,6 +213,8 @@ const List dataTypeKeysIOS = [
HealthDataType.MENSTRUATION_FLOW,
HealthDataType.WATER_TEMPERATURE,
HealthDataType.UNDERWATER_DEPTH,
+ HealthDataType.UV_INDEX,
+ HealthDataType.TOTAL_CALORIES_BURNED,
];
/// List of data types available on Android
@@ -362,6 +365,7 @@ const Map dataTypeToUnit = {
HealthDataType.MENSTRUATION_FLOW: HealthDataUnit.NO_UNIT,
HealthDataType.WATER_TEMPERATURE: HealthDataUnit.DEGREE_CELSIUS,
HealthDataType.UNDERWATER_DEPTH: HealthDataUnit.METER,
+ HealthDataType.UV_INDEX: HealthDataUnit.COUNT,
// Health Connect
HealthDataType.TOTAL_CALORIES_BURNED: HealthDataUnit.KILOCALORIE,
diff --git a/packages/health/pubspec.yaml b/packages/health/pubspec.yaml
index ed206e548..a50c18d0a 100644
--- a/packages/health/pubspec.yaml
+++ b/packages/health/pubspec.yaml
@@ -27,6 +27,7 @@ dev_dependencies:
build_runner: any
json_serializable: any
mocktail: ^1.0.4
+ mockito: ^5.4.5
flutter:
plugin:
diff --git a/packages/health/test/swift_test.dart b/packages/health/test/swift_test.dart
new file mode 100644
index 000000000..1ab431831
--- /dev/null
+++ b/packages/health/test/swift_test.dart
@@ -0,0 +1,174 @@
+import 'package:flutter/services.dart';
+import 'package:flutter_test/flutter_test.dart';
+import 'package:health/health.dart';
+import 'package:mockito/mockito.dart';
+
+import 'mocks/device_info_mock.dart';
+
+// Mock MethodChannel to simulate native responses
+class MockMethodChannel extends Mock implements MethodChannel {
+ @override
+ Future invokeMethod(String method, [dynamic arguments]) async {
+ if (method == 'getData') {
+ final dataTypeKey = (arguments as Map)['dataTypeKey'];
+ switch (dataTypeKey) {
+ case 'HEART_RATE':
+ return Future.value(