Open maxbeech opened 1 year ago
Hi, I just tested by reading an ECG measurement from an Apple watch, which worked normally for me:
flutter: HealthDataPoint - value: 15360 values, 60.0 BPM, 512.0 HZ, ElectrocardiogramClassification.SINUS_RHYTHM, unit: VOLT, dateFrom: 2023-10-11 12:51:16.071, dateTo: 2023-10-11 12:51:46.071, dataType: ELECTROCARDIOGRAM, platform: PlatformType.IOS, deviceId:, sourceId: com.apple.NanoHeartRhythm, sourceName: EKG
Can you describe your steps in more detail? Are you using the example app or your own app? If you use the example app, does the measurement show up correctly?
Many thanks for the reply @hoffmatteo. I was using my own app, however I have just tried it on the example app and the same issue occurred. Do you have any suggestions please? Thanks a ton..
iPhone 7, iOS 15.7.3
Launching lib/main.dart on iPhone (2) in debug mode...
Automatically signing iOS for device deployment using specified development team in Xcode project: E353LGUVGH
Running Xcode build...
Xcode build done. 15.0s
Installing and launching...
(lldb) 2023-10-12 14:37:48.457536+0100 Runner[577:18074] [VERBOSE-2:FlutterDarwinContextMetalImpeller.mm(37)] Using the Impeller rendering backend.
Warning: Unable to create restoration in progress marker file
fopen failed for data file: errno = 2 (No such file or directory)
Errors found! Invalidating cache...
Debug service listening on ws://127.0.0.1:63188/JLkk8ZIowIU=/ws
Syncing files to device iPhone (2)...
* thread #25, queue = 'com.apple.HealthKit.HKHealthStore.client.0x2812b8750', stop reason = EXC_BAD_ACCESS (code=1, address=0x0)
frame #0: 0x0000000000000000
error: memory read failed for 0x0
Target 0: (Runner) stopped.
Lost connection to device.
main.dart
:import 'dart:async';
import 'package:flutter/material.dart';
import 'package:health/health.dart';
void main() => runApp(HealthApp());
class HealthApp extends StatefulWidget {
@override
_HealthAppState createState() => _HealthAppState();
}
enum AppState {
DATA_NOT_FETCHED,
FETCHING_DATA,
DATA_READY,
NO_DATA,
AUTHORIZED,
AUTH_NOT_GRANTED,
DATA_ADDED,
DATA_DELETED,
DATA_NOT_ADDED,
DATA_NOT_DELETED,
STEPS_READY,
}
class _HealthAppState extends State<HealthApp> {
List<HealthDataPoint> _healthDataList = [];
AppState _state = AppState.DATA_NOT_FETCHED;
static final types = [
HealthDataType.ELECTROCARDIOGRAM
];
final permissions = types.map((e) => HealthDataAccess.READ).toList();
HealthFactory health = HealthFactory(useHealthConnectIfAvailable: true);
Future authorize() async {
// Check if we have permission
bool? hasPermissions =
await health.hasPermissions(types, permissions: permissions);
hasPermissions = false;
bool authorized = false;
if (!hasPermissions) {
// requesting access to the data types before reading them
try {
authorized =
await health.requestAuthorization(types, permissions: permissions);
} catch (error) {
print("Exception in authorize: $error");
}
}
setState(() => _state =
(authorized) ? AppState.AUTHORIZED : AppState.AUTH_NOT_GRANTED);
}
Future fetchData() async {
setState(() => _state = AppState.FETCHING_DATA);
final start = DateTime(2022,03,31);
final end = DateTime(2022,04,02);
_healthDataList.clear();
try {
List<HealthDataPoint> healthData =
await health.getHealthDataFromTypes(start, end, types);
_healthDataList.addAll(
(healthData.length < 100) ? healthData : healthData.sublist(0, 100));
} catch (error) {
print("Exception in getHealthDataFromTypes: $error");
}
_healthDataList = HealthFactory.removeDuplicates(_healthDataList);
_healthDataList.forEach((x) => print(x));
setState(() {
_state = _healthDataList.isEmpty ? AppState.NO_DATA : AppState.DATA_READY;
});
}
Widget _contentFetchingData() {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
padding: EdgeInsets.all(20),
child: CircularProgressIndicator(
strokeWidth: 10,
)),
Text('Fetching data...')
],
);
}
Widget _contentDataReady() {
return ListView.builder(
itemCount: _healthDataList.length,
itemBuilder: (_, index) {
HealthDataPoint p = _healthDataList[index];
if (p.value is AudiogramHealthValue) {
return ListTile(
title: Text("${p.typeString}: ${p.value}"),
trailing: Text('${p.unitString}'),
subtitle: Text('${p.dateFrom} - ${p.dateTo}'),
);
}
if (p.value is WorkoutHealthValue) {
return ListTile(
title: Text(
"${p.typeString}: ${(p.value as WorkoutHealthValue).totalEnergyBurned} ${(p.value as WorkoutHealthValue).totalEnergyBurnedUnit?.name}"),
trailing: Text(
'${(p.value as WorkoutHealthValue).workoutActivityType.name}'),
subtitle: Text('${p.dateFrom} - ${p.dateTo}'),
);
}
return ListTile(
title: Text("${p.typeString}: ${p.value}"),
trailing: Text('${p.unitString}'),
subtitle: Text('${p.dateFrom} - ${p.dateTo}'),
);
});
}
Widget _contentNoData() {
return Text('No Data to show');
}
Widget _contentNotFetched() {
return Column(
children: [
Text('Press the download button to fetch data.'),
Text('Press the plus button to insert some random data.'),
Text('Press the walking button to get total step count.'),
],
mainAxisAlignment: MainAxisAlignment.center,
);
}
Widget _authorized() {
return Text('Authorization granted!');
}
Widget _authorizationNotGranted() {
return Text('Authorization not given. '
'For Android please check your OAUTH2 client ID is correct in Google Developer Console. '
'For iOS check your permissions in Apple Health.');
}
Widget _content() {
if (_state == AppState.DATA_READY)
return _contentDataReady();
else if (_state == AppState.NO_DATA)
return _contentNoData();
else if (_state == AppState.FETCHING_DATA)
return _contentFetchingData();
else if (_state == AppState.AUTHORIZED)
return _authorized();
else if (_state == AppState.AUTH_NOT_GRANTED)
return _authorizationNotGranted();
else
return _contentNotFetched();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Health Example'),
),
body: Container(
child: Column(
children: [
Wrap(
spacing: 10,
children: [
TextButton(
onPressed: authorize,
child:
Text("Auth", style: TextStyle(color: Colors.white)),
style: ButtonStyle(
backgroundColor:
MaterialStatePropertyAll(Colors.blue))),
TextButton(
onPressed: fetchData,
child: Text("Fetch Data",
style: TextStyle(color: Colors.white)),
style: ButtonStyle(
backgroundColor:
MaterialStatePropertyAll(Colors.blue))),
],
),
Divider(thickness: 3),
Expanded(child: Center(child: _content()))
],
),
),
),
);
}
}
Hi there, thanks so much for the fantastic package!!
I am trying to retrieve iOS ECG data including voltage information within my Flutter app. Sadly the app crashes when I try to retrieve just a single ECG reading. I have tracked them back and I'm pretty sure they crash within the Swift file at the
HKElectrocardiogramQuery(ecg_sample_here)
function ie. when it's actually retrieving the data (relevant Apple documentation here).Whenever I do it, I get the memory error:
I get the same error on both an iPhone 7 and iPhone 11 (both running min. iOS 14) using various different readings.
Does anyone know how to avoid this happening please?
I'm not sure if say there's a way to retrieve a part of a reading for example, as I think the app is just struggling to handle the size of variable.
Thanks a lot!