Simplify experimental pedometer example (#2104)

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 https://github.com/dart-lang/native/issues/835
pull/2106/head
Liam Appelbe 7 months ago committed by GitHub
parent e598d6a771
commit f0e6da6d24
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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';
@ -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
@ -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();
}
}

@ -7,15 +7,16 @@ 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 apps Info.plist file

File diff suppressed because it is too large Load Diff

@ -1,15 +1,7 @@
#import <CoreMotion/CoreMotion.h>
#import <CoreMotion/CMPedometer.h>
#import <Foundation/Foundation.h>
#import <Foundation/NSDate.h>
#include "dart-sdk/include/dart_api_dl.h"
@interface PedometerHelper : NSObject
+ (void) startPedometerWithPort: (Dart_Port) sendPort
pedometer: (CMPedometer*) pedometer
start: (NSDate*) start
end: (NSDate*) end;
@end
// TODO(https://github.com/dart-lang/native/issues/835): Generate this wrapper
// automatically.
CMPedometerHandler wrapCallback(CMPedometerHandler callback);

@ -2,37 +2,11 @@
#import <CoreMotion/CoreMotion.h>
#import <CoreMotion/CMPedometer.h>
#import <Foundation/Foundation.h>
#import <Foundation/NSDate.h>
#include "dart-sdk/include/dart_api_dl.h"
// Need to import the dart headers to get the definitions Dart_CObject
#include "dart-sdk/include/dart_api_dl.h"
// Helper function that takes a pointer to CMPedometer data and converts it to a Dart C Object
static Dart_CObject NSObjectToCObject(CMPedometerData* n) {
Dart_CObject cobj;
cobj.type = Dart_CObject_kInt64;
cobj.value.as_int64 = (int64_t) n;
return cobj;
}
@implementation PedometerHelper
+(void)startPedometerWithPort: (Dart_Port) sendPort pedometer: (CMPedometer*) pedometer start: (NSDate*) start end: (NSDate*) end {
// Start the pedometer
[pedometer queryPedometerDataFromDate:start toDate:end withHandler:^(CMPedometerData *pedometerData, NSError *error) {
if(error == nil){
pedometerData = [pedometerData retain];
Dart_CObject data = NSObjectToCObject(pedometerData);
const bool success = Dart_PostCObject_DL(sendPort, &data);
NSAssert(success, @"Couldn't send to Dart@");
}
else{
NSLog(@"Error:%@", error);
}
}];
// TODO(https://github.com/dart-lang/native/issues/835): Generate this wrapper
// automatically.
CMPedometerHandler wrapCallback(CMPedometerHandler callback) {
return [^(CMPedometerData *data, NSError *error) {
return callback([data retain], [error retain]);
} copy];
}
@end

Loading…
Cancel
Save