在Dart 2.18中,Dart团队引入了Objective-C/Swift互操作。允许使用dart ffi 和 C 作为互操作性层,直接从 Dart 代码库调用 iOS 上的本机代码。这意味着可以绕开 platform method channel 直接调用 iOS 上的原生功能。
一、背景需求
-
获取操作系统以及设备信息,如电池、蓝牙、NFC、权限等
-
可使用 ffi 替换 method channel 封装的一些原生库,提升效率
二、什么是 Objective-C/Swift Interop?
允许直接从 Dart 代码调用 Swift/Object-C 代码,Objective-C/Swift Interop 将从 Swift/Objective-C代码创建 C 代码绑定,然后可以直接从Dart- code使用 Dart FFI(外部函数接口)调用。
三、实践
1、创建demo 工程 $ flutter create -i swift -a kotlin native_info_demo
2、切换到工程目录添加 ffigen 以及 ffi 插件
$ dart pub add ffi
$ dart pub add --dev ffigen
3、在 pubspec.yaml 中添加 ffigen config 信息
ffigen: name: UIDevice description: Bindings for UIDevice. language: objc output: './lib/src/uidevice_bindings.dart' ## 指定生成绑定代码所在文件目录 exclude-all-by-default: true objc-interfaces: include: - 'UIDevice' headers: entry-points: - '/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/System/iOSSupport/System/Library/Frameworks/UIKit.framework/Headers/UIDevice.h'
添加完成如下图:
如果使用 swift 语言需要生成一个Objective-C包装头文件。可以使用Xcode,或者Swift命令行编译器swiftc。如使用命令行:
swiftc -c swift_api.swift \
-module-name swift_module \
-emit-objc-header-path swift_api.h \
-emit-library -o libswiftapi.dylib
该命令编译Swift文件swift_api.swift,并生成一个头文件swift_api.h、以及后面需要加载的动态库 libswiftapi.dylib。
使用swift_api.swift 的 ffigen 配置文件需修改如下
ffigen:
name: SwiftLibrary
description: Bindings for swift_api.
language: objc
output: 'swift_api_bindings.dart'
exclude-all-by-default: true
objc-interfaces:
include:
- 'SwiftClass'
module:
'SwiftClass': 'swift_module'
headers:
entry-points:
- 'swift_api.h'
preamble: |
// ignore_for_file: camel_case_types, non_constant_identifier_names, unused_element, unused_field, return_of_invalid_type, void_checks, annotate_overrides, no_leading_underscores_for_local_identifiers, library_private_types_in_public_api
4、生成绑定代码
$ dart run ffigen
生成过程中如有命名冲突,可能会用类名1原来的类名,如UIDevice1替换UIDevice绑定代码生成完结构如图:
5、在Flutter项目中使用绑定代码
import 'src/uidevice_bindings.dart';import 'dart:ffi';...
@override void initState() { super.initState(); getNativeDeviceInfo(); } void getNativeDeviceInfo() { final lib = UIDevice1(DynamicLibrary.process()); final device = UIDevice.new1(lib); _systemName = '${device.systemName}${device.systemVersion}'; if (device.batteryMonitoringEnabled) { final batteryLevel = device.batteryLevel; _batteryLevel = batteryLevel; print('Battery level: $batteryLevel'); } }...
显示效果:
到这里基本已完成整个 ffi 调用 iOS 的过程了,iOS的小伙伴可愉快的使用起来了~