Skip to content

Commit

Permalink
Simplify experimental pedometer example (#2104)
Browse files Browse the repository at this point in the history
Since ffigen added support for
[`NativeCallable.listener`](https://api.flutter.dev/flutter/dart-ffi/NativeCallable/NativeCallable.listener.html)
to its ObjC bindings, this example can be simplified. We can replace the
`Dart_Port` logic with `ObjCBlock.listener`, which lets us get rid of
most of the native code.

We still need a small bit of native code to `retain` a reference to the
callback's arguments before invoking the listener, otherwise the
arguments may be ref counted and deleted before the Dart side of the
callback is invoked. See dart-lang/native#835
  • Loading branch information
liamappelbe authored Dec 6, 2023
1 parent e598d6a commit f0e6da6
Show file tree
Hide file tree
Showing 5 changed files with 33,651 additions and 27,595 deletions.
51 changes: 25 additions & 26 deletions experimental/pedometer/example/lib/steps_repo.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import 'dart:async';
import 'dart:collection';
import 'dart:ffi' as ffi;
import 'dart:io';
import 'dart:isolate';

import 'package:flutter/foundation.dart';
import 'package:intl/intl.dart';
Expand Down Expand Up @@ -73,7 +74,7 @@ class _IOSStepsRepo implements StepsRepo {
// Create a new NSString with the formatted date and timezone.
final nString = pd.NSString(lib, "$formattedDate $tz");
// Convert the NSString to NSDate.
return formatter.dateFromString_(nString);
return formatter.dateFromString_(nString)!;
}

@override
Expand All @@ -83,37 +84,35 @@ class _IOSStepsRepo implements StepsRepo {
return [];
}

final futures = <Future>[];
final handlers = [];
final futures = <Future<Steps?>>[];
final now = DateTime.now();

for (var h = 0; h <= now.hour; h++) {
// Open up a port to receive data from native side.
final receivePort = ReceivePort();
final nativePort = receivePort.sendPort.nativePort;
final start = dateConverter(DateTime(now.year, now.month, now.day, h));
final end = dateConverter(DateTime(now.year, now.month, now.day, h + 1));

pd.PedometerHelper.startPedometerWithPort_pedometer_start_end_(
helpLib,
nativePort,
client,
start,
end,
);
// Handle the data received from native side.
futures.add(receivePort.first);
final completer = Completer<Steps?>();
futures.add(completer.future);

final handler = helpLib.wrapCallback(
pd.ObjCBlock_ffiVoid_CMPedometerData_NSError.listener(lib,
(pd.CMPedometerData? result, pd.NSError? error) {
if (result != null) {
final stepCount = result.numberOfSteps.intValue;
final startHour =
hourFormatter.stringFromDate_(result.startDate).toString();
completer.complete(Steps(startHour, stepCount));
} else {
debugPrint("Query error: ${error?.localizedDescription}");
completer.complete(null);
}
}));
handlers.add(handler);
client.queryPedometerDataFromDate_toDate_withHandler_(
start, end, handler);
}

final data = await Future.wait(futures);
return data.where((e) => e != null).cast<int>().map((address) {
final result = ffi.Pointer<pd.ObjCObject>.fromAddress(address);
final pedometerData =
pd.CMPedometerData.castFromPointer(lib, result, release: true);
final stepCount = pedometerData.numberOfSteps?.intValue ?? 0;
final startHour =
hourFormatter.stringFromDate_(pedometerData.startDate!).toString();
return Steps(startHour, stepCount);
}).toList();
return (await Future.wait(futures)).nonNulls.toList();
}
}

Expand Down
7 changes: 4 additions & 3 deletions experimental/pedometer/ffigen.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,18 @@ compiler-opts:
- "-F/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks"
- "-mios-version-min=13.0"
exclude-all-by-default: true
functions:
include:
- "wrapCallback"
objc-interfaces:
include:
- "CMPedometer"
- "PedometerHelper"
- "NSDate"
- "NSDateFormatter"
headers:
entry-points:
- "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/CoreMotion.framework/Headers/CMPedometer.h"
- "src/pedometerHelper.h"

# To use this API, you must include the NSMotionUsageDescription key in your app’s Info.plist file
# and provide a usage description string for this key.
# The usage description appears in the prompt that the user must accept the first time the system asks the user to access motion data for your app.
# The usage description appears in the prompt that the user must accept the first time the system asks the user to access motion data for your app.
Loading

0 comments on commit f0e6da6

Please sign in to comment.