diff --git a/.github/workflows/ffigen.yml b/.github/workflows/ffigen.yml index 18b2d9f4da..d8823f09e7 100644 --- a/.github/workflows/ffigen.yml +++ b/.github/workflows/ffigen.yml @@ -106,7 +106,7 @@ jobs: - name: Upload coverage uses: coverallsapp/github-action@648a8eb78e6d50909eff900e4ec85cab4524a45b with: - carryforward: "ffigen,jni,jnigen,native_pkgs_macos,native_pkgs_ubuntu,native_pkgs_windows,objective_c,swift2objc" + carryforward: "ffigen,jni,jnigen,native_pkgs_macos,native_pkgs_ubuntu,native_pkgs_windows,objective_c,swift2objc,swiftgen" github-token: ${{ secrets.GITHUB_TOKEN }} parallel-finished: true diff --git a/.github/workflows/jnigen.yaml b/.github/workflows/jnigen.yaml index 7aae7e478e..dfd0a1719e 100644 --- a/.github/workflows/jnigen.yaml +++ b/.github/workflows/jnigen.yaml @@ -447,6 +447,6 @@ jobs: - name: Coveralls finished uses: coverallsapp/github-action@648a8eb78e6d50909eff900e4ec85cab4524a45b with: - carryforward: "ffigen,jni,jnigen,native_pkgs_macos,native_pkgs_ubuntu,native_pkgs_windows,objective_c,swift2objc" + carryforward: "ffigen,jni,jnigen,native_pkgs_macos,native_pkgs_ubuntu,native_pkgs_windows,objective_c,swift2objc,swiftgen" github-token: ${{ secrets.github_token }} parallel-finished: true diff --git a/.github/workflows/native.yaml b/.github/workflows/native.yaml index 33ad42f5e1..8126dbd7c0 100644 --- a/.github/workflows/native.yaml +++ b/.github/workflows/native.yaml @@ -82,6 +82,6 @@ jobs: - name: Upload coverage uses: coverallsapp/github-action@648a8eb78e6d50909eff900e4ec85cab4524a45b with: - carryforward: "ffigen,jni,jnigen,native_pkgs_macos,native_pkgs_ubuntu,native_pkgs_windows,objective_c,swift2objc" + carryforward: "ffigen,jni,jnigen,native_pkgs_macos,native_pkgs_ubuntu,native_pkgs_windows,objective_c,swift2objc,swiftgen" github-token: ${{ secrets.GITHUB_TOKEN }} parallel-finished: true diff --git a/.github/workflows/objective_c.yaml b/.github/workflows/objective_c.yaml index 34cc379843..cfdc211cc4 100644 --- a/.github/workflows/objective_c.yaml +++ b/.github/workflows/objective_c.yaml @@ -78,7 +78,7 @@ jobs: - name: Upload coverage uses: coverallsapp/github-action@648a8eb78e6d50909eff900e4ec85cab4524a45b with: - carryforward: "ffigen,jni,jnigen,native_pkgs_macos,native_pkgs_ubuntu,native_pkgs_windows,objective_c,swift2objc" + carryforward: "ffigen,jni,jnigen,native_pkgs_macos,native_pkgs_ubuntu,native_pkgs_windows,objective_c,swift2objc,swiftgen" github-token: ${{ secrets.GITHUB_TOKEN }} parallel-finished: true diff --git a/.github/workflows/swift2objc.yaml b/.github/workflows/swift2objc.yaml index bbbb0038b5..7d8a9484b0 100644 --- a/.github/workflows/swift2objc.yaml +++ b/.github/workflows/swift2objc.yaml @@ -69,6 +69,6 @@ jobs: - name: Upload coverage uses: coverallsapp/github-action@648a8eb78e6d50909eff900e4ec85cab4524a45b with: - carryforward: "ffigen,jni,jnigen,native_pkgs_macos,native_pkgs_ubuntu,native_pkgs_windows,objective_c,swift2objc" + carryforward: "ffigen,jni,jnigen,native_pkgs_macos,native_pkgs_ubuntu,native_pkgs_windows,objective_c,swift2objc,swiftgen" github-token: ${{ secrets.GITHUB_TOKEN }} parallel-finished: true diff --git a/.github/workflows/swiftgen.yaml b/.github/workflows/swiftgen.yaml new file mode 100644 index 0000000000..44622254ac --- /dev/null +++ b/.github/workflows/swiftgen.yaml @@ -0,0 +1,84 @@ +name: swiftgen + +on: + # Run on PRs and pushes to the default branch. + push: + branches: [main, stable] + paths: + - '.github/workflows/swiftgen.yaml' + - 'pkgs/swiftgen/**' + pull_request: + branches: [main, stable] + paths: + - '.github/workflows/swiftgen.yaml' + - 'pkgs/ffigen/**' + - 'pkgs/objective_c/**' + - 'pkgs/swift2objc/**' + - 'pkgs/swiftgen/**' + schedule: + - cron: "0 0 * * 0" + +env: + PUB_ENVIRONMENT: bot.github + +jobs: + # Check code formatting and static analysis. + analyze: + runs-on: macos-latest + defaults: + run: + working-directory: pkgs/swiftgen/ + strategy: + fail-fast: false + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + - uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 + with: + channel: 'stable' + - id: install + name: Install dependencies + run: flutter pub get + - name: Check formatting + run: dart format --output=none --set-exit-if-changed . + if: always() && steps.install.outcome == 'success' + - name: Analyze code + run: dart analyze --fatal-infos + if: always() && steps.install.outcome == 'success' + + test-mac: + needs: analyze + runs-on: 'macos-latest' + defaults: + run: + working-directory: pkgs/swiftgen/ + steps: + - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 + - uses: subosito/flutter-action@e938fdf56512cc96ef2f93601a5a40bde3801046 + with: + channel: 'stable' + - name: Install dependencies + run: flutter pub get + - name: Install ObjC dependencies + working-directory: pkgs/objective_c/ + run: flutter pub get + - name: Build ObjC test dylib + working-directory: pkgs/objective_c/ + # TODO(https://github.com/dart-lang/native/issues/1068): Remove this. + run: dart test/setup.dart + - name: Install coverage + run: dart pub global activate coverage + - name: Run VM tests and collect coverage + run: dart pub global run coverage:test_with_coverage --scope-output=swiftgen + - name: Upload coverage + uses: coverallsapp/github-action@648a8eb78e6d50909eff900e4ec85cab4524a45b + with: + flag-name: swiftgen + github-token: ${{ secrets.GITHUB_TOKEN }} + parallel: true + path-to-lcov: pkgs/swiftgen/coverage/lcov.info + - name: Upload coverage + uses: coverallsapp/github-action@648a8eb78e6d50909eff900e4ec85cab4524a45b + with: + carryforward: "ffigen,jni,jnigen,native_pkgs_macos,native_pkgs_ubuntu,native_pkgs_windows,objective_c,swift2objc,swiftgen" + github-token: ${{ secrets.GITHUB_TOKEN }} + parallel-finished: true diff --git a/pkgs/ffigen/lib/ffigen.dart b/pkgs/ffigen/lib/ffigen.dart index aa1f364e52..717f2ff076 100644 --- a/pkgs/ffigen/lib/ffigen.dart +++ b/pkgs/ffigen/lib/ffigen.dart @@ -8,12 +8,21 @@ /// https://pub.dev/packages/ffigen for details. library ffigen; +export 'src/code_generator/imports.dart' show ImportedType, LibraryImport; export 'src/config_provider.dart' show + CommentType, + CompoundDependencies, Config, + Declaration, DeclarationFilters, ExternalVersions, + FfiNativeConfig, Language, + PackingValue, + SymbolFile, + VarArgFunction, Versions, - YamlConfig; + YamlConfig, + defaultCompilerOpts; export 'src/ffigen.dart' show FfiGen; diff --git a/pkgs/ffigen/lib/src/config_provider.dart b/pkgs/ffigen/lib/src/config_provider.dart index 8feb6fe89b..13bb83cc6c 100644 --- a/pkgs/ffigen/lib/src/config_provider.dart +++ b/pkgs/ffigen/lib/src/config_provider.dart @@ -7,4 +7,5 @@ library config_provider; export 'config_provider/config.dart'; export 'config_provider/config_types.dart'; +export 'config_provider/path_finder.dart'; export 'config_provider/yaml_config.dart'; diff --git a/pkgs/ffigen/lib/src/config_provider/config.dart b/pkgs/ffigen/lib/src/config_provider/config.dart index 7070ca00d4..3700e3b6d6 100644 --- a/pkgs/ffigen/lib/src/config_provider/config.dart +++ b/pkgs/ffigen/lib/src/config_provider/config.dart @@ -200,7 +200,7 @@ abstract interface class Config { Uri? outputObjC, SymbolFile? symbolFile, Language language = Language.c, - required List entryPoints, + List entryPoints = const [], bool Function(Uri header)? shouldIncludeHeaderFunc, List? compilerOpts, Map> varArgFunctions = diff --git a/pkgs/ffigen/test/large_integration_tests/large_objc_test.dart b/pkgs/ffigen/test/large_integration_tests/large_objc_test.dart index 0125030b6e..5b95500a6a 100644 --- a/pkgs/ffigen/test/large_integration_tests/large_objc_test.dart +++ b/pkgs/ffigen/test/large_integration_tests/large_objc_test.dart @@ -13,7 +13,6 @@ import 'dart:io'; import 'package:ffigen/ffigen.dart'; import 'package:ffigen/src/code_generator/utils.dart'; -import 'package:ffigen/src/config_provider/config_types.dart'; import 'package:logging/logging.dart'; import 'package:path/path.dart' as path; import 'package:pub_semver/pub_semver.dart'; diff --git a/pkgs/swiftgen/example/.gitignore b/pkgs/swiftgen/example/.gitignore new file mode 100644 index 0000000000..a6d7ecd9ea --- /dev/null +++ b/pkgs/swiftgen/example/.gitignore @@ -0,0 +1 @@ +temp/ diff --git a/pkgs/swiftgen/example/README.md b/pkgs/swiftgen/example/README.md new file mode 100644 index 0000000000..bedbdcae1a --- /dev/null +++ b/pkgs/swiftgen/example/README.md @@ -0,0 +1,15 @@ +# swiftgen example + +Demonstrates how to use swiftgen to generate Dart bindings for a Swift API. + +Regenerating the bindings: + +```shell +dart generate_code.dart +``` + +Running the example: + +```shell +dart play_audio.dart +``` diff --git a/pkgs/swiftgen/example/avf_audio_bindings.dart b/pkgs/swiftgen/example/avf_audio_bindings.dart new file mode 100644 index 0000000000..a8046a713b --- /dev/null +++ b/pkgs/swiftgen/example/avf_audio_bindings.dart @@ -0,0 +1,954 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// ignore_for_file: always_specify_types +// ignore_for_file: camel_case_types +// ignore_for_file: non_constant_identifier_names +// ignore_for_file: unnecessary_non_null_assertion +// ignore_for_file: unused_element +// ignore_for_file: unused_field +// coverage:ignore-file + +// AUTO GENERATED FILE, DO NOT EDIT. +// +// Generated by `package:ffigen`. +// ignore_for_file: type=lint +import 'dart:ffi' as ffi; +import 'package:objective_c/objective_c.dart' as objc; + +@ffi.Native< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ) +>() +external ffi.Pointer +_AVFAudioWrapper_protocolTrampoline_1mbt9g9( + ffi.Pointer target, + ffi.Pointer arg0, +); + +/// WARNING: AVAudioFormatWrapper is a stub. To generate bindings for this class, include +/// AVAudioFormatWrapper in your config's objc-interfaces list. +/// +/// AVAudioFormatWrapper +class AVAudioFormatWrapper extends objc.NSObject { + AVAudioFormatWrapper._( + ffi.Pointer pointer, { + bool retain = false, + bool release = false, + }) : super.castFromPointer(pointer, retain: retain, release: release); + + /// Constructs a [AVAudioFormatWrapper] that points to the same underlying object as [other]. + AVAudioFormatWrapper.castFrom(objc.ObjCObjectBase other) + : this._(other.ref.pointer, retain: true, release: true); + + /// Constructs a [AVAudioFormatWrapper] that wraps the given raw object pointer. + AVAudioFormatWrapper.castFromPointer( + ffi.Pointer other, { + bool retain = false, + bool release = false, + }) : this._(other, retain: retain, release: release); +} + +late final _class_AVAudioPlayerWrapper = objc.getClass( + "AVFAudioWrapper.AVAudioPlayerWrapper", +); +late final _sel_isKindOfClass_ = objc.registerName("isKindOfClass:"); +final _objc_msgSend_19nvye5 = objc.msgSendPointer + .cast< + ffi.NativeFunction< + ffi.Bool Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ) + > + >() + .asFunction< + bool Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ) + >(); +late final _sel_currentDevice = objc.registerName("currentDevice"); +final _objc_msgSend_151sglz = objc.msgSendPointer + .cast< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ) + > + >() + .asFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ) + >(); +late final _sel_setCurrentDevice_ = objc.registerName("setCurrentDevice:"); +final _objc_msgSend_xtuoz7 = objc.msgSendPointer + .cast< + ffi.NativeFunction< + ffi.Void Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ) + > + >() + .asFunction< + void Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ) + >(); +late final _sel_currentTime = objc.registerName("currentTime"); +final _objc_msgSend_1ukqyt8 = objc.msgSendPointer + .cast< + ffi.NativeFunction< + ffi.Double Function( + ffi.Pointer, + ffi.Pointer, + ) + > + >() + .asFunction< + double Function( + ffi.Pointer, + ffi.Pointer, + ) + >(); +final _objc_msgSend_1ukqyt8Fpret = objc.msgSendFpretPointer + .cast< + ffi.NativeFunction< + ffi.Double Function( + ffi.Pointer, + ffi.Pointer, + ) + > + >() + .asFunction< + double Function( + ffi.Pointer, + ffi.Pointer, + ) + >(); +late final _sel_setCurrentTime_ = objc.registerName("setCurrentTime:"); +final _objc_msgSend_hwm8nu = objc.msgSendPointer + .cast< + ffi.NativeFunction< + ffi.Void Function( + ffi.Pointer, + ffi.Pointer, + ffi.Double, + ) + > + >() + .asFunction< + void Function( + ffi.Pointer, + ffi.Pointer, + double, + ) + >(); +late final _sel_deviceCurrentTime = objc.registerName("deviceCurrentTime"); +late final _sel_duration = objc.registerName("duration"); +late final _sel_enableRate = objc.registerName("enableRate"); +final _objc_msgSend_91o635 = objc.msgSendPointer + .cast< + ffi.NativeFunction< + ffi.Bool Function( + ffi.Pointer, + ffi.Pointer, + ) + > + >() + .asFunction< + bool Function( + ffi.Pointer, + ffi.Pointer, + ) + >(); +late final _sel_setEnableRate_ = objc.registerName("setEnableRate:"); +final _objc_msgSend_1s56lr9 = objc.msgSendPointer + .cast< + ffi.NativeFunction< + ffi.Void Function( + ffi.Pointer, + ffi.Pointer, + ffi.Bool, + ) + > + >() + .asFunction< + void Function( + ffi.Pointer, + ffi.Pointer, + bool, + ) + >(); +late final _sel_format = objc.registerName("format"); +late final _sel_isMeteringEnabled = objc.registerName("isMeteringEnabled"); +late final _sel_setIsMeteringEnabled_ = objc.registerName( + "setIsMeteringEnabled:", +); +late final _sel_numberOfChannels = objc.registerName("numberOfChannels"); +final _objc_msgSend_1hz7y9r = objc.msgSendPointer + .cast< + ffi.NativeFunction< + ffi.Long Function( + ffi.Pointer, + ffi.Pointer, + ) + > + >() + .asFunction< + int Function(ffi.Pointer, ffi.Pointer) + >(); +late final _sel_numberOfLoops = objc.registerName("numberOfLoops"); +late final _sel_setNumberOfLoops_ = objc.registerName("setNumberOfLoops:"); +final _objc_msgSend_4sp4xj = objc.msgSendPointer + .cast< + ffi.NativeFunction< + ffi.Void Function( + ffi.Pointer, + ffi.Pointer, + ffi.Long, + ) + > + >() + .asFunction< + void Function( + ffi.Pointer, + ffi.Pointer, + int, + ) + >(); +late final _sel_pan = objc.registerName("pan"); +final _objc_msgSend_2cgrxl = objc.msgSendPointer + .cast< + ffi.NativeFunction< + ffi.Float Function( + ffi.Pointer, + ffi.Pointer, + ) + > + >() + .asFunction< + double Function( + ffi.Pointer, + ffi.Pointer, + ) + >(); +final _objc_msgSend_2cgrxlFpret = objc.msgSendFpretPointer + .cast< + ffi.NativeFunction< + ffi.Float Function( + ffi.Pointer, + ffi.Pointer, + ) + > + >() + .asFunction< + double Function( + ffi.Pointer, + ffi.Pointer, + ) + >(); +late final _sel_setPan_ = objc.registerName("setPan:"); +final _objc_msgSend_v5hmet = objc.msgSendPointer + .cast< + ffi.NativeFunction< + ffi.Void Function( + ffi.Pointer, + ffi.Pointer, + ffi.Float, + ) + > + >() + .asFunction< + void Function( + ffi.Pointer, + ffi.Pointer, + double, + ) + >(); +late final _sel_isPlaying = objc.registerName("isPlaying"); +late final _sel_rate = objc.registerName("rate"); +late final _sel_setRate_ = objc.registerName("setRate:"); +late final _sel_url = objc.registerName("url"); +late final _sel_volume = objc.registerName("volume"); +late final _sel_setVolume_ = objc.registerName("setVolume:"); +typedef instancetype = ffi.Pointer; +typedef Dartinstancetype = objc.ObjCObjectBase; +late final _sel_initWithContentsOf_error_ = objc.registerName( + "initWithContentsOf:error:", +); +final _objc_msgSend_1lhpu4m = objc.msgSendPointer + .cast< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer>, + ) + > + >() + .asFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer>, + ) + >(); +late final _sel_initWithContentsOf_fileTypeHint_error_ = objc.registerName( + "initWithContentsOf:fileTypeHint:error:", +); +final _objc_msgSend_1pnyuds = objc.msgSendPointer + .cast< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer>, + ) + > + >() + .asFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ffi.Pointer>, + ) + >(); +late final _sel_averagePowerForChannel_ = objc.registerName( + "averagePowerForChannel:", +); +final _objc_msgSend_1o3b4v9 = objc.msgSendPointer + .cast< + ffi.NativeFunction< + ffi.Float Function( + ffi.Pointer, + ffi.Pointer, + ffi.Long, + ) + > + >() + .asFunction< + double Function( + ffi.Pointer, + ffi.Pointer, + int, + ) + >(); +final _objc_msgSend_1o3b4v9Fpret = objc.msgSendFpretPointer + .cast< + ffi.NativeFunction< + ffi.Float Function( + ffi.Pointer, + ffi.Pointer, + ffi.Long, + ) + > + >() + .asFunction< + double Function( + ffi.Pointer, + ffi.Pointer, + int, + ) + >(); +late final _sel_pause = objc.registerName("pause"); +final _objc_msgSend_1pl9qdv = objc.msgSendPointer + .cast< + ffi.NativeFunction< + ffi.Void Function( + ffi.Pointer, + ffi.Pointer, + ) + > + >() + .asFunction< + void Function( + ffi.Pointer, + ffi.Pointer, + ) + >(); +late final _sel_peakPowerForChannel_ = objc.registerName( + "peakPowerForChannel:", +); +late final _sel_play = objc.registerName("play"); +late final _sel_playAtTime_ = objc.registerName("playAtTime:"); +final _objc_msgSend_18chyc = objc.msgSendPointer + .cast< + ffi.NativeFunction< + ffi.Bool Function( + ffi.Pointer, + ffi.Pointer, + ffi.Double, + ) + > + >() + .asFunction< + bool Function( + ffi.Pointer, + ffi.Pointer, + double, + ) + >(); +late final _sel_prepareToPlay = objc.registerName("prepareToPlay"); +late final _sel_setVolume_fadeDuration_ = objc.registerName( + "setVolume:fadeDuration:", +); +final _objc_msgSend_1p4uk9e = objc.msgSendPointer + .cast< + ffi.NativeFunction< + ffi.Void Function( + ffi.Pointer, + ffi.Pointer, + ffi.Float, + ffi.Double, + ) + > + >() + .asFunction< + void Function( + ffi.Pointer, + ffi.Pointer, + double, + double, + ) + >(); +late final _sel_stop = objc.registerName("stop"); +late final _sel_updateMeters = objc.registerName("updateMeters"); +late final _sel_init = objc.registerName("init"); +late final _sel_new = objc.registerName("new"); +late final _sel_allocWithZone_ = objc.registerName("allocWithZone:"); +final _objc_msgSend_1cwp428 = objc.msgSendPointer + .cast< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ) + > + >() + .asFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ) + >(); +late final _sel_alloc = objc.registerName("alloc"); +late final _sel_self = objc.registerName("self"); +ffi.Pointer _ObjCBlock_objcObjCObject_ffiVoid_fnPtrTrampoline( + ffi.Pointer block, + ffi.Pointer arg0, +) => block.ref.target + .cast< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Pointer arg0) + > + >() + .asFunction< + ffi.Pointer Function(ffi.Pointer) + >()(arg0); +ffi.Pointer _ObjCBlock_objcObjCObject_ffiVoid_fnPtrCallable = + ffi.Pointer.fromFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ) + >(_ObjCBlock_objcObjCObject_ffiVoid_fnPtrTrampoline) + .cast(); +ffi.Pointer +_ObjCBlock_objcObjCObject_ffiVoid_closureTrampoline( + ffi.Pointer block, + ffi.Pointer arg0, +) => + (objc.getBlockClosure(block) + as ffi.Pointer Function(ffi.Pointer))(arg0); +ffi.Pointer _ObjCBlock_objcObjCObject_ffiVoid_closureCallable = + ffi.Pointer.fromFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ) + >(_ObjCBlock_objcObjCObject_ffiVoid_closureTrampoline) + .cast(); + +/// Construction methods for `objc.ObjCBlock Function(ffi.Pointer)>`. +abstract final class ObjCBlock_objcObjCObject_ffiVoid { + /// Returns a block that wraps the given raw block pointer. + static objc.ObjCBlock< + ffi.Pointer Function(ffi.Pointer) + > + castFromPointer( + ffi.Pointer pointer, { + bool retain = false, + bool release = false, + }) => + objc.ObjCBlock< + ffi.Pointer Function(ffi.Pointer) + >(pointer, retain: retain, release: release); + + /// Creates a block from a C function pointer. + /// + /// This block must be invoked by native code running on the same thread as + /// the isolate that registered it. Invoking the block on the wrong thread + /// will result in a crash. + static objc.ObjCBlock< + ffi.Pointer Function(ffi.Pointer) + > + fromFunctionPointer( + ffi.Pointer< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Pointer arg0) + > + > + ptr, + ) => + objc.ObjCBlock< + ffi.Pointer Function(ffi.Pointer) + >( + objc.newPointerBlock( + _ObjCBlock_objcObjCObject_ffiVoid_fnPtrCallable, + ptr.cast(), + ), + retain: false, + release: true, + ); + + /// Creates a block from a Dart function. + /// + /// This block must be invoked by native code running on the same thread as + /// the isolate that registered it. Invoking the block on the wrong thread + /// will result in a crash. + /// + /// If `keepIsolateAlive` is true, this block will keep this isolate alive + /// until it is garbage collected by both Dart and ObjC. + static objc.ObjCBlock< + ffi.Pointer Function(ffi.Pointer) + > + fromFunction( + objc.ObjCObjectBase Function(ffi.Pointer) fn, { + bool keepIsolateAlive = true, + }) => + objc.ObjCBlock< + ffi.Pointer Function(ffi.Pointer) + >( + objc.newClosureBlock( + _ObjCBlock_objcObjCObject_ffiVoid_closureCallable, + (ffi.Pointer arg0) => fn(arg0).ref.retainAndAutorelease(), + keepIsolateAlive, + ), + retain: false, + release: true, + ); +} + +/// Call operator for `objc.ObjCBlock Function(ffi.Pointer)>`. +extension ObjCBlock_objcObjCObject_ffiVoid_CallExtension + on + objc.ObjCBlock< + ffi.Pointer Function(ffi.Pointer) + > { + objc.ObjCObjectBase call(ffi.Pointer arg0) => objc.ObjCObjectBase( + ref.pointer.ref.invoke + .cast< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer block, + ffi.Pointer arg0, + ) + > + >() + .asFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ) + >()(ref.pointer, arg0), + retain: true, + release: true, + ); +} + +late final _sel_retain = objc.registerName("retain"); +late final _sel_autorelease = objc.registerName("autorelease"); + +/// AVAudioPlayerWrapper +class AVAudioPlayerWrapper extends objc.NSObject { + AVAudioPlayerWrapper._( + ffi.Pointer pointer, { + bool retain = false, + bool release = false, + }) : super.castFromPointer(pointer, retain: retain, release: release); + + /// Constructs a [AVAudioPlayerWrapper] that points to the same underlying object as [other]. + AVAudioPlayerWrapper.castFrom(objc.ObjCObjectBase other) + : this._(other.ref.pointer, retain: true, release: true); + + /// Constructs a [AVAudioPlayerWrapper] that wraps the given raw object pointer. + AVAudioPlayerWrapper.castFromPointer( + ffi.Pointer other, { + bool retain = false, + bool release = false, + }) : this._(other, retain: retain, release: release); + + /// Returns whether [obj] is an instance of [AVAudioPlayerWrapper]. + static bool isInstance(objc.ObjCObjectBase obj) { + return _objc_msgSend_19nvye5( + obj.ref.pointer, + _sel_isKindOfClass_, + _class_AVAudioPlayerWrapper, + ); + } + + /// currentDevice + objc.NSString? get currentDevice { + final _ret = _objc_msgSend_151sglz(this.ref.pointer, _sel_currentDevice); + return _ret.address == 0 + ? null + : objc.NSString.castFromPointer(_ret, retain: true, release: true); + } + + /// setCurrentDevice: + set currentDevice(objc.NSString? value) { + _objc_msgSend_xtuoz7( + this.ref.pointer, + _sel_setCurrentDevice_, + value?.ref.pointer ?? ffi.nullptr, + ); + } + + /// currentTime + double get currentTime { + return objc.useMsgSendVariants + ? _objc_msgSend_1ukqyt8Fpret(this.ref.pointer, _sel_currentTime) + : _objc_msgSend_1ukqyt8(this.ref.pointer, _sel_currentTime); + } + + /// setCurrentTime: + set currentTime(double value) { + _objc_msgSend_hwm8nu(this.ref.pointer, _sel_setCurrentTime_, value); + } + + /// deviceCurrentTime + double get deviceCurrentTime { + return objc.useMsgSendVariants + ? _objc_msgSend_1ukqyt8Fpret(this.ref.pointer, _sel_deviceCurrentTime) + : _objc_msgSend_1ukqyt8(this.ref.pointer, _sel_deviceCurrentTime); + } + + /// duration + double get duration { + return objc.useMsgSendVariants + ? _objc_msgSend_1ukqyt8Fpret(this.ref.pointer, _sel_duration) + : _objc_msgSend_1ukqyt8(this.ref.pointer, _sel_duration); + } + + /// enableRate + bool get enableRate { + return _objc_msgSend_91o635(this.ref.pointer, _sel_enableRate); + } + + /// setEnableRate: + set enableRate(bool value) { + _objc_msgSend_1s56lr9(this.ref.pointer, _sel_setEnableRate_, value); + } + + /// format + AVAudioFormatWrapper get format { + final _ret = _objc_msgSend_151sglz(this.ref.pointer, _sel_format); + return AVAudioFormatWrapper.castFromPointer( + _ret, + retain: true, + release: true, + ); + } + + /// isMeteringEnabled + bool get isMeteringEnabled { + return _objc_msgSend_91o635(this.ref.pointer, _sel_isMeteringEnabled); + } + + /// setIsMeteringEnabled: + set isMeteringEnabled(bool value) { + _objc_msgSend_1s56lr9(this.ref.pointer, _sel_setIsMeteringEnabled_, value); + } + + /// numberOfChannels + int get numberOfChannels { + return _objc_msgSend_1hz7y9r(this.ref.pointer, _sel_numberOfChannels); + } + + /// numberOfLoops + int get numberOfLoops { + return _objc_msgSend_1hz7y9r(this.ref.pointer, _sel_numberOfLoops); + } + + /// setNumberOfLoops: + set numberOfLoops(int value) { + _objc_msgSend_4sp4xj(this.ref.pointer, _sel_setNumberOfLoops_, value); + } + + /// pan + double get pan { + return objc.useMsgSendVariants + ? _objc_msgSend_2cgrxlFpret(this.ref.pointer, _sel_pan) + : _objc_msgSend_2cgrxl(this.ref.pointer, _sel_pan); + } + + /// setPan: + set pan(double value) { + _objc_msgSend_v5hmet(this.ref.pointer, _sel_setPan_, value); + } + + /// isPlaying + bool get isPlaying { + return _objc_msgSend_91o635(this.ref.pointer, _sel_isPlaying); + } + + /// rate + double get rate { + return objc.useMsgSendVariants + ? _objc_msgSend_2cgrxlFpret(this.ref.pointer, _sel_rate) + : _objc_msgSend_2cgrxl(this.ref.pointer, _sel_rate); + } + + /// setRate: + set rate(double value) { + _objc_msgSend_v5hmet(this.ref.pointer, _sel_setRate_, value); + } + + /// url + objc.NSURL? get url { + final _ret = _objc_msgSend_151sglz(this.ref.pointer, _sel_url); + return _ret.address == 0 + ? null + : objc.NSURL.castFromPointer(_ret, retain: true, release: true); + } + + /// volume + double get volume { + return objc.useMsgSendVariants + ? _objc_msgSend_2cgrxlFpret(this.ref.pointer, _sel_volume) + : _objc_msgSend_2cgrxl(this.ref.pointer, _sel_volume); + } + + /// setVolume: + set volume(double value) { + _objc_msgSend_v5hmet(this.ref.pointer, _sel_setVolume_, value); + } + + /// initWithContentsOf:error: + AVAudioPlayerWrapper? initWithContentsOf( + objc.NSURL url$1, { + required ffi.Pointer> error, + }) { + final _ret = _objc_msgSend_1lhpu4m( + this.ref.retainAndReturnPointer(), + _sel_initWithContentsOf_error_, + url$1.ref.pointer, + error, + ); + return _ret.address == 0 + ? null + : AVAudioPlayerWrapper.castFromPointer( + _ret, + retain: false, + release: true, + ); + } + + /// initWithContentsOf:fileTypeHint:error: + AVAudioPlayerWrapper? initWithContentsOf$1( + objc.NSURL url$1, { + objc.NSString? fileTypeHint, + required ffi.Pointer> error, + }) { + final _ret = _objc_msgSend_1pnyuds( + this.ref.retainAndReturnPointer(), + _sel_initWithContentsOf_fileTypeHint_error_, + url$1.ref.pointer, + fileTypeHint?.ref.pointer ?? ffi.nullptr, + error, + ); + return _ret.address == 0 + ? null + : AVAudioPlayerWrapper.castFromPointer( + _ret, + retain: false, + release: true, + ); + } + + /// averagePowerForChannel: + double averagePowerForChannel(int channelNumber) { + return objc.useMsgSendVariants + ? _objc_msgSend_1o3b4v9Fpret( + this.ref.pointer, + _sel_averagePowerForChannel_, + channelNumber, + ) + : _objc_msgSend_1o3b4v9( + this.ref.pointer, + _sel_averagePowerForChannel_, + channelNumber, + ); + } + + /// pause + void pause() { + _objc_msgSend_1pl9qdv(this.ref.pointer, _sel_pause); + } + + /// peakPowerForChannel: + double peakPowerForChannel(int channelNumber) { + return objc.useMsgSendVariants + ? _objc_msgSend_1o3b4v9Fpret( + this.ref.pointer, + _sel_peakPowerForChannel_, + channelNumber, + ) + : _objc_msgSend_1o3b4v9( + this.ref.pointer, + _sel_peakPowerForChannel_, + channelNumber, + ); + } + + /// play + bool play() { + return _objc_msgSend_91o635(this.ref.pointer, _sel_play); + } + + /// playAtTime: + bool playAtTime(double time) { + return _objc_msgSend_18chyc(this.ref.pointer, _sel_playAtTime_, time); + } + + /// prepareToPlay + bool prepareToPlay() { + return _objc_msgSend_91o635(this.ref.pointer, _sel_prepareToPlay); + } + + /// setVolume:fadeDuration: + void setVolume(double volume$1, {required double fadeDuration}) { + _objc_msgSend_1p4uk9e( + this.ref.pointer, + _sel_setVolume_fadeDuration_, + volume$1, + fadeDuration, + ); + } + + /// stop + void stop() { + _objc_msgSend_1pl9qdv(this.ref.pointer, _sel_stop); + } + + /// updateMeters + void updateMeters() { + _objc_msgSend_1pl9qdv(this.ref.pointer, _sel_updateMeters); + } + + /// init + AVAudioPlayerWrapper init() { + objc.checkOsVersionInternal( + 'AVAudioPlayerWrapper.init', + iOS: (false, (2, 0, 0)), + macOS: (false, (10, 0, 0)), + ); + final _ret = _objc_msgSend_151sglz( + this.ref.retainAndReturnPointer(), + _sel_init, + ); + return AVAudioPlayerWrapper.castFromPointer( + _ret, + retain: false, + release: true, + ); + } + + /// new + static AVAudioPlayerWrapper new$() { + final _ret = _objc_msgSend_151sglz(_class_AVAudioPlayerWrapper, _sel_new); + return AVAudioPlayerWrapper.castFromPointer( + _ret, + retain: false, + release: true, + ); + } + + /// allocWithZone: + static AVAudioPlayerWrapper allocWithZone(ffi.Pointer zone) { + final _ret = _objc_msgSend_1cwp428( + _class_AVAudioPlayerWrapper, + _sel_allocWithZone_, + zone, + ); + return AVAudioPlayerWrapper.castFromPointer( + _ret, + retain: false, + release: true, + ); + } + + /// alloc + static AVAudioPlayerWrapper alloc() { + final _ret = _objc_msgSend_151sglz(_class_AVAudioPlayerWrapper, _sel_alloc); + return AVAudioPlayerWrapper.castFromPointer( + _ret, + retain: false, + release: true, + ); + } + + /// self + AVAudioPlayerWrapper self$1() { + final _ret = _objc_msgSend_151sglz(this.ref.pointer, _sel_self); + return AVAudioPlayerWrapper.castFromPointer( + _ret, + retain: true, + release: true, + ); + } + + /// retain + AVAudioPlayerWrapper retain() { + final _ret = _objc_msgSend_151sglz(this.ref.pointer, _sel_retain); + return AVAudioPlayerWrapper.castFromPointer( + _ret, + retain: true, + release: true, + ); + } + + /// autorelease + AVAudioPlayerWrapper autorelease() { + final _ret = _objc_msgSend_151sglz(this.ref.pointer, _sel_autorelease); + return AVAudioPlayerWrapper.castFromPointer( + _ret, + retain: true, + release: true, + ); + } + + /// Returns a new instance of AVAudioPlayerWrapper constructed with the default `new` method. + factory AVAudioPlayerWrapper() => new$(); +} diff --git a/pkgs/swiftgen/example/avf_audio_wrapper.m b/pkgs/swiftgen/example/avf_audio_wrapper.m new file mode 100644 index 0000000000..6a0491f353 --- /dev/null +++ b/pkgs/swiftgen/example/avf_audio_wrapper.m @@ -0,0 +1,59 @@ +#include +#import +#import +#import "temp/AVFAudioWrapper.h" + +#if !__has_feature(objc_arc) +#error "This file must be compiled with ARC enabled" +#endif + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wundeclared-selector" + +typedef struct { + int64_t version; + void* (*newWaiter)(void); + void (*awaitWaiter)(void*); + void* (*currentIsolate)(void); + void (*enterIsolate)(void*); + void (*exitIsolate)(void); + int64_t (*getMainPortId)(void); + bool (*getCurrentThreadOwnsIsolate)(int64_t); +} DOBJC_Context; + +id objc_retainBlock(id); + +#define BLOCKING_BLOCK_IMPL(ctx, BLOCK_SIG, INVOKE_DIRECT, INVOKE_LISTENER) \ + assert(ctx->version >= 1); \ + void* targetIsolate = ctx->currentIsolate(); \ + int64_t targetPort = ctx->getMainPortId == NULL ? 0 : ctx->getMainPortId(); \ + return BLOCK_SIG { \ + void* currentIsolate = ctx->currentIsolate(); \ + bool mayEnterIsolate = \ + currentIsolate == NULL && \ + ctx->getCurrentThreadOwnsIsolate != NULL && \ + ctx->getCurrentThreadOwnsIsolate(targetPort); \ + if (currentIsolate == targetIsolate || mayEnterIsolate) { \ + if (mayEnterIsolate) { \ + ctx->enterIsolate(targetIsolate); \ + } \ + INVOKE_DIRECT; \ + if (mayEnterIsolate) { \ + ctx->exitIsolate(); \ + } \ + } else { \ + void* waiter = ctx->newWaiter(); \ + INVOKE_LISTENER; \ + ctx->awaitWaiter(waiter); \ + } \ + }; + + +typedef id (^ProtocolTrampoline)(void * sel); +__attribute__((visibility("default"))) __attribute__((used)) +id _AVFAudioWrapper_protocolTrampoline_1mbt9g9(id target, void * sel) { + return ((ProtocolTrampoline)((id (*)(id, SEL, SEL))objc_msgSend)(target, @selector(getDOBJCDartProtocolMethodForSelector:), sel))(sel); +} +#undef BLOCKING_BLOCK_IMPL + +#pragma clang diagnostic pop diff --git a/pkgs/swiftgen/example/avf_audio_wrapper.swift b/pkgs/swiftgen/example/avf_audio_wrapper.swift new file mode 100644 index 0000000000..ff373096ca --- /dev/null +++ b/pkgs/swiftgen/example/avf_audio_wrapper.swift @@ -0,0 +1,2870 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import AVFAudio +import Foundation + +@objc public class AVAudioPlayerNodeBufferOptionsWrapper: NSObject { + var wrappedInstance: AVAudioPlayerNodeBufferOptions + + @objc static public var interrupts: AVAudioPlayerNodeBufferOptionsWrapper { + get { + AVAudioPlayerNodeBufferOptionsWrapper(AVAudioPlayerNodeBufferOptions.interrupts) + } + } + + @objc static public var interruptsAtLoop: AVAudioPlayerNodeBufferOptionsWrapper { + get { + AVAudioPlayerNodeBufferOptionsWrapper(AVAudioPlayerNodeBufferOptions.interruptsAtLoop) + } + } + + @objc static public var loops: AVAudioPlayerNodeBufferOptionsWrapper { + get { + AVAudioPlayerNodeBufferOptionsWrapper(AVAudioPlayerNodeBufferOptions.loops) + } + } + + @objc public var isEmpty: Bool { + get { + wrappedInstance.isEmpty + } + } + + init(_ wrappedInstance: AVAudioPlayerNodeBufferOptions) { + self.wrappedInstance = wrappedInstance + } + + @objc override init() { + wrappedInstance = AVAudioPlayerNodeBufferOptions() + } + +} + +@objc public class AVAudioSessionActivationOptionsWrapper: NSObject { + var wrappedInstance: AVAudioSessionActivationOptions + + @objc public var isEmpty: Bool { + get { + wrappedInstance.isEmpty + } + } + + init(_ wrappedInstance: AVAudioSessionActivationOptions) { + self.wrappedInstance = wrappedInstance + } + + @objc override init() { + wrappedInstance = AVAudioSessionActivationOptions() + } + +} + +@objc public class AVMusicSequenceLoadOptionsWrapper: NSObject { + var wrappedInstance: AVMusicSequenceLoadOptions + + @objc static public var smf_ChannelsToTracks: AVMusicSequenceLoadOptionsWrapper { + get { + AVMusicSequenceLoadOptionsWrapper(AVMusicSequenceLoadOptions.smf_ChannelsToTracks) + } + } + + @objc public var isEmpty: Bool { + get { + wrappedInstance.isEmpty + } + } + + init(_ wrappedInstance: AVMusicSequenceLoadOptions) { + self.wrappedInstance = wrappedInstance + } + + @objc override init() { + wrappedInstance = AVMusicSequenceLoadOptions() + } + +} + +@objc public class AVAudio3DAngularOrientationWrapper: NSObject { + var wrappedInstance: AVAudio3DAngularOrientation + + @objc public var pitch: Float { + get { + wrappedInstance.pitch + } + set { + wrappedInstance.pitch = newValue + } + } + + @objc public var roll: Float { + get { + wrappedInstance.roll + } + set { + wrappedInstance.roll = newValue + } + } + + @objc public var yaw: Float { + get { + wrappedInstance.yaw + } + set { + wrappedInstance.yaw = newValue + } + } + + init(_ wrappedInstance: AVAudio3DAngularOrientation) { + self.wrappedInstance = wrappedInstance + } + + @objc init(yaw: Float, pitch: Float, roll: Float) { + wrappedInstance = AVAudio3DAngularOrientation(yaw: yaw, pitch: pitch, roll: roll) + } + + @objc override init() { + wrappedInstance = AVAudio3DAngularOrientation() + } + +} + +@objc public class AVAudio3DPointWrapper: NSObject { + var wrappedInstance: AVAudio3DPoint + + @objc public var x: Float { + get { + wrappedInstance.x + } + set { + wrappedInstance.x = newValue + } + } + + @objc public var y: Float { + get { + wrappedInstance.y + } + set { + wrappedInstance.y = newValue + } + } + + @objc public var z: Float { + get { + wrappedInstance.z + } + set { + wrappedInstance.z = newValue + } + } + + init(_ wrappedInstance: AVAudio3DPoint) { + self.wrappedInstance = wrappedInstance + } + + @objc init(x: Float, y: Float, z: Float) { + wrappedInstance = AVAudio3DPoint(x: x, y: y, z: z) + } + + @objc override init() { + wrappedInstance = AVAudio3DPoint() + } + +} + +@objc public class AVAudio3DVectorOrientationWrapper: NSObject { + var wrappedInstance: AVAudio3DVectorOrientation + + init(_ wrappedInstance: AVAudio3DVectorOrientation) { + self.wrappedInstance = wrappedInstance + } + + @objc override init() { + wrappedInstance = AVAudio3DVectorOrientation() + } + +} + +@objc public class AVAudioConverterPrimeInfoWrapper: NSObject { + var wrappedInstance: AVAudioConverterPrimeInfo + + init(_ wrappedInstance: AVAudioConverterPrimeInfo) { + self.wrappedInstance = wrappedInstance + } + + @objc override init() { + wrappedInstance = AVAudioConverterPrimeInfo() + } + +} + +@objc public class AVAudioVoiceProcessingOtherAudioDuckingConfigurationWrapper: NSObject { + var wrappedInstance: AVAudioVoiceProcessingOtherAudioDuckingConfiguration + + init(_ wrappedInstance: AVAudioVoiceProcessingOtherAudioDuckingConfiguration) { + self.wrappedInstance = wrappedInstance + } + + @objc override init() { + wrappedInstance = AVAudioVoiceProcessingOtherAudioDuckingConfiguration() + } + +} + +@objc public class AVAUPresetEventWrapper: NSObject { + var wrappedInstance: AVAUPresetEvent + + init(_ wrappedInstance: AVAUPresetEvent) { + self.wrappedInstance = wrappedInstance + } + +} + +@objc public class AVAudioApplicationWrapper: NSObject { + var wrappedInstance: AVAudioApplication + + @objc static public var muteStateKey: String { + get { + AVAudioApplication.muteStateKey + } + } + + @objc static public var shared: AVAudioApplicationWrapper { + get { + AVAudioApplicationWrapper(AVAudioApplication.shared) + } + } + + @objc public var isInputMuted: Bool { + get { + wrappedInstance.isInputMuted + } + } + + init(_ wrappedInstance: AVAudioApplication) { + self.wrappedInstance = wrappedInstance + } + + @objc public func setInputMuted(_ muted: Bool) throws { + return try wrappedInstance.setInputMuted(muted) + } + +} + +@objc public class AVAudioBufferWrapper: NSObject { + var wrappedInstance: AVAudioBuffer + + @objc public var format: AVAudioFormatWrapper { + get { + AVAudioFormatWrapper(wrappedInstance.format) + } + } + + init(_ wrappedInstance: AVAudioBuffer) { + self.wrappedInstance = wrappedInstance + } + +} + +@objc public class AVAudioChannelLayoutWrapper: NSObject { + var wrappedInstance: AVAudioChannelLayout + + init(_ wrappedInstance: AVAudioChannelLayout) { + self.wrappedInstance = wrappedInstance + } + +} + +@objc public class AVAudioCompressedBufferWrapper: NSObject { + var wrappedInstance: AVAudioCompressedBuffer + + @objc public var maximumPacketSize: Int { + get { + wrappedInstance.maximumPacketSize + } + } + + init(_ wrappedInstance: AVAudioCompressedBuffer) { + self.wrappedInstance = wrappedInstance + } + +} + +@objc public class AVAudioConnectionPointWrapper: NSObject { + var wrappedInstance: AVAudioConnectionPoint + + @objc public var node: AVAudioNodeWrapper? { + get { + wrappedInstance.node == nil ? nil : AVAudioNodeWrapper(wrappedInstance.node!) + } + } + + init(_ wrappedInstance: AVAudioConnectionPoint) { + self.wrappedInstance = wrappedInstance + } + +} + +@objc public class AVAudioConverterWrapper: NSObject { + var wrappedInstance: AVAudioConverter + + @objc public var bitRate: Int { + get { + wrappedInstance.bitRate + } + set { + wrappedInstance.bitRate = newValue + } + } + + @objc public var bitRateStrategy: String? { + get { + wrappedInstance.bitRateStrategy + } + set { + wrappedInstance.bitRateStrategy = newValue + } + } + + @objc public var dither: Bool { + get { + wrappedInstance.dither + } + set { + wrappedInstance.dither = newValue + } + } + + @objc public var downmix: Bool { + get { + wrappedInstance.downmix + } + set { + wrappedInstance.downmix = newValue + } + } + + @objc public var inputFormat: AVAudioFormatWrapper { + get { + AVAudioFormatWrapper(wrappedInstance.inputFormat) + } + } + + @objc public var maximumOutputPacketSize: Int { + get { + wrappedInstance.maximumOutputPacketSize + } + } + + @objc public var outputFormat: AVAudioFormatWrapper { + get { + AVAudioFormatWrapper(wrappedInstance.outputFormat) + } + } + + @objc public var primeInfo: AVAudioConverterPrimeInfoWrapper { + get { + AVAudioConverterPrimeInfoWrapper(wrappedInstance.primeInfo) + } + set { + wrappedInstance.primeInfo = newValue.wrappedInstance + } + } + + @objc public var sampleRateConverterAlgorithm: String? { + get { + wrappedInstance.sampleRateConverterAlgorithm + } + set { + wrappedInstance.sampleRateConverterAlgorithm = newValue + } + } + + @objc public var sampleRateConverterQuality: Int { + get { + wrappedInstance.sampleRateConverterQuality + } + set { + wrappedInstance.sampleRateConverterQuality = newValue + } + } + + init(_ wrappedInstance: AVAudioConverter) { + self.wrappedInstance = wrappedInstance + } + + @objc init?(from fromFormat: AVAudioFormatWrapper, to toFormat: AVAudioFormatWrapper) { + if let instance = AVAudioConverter(from: fromFormat.wrappedInstance, to: toFormat.wrappedInstance) { + wrappedInstance = instance + } else { + return nil + } + } + + @objc public func convert(to outputBuffer: AVAudioPCMBufferWrapper, from inputBuffer: AVAudioPCMBufferWrapper) throws { + return try wrappedInstance.convert(to: outputBuffer.wrappedInstance, from: inputBuffer.wrappedInstance) + } + + @objc public func reset() { + return wrappedInstance.reset() + } + +} + +@objc public class AVAudioEngineWrapper: NSObject { + var wrappedInstance: AVAudioEngine + + @objc public var isAutoShutdownEnabled: Bool { + get { + wrappedInstance.isAutoShutdownEnabled + } + set { + wrappedInstance.isAutoShutdownEnabled = newValue + } + } + + @objc public var inputNode: AVAudioInputNodeWrapper { + get { + AVAudioInputNodeWrapper(wrappedInstance.inputNode) + } + } + + @objc public var isInManualRenderingMode: Bool { + get { + wrappedInstance.isInManualRenderingMode + } + } + + @objc public var mainMixerNode: AVAudioMixerNodeWrapper { + get { + AVAudioMixerNodeWrapper(wrappedInstance.mainMixerNode) + } + } + + @objc public var manualRenderingFormat: AVAudioFormatWrapper { + get { + AVAudioFormatWrapper(wrappedInstance.manualRenderingFormat) + } + } + + @objc public var outputNode: AVAudioOutputNodeWrapper { + get { + AVAudioOutputNodeWrapper(wrappedInstance.outputNode) + } + } + + @objc public var isRunning: Bool { + get { + wrappedInstance.isRunning + } + } + + init(_ wrappedInstance: AVAudioEngine) { + self.wrappedInstance = wrappedInstance + } + + @objc override init() { + wrappedInstance = AVAudioEngine() + } + + @objc public func attach(_ node: AVAudioNodeWrapper) { + return wrappedInstance.attach(node.wrappedInstance) + } + + @objc public func connect(_ node1: AVAudioNodeWrapper, to node2: AVAudioNodeWrapper, format: AVAudioFormatWrapper?) { + return wrappedInstance.connect(node1.wrappedInstance, to: node2.wrappedInstance, format: format?.wrappedInstance) + } + + @objc public func detach(_ node: AVAudioNodeWrapper) { + return wrappedInstance.detach(node.wrappedInstance) + } + + @objc public func disableManualRenderingMode() { + return wrappedInstance.disableManualRenderingMode() + } + + @objc public func disconnectMIDI(_ sourceNode: AVAudioNodeWrapper, from destinationNode: AVAudioNodeWrapper) { + return wrappedInstance.disconnectMIDI(sourceNode.wrappedInstance, from: destinationNode.wrappedInstance) + } + + @objc public func disconnectMIDIInput(_ node: AVAudioNodeWrapper) { + return wrappedInstance.disconnectMIDIInput(node.wrappedInstance) + } + + @objc public func disconnectMIDIOutput(_ node: AVAudioNodeWrapper) { + return wrappedInstance.disconnectMIDIOutput(node.wrappedInstance) + } + + @objc public func disconnectNodeInput(_ node: AVAudioNodeWrapper) { + return wrappedInstance.disconnectNodeInput(node.wrappedInstance) + } + + @objc public func disconnectNodeOutput(_ node: AVAudioNodeWrapper) { + return wrappedInstance.disconnectNodeOutput(node.wrappedInstance) + } + + @objc public func pause() { + return wrappedInstance.pause() + } + + @objc public func prepare() { + return wrappedInstance.prepare() + } + + @objc public func reset() { + return wrappedInstance.reset() + } + + @objc public func start() throws { + return try wrappedInstance.start() + } + + @objc public func stop() { + return wrappedInstance.stop() + } + +} + +@objc public class AVAudioEnvironmentDistanceAttenuationParametersWrapper: NSObject { + var wrappedInstance: AVAudioEnvironmentDistanceAttenuationParameters + + @objc public var maximumDistance: Float { + get { + wrappedInstance.maximumDistance + } + set { + wrappedInstance.maximumDistance = newValue + } + } + + @objc public var referenceDistance: Float { + get { + wrappedInstance.referenceDistance + } + set { + wrappedInstance.referenceDistance = newValue + } + } + + @objc public var rolloffFactor: Float { + get { + wrappedInstance.rolloffFactor + } + set { + wrappedInstance.rolloffFactor = newValue + } + } + + init(_ wrappedInstance: AVAudioEnvironmentDistanceAttenuationParameters) { + self.wrappedInstance = wrappedInstance + } + +} + +@objc public class AVAudioEnvironmentNodeWrapper: NSObject { + var wrappedInstance: AVAudioEnvironmentNode + + @objc public var distanceAttenuationParameters: AVAudioEnvironmentDistanceAttenuationParametersWrapper { + get { + AVAudioEnvironmentDistanceAttenuationParametersWrapper(wrappedInstance.distanceAttenuationParameters) + } + } + + @objc public var listenerAngularOrientation: AVAudio3DAngularOrientationWrapper { + get { + AVAudio3DAngularOrientationWrapper(wrappedInstance.listenerAngularOrientation) + } + set { + wrappedInstance.listenerAngularOrientation = newValue.wrappedInstance + } + } + + @objc public var listenerPosition: AVAudio3DPointWrapper { + get { + AVAudio3DPointWrapper(wrappedInstance.listenerPosition) + } + set { + wrappedInstance.listenerPosition = newValue.wrappedInstance + } + } + + @objc public var listenerVectorOrientation: AVAudio3DVectorOrientationWrapper { + get { + AVAudio3DVectorOrientationWrapper(wrappedInstance.listenerVectorOrientation) + } + set { + wrappedInstance.listenerVectorOrientation = newValue.wrappedInstance + } + } + + @objc public var outputVolume: Float { + get { + wrappedInstance.outputVolume + } + set { + wrappedInstance.outputVolume = newValue + } + } + + @objc public var reverbParameters: AVAudioEnvironmentReverbParametersWrapper { + get { + AVAudioEnvironmentReverbParametersWrapper(wrappedInstance.reverbParameters) + } + } + + init(_ wrappedInstance: AVAudioEnvironmentNode) { + self.wrappedInstance = wrappedInstance + } + + @objc override init() { + wrappedInstance = AVAudioEnvironmentNode() + } + +} + +@objc public class AVAudioEnvironmentReverbParametersWrapper: NSObject { + var wrappedInstance: AVAudioEnvironmentReverbParameters + + @objc public var enable: Bool { + get { + wrappedInstance.enable + } + set { + wrappedInstance.enable = newValue + } + } + + @objc public var filterParameters: AVAudioUnitEQFilterParametersWrapper { + get { + AVAudioUnitEQFilterParametersWrapper(wrappedInstance.filterParameters) + } + } + + @objc public var level: Float { + get { + wrappedInstance.level + } + set { + wrappedInstance.level = newValue + } + } + + init(_ wrappedInstance: AVAudioEnvironmentReverbParameters) { + self.wrappedInstance = wrappedInstance + } + +} + +@objc public class AVAudioFileWrapper: NSObject { + var wrappedInstance: AVAudioFile + + @objc public var fileFormat: AVAudioFormatWrapper { + get { + AVAudioFormatWrapper(wrappedInstance.fileFormat) + } + } + + @objc public var processingFormat: AVAudioFormatWrapper { + get { + AVAudioFormatWrapper(wrappedInstance.processingFormat) + } + } + + @objc public var url: URL { + get { + wrappedInstance.url + } + } + + init(_ wrappedInstance: AVAudioFile) { + self.wrappedInstance = wrappedInstance + } + + @objc init(forReading fileURL: URL) throws { + wrappedInstance = try AVAudioFile(forReading: fileURL) + } + + @objc public func read(into buffer: AVAudioPCMBufferWrapper) throws { + return try wrappedInstance.read(into: buffer.wrappedInstance) + } + + @objc public func write(from buffer: AVAudioPCMBufferWrapper) throws { + return try wrappedInstance.write(from: buffer.wrappedInstance) + } + +} + +@objc public class AVAudioFormatWrapper: NSObject { + var wrappedInstance: AVAudioFormat + + @objc public var channelLayout: AVAudioChannelLayoutWrapper? { + get { + wrappedInstance.channelLayout == nil ? nil : AVAudioChannelLayoutWrapper(wrappedInstance.channelLayout!) + } + } + + @objc public var isInterleaved: Bool { + get { + wrappedInstance.isInterleaved + } + } + + @objc public var sampleRate: Double { + get { + wrappedInstance.sampleRate + } + } + + @objc public var isStandard: Bool { + get { + wrappedInstance.isStandard + } + } + + init(_ wrappedInstance: AVAudioFormat) { + self.wrappedInstance = wrappedInstance + } + + @objc init(standardFormatWithSampleRate sampleRate: Double, channelLayout layout: AVAudioChannelLayoutWrapper) { + wrappedInstance = AVAudioFormat(standardFormatWithSampleRate: sampleRate, channelLayout: layout.wrappedInstance) + } + +} + +@objc public class AVAudioIONodeWrapper: NSObject { + var wrappedInstance: AVAudioIONode + + @objc public var presentationLatency: TimeInterval { + get { + wrappedInstance.presentationLatency + } + } + + @objc public var isVoiceProcessingEnabled: Bool { + get { + wrappedInstance.isVoiceProcessingEnabled + } + } + + init(_ wrappedInstance: AVAudioIONode) { + self.wrappedInstance = wrappedInstance + } + + @objc public func setVoiceProcessingEnabled(_ enabled: Bool) throws { + return try wrappedInstance.setVoiceProcessingEnabled(enabled) + } + +} + +@objc public class AVAudioInputNodeWrapper: NSObject { + var wrappedInstance: AVAudioInputNode + + @objc public var isVoiceProcessingAGCEnabled: Bool { + get { + wrappedInstance.isVoiceProcessingAGCEnabled + } + set { + wrappedInstance.isVoiceProcessingAGCEnabled = newValue + } + } + + @objc public var isVoiceProcessingBypassed: Bool { + get { + wrappedInstance.isVoiceProcessingBypassed + } + set { + wrappedInstance.isVoiceProcessingBypassed = newValue + } + } + + @objc public var isVoiceProcessingInputMuted: Bool { + get { + wrappedInstance.isVoiceProcessingInputMuted + } + set { + wrappedInstance.isVoiceProcessingInputMuted = newValue + } + } + + @objc public var voiceProcessingOtherAudioDuckingConfiguration: AVAudioVoiceProcessingOtherAudioDuckingConfigurationWrapper { + get { + AVAudioVoiceProcessingOtherAudioDuckingConfigurationWrapper(wrappedInstance.voiceProcessingOtherAudioDuckingConfiguration) + } + set { + wrappedInstance.voiceProcessingOtherAudioDuckingConfiguration = newValue.wrappedInstance + } + } + + init(_ wrappedInstance: AVAudioInputNode) { + self.wrappedInstance = wrappedInstance + } + +} + +@objc public class AVAudioMixerNodeWrapper: NSObject { + var wrappedInstance: AVAudioMixerNode + + @objc public var outputVolume: Float { + get { + wrappedInstance.outputVolume + } + set { + wrappedInstance.outputVolume = newValue + } + } + + init(_ wrappedInstance: AVAudioMixerNode) { + self.wrappedInstance = wrappedInstance + } + + @objc override init() { + wrappedInstance = AVAudioMixerNode() + } + +} + +@objc public class AVAudioMixingDestinationWrapper: NSObject { + var wrappedInstance: AVAudioMixingDestination + + @objc public var connectionPoint: AVAudioConnectionPointWrapper { + get { + AVAudioConnectionPointWrapper(wrappedInstance.connectionPoint) + } + } + + init(_ wrappedInstance: AVAudioMixingDestination) { + self.wrappedInstance = wrappedInstance + } + +} + +@objc public class AVAudioNodeWrapper: NSObject { + var wrappedInstance: AVAudioNode + + @objc public var engine: AVAudioEngineWrapper? { + get { + wrappedInstance.engine == nil ? nil : AVAudioEngineWrapper(wrappedInstance.engine!) + } + } + + @objc public var lastRenderTime: AVAudioTimeWrapper? { + get { + wrappedInstance.lastRenderTime == nil ? nil : AVAudioTimeWrapper(wrappedInstance.lastRenderTime!) + } + } + + @objc public var latency: TimeInterval { + get { + wrappedInstance.latency + } + } + + @objc public var numberOfInputs: Int { + get { + wrappedInstance.numberOfInputs + } + } + + @objc public var numberOfOutputs: Int { + get { + wrappedInstance.numberOfOutputs + } + } + + @objc public var outputPresentationLatency: TimeInterval { + get { + wrappedInstance.outputPresentationLatency + } + } + + init(_ wrappedInstance: AVAudioNode) { + self.wrappedInstance = wrappedInstance + } + + @objc public func reset() { + return wrappedInstance.reset() + } + +} + +@objc public class AVAudioOutputNodeWrapper: NSObject { + var wrappedInstance: AVAudioOutputNode + + init(_ wrappedInstance: AVAudioOutputNode) { + self.wrappedInstance = wrappedInstance + } + +} + +@objc public class AVAudioPCMBufferWrapper: NSObject { + var wrappedInstance: AVAudioPCMBuffer + + @objc public var stride: Int { + get { + wrappedInstance.stride + } + } + + init(_ wrappedInstance: AVAudioPCMBuffer) { + self.wrappedInstance = wrappedInstance + } + +} + +@objc public class AVAudioPlayerWrapper: NSObject { + var wrappedInstance: AVAudioPlayer + + @objc public var currentDevice: String? { + get { + wrappedInstance.currentDevice + } + set { + wrappedInstance.currentDevice = newValue + } + } + + @objc public var currentTime: TimeInterval { + get { + wrappedInstance.currentTime + } + set { + wrappedInstance.currentTime = newValue + } + } + + @objc public var deviceCurrentTime: TimeInterval { + get { + wrappedInstance.deviceCurrentTime + } + } + + @objc public var duration: TimeInterval { + get { + wrappedInstance.duration + } + } + + @objc public var enableRate: Bool { + get { + wrappedInstance.enableRate + } + set { + wrappedInstance.enableRate = newValue + } + } + + @objc public var format: AVAudioFormatWrapper { + get { + AVAudioFormatWrapper(wrappedInstance.format) + } + } + + @objc public var isMeteringEnabled: Bool { + get { + wrappedInstance.isMeteringEnabled + } + set { + wrappedInstance.isMeteringEnabled = newValue + } + } + + @objc public var numberOfChannels: Int { + get { + wrappedInstance.numberOfChannels + } + } + + @objc public var numberOfLoops: Int { + get { + wrappedInstance.numberOfLoops + } + set { + wrappedInstance.numberOfLoops = newValue + } + } + + @objc public var pan: Float { + get { + wrappedInstance.pan + } + set { + wrappedInstance.pan = newValue + } + } + + @objc public var isPlaying: Bool { + get { + wrappedInstance.isPlaying + } + } + + @objc public var rate: Float { + get { + wrappedInstance.rate + } + set { + wrappedInstance.rate = newValue + } + } + + @objc public var url: URL? { + get { + wrappedInstance.url + } + } + + @objc public var volume: Float { + get { + wrappedInstance.volume + } + set { + wrappedInstance.volume = newValue + } + } + + init(_ wrappedInstance: AVAudioPlayer) { + self.wrappedInstance = wrappedInstance + } + + @objc init(contentsOf url: URL) throws { + wrappedInstance = try AVAudioPlayer(contentsOf: url) + } + + @objc init(contentsOf url: URL, fileTypeHint utiString: String?) throws { + wrappedInstance = try AVAudioPlayer(contentsOf: url, fileTypeHint: utiString) + } + + @objc public func averagePower(forChannel channelNumber: Int) -> Float { + return wrappedInstance.averagePower(forChannel: channelNumber) + } + + @objc public func pause() { + return wrappedInstance.pause() + } + + @objc public func peakPower(forChannel channelNumber: Int) -> Float { + return wrappedInstance.peakPower(forChannel: channelNumber) + } + + @objc public func play() -> Bool { + return wrappedInstance.play() + } + + @objc public func play(atTime time: TimeInterval) -> Bool { + return wrappedInstance.play(atTime: time) + } + + @objc public func prepareToPlay() -> Bool { + return wrappedInstance.prepareToPlay() + } + + @objc public func setVolume(_ volume: Float, fadeDuration duration: TimeInterval) { + return wrappedInstance.setVolume(volume, fadeDuration: duration) + } + + @objc public func stop() { + return wrappedInstance.stop() + } + + @objc public func updateMeters() { + return wrappedInstance.updateMeters() + } + +} + +@objc public class AVAudioPlayerNodeWrapper: NSObject { + var wrappedInstance: AVAudioPlayerNode + + @objc public var isPlaying: Bool { + get { + wrappedInstance.isPlaying + } + } + + init(_ wrappedInstance: AVAudioPlayerNode) { + self.wrappedInstance = wrappedInstance + } + + @objc override init() { + wrappedInstance = AVAudioPlayerNode() + } + + @objc public func nodeTime(forPlayerTime playerTime: AVAudioTimeWrapper) -> AVAudioTimeWrapper? { + let result = wrappedInstance.nodeTime(forPlayerTime: playerTime.wrappedInstance) + return result == nil ? nil : AVAudioTimeWrapper(result!) + } + + @objc public func pause() { + return wrappedInstance.pause() + } + + @objc public func play() { + return wrappedInstance.play() + } + + @objc public func play(at when: AVAudioTimeWrapper?) { + return wrappedInstance.play(at: when?.wrappedInstance) + } + + @objc public func playerTime(forNodeTime nodeTime: AVAudioTimeWrapper) -> AVAudioTimeWrapper? { + let result = wrappedInstance.playerTime(forNodeTime: nodeTime.wrappedInstance) + return result == nil ? nil : AVAudioTimeWrapper(result!) + } + + @objc public func scheduleBuffer(_ buffer: AVAudioPCMBufferWrapper) async { + return await wrappedInstance.scheduleBuffer(buffer.wrappedInstance) + } + + @objc public func scheduleFile(_ file: AVAudioFileWrapper, at when: AVAudioTimeWrapper?) async { + return await wrappedInstance.scheduleFile(file.wrappedInstance, at: when?.wrappedInstance) + } + + @objc public func stop() { + return wrappedInstance.stop() + } + +} + +@objc public class AVAudioRecorderWrapper: NSObject { + var wrappedInstance: AVAudioRecorder + + @objc public var currentTime: TimeInterval { + get { + wrappedInstance.currentTime + } + } + + @objc public var deviceCurrentTime: TimeInterval { + get { + wrappedInstance.deviceCurrentTime + } + } + + @objc public var format: AVAudioFormatWrapper { + get { + AVAudioFormatWrapper(wrappedInstance.format) + } + } + + @objc public var isMeteringEnabled: Bool { + get { + wrappedInstance.isMeteringEnabled + } + set { + wrappedInstance.isMeteringEnabled = newValue + } + } + + @objc public var isRecording: Bool { + get { + wrappedInstance.isRecording + } + } + + @objc public var url: URL { + get { + wrappedInstance.url + } + } + + init(_ wrappedInstance: AVAudioRecorder) { + self.wrappedInstance = wrappedInstance + } + + @objc init(url: URL, format: AVAudioFormatWrapper) throws { + wrappedInstance = try AVAudioRecorder(url: url, format: format.wrappedInstance) + } + + @objc public func averagePower(forChannel channelNumber: Int) -> Float { + return wrappedInstance.averagePower(forChannel: channelNumber) + } + + @objc public func deleteRecording() -> Bool { + return wrappedInstance.deleteRecording() + } + + @objc public func pause() { + return wrappedInstance.pause() + } + + @objc public func peakPower(forChannel channelNumber: Int) -> Float { + return wrappedInstance.peakPower(forChannel: channelNumber) + } + + @objc public func prepareToRecord() -> Bool { + return wrappedInstance.prepareToRecord() + } + + @objc public func record() -> Bool { + return wrappedInstance.record() + } + + @objc public func record(atTime time: TimeInterval) -> Bool { + return wrappedInstance.record(atTime: time) + } + + @objc public func record(atTime time: TimeInterval, forDuration duration: TimeInterval) -> Bool { + return wrappedInstance.record(atTime: time, forDuration: duration) + } + + @objc public func record(forDuration duration: TimeInterval) -> Bool { + return wrappedInstance.record(forDuration: duration) + } + + @objc public func stop() { + return wrappedInstance.stop() + } + + @objc public func updateMeters() { + return wrappedInstance.updateMeters() + } + +} + +@objc public class AVAudioRoutingArbiterWrapper: NSObject { + var wrappedInstance: AVAudioRoutingArbiter + + @objc static public var shared: AVAudioRoutingArbiterWrapper { + get { + AVAudioRoutingArbiterWrapper(AVAudioRoutingArbiter.shared) + } + } + + init(_ wrappedInstance: AVAudioRoutingArbiter) { + self.wrappedInstance = wrappedInstance + } + + @objc public func leave() { + return wrappedInstance.leave() + } + +} + +@objc public class AVAudioSequencerWrapper: NSObject { + var wrappedInstance: AVAudioSequencer + + @objc public var currentPositionInBeats: TimeInterval { + get { + wrappedInstance.currentPositionInBeats + } + set { + wrappedInstance.currentPositionInBeats = newValue + } + } + + @objc public var currentPositionInSeconds: TimeInterval { + get { + wrappedInstance.currentPositionInSeconds + } + set { + wrappedInstance.currentPositionInSeconds = newValue + } + } + + @objc public var isPlaying: Bool { + get { + wrappedInstance.isPlaying + } + } + + @objc public var rate: Float { + get { + wrappedInstance.rate + } + set { + wrappedInstance.rate = newValue + } + } + + @objc public var tempoTrack: AVMusicTrackWrapper { + get { + AVMusicTrackWrapper(wrappedInstance.tempoTrack) + } + } + + init(_ wrappedInstance: AVAudioSequencer) { + self.wrappedInstance = wrappedInstance + } + + @objc override init() { + wrappedInstance = AVAudioSequencer() + } + + @objc init(audioEngine engine: AVAudioEngineWrapper) { + wrappedInstance = AVAudioSequencer(audioEngine: engine.wrappedInstance) + } + + @objc public func createAndAppendTrack() -> AVMusicTrackWrapper { + let result = wrappedInstance.createAndAppendTrack() + return AVMusicTrackWrapper(result) + } + + @objc public func prepareToPlay() { + return wrappedInstance.prepareToPlay() + } + + @objc public func removeTrack(_ track: AVMusicTrackWrapper) -> Bool { + return wrappedInstance.removeTrack(track.wrappedInstance) + } + + @objc public func reverseEvents() { + return wrappedInstance.reverseEvents() + } + + @objc public func start() throws { + return try wrappedInstance.start() + } + + @objc public func stop() { + return wrappedInstance.stop() + } + + @objc public func write(to fileURL: URL, smpteResolution resolution: Int, replaceExisting replace: Bool) throws { + return try wrappedInstance.write(to: fileURL, smpteResolution: resolution, replaceExisting: replace) + } + + @objc public class InfoDictionaryKeyWrapper: NSObject { + var wrappedInstance: AVAudioSequencer.InfoDictionaryKey + + @objc static public var album: AVAudioSequencerWrapper.InfoDictionaryKeyWrapper { + get { + InfoDictionaryKeyWrapper(AVAudioSequencer.InfoDictionaryKey.album) + } + } + + @objc static public var approximateDurationInSeconds: AVAudioSequencerWrapper.InfoDictionaryKeyWrapper { + get { + InfoDictionaryKeyWrapper(AVAudioSequencer.InfoDictionaryKey.approximateDurationInSeconds) + } + } + + @objc static public var artist: AVAudioSequencerWrapper.InfoDictionaryKeyWrapper { + get { + InfoDictionaryKeyWrapper(AVAudioSequencer.InfoDictionaryKey.artist) + } + } + + @objc static public var channelLayout: AVAudioSequencerWrapper.InfoDictionaryKeyWrapper { + get { + InfoDictionaryKeyWrapper(AVAudioSequencer.InfoDictionaryKey.channelLayout) + } + } + + @objc static public var comments: AVAudioSequencerWrapper.InfoDictionaryKeyWrapper { + get { + InfoDictionaryKeyWrapper(AVAudioSequencer.InfoDictionaryKey.comments) + } + } + + @objc static public var composer: AVAudioSequencerWrapper.InfoDictionaryKeyWrapper { + get { + InfoDictionaryKeyWrapper(AVAudioSequencer.InfoDictionaryKey.composer) + } + } + + @objc static public var copyright: AVAudioSequencerWrapper.InfoDictionaryKeyWrapper { + get { + InfoDictionaryKeyWrapper(AVAudioSequencer.InfoDictionaryKey.copyright) + } + } + + @objc static public var encodingApplication: AVAudioSequencerWrapper.InfoDictionaryKeyWrapper { + get { + InfoDictionaryKeyWrapper(AVAudioSequencer.InfoDictionaryKey.encodingApplication) + } + } + + @objc static public var genre: AVAudioSequencerWrapper.InfoDictionaryKeyWrapper { + get { + InfoDictionaryKeyWrapper(AVAudioSequencer.InfoDictionaryKey.genre) + } + } + + @objc static public var ISRC: AVAudioSequencerWrapper.InfoDictionaryKeyWrapper { + get { + InfoDictionaryKeyWrapper(AVAudioSequencer.InfoDictionaryKey.ISRC) + } + } + + @objc static public var keySignature: AVAudioSequencerWrapper.InfoDictionaryKeyWrapper { + get { + InfoDictionaryKeyWrapper(AVAudioSequencer.InfoDictionaryKey.keySignature) + } + } + + @objc static public var lyricist: AVAudioSequencerWrapper.InfoDictionaryKeyWrapper { + get { + InfoDictionaryKeyWrapper(AVAudioSequencer.InfoDictionaryKey.lyricist) + } + } + + @objc static public var nominalBitRate: AVAudioSequencerWrapper.InfoDictionaryKeyWrapper { + get { + InfoDictionaryKeyWrapper(AVAudioSequencer.InfoDictionaryKey.nominalBitRate) + } + } + + @objc static public var recordedDate: AVAudioSequencerWrapper.InfoDictionaryKeyWrapper { + get { + InfoDictionaryKeyWrapper(AVAudioSequencer.InfoDictionaryKey.recordedDate) + } + } + + @objc static public var sourceBitDepth: AVAudioSequencerWrapper.InfoDictionaryKeyWrapper { + get { + InfoDictionaryKeyWrapper(AVAudioSequencer.InfoDictionaryKey.sourceBitDepth) + } + } + + @objc static public var sourceEncoder: AVAudioSequencerWrapper.InfoDictionaryKeyWrapper { + get { + InfoDictionaryKeyWrapper(AVAudioSequencer.InfoDictionaryKey.sourceEncoder) + } + } + + @objc static public var subTitle: AVAudioSequencerWrapper.InfoDictionaryKeyWrapper { + get { + InfoDictionaryKeyWrapper(AVAudioSequencer.InfoDictionaryKey.subTitle) + } + } + + @objc static public var tempo: AVAudioSequencerWrapper.InfoDictionaryKeyWrapper { + get { + InfoDictionaryKeyWrapper(AVAudioSequencer.InfoDictionaryKey.tempo) + } + } + + @objc static public var timeSignature: AVAudioSequencerWrapper.InfoDictionaryKeyWrapper { + get { + InfoDictionaryKeyWrapper(AVAudioSequencer.InfoDictionaryKey.timeSignature) + } + } + + @objc static public var title: AVAudioSequencerWrapper.InfoDictionaryKeyWrapper { + get { + InfoDictionaryKeyWrapper(AVAudioSequencer.InfoDictionaryKey.title) + } + } + + @objc static public var trackNumber: AVAudioSequencerWrapper.InfoDictionaryKeyWrapper { + get { + InfoDictionaryKeyWrapper(AVAudioSequencer.InfoDictionaryKey.trackNumber) + } + } + + @objc static public var year: AVAudioSequencerWrapper.InfoDictionaryKeyWrapper { + get { + InfoDictionaryKeyWrapper(AVAudioSequencer.InfoDictionaryKey.year) + } + } + + init(_ wrappedInstance: AVAudioSequencer.InfoDictionaryKey) { + self.wrappedInstance = wrappedInstance + } + + @objc init(rawValue: String) { + wrappedInstance = AVAudioSequencer.InfoDictionaryKey(rawValue: rawValue) + } + + } + +} + +@objc public class AVAudioSinkNodeWrapper: NSObject { + var wrappedInstance: AVAudioSinkNode + + init(_ wrappedInstance: AVAudioSinkNode) { + self.wrappedInstance = wrappedInstance + } + +} + +@objc public class AVAudioSourceNodeWrapper: NSObject { + var wrappedInstance: AVAudioSourceNode + + init(_ wrappedInstance: AVAudioSourceNode) { + self.wrappedInstance = wrappedInstance + } + +} + +@objc public class AVAudioTimeWrapper: NSObject { + var wrappedInstance: AVAudioTime + + @objc public var isHostTimeValid: Bool { + get { + wrappedInstance.isHostTimeValid + } + } + + @objc public var sampleRate: Double { + get { + wrappedInstance.sampleRate + } + } + + @objc public var isSampleTimeValid: Bool { + get { + wrappedInstance.isSampleTimeValid + } + } + + init(_ wrappedInstance: AVAudioTime) { + self.wrappedInstance = wrappedInstance + } + + @objc public func extrapolateTime(fromAnchor anchorTime: AVAudioTimeWrapper) -> AVAudioTimeWrapper? { + let result = wrappedInstance.extrapolateTime(fromAnchor: anchorTime.wrappedInstance) + return result == nil ? nil : AVAudioTimeWrapper(result!) + } + +} + +@objc public class AVAudioUnitWrapper: NSObject { + var wrappedInstance: AVAudioUnit + + @objc public var manufacturerName: String { + get { + wrappedInstance.manufacturerName + } + } + + @objc public var name: String { + get { + wrappedInstance.name + } + } + + @objc public var version: Int { + get { + wrappedInstance.version + } + } + + init(_ wrappedInstance: AVAudioUnit) { + self.wrappedInstance = wrappedInstance + } + + @objc public func loadPreset(at url: URL) throws { + return try wrappedInstance.loadPreset(at: url) + } + +} + +@objc public class AVAudioUnitComponentWrapper: NSObject { + var wrappedInstance: AVAudioUnitComponent + + @objc public var componentURL: URL? { + get { + wrappedInstance.componentURL + } + } + + @objc public var hasCustomView: Bool { + get { + wrappedInstance.hasCustomView + } + } + + @objc public var hasMIDIInput: Bool { + get { + wrappedInstance.hasMIDIInput + } + } + + @objc public var hasMIDIOutput: Bool { + get { + wrappedInstance.hasMIDIOutput + } + } + + @objc public var iconURL: URL? { + get { + wrappedInstance.iconURL + } + } + + @objc public var localizedTypeName: String { + get { + wrappedInstance.localizedTypeName + } + } + + @objc public var manufacturerName: String { + get { + wrappedInstance.manufacturerName + } + } + + @objc public var name: String { + get { + wrappedInstance.name + } + } + + @objc public var passesAUVal: Bool { + get { + wrappedInstance.passesAUVal + } + } + + @objc public var isSandboxSafe: Bool { + get { + wrappedInstance.isSandboxSafe + } + } + + @objc public var typeName: String { + get { + wrappedInstance.typeName + } + } + + @objc public var version: Int { + get { + wrappedInstance.version + } + } + + @objc public var versionString: String { + get { + wrappedInstance.versionString + } + } + + init(_ wrappedInstance: AVAudioUnitComponent) { + self.wrappedInstance = wrappedInstance + } + + @objc public func supportsNumberInputChannels(_ numInputChannels: Int, outputChannels numOutputChannels: Int) -> Bool { + return wrappedInstance.supportsNumberInputChannels(numInputChannels, outputChannels: numOutputChannels) + } + +} + +@objc public class AVAudioUnitComponentManagerWrapper: NSObject { + var wrappedInstance: AVAudioUnitComponentManager + + init(_ wrappedInstance: AVAudioUnitComponentManager) { + self.wrappedInstance = wrappedInstance + } + +} + +@objc public class AVAudioUnitDelayWrapper: NSObject { + var wrappedInstance: AVAudioUnitDelay + + @objc public var delayTime: TimeInterval { + get { + wrappedInstance.delayTime + } + set { + wrappedInstance.delayTime = newValue + } + } + + @objc public var feedback: Float { + get { + wrappedInstance.feedback + } + set { + wrappedInstance.feedback = newValue + } + } + + @objc public var lowPassCutoff: Float { + get { + wrappedInstance.lowPassCutoff + } + set { + wrappedInstance.lowPassCutoff = newValue + } + } + + @objc public var wetDryMix: Float { + get { + wrappedInstance.wetDryMix + } + set { + wrappedInstance.wetDryMix = newValue + } + } + + init(_ wrappedInstance: AVAudioUnitDelay) { + self.wrappedInstance = wrappedInstance + } + +} + +@objc public class AVAudioUnitDistortionWrapper: NSObject { + var wrappedInstance: AVAudioUnitDistortion + + @objc public var preGain: Float { + get { + wrappedInstance.preGain + } + set { + wrappedInstance.preGain = newValue + } + } + + @objc public var wetDryMix: Float { + get { + wrappedInstance.wetDryMix + } + set { + wrappedInstance.wetDryMix = newValue + } + } + + init(_ wrappedInstance: AVAudioUnitDistortion) { + self.wrappedInstance = wrappedInstance + } + +} + +@objc public class AVAudioUnitEQWrapper: NSObject { + var wrappedInstance: AVAudioUnitEQ + + @objc public var globalGain: Float { + get { + wrappedInstance.globalGain + } + set { + wrappedInstance.globalGain = newValue + } + } + + init(_ wrappedInstance: AVAudioUnitEQ) { + self.wrappedInstance = wrappedInstance + } + + @objc init(numberOfBands: Int) { + wrappedInstance = AVAudioUnitEQ(numberOfBands: numberOfBands) + } + +} + +@objc public class AVAudioUnitEQFilterParametersWrapper: NSObject { + var wrappedInstance: AVAudioUnitEQFilterParameters + + @objc public var bandwidth: Float { + get { + wrappedInstance.bandwidth + } + set { + wrappedInstance.bandwidth = newValue + } + } + + @objc public var bypass: Bool { + get { + wrappedInstance.bypass + } + set { + wrappedInstance.bypass = newValue + } + } + + @objc public var frequency: Float { + get { + wrappedInstance.frequency + } + set { + wrappedInstance.frequency = newValue + } + } + + @objc public var gain: Float { + get { + wrappedInstance.gain + } + set { + wrappedInstance.gain = newValue + } + } + + init(_ wrappedInstance: AVAudioUnitEQFilterParameters) { + self.wrappedInstance = wrappedInstance + } + +} + +@objc public class AVAudioUnitEffectWrapper: NSObject { + var wrappedInstance: AVAudioUnitEffect + + @objc public var bypass: Bool { + get { + wrappedInstance.bypass + } + set { + wrappedInstance.bypass = newValue + } + } + + init(_ wrappedInstance: AVAudioUnitEffect) { + self.wrappedInstance = wrappedInstance + } + +} + +@objc public class AVAudioUnitGeneratorWrapper: NSObject { + var wrappedInstance: AVAudioUnitGenerator + + @objc public var bypass: Bool { + get { + wrappedInstance.bypass + } + set { + wrappedInstance.bypass = newValue + } + } + + init(_ wrappedInstance: AVAudioUnitGenerator) { + self.wrappedInstance = wrappedInstance + } + +} + +@objc public class AVAudioUnitMIDIInstrumentWrapper: NSObject { + var wrappedInstance: AVAudioUnitMIDIInstrument + + init(_ wrappedInstance: AVAudioUnitMIDIInstrument) { + self.wrappedInstance = wrappedInstance + } + +} + +@objc public class AVAudioUnitReverbWrapper: NSObject { + var wrappedInstance: AVAudioUnitReverb + + @objc public var wetDryMix: Float { + get { + wrappedInstance.wetDryMix + } + set { + wrappedInstance.wetDryMix = newValue + } + } + + init(_ wrappedInstance: AVAudioUnitReverb) { + self.wrappedInstance = wrappedInstance + } + +} + +@objc public class AVAudioUnitSamplerWrapper: NSObject { + var wrappedInstance: AVAudioUnitSampler + + @objc public var globalTuning: Float { + get { + wrappedInstance.globalTuning + } + set { + wrappedInstance.globalTuning = newValue + } + } + + @objc public var masterGain: Float { + get { + wrappedInstance.masterGain + } + set { + wrappedInstance.masterGain = newValue + } + } + + @objc public var overallGain: Float { + get { + wrappedInstance.overallGain + } + set { + wrappedInstance.overallGain = newValue + } + } + + @objc public var stereoPan: Float { + get { + wrappedInstance.stereoPan + } + set { + wrappedInstance.stereoPan = newValue + } + } + + init(_ wrappedInstance: AVAudioUnitSampler) { + self.wrappedInstance = wrappedInstance + } + + @objc public func loadInstrument(at instrumentURL: URL) throws { + return try wrappedInstance.loadInstrument(at: instrumentURL) + } + +} + +@objc public class AVAudioUnitTimeEffectWrapper: NSObject { + var wrappedInstance: AVAudioUnitTimeEffect + + @objc public var bypass: Bool { + get { + wrappedInstance.bypass + } + set { + wrappedInstance.bypass = newValue + } + } + + init(_ wrappedInstance: AVAudioUnitTimeEffect) { + self.wrappedInstance = wrappedInstance + } + +} + +@objc public class AVAudioUnitTimePitchWrapper: NSObject { + var wrappedInstance: AVAudioUnitTimePitch + + @objc public var overlap: Float { + get { + wrappedInstance.overlap + } + set { + wrappedInstance.overlap = newValue + } + } + + @objc public var pitch: Float { + get { + wrappedInstance.pitch + } + set { + wrappedInstance.pitch = newValue + } + } + + @objc public var rate: Float { + get { + wrappedInstance.rate + } + set { + wrappedInstance.rate = newValue + } + } + + init(_ wrappedInstance: AVAudioUnitTimePitch) { + self.wrappedInstance = wrappedInstance + } + +} + +@objc public class AVAudioUnitVarispeedWrapper: NSObject { + var wrappedInstance: AVAudioUnitVarispeed + + @objc public var rate: Float { + get { + wrappedInstance.rate + } + set { + wrappedInstance.rate = newValue + } + } + + init(_ wrappedInstance: AVAudioUnitVarispeed) { + self.wrappedInstance = wrappedInstance + } + +} + +@objc public class AVExtendedNoteOnEventWrapper: NSObject { + var wrappedInstance: AVExtendedNoteOnEvent + + @objc public var midiNote: Float { + get { + wrappedInstance.midiNote + } + set { + wrappedInstance.midiNote = newValue + } + } + + @objc public var velocity: Float { + get { + wrappedInstance.velocity + } + set { + wrappedInstance.velocity = newValue + } + } + + init(_ wrappedInstance: AVExtendedNoteOnEvent) { + self.wrappedInstance = wrappedInstance + } + +} + +@objc public class AVExtendedTempoEventWrapper: NSObject { + var wrappedInstance: AVExtendedTempoEvent + + @objc public var tempo: Double { + get { + wrappedInstance.tempo + } + set { + wrappedInstance.tempo = newValue + } + } + + init(_ wrappedInstance: AVExtendedTempoEvent) { + self.wrappedInstance = wrappedInstance + } + + @objc init(tempo: Double) { + wrappedInstance = AVExtendedTempoEvent(tempo: tempo) + } + +} + +@objc public class AVMIDIChannelEventWrapper: NSObject { + var wrappedInstance: AVMIDIChannelEvent + + init(_ wrappedInstance: AVMIDIChannelEvent) { + self.wrappedInstance = wrappedInstance + } + +} + +@objc public class AVMIDIChannelPressureEventWrapper: NSObject { + var wrappedInstance: AVMIDIChannelPressureEvent + + init(_ wrappedInstance: AVMIDIChannelPressureEvent) { + self.wrappedInstance = wrappedInstance + } + +} + +@objc public class AVMIDIControlChangeEventWrapper: NSObject { + var wrappedInstance: AVMIDIControlChangeEvent + + init(_ wrappedInstance: AVMIDIControlChangeEvent) { + self.wrappedInstance = wrappedInstance + } + +} + +@objc public class AVMIDIMetaEventWrapper: NSObject { + var wrappedInstance: AVMIDIMetaEvent + + init(_ wrappedInstance: AVMIDIMetaEvent) { + self.wrappedInstance = wrappedInstance + } + +} + +@objc public class AVMIDINoteEventWrapper: NSObject { + var wrappedInstance: AVMIDINoteEvent + + init(_ wrappedInstance: AVMIDINoteEvent) { + self.wrappedInstance = wrappedInstance + } + +} + +@objc public class AVMIDIPitchBendEventWrapper: NSObject { + var wrappedInstance: AVMIDIPitchBendEvent + + init(_ wrappedInstance: AVMIDIPitchBendEvent) { + self.wrappedInstance = wrappedInstance + } + +} + +@objc public class AVMIDIPlayerWrapper: NSObject { + var wrappedInstance: AVMIDIPlayer + + @objc public var currentPosition: TimeInterval { + get { + wrappedInstance.currentPosition + } + set { + wrappedInstance.currentPosition = newValue + } + } + + @objc public var duration: TimeInterval { + get { + wrappedInstance.duration + } + } + + @objc public var isPlaying: Bool { + get { + wrappedInstance.isPlaying + } + } + + @objc public var rate: Float { + get { + wrappedInstance.rate + } + set { + wrappedInstance.rate = newValue + } + } + + init(_ wrappedInstance: AVMIDIPlayer) { + self.wrappedInstance = wrappedInstance + } + + @objc init(contentsOf inURL: URL, soundBankURL bankURL: URL?) throws { + wrappedInstance = try AVMIDIPlayer(contentsOf: inURL, soundBankURL: bankURL) + } + + @objc public func prepareToPlay() { + return wrappedInstance.prepareToPlay() + } + + @objc public func stop() { + return wrappedInstance.stop() + } + +} + +@objc public class AVMIDIPolyPressureEventWrapper: NSObject { + var wrappedInstance: AVMIDIPolyPressureEvent + + init(_ wrappedInstance: AVMIDIPolyPressureEvent) { + self.wrappedInstance = wrappedInstance + } + +} + +@objc public class AVMIDIProgramChangeEventWrapper: NSObject { + var wrappedInstance: AVMIDIProgramChangeEvent + + init(_ wrappedInstance: AVMIDIProgramChangeEvent) { + self.wrappedInstance = wrappedInstance + } + +} + +@objc public class AVMIDISysexEventWrapper: NSObject { + var wrappedInstance: AVMIDISysexEvent + + init(_ wrappedInstance: AVMIDISysexEvent) { + self.wrappedInstance = wrappedInstance + } + +} + +@objc public class AVMusicEventWrapper: NSObject { + var wrappedInstance: AVMusicEvent + + init(_ wrappedInstance: AVMusicEvent) { + self.wrappedInstance = wrappedInstance + } + +} + +@objc public class AVMusicTrackWrapper: NSObject { + var wrappedInstance: AVMusicTrack + + @objc public var destinationAudioUnit: AVAudioUnitWrapper? { + get { + wrappedInstance.destinationAudioUnit == nil ? nil : AVAudioUnitWrapper(wrappedInstance.destinationAudioUnit!) + } + set { + wrappedInstance.destinationAudioUnit = newValue?.wrappedInstance + } + } + + @objc public var lengthInSeconds: TimeInterval { + get { + wrappedInstance.lengthInSeconds + } + set { + wrappedInstance.lengthInSeconds = newValue + } + } + + @objc public var isLoopingEnabled: Bool { + get { + wrappedInstance.isLoopingEnabled + } + set { + wrappedInstance.isLoopingEnabled = newValue + } + } + + @objc public var isMuted: Bool { + get { + wrappedInstance.isMuted + } + set { + wrappedInstance.isMuted = newValue + } + } + + @objc public var numberOfLoops: Int { + get { + wrappedInstance.numberOfLoops + } + set { + wrappedInstance.numberOfLoops = newValue + } + } + + @objc public var isSoloed: Bool { + get { + wrappedInstance.isSoloed + } + set { + wrappedInstance.isSoloed = newValue + } + } + + @objc public var timeResolution: Int { + get { + wrappedInstance.timeResolution + } + } + + @objc public var usesAutomatedParameters: Bool { + get { + wrappedInstance.usesAutomatedParameters + } + set { + wrappedInstance.usesAutomatedParameters = newValue + } + } + + init(_ wrappedInstance: AVMusicTrack) { + self.wrappedInstance = wrappedInstance + } + +} + +@objc public class AVMusicUserEventWrapper: NSObject { + var wrappedInstance: AVMusicUserEvent + + init(_ wrappedInstance: AVMusicUserEvent) { + self.wrappedInstance = wrappedInstance + } + +} + +@objc public class AVParameterEventWrapper: NSObject { + var wrappedInstance: AVParameterEvent + + @objc public var value: Float { + get { + wrappedInstance.value + } + set { + wrappedInstance.value = newValue + } + } + + init(_ wrappedInstance: AVParameterEvent) { + self.wrappedInstance = wrappedInstance + } + +} + +@objc public class AVSpeechSynthesisMarkerWrapper: NSObject { + var wrappedInstance: AVSpeechSynthesisMarker + + @objc public var bookmarkName: String { + get { + wrappedInstance.bookmarkName + } + set { + wrappedInstance.bookmarkName = newValue + } + } + + @objc public var byteSampleOffset: Int { + get { + wrappedInstance.byteSampleOffset + } + set { + wrappedInstance.byteSampleOffset = newValue + } + } + + @objc public var phoneme: String { + get { + wrappedInstance.phoneme + } + set { + wrappedInstance.phoneme = newValue + } + } + + init(_ wrappedInstance: AVSpeechSynthesisMarker) { + self.wrappedInstance = wrappedInstance + } + + @objc init(bookmarkName mark: String, atByteSampleOffset byteSampleOffset: Int) { + wrappedInstance = AVSpeechSynthesisMarker(bookmarkName: mark, atByteSampleOffset: byteSampleOffset) + } + + @objc init(phonemeString phoneme: String, atByteSampleOffset byteSampleOffset: Int) { + wrappedInstance = AVSpeechSynthesisMarker(phonemeString: phoneme, atByteSampleOffset: byteSampleOffset) + } + +} + +@objc public class AVSpeechSynthesisProviderAudioUnitWrapper: NSObject { + var wrappedInstance: AVSpeechSynthesisProviderAudioUnit + + init(_ wrappedInstance: AVSpeechSynthesisProviderAudioUnit) { + self.wrappedInstance = wrappedInstance + } + + @objc public func cancelSpeechRequest() { + return wrappedInstance.cancelSpeechRequest() + } + + @objc public func synthesizeSpeechRequest(_ speechRequest: AVSpeechSynthesisProviderRequestWrapper) { + return wrappedInstance.synthesizeSpeechRequest(speechRequest.wrappedInstance) + } + +} + +@objc public class AVSpeechSynthesisProviderRequestWrapper: NSObject { + var wrappedInstance: AVSpeechSynthesisProviderRequest + + @objc public var ssmlRepresentation: String { + get { + wrappedInstance.ssmlRepresentation + } + } + + @objc public var voice: AVSpeechSynthesisProviderVoiceWrapper { + get { + AVSpeechSynthesisProviderVoiceWrapper(wrappedInstance.voice) + } + } + + init(_ wrappedInstance: AVSpeechSynthesisProviderRequest) { + self.wrappedInstance = wrappedInstance + } + + @objc init(ssmlRepresentation text: String, voice: AVSpeechSynthesisProviderVoiceWrapper) { + wrappedInstance = AVSpeechSynthesisProviderRequest(ssmlRepresentation: text, voice: voice.wrappedInstance) + } + +} + +@objc public class AVSpeechSynthesisProviderVoiceWrapper: NSObject { + var wrappedInstance: AVSpeechSynthesisProviderVoice + + @objc public var age: Int { + get { + wrappedInstance.age + } + set { + wrappedInstance.age = newValue + } + } + + @objc public var identifier: String { + get { + wrappedInstance.identifier + } + } + + @objc public var name: String { + get { + wrappedInstance.name + } + } + + @objc public var version: String { + get { + wrappedInstance.version + } + set { + wrappedInstance.version = newValue + } + } + + init(_ wrappedInstance: AVSpeechSynthesisProviderVoice) { + self.wrappedInstance = wrappedInstance + } + + @objc static public func updateSpeechVoices() { + return AVSpeechSynthesisProviderVoice.updateSpeechVoices() + } + +} + +@objc public class AVSpeechSynthesisVoiceWrapper: NSObject { + var wrappedInstance: AVSpeechSynthesisVoice + + @objc public var identifier: String { + get { + wrappedInstance.identifier + } + } + + @objc public var language: String { + get { + wrappedInstance.language + } + } + + @objc public var name: String { + get { + wrappedInstance.name + } + } + + @objc public var voiceTraits: AVSpeechSynthesisVoiceWrapper.TraitsWrapper { + get { + TraitsWrapper(wrappedInstance.voiceTraits) + } + } + + init(_ wrappedInstance: AVSpeechSynthesisVoice) { + self.wrappedInstance = wrappedInstance + } + + @objc init?(identifier: String) { + if let instance = AVSpeechSynthesisVoice(identifier: identifier) { + wrappedInstance = instance + } else { + return nil + } + } + + @objc init?(language languageCode: String?) { + if let instance = AVSpeechSynthesisVoice(language: languageCode) { + wrappedInstance = instance + } else { + return nil + } + } + + @objc static public func currentLanguageCode() -> String { + return AVSpeechSynthesisVoice.currentLanguageCode() + } + + @objc public class TraitsWrapper: NSObject { + var wrappedInstance: AVSpeechSynthesisVoice.Traits + + @objc static public var isNoveltyVoice: AVSpeechSynthesisVoiceWrapper.TraitsWrapper { + get { + TraitsWrapper(AVSpeechSynthesisVoice.Traits.isNoveltyVoice) + } + } + + @objc static public var isPersonalVoice: AVSpeechSynthesisVoiceWrapper.TraitsWrapper { + get { + TraitsWrapper(AVSpeechSynthesisVoice.Traits.isPersonalVoice) + } + } + + @objc public var isEmpty: Bool { + get { + wrappedInstance.isEmpty + } + } + + init(_ wrappedInstance: AVSpeechSynthesisVoice.Traits) { + self.wrappedInstance = wrappedInstance + } + + @objc override init() { + wrappedInstance = AVSpeechSynthesisVoice.Traits() + } + + } + +} + +@objc public class AVSpeechSynthesizerWrapper: NSObject { + var wrappedInstance: AVSpeechSynthesizer + + @objc public var isPaused: Bool { + get { + wrappedInstance.isPaused + } + } + + @objc public var isSpeaking: Bool { + get { + wrappedInstance.isSpeaking + } + } + + init(_ wrappedInstance: AVSpeechSynthesizer) { + self.wrappedInstance = wrappedInstance + } + + @objc public func continueSpeaking() -> Bool { + return wrappedInstance.continueSpeaking() + } + + @objc public func speak(_ utterance: AVSpeechUtteranceWrapper) { + return wrappedInstance.speak(utterance.wrappedInstance) + } + +} + +@objc public class AVSpeechUtteranceWrapper: NSObject { + var wrappedInstance: AVSpeechUtterance + + @objc public var pitchMultiplier: Float { + get { + wrappedInstance.pitchMultiplier + } + set { + wrappedInstance.pitchMultiplier = newValue + } + } + + @objc public var postUtteranceDelay: TimeInterval { + get { + wrappedInstance.postUtteranceDelay + } + set { + wrappedInstance.postUtteranceDelay = newValue + } + } + + @objc public var preUtteranceDelay: TimeInterval { + get { + wrappedInstance.preUtteranceDelay + } + set { + wrappedInstance.preUtteranceDelay = newValue + } + } + + @objc public var prefersAssistiveTechnologySettings: Bool { + get { + wrappedInstance.prefersAssistiveTechnologySettings + } + set { + wrappedInstance.prefersAssistiveTechnologySettings = newValue + } + } + + @objc public var rate: Float { + get { + wrappedInstance.rate + } + set { + wrappedInstance.rate = newValue + } + } + + @objc public var speechString: String { + get { + wrappedInstance.speechString + } + } + + @objc public var voice: AVSpeechSynthesisVoiceWrapper? { + get { + wrappedInstance.voice == nil ? nil : AVSpeechSynthesisVoiceWrapper(wrappedInstance.voice!) + } + set { + wrappedInstance.voice = newValue?.wrappedInstance + } + } + + @objc public var volume: Float { + get { + wrappedInstance.volume + } + set { + wrappedInstance.volume = newValue + } + } + + init(_ wrappedInstance: AVSpeechUtterance) { + self.wrappedInstance = wrappedInstance + } + + @objc init?(ssmlRepresentation string: String) { + if let instance = AVSpeechUtterance(ssmlRepresentation: string) { + wrappedInstance = instance + } else { + return nil + } + } + + @objc init(string: String) { + wrappedInstance = AVSpeechUtterance(string: string) + } + +} + +@objc public class GlobalsWrapper: NSObject { + @objc static public var AVAudioBitRateStrategy_ConstantWrapper: String { + get { + AVAudioBitRateStrategy_Constant + } + } + + @objc static public var AVAudioBitRateStrategy_LongTermAverageWrapper: String { + get { + AVAudioBitRateStrategy_LongTermAverage + } + } + + @objc static public var AVAudioBitRateStrategy_VariableWrapper: String { + get { + AVAudioBitRateStrategy_Variable + } + } + + @objc static public var AVAudioBitRateStrategy_VariableConstrainedWrapper: String { + get { + AVAudioBitRateStrategy_VariableConstrained + } + } + + @objc static public var AVAudioFileTypeKeyWrapper: String { + get { + AVAudioFileTypeKey + } + } + + @objc static public var AVAudioSessionInterruptionWasSuspendedKeyWrapper: String { + get { + AVAudioSessionInterruptionWasSuspendedKey + } + } + + @objc static public var AVAudioUnitManufacturerNameAppleWrapper: String { + get { + AVAudioUnitManufacturerNameApple + } + } + + @objc static public var AVAudioUnitTypeEffectWrapper: String { + get { + AVAudioUnitTypeEffect + } + } + + @objc static public var AVAudioUnitTypeFormatConverterWrapper: String { + get { + AVAudioUnitTypeFormatConverter + } + } + + @objc static public var AVAudioUnitTypeGeneratorWrapper: String { + get { + AVAudioUnitTypeGenerator + } + } + + @objc static public var AVAudioUnitTypeMIDIProcessorWrapper: String { + get { + AVAudioUnitTypeMIDIProcessor + } + } + + @objc static public var AVAudioUnitTypeMixerWrapper: String { + get { + AVAudioUnitTypeMixer + } + } + + @objc static public var AVAudioUnitTypeMusicDeviceWrapper: String { + get { + AVAudioUnitTypeMusicDevice + } + } + + @objc static public var AVAudioUnitTypeMusicEffectWrapper: String { + get { + AVAudioUnitTypeMusicEffect + } + } + + @objc static public var AVAudioUnitTypeOfflineEffectWrapper: String { + get { + AVAudioUnitTypeOfflineEffect + } + } + + @objc static public var AVAudioUnitTypeOutputWrapper: String { + get { + AVAudioUnitTypeOutput + } + } + + @objc static public var AVAudioUnitTypePannerWrapper: String { + get { + AVAudioUnitTypePanner + } + } + + @objc static public var AVChannelLayoutKeyWrapper: String { + get { + AVChannelLayoutKey + } + } + + @objc static public var AVEncoderAudioQualityForVBRKeyWrapper: String { + get { + AVEncoderAudioQualityForVBRKey + } + } + + @objc static public var AVEncoderAudioQualityKeyWrapper: String { + get { + AVEncoderAudioQualityKey + } + } + + @objc static public var AVEncoderBitDepthHintKeyWrapper: String { + get { + AVEncoderBitDepthHintKey + } + } + + @objc static public var AVEncoderBitRateKeyWrapper: String { + get { + AVEncoderBitRateKey + } + } + + @objc static public var AVEncoderBitRatePerChannelKeyWrapper: String { + get { + AVEncoderBitRatePerChannelKey + } + } + + @objc static public var AVEncoderBitRateStrategyKeyWrapper: String { + get { + AVEncoderBitRateStrategyKey + } + } + + @objc static public var AVFormatIDKeyWrapper: String { + get { + AVFormatIDKey + } + } + + @objc static public var AVLinearPCMBitDepthKeyWrapper: String { + get { + AVLinearPCMBitDepthKey + } + } + + @objc static public var AVLinearPCMIsBigEndianKeyWrapper: String { + get { + AVLinearPCMIsBigEndianKey + } + } + + @objc static public var AVLinearPCMIsFloatKeyWrapper: String { + get { + AVLinearPCMIsFloatKey + } + } + + @objc static public var AVLinearPCMIsNonInterleavedWrapper: String { + get { + AVLinearPCMIsNonInterleaved + } + } + + @objc static public var AVNumberOfChannelsKeyWrapper: String { + get { + AVNumberOfChannelsKey + } + } + + @objc static public var AVSampleRateConverterAlgorithmKeyWrapper: String { + get { + AVSampleRateConverterAlgorithmKey + } + } + + @objc static public var AVSampleRateConverterAlgorithm_MasteringWrapper: String { + get { + AVSampleRateConverterAlgorithm_Mastering + } + } + + @objc static public var AVSampleRateConverterAlgorithm_MinimumPhaseWrapper: String { + get { + AVSampleRateConverterAlgorithm_MinimumPhase + } + } + + @objc static public var AVSampleRateConverterAlgorithm_NormalWrapper: String { + get { + AVSampleRateConverterAlgorithm_Normal + } + } + + @objc static public var AVSampleRateConverterAudioQualityKeyWrapper: String { + get { + AVSampleRateConverterAudioQualityKey + } + } + + @objc static public var AVSampleRateKeyWrapper: String { + get { + AVSampleRateKey + } + } + + @objc static public var AVSpeechSynthesisIPANotationAttributeWrapper: String { + get { + AVSpeechSynthesisIPANotationAttribute + } + } + + @objc static public var AVSpeechSynthesisVoiceIdentifierAlexWrapper: String { + get { + AVSpeechSynthesisVoiceIdentifierAlex + } + } + + @objc static public var AVSpeechUtteranceDefaultSpeechRateWrapper: Float { + get { + AVSpeechUtteranceDefaultSpeechRate + } + } + + @objc static public var AVSpeechUtteranceMaximumSpeechRateWrapper: Float { + get { + AVSpeechUtteranceMaximumSpeechRate + } + } + + @objc static public var AVSpeechUtteranceMinimumSpeechRateWrapper: Float { + get { + AVSpeechUtteranceMinimumSpeechRate + } + } + + @objc static public var AVMusicTimeStampEndOfTrackWrapper: Double { + get { + AVMusicTimeStampEndOfTrack + } + } + + @objc static public func AVAudioMake3DAngularOrientationWrapper(_ yaw: Float, _ pitch: Float, _ roll: Float) -> AVAudio3DAngularOrientationWrapper { + let result = AVAudioMake3DAngularOrientation(yaw, pitch, roll) + return AVAudio3DAngularOrientationWrapper(result) + } + + @objc static public func AVAudioMake3DPointWrapper(_ x: Float, _ y: Float, _ z: Float) -> AVAudio3DPointWrapper { + let result = AVAudioMake3DPoint(x, y, z) + return AVAudio3DPointWrapper(result) + } + +} + diff --git a/pkgs/swiftgen/example/generate_code.dart b/pkgs/swiftgen/example/generate_code.dart new file mode 100644 index 0000000000..f10623f708 --- /dev/null +++ b/pkgs/swiftgen/example/generate_code.dart @@ -0,0 +1,74 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:io'; + +import 'package:ffigen/ffigen.dart' as fg; +import 'package:logging/logging.dart'; +import 'package:pub_semver/pub_semver.dart'; +import 'package:swiftgen/swiftgen.dart'; + +Future main() async { + // TODO(https://github.com/dart-lang/native/issues/2371): Remove this. + Logger.root.onRecord.listen((record) { + stderr.writeln('${record.level.name}: ${record.message}'); + }); + + await SwiftGen( + target: Target( + triple: 'x86_64-apple-macosx14.0', + sdk: Uri.directory( + '/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk', + ), + ), + input: ObjCCompatibleSwiftFileInput( + module: 'AVFAudio', + files: [Uri.file('avf_audio_wrapper.swift')], + ), + tempDirectory: Uri.directory('temp'), + outputModule: 'AVFAudioWrapper', + ffigen: FfiGenConfig( + output: Uri.file('avf_audio_bindings.dart'), + outputObjC: Uri.file('avf_audio_wrapper.m'), + externalVersions: fg.ExternalVersions( + ios: fg.Versions(min: Version(12, 0, 0)), + macos: fg.Versions(min: Version(10, 14, 0)), + ), + objcInterfaces: fg.DeclarationFilters( + shouldInclude: (decl) => decl.originalName == 'AVAudioPlayerWrapper', + ), + preamble: ''' +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// ignore_for_file: always_specify_types +// ignore_for_file: camel_case_types +// ignore_for_file: non_constant_identifier_names +// ignore_for_file: unnecessary_non_null_assertion +// ignore_for_file: unused_element +// ignore_for_file: unused_field +// coverage:ignore-file +''', + ), + ).generate(); + + final result = Process.runSync('swiftc', [ + '-emit-library', + '-o', + 'avf_audio_wrapper.dylib', + '-module-name', + 'AVFAudioWrapper', + 'avf_audio_wrapper.swift', + '-framework', + 'AVFAudio', + '-framework', + 'Foundation', + ]); + if (result.exitCode != 0) { + print('Failed to build the swift wrapper library'); + print(result.stdout); + print(result.stderr); + } +} diff --git a/pkgs/swiftgen/example/play_audio.dart b/pkgs/swiftgen/example/play_audio.dart new file mode 100644 index 0000000000..86ae151562 --- /dev/null +++ b/pkgs/swiftgen/example/play_audio.dart @@ -0,0 +1,51 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:ffi'; +import 'dart:io'; + +import 'package:objective_c/objective_c.dart'; + +// TODO(https://github.com/dart-lang/native/issues/1068): Remove this. +import '../../objective_c/test/setup.dart' as objc_setup; + +import 'avf_audio_bindings.dart'; + +const _dylibPath = + '/System/Library/Frameworks/AVFAudio.framework/Versions/Current/AVFAudio'; + +const _wrapperDylib = 'avf_audio_wrapper.dylib'; + +void main(List args) async { + if (args.isEmpty) { + print('Usage: dart play_audio.dart file1.wav file2.mp3 ...'); + return; + } + + objc_setup.main([]); + DynamicLibrary.open(_dylibPath); + DynamicLibrary.open(Platform.script.resolve(_wrapperDylib).toFilePath()); + for (final file in args) { + final fileStr = NSString(file); + print('Loading ${fileStr.toDartString()}'); + final fileUrl = NSURL.fileURLWithPath(fileStr); + final player = AVAudioPlayerWrapper.alloc().initWithContentsOf( + fileUrl, + error: nullptr, + ); + if (player == null) { + print('Failed to load audio'); + continue; + } + final durationSeconds = player.duration.ceil(); + print('$durationSeconds sec'); + final status = player.play(); + if (status) { + print('Playing...'); + await Future.delayed(Duration(seconds: durationSeconds)); + } else { + print('Failed to play audio.'); + } + } +} diff --git a/pkgs/swiftgen/example/pubspec.yaml b/pkgs/swiftgen/example/pubspec.yaml new file mode 100644 index 0000000000..43a73c3bb7 --- /dev/null +++ b/pkgs/swiftgen/example/pubspec.yaml @@ -0,0 +1,30 @@ +# Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +# for details. All rights reserved. Use of this source code is governed by a +# BSD-style license that can be found in the LICENSE file. + +name: example +description: 'Demonstrates using swiftgen' +version: 0.1.0 +publish_to: 'none' + +environment: + sdk: '>=3.8.0 <4.0.0' + +dependencies: + ffi: ^2.1.0 + ffigen: ^19.0.0 + logging: ^1.3.0 + objective_c: ^8.0.0 + pub_semver: ^2.2.0 + swiftgen: + path: .. + +dev_dependencies: + dart_flutter_team_lints: ^3.5.1 + test: ^1.21.1 + +dependency_overrides: + ffigen: + path: ../../ffigen/ + objective_c: + path: ../../objective_c/ diff --git a/pkgs/swiftgen/lib/src/config.dart b/pkgs/swiftgen/lib/src/config.dart new file mode 100644 index 0000000000..c023a41585 --- /dev/null +++ b/pkgs/swiftgen/lib/src/config.dart @@ -0,0 +1,141 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:ffigen/ffigen.dart' as ffigen; + +import 'util.dart'; + +/// Config options for swiftgen. +class SwiftGen { + final Target target; + final SwiftGenInput input; + final Uri tempDir; + final String? outputModule; + final FfiGenConfig ffigen; + + SwiftGen({ + required this.target, + required this.input, + Uri? tempDirectory, + this.outputModule, + required this.ffigen, + }) : tempDir = tempDirectory ?? createTempDirectory(); +} + +/// A target is a target triple string and an iOS/macOS SDK directory. +class Target { + String triple; + Uri sdk; + + Target({required this.triple, required this.sdk}); + + static Future host() => getHostTarget(); +} + +/// Describes the inputs to the swiftgen pipeline. +abstract interface class SwiftGenInput { + String get module; + Iterable get files; + Iterable get compileArgs; +} + +/// Input swift files that are already annotated with @objc. +class ObjCCompatibleSwiftFileInput implements SwiftGenInput { + @override + final String module; + + @override + final List files; + + ObjCCompatibleSwiftFileInput({required this.module, required this.files}); + + @override + Iterable get compileArgs => const []; +} + +/// Selected options from [ffigen.Config]. +class FfiGenConfig { + /// [ffigen.Config.output] + final Uri output; + + /// [ffigen.Config.outputObjC] + final Uri outputObjC; + + /// [ffigen.Config.wrapperName] + /// Defaults to the swift module name. + final String? wrapperName; + + /// [ffigen.Config.wrapperDocComment] + final String? wrapperDocComment; + + /// [ffigen.Config.preamble] + final String? preamble; + + /// [ffigen.Config.functionDecl] + /// Defaults to [ffigen.DeclarationFilters.excludeAll] + final ffigen.DeclarationFilters? functionDecl; + + /// [ffigen.Config.structDecl] + /// Defaults to [ffigen.DeclarationFilters.excludeAll] + final ffigen.DeclarationFilters? structDecl; + + /// [ffigen.Config.unionDecl] + /// Defaults to [ffigen.DeclarationFilters.excludeAll] + final ffigen.DeclarationFilters? unionDecl; + + /// [ffigen.Config.enumClassDecl] + /// Defaults to [ffigen.DeclarationFilters.excludeAll] + final ffigen.DeclarationFilters? enumClassDecl; + + /// [ffigen.Config.unnamedEnumConstants] + /// Defaults to [ffigen.DeclarationFilters.excludeAll] + final ffigen.DeclarationFilters? unnamedEnumConstants; + + /// [ffigen.Config.globals] + /// Defaults to [ffigen.DeclarationFilters.excludeAll] + final ffigen.DeclarationFilters? globals; + + /// [ffigen.Config.macroDecl] + /// Defaults to [ffigen.DeclarationFilters.excludeAll] + final ffigen.DeclarationFilters? macroDecl; + + /// [ffigen.Config.typedefs] + /// Defaults to [ffigen.DeclarationFilters.excludeAll] + final ffigen.DeclarationFilters? typedefs; + + /// [ffigen.Config.objcInterfaces] + /// Defaults to [ffigen.DeclarationFilters.excludeAll] + final ffigen.DeclarationFilters? objcInterfaces; + + /// [ffigen.Config.objcProtocols] + /// Defaults to [ffigen.DeclarationFilters.excludeAll] + final ffigen.DeclarationFilters? objcProtocols; + + /// [ffigen.Config.objcCategories] + /// Defaults to [ffigen.DeclarationFilters.excludeAll] + final ffigen.DeclarationFilters? objcCategories; + + /// [ffigen.Config.externalVersions] + final ffigen.ExternalVersions externalVersions; + + FfiGenConfig({ + required this.output, + required this.outputObjC, + this.wrapperName, + this.wrapperDocComment, + this.preamble, + this.functionDecl, + this.structDecl, + this.unionDecl, + this.enumClassDecl, + this.unnamedEnumConstants, + this.globals, + this.macroDecl, + this.typedefs, + this.objcInterfaces, + this.objcProtocols, + this.objcCategories, + this.externalVersions = const ffigen.ExternalVersions(), + }); +} diff --git a/pkgs/swiftgen/lib/src/generator.dart b/pkgs/swiftgen/lib/src/generator.dart new file mode 100644 index 0000000000..29affeb1f4 --- /dev/null +++ b/pkgs/swiftgen/lib/src/generator.dart @@ -0,0 +1,74 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:io'; + +import 'package:ffigen/ffigen.dart' as fg; +import 'package:logging/logging.dart'; +import 'package:path/path.dart' as p; + +import 'config.dart'; +import 'util.dart'; + +extension SwiftGenGenerator on SwiftGen { + Future generate() async { + Directory(absTempDir).createSync(recursive: true); + await _generateObjCFile(); + _generateDartFile(); + } + + String get absTempDir => p.absolute(tempDir.toFilePath()); + String get outModule => outputModule ?? input.module; + String get objcHeader => p.join(absTempDir, '$outModule.h'); + + Future _generateObjCFile() => run('swiftc', [ + '-c', + for (final uri in input.files) p.absolute(uri.toFilePath()), + '-module-name', + outModule, + '-emit-objc-header-path', + objcHeader, + '-target', + target.triple, + '-sdk', + p.absolute(target.sdk.toFilePath()), + ...input.compileArgs, + ], absTempDir); + + void _generateDartFile() { + final generator = fg.FfiGen(logLevel: Level.SEVERE); + generator.run( + fg.Config( + language: fg.Language.objc, + output: ffigen.output, + outputObjC: ffigen.outputObjC, + wrapperName: ffigen.wrapperName ?? outModule, + wrapperDocComment: ffigen.wrapperDocComment, + preamble: ffigen.preamble, + functionDecl: ffigen.functionDecl ?? fg.DeclarationFilters.excludeAll, + structDecl: ffigen.structDecl ?? fg.DeclarationFilters.excludeAll, + unionDecl: ffigen.unionDecl ?? fg.DeclarationFilters.excludeAll, + enumClassDecl: ffigen.enumClassDecl ?? fg.DeclarationFilters.excludeAll, + unnamedEnumConstants: + ffigen.unnamedEnumConstants ?? fg.DeclarationFilters.excludeAll, + globals: ffigen.globals ?? fg.DeclarationFilters.excludeAll, + macroDecl: ffigen.macroDecl ?? fg.DeclarationFilters.excludeAll, + typedefs: ffigen.typedefs ?? fg.DeclarationFilters.excludeAll, + objcInterfaces: + ffigen.objcInterfaces ?? fg.DeclarationFilters.excludeAll, + objcProtocols: ffigen.objcProtocols ?? fg.DeclarationFilters.excludeAll, + objcCategories: + ffigen.objcCategories ?? fg.DeclarationFilters.excludeAll, + entryPoints: [Uri.file(objcHeader)], + compilerOpts: [ + ...fg.defaultCompilerOpts(), + '-Wno-nullability-completeness', + ], + interfaceModuleFunc: (_) => outModule, + protocolModuleFunc: (_) => outModule, + externalVersions: ffigen.externalVersions, + ), + ); + } +} diff --git a/pkgs/swiftgen/lib/src/util.dart b/pkgs/swiftgen/lib/src/util.dart new file mode 100644 index 0000000000..f1994c5b74 --- /dev/null +++ b/pkgs/swiftgen/lib/src/util.dart @@ -0,0 +1,52 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:convert'; +import 'dart:io'; + +import 'config.dart'; + +Future run( + String executable, + List arguments, + String workingDir, +) async { + final process = await Process.start( + executable, + arguments, + workingDirectory: workingDir, + ); + process.stdout.listen(stdout.add); + process.stderr.listen(stderr.add); + if ((await process.exitCode) != 0) { + throw ProcessException(executable, arguments); + } +} + +Future runGetStdout(String executable, List arguments) async { + final process = await Process.start(executable, arguments); + final s = StringBuffer(); + process.stdout.transform(utf8.decoder).listen(s.write); + process.stderr.listen(stderr.add); + if ((await process.exitCode) != 0) { + throw ProcessException(executable, arguments); + } + return s.toString(); +} + +Future getHostTriple() async { + final info = + json.decode(await runGetStdout('swiftc', ['-print-target-info'])) as Map; + final target = info['target'] as Map; + return target['triple'] as String; +} + +Future getHostSdk() async => + Uri.directory((await runGetStdout('xcrun', ['--show-sdk-path'])).trim()); + +Future getHostTarget() async => + Target(triple: await getHostTriple(), sdk: await getHostSdk()); + +Uri createTempDirectory() => + Uri.directory(Directory.systemTemp.createTempSync().path); diff --git a/pkgs/swiftgen/lib/swiftgen.dart b/pkgs/swiftgen/lib/swiftgen.dart new file mode 100644 index 0000000000..fc27ea2b4d --- /dev/null +++ b/pkgs/swiftgen/lib/swiftgen.dart @@ -0,0 +1,7 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +export 'src/config.dart' + show FfiGenConfig, ObjCCompatibleSwiftFileInput, SwiftGen, Target; +export 'src/generator.dart' show SwiftGenGenerator; diff --git a/pkgs/swiftgen/pubspec.yaml b/pkgs/swiftgen/pubspec.yaml index deb17a7737..b853bae9f6 100644 --- a/pkgs/swiftgen/pubspec.yaml +++ b/pkgs/swiftgen/pubspec.yaml @@ -15,12 +15,22 @@ topics: - codegen environment: - sdk: '>=3.6.0 <4.0.0' + sdk: '>=3.8.0 <4.0.0' dependencies: ffi: ^2.1.0 + ffigen: ^19.0.0 + logging: ^1.3.0 + objective_c: ^8.0.0 + package_config: ^2.2.0 + path: ^1.9.1 dev_dependencies: dart_flutter_team_lints: ^3.5.1 - ffigen: ^19.0.0 test: ^1.21.1 + +dependency_overrides: + ffigen: + path: ../ffigen/ + objective_c: + path: ../objective_c/ diff --git a/pkgs/swiftgen/test/integration/classes_bindings.dart b/pkgs/swiftgen/test/integration/classes_bindings.dart new file mode 100644 index 0000000000..ab70b9acc1 --- /dev/null +++ b/pkgs/swiftgen/test/integration/classes_bindings.dart @@ -0,0 +1,434 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// ignore_for_file: always_specify_types +// ignore_for_file: camel_case_types +// ignore_for_file: non_constant_identifier_names +// ignore_for_file: unnecessary_non_null_assertion +// ignore_for_file: unused_element +// ignore_for_file: unused_field +// coverage:ignore-file + +// AUTO GENERATED FILE, DO NOT EDIT. +// +// Generated by `package:ffigen`. +// ignore_for_file: type=lint +import 'dart:ffi' as ffi; +import 'package:objective_c/objective_c.dart' as objc; + +@ffi.Native< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ) +>() +external ffi.Pointer _classes_protocolTrampoline_1mbt9g9( + ffi.Pointer target, + ffi.Pointer arg0, +); + +late final _class_TestClass = objc.getClass("classes.TestClass"); +late final _sel_isKindOfClass_ = objc.registerName("isKindOfClass:"); +final _objc_msgSend_19nvye5 = objc.msgSendPointer + .cast< + ffi.NativeFunction< + ffi.Bool Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ) + > + >() + .asFunction< + bool Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ) + >(); +late final _class_TestOtherClass = objc.getClass("classes.TestOtherClass"); +late final _sel_times10WithX_ = objc.registerName("times10WithX:"); +final _objc_msgSend_12hwf9n = objc.msgSendPointer + .cast< + ffi.NativeFunction< + ffi.Long Function( + ffi.Pointer, + ffi.Pointer, + ffi.Long, + ) + > + >() + .asFunction< + int Function( + ffi.Pointer, + ffi.Pointer, + int, + ) + >(); +typedef instancetype = ffi.Pointer; +typedef Dartinstancetype = objc.ObjCObjectBase; +late final _sel_init = objc.registerName("init"); +final _objc_msgSend_151sglz = objc.msgSendPointer + .cast< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ) + > + >() + .asFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ) + >(); +late final _sel_new = objc.registerName("new"); +late final _sel_allocWithZone_ = objc.registerName("allocWithZone:"); +final _objc_msgSend_1cwp428 = objc.msgSendPointer + .cast< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ) + > + >() + .asFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ffi.Pointer, + ) + >(); +late final _sel_alloc = objc.registerName("alloc"); +late final _sel_self = objc.registerName("self"); +ffi.Pointer _ObjCBlock_objcObjCObject_ffiVoid_fnPtrTrampoline( + ffi.Pointer block, + ffi.Pointer arg0, +) => block.ref.target + .cast< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Pointer arg0) + > + >() + .asFunction< + ffi.Pointer Function(ffi.Pointer) + >()(arg0); +ffi.Pointer _ObjCBlock_objcObjCObject_ffiVoid_fnPtrCallable = + ffi.Pointer.fromFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ) + >(_ObjCBlock_objcObjCObject_ffiVoid_fnPtrTrampoline) + .cast(); +ffi.Pointer +_ObjCBlock_objcObjCObject_ffiVoid_closureTrampoline( + ffi.Pointer block, + ffi.Pointer arg0, +) => + (objc.getBlockClosure(block) + as ffi.Pointer Function(ffi.Pointer))(arg0); +ffi.Pointer _ObjCBlock_objcObjCObject_ffiVoid_closureCallable = + ffi.Pointer.fromFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ) + >(_ObjCBlock_objcObjCObject_ffiVoid_closureTrampoline) + .cast(); + +/// Construction methods for `objc.ObjCBlock Function(ffi.Pointer)>`. +abstract final class ObjCBlock_objcObjCObject_ffiVoid { + /// Returns a block that wraps the given raw block pointer. + static objc.ObjCBlock< + ffi.Pointer Function(ffi.Pointer) + > + castFromPointer( + ffi.Pointer pointer, { + bool retain = false, + bool release = false, + }) => + objc.ObjCBlock< + ffi.Pointer Function(ffi.Pointer) + >(pointer, retain: retain, release: release); + + /// Creates a block from a C function pointer. + /// + /// This block must be invoked by native code running on the same thread as + /// the isolate that registered it. Invoking the block on the wrong thread + /// will result in a crash. + static objc.ObjCBlock< + ffi.Pointer Function(ffi.Pointer) + > + fromFunctionPointer( + ffi.Pointer< + ffi.NativeFunction< + ffi.Pointer Function(ffi.Pointer arg0) + > + > + ptr, + ) => + objc.ObjCBlock< + ffi.Pointer Function(ffi.Pointer) + >( + objc.newPointerBlock( + _ObjCBlock_objcObjCObject_ffiVoid_fnPtrCallable, + ptr.cast(), + ), + retain: false, + release: true, + ); + + /// Creates a block from a Dart function. + /// + /// This block must be invoked by native code running on the same thread as + /// the isolate that registered it. Invoking the block on the wrong thread + /// will result in a crash. + /// + /// If `keepIsolateAlive` is true, this block will keep this isolate alive + /// until it is garbage collected by both Dart and ObjC. + static objc.ObjCBlock< + ffi.Pointer Function(ffi.Pointer) + > + fromFunction( + objc.ObjCObjectBase Function(ffi.Pointer) fn, { + bool keepIsolateAlive = true, + }) => + objc.ObjCBlock< + ffi.Pointer Function(ffi.Pointer) + >( + objc.newClosureBlock( + _ObjCBlock_objcObjCObject_ffiVoid_closureCallable, + (ffi.Pointer arg0) => fn(arg0).ref.retainAndAutorelease(), + keepIsolateAlive, + ), + retain: false, + release: true, + ); +} + +/// Call operator for `objc.ObjCBlock Function(ffi.Pointer)>`. +extension ObjCBlock_objcObjCObject_ffiVoid_CallExtension + on + objc.ObjCBlock< + ffi.Pointer Function(ffi.Pointer) + > { + objc.ObjCObjectBase call(ffi.Pointer arg0) => objc.ObjCObjectBase( + ref.pointer.ref.invoke + .cast< + ffi.NativeFunction< + ffi.Pointer Function( + ffi.Pointer block, + ffi.Pointer arg0, + ) + > + >() + .asFunction< + ffi.Pointer Function( + ffi.Pointer, + ffi.Pointer, + ) + >()(ref.pointer, arg0), + retain: true, + release: true, + ); +} + +late final _sel_retain = objc.registerName("retain"); +late final _sel_autorelease = objc.registerName("autorelease"); + +/// TestOtherClass +class TestOtherClass extends objc.NSObject { + TestOtherClass._( + ffi.Pointer pointer, { + bool retain = false, + bool release = false, + }) : super.castFromPointer(pointer, retain: retain, release: release); + + /// Constructs a [TestOtherClass] that points to the same underlying object as [other]. + TestOtherClass.castFrom(objc.ObjCObjectBase other) + : this._(other.ref.pointer, retain: true, release: true); + + /// Constructs a [TestOtherClass] that wraps the given raw object pointer. + TestOtherClass.castFromPointer( + ffi.Pointer other, { + bool retain = false, + bool release = false, + }) : this._(other, retain: retain, release: release); + + /// Returns whether [obj] is an instance of [TestOtherClass]. + static bool isInstance(objc.ObjCObjectBase obj) { + return _objc_msgSend_19nvye5( + obj.ref.pointer, + _sel_isKindOfClass_, + _class_TestOtherClass, + ); + } + + /// times10WithX: + int times10WithX(int x) { + return _objc_msgSend_12hwf9n(this.ref.pointer, _sel_times10WithX_, x); + } + + /// init + TestOtherClass init() { + objc.checkOsVersionInternal( + 'TestOtherClass.init', + iOS: (false, (2, 0, 0)), + macOS: (false, (10, 0, 0)), + ); + final _ret = _objc_msgSend_151sglz( + this.ref.retainAndReturnPointer(), + _sel_init, + ); + return TestOtherClass.castFromPointer(_ret, retain: false, release: true); + } + + /// new + static TestOtherClass new$() { + final _ret = _objc_msgSend_151sglz(_class_TestOtherClass, _sel_new); + return TestOtherClass.castFromPointer(_ret, retain: false, release: true); + } + + /// allocWithZone: + static TestOtherClass allocWithZone(ffi.Pointer zone) { + final _ret = _objc_msgSend_1cwp428( + _class_TestOtherClass, + _sel_allocWithZone_, + zone, + ); + return TestOtherClass.castFromPointer(_ret, retain: false, release: true); + } + + /// alloc + static TestOtherClass alloc() { + final _ret = _objc_msgSend_151sglz(_class_TestOtherClass, _sel_alloc); + return TestOtherClass.castFromPointer(_ret, retain: false, release: true); + } + + /// self + TestOtherClass self$1() { + final _ret = _objc_msgSend_151sglz(this.ref.pointer, _sel_self); + return TestOtherClass.castFromPointer(_ret, retain: true, release: true); + } + + /// retain + TestOtherClass retain() { + final _ret = _objc_msgSend_151sglz(this.ref.pointer, _sel_retain); + return TestOtherClass.castFromPointer(_ret, retain: true, release: true); + } + + /// autorelease + TestOtherClass autorelease() { + final _ret = _objc_msgSend_151sglz(this.ref.pointer, _sel_autorelease); + return TestOtherClass.castFromPointer(_ret, retain: true, release: true); + } + + /// Returns a new instance of TestOtherClass constructed with the default `new` method. + factory TestOtherClass() => new$(); +} + +late final _sel_myMethod = objc.registerName("myMethod"); +late final _sel_create = objc.registerName("create"); + +/// TestClass +class TestClass extends objc.NSObject { + TestClass._( + ffi.Pointer pointer, { + bool retain = false, + bool release = false, + }) : super.castFromPointer(pointer, retain: retain, release: release); + + /// Constructs a [TestClass] that points to the same underlying object as [other]. + TestClass.castFrom(objc.ObjCObjectBase other) + : this._(other.ref.pointer, retain: true, release: true); + + /// Constructs a [TestClass] that wraps the given raw object pointer. + TestClass.castFromPointer( + ffi.Pointer other, { + bool retain = false, + bool release = false, + }) : this._(other, retain: retain, release: release); + + /// Returns whether [obj] is an instance of [TestClass]. + static bool isInstance(objc.ObjCObjectBase obj) { + return _objc_msgSend_19nvye5( + obj.ref.pointer, + _sel_isKindOfClass_, + _class_TestClass, + ); + } + + /// myMethod + TestOtherClass myMethod() { + final _ret = _objc_msgSend_151sglz(this.ref.pointer, _sel_myMethod); + return TestOtherClass.castFromPointer(_ret, retain: true, release: true); + } + + /// create + static TestClass create() { + final _ret = _objc_msgSend_151sglz(_class_TestClass, _sel_create); + return TestClass.castFromPointer(_ret, retain: true, release: true); + } + + /// init + TestClass init() { + objc.checkOsVersionInternal( + 'TestClass.init', + iOS: (false, (2, 0, 0)), + macOS: (false, (10, 0, 0)), + ); + final _ret = _objc_msgSend_151sglz( + this.ref.retainAndReturnPointer(), + _sel_init, + ); + return TestClass.castFromPointer(_ret, retain: false, release: true); + } + + /// new + static TestClass new$() { + final _ret = _objc_msgSend_151sglz(_class_TestClass, _sel_new); + return TestClass.castFromPointer(_ret, retain: false, release: true); + } + + /// allocWithZone: + static TestClass allocWithZone(ffi.Pointer zone) { + final _ret = _objc_msgSend_1cwp428( + _class_TestClass, + _sel_allocWithZone_, + zone, + ); + return TestClass.castFromPointer(_ret, retain: false, release: true); + } + + /// alloc + static TestClass alloc() { + final _ret = _objc_msgSend_151sglz(_class_TestClass, _sel_alloc); + return TestClass.castFromPointer(_ret, retain: false, release: true); + } + + /// self + TestClass self$1() { + final _ret = _objc_msgSend_151sglz(this.ref.pointer, _sel_self); + return TestClass.castFromPointer(_ret, retain: true, release: true); + } + + /// retain + TestClass retain() { + final _ret = _objc_msgSend_151sglz(this.ref.pointer, _sel_retain); + return TestClass.castFromPointer(_ret, retain: true, release: true); + } + + /// autorelease + TestClass autorelease() { + final _ret = _objc_msgSend_151sglz(this.ref.pointer, _sel_autorelease); + return TestClass.castFromPointer(_ret, retain: true, release: true); + } + + /// Returns a new instance of TestClass constructed with the default `new` method. + factory TestClass() => new$(); +} diff --git a/pkgs/swiftgen/test/integration/classes_test.dart b/pkgs/swiftgen/test/integration/classes_test.dart new file mode 100644 index 0000000000..ec4daadedc --- /dev/null +++ b/pkgs/swiftgen/test/integration/classes_test.dart @@ -0,0 +1,32 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +@Timeout(Duration(minutes: 2)) +library; + +import 'dart:ffi'; + +import 'package:test/test.dart'; + +import 'classes_bindings.dart'; +import 'util.dart'; + +void main() { + group('Classes', () { + setUpAll(() async { + final gen = TestGenerator('classes'); + await gen.generateAndVerifyBindings(); + DynamicLibrary.open(gen.dylibFile); + + // TODO(https://github.com/dart-lang/native/issues/1068): Remove this. + DynamicLibrary.open(objCTestDylib); + }); + + test('method invocation', () { + final testClass = TestClass.create(); + final testOtherClass = testClass.myMethod(); + expect(testOtherClass.times10WithX(123), 1230); + }); + }); +} diff --git a/pkgs/swiftgen/test/integration/classes_wrapper.swift b/pkgs/swiftgen/test/integration/classes_wrapper.swift new file mode 100644 index 0000000000..a40c03d4b4 --- /dev/null +++ b/pkgs/swiftgen/test/integration/classes_wrapper.swift @@ -0,0 +1,17 @@ +import Foundation + +@objc public class TestClass: NSObject { + @objc public func myMethod() -> TestOtherClass { + return TestOtherClass() + } + + @objc public static func create() -> TestClass { + return TestClass() + } +} + +@objc public class TestOtherClass: NSObject { + @objc public func times10(x: Int) -> Int { + return x * 10 + } +} diff --git a/pkgs/swiftgen/test/integration/temp/.gitignore b/pkgs/swiftgen/test/integration/temp/.gitignore new file mode 100644 index 0000000000..d6b7ef32c8 --- /dev/null +++ b/pkgs/swiftgen/test/integration/temp/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/pkgs/swiftgen/test/integration/util.dart b/pkgs/swiftgen/test/integration/util.dart new file mode 100644 index 0000000000..bc2ce26f5b --- /dev/null +++ b/pkgs/swiftgen/test/integration/util.dart @@ -0,0 +1,166 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:io'; + +import 'package:ffigen/ffigen.dart' as fg; +import 'package:path/path.dart' as path; +import 'package:swiftgen/src/util.dart'; +import 'package:swiftgen/swiftgen.dart'; +import 'package:test/test.dart'; + +String pkgDir = findPackageRoot('swiftgen').toFilePath(); + +// TODO(https://github.com/dart-lang/native/issues/1068): Remove this. +String objCTestDylib = path.join( + pkgDir, + '..', + 'objective_c', + 'test', + 'objective_c.dylib', +); + +class TestGenerator { + final String name; + late final String testDir; + late final String tempDir; + late final String inputFile; + late final String outputFile; + late final String outputObjCFile; + late final String objInputFile; + late final String objWrapperFile; + late final String objObjCFile; + late final String dylibFile; + late final String actualOutputFile; + + TestGenerator(this.name) { + testDir = path.absolute(path.join(pkgDir, 'test/integration')); + tempDir = path.join(testDir, 'temp'); + inputFile = path.join(testDir, '${name}_wrapper.swift'); + outputFile = path.join(tempDir, '${name}_output.dart'); + outputObjCFile = path.join(tempDir, '${name}_output.m'); + objInputFile = path.join(tempDir, '$name.o'); + objWrapperFile = path.join(tempDir, '${name}_wrapper.o'); + objObjCFile = path.join(tempDir, '${name}_output_m.o'); + dylibFile = path.join(tempDir, '$name.dylib'); + actualOutputFile = path.join(testDir, '${name}_bindings.dart'); + } + + Future generateBindings() async => SwiftGen( + target: await Target.host(), + input: ObjCCompatibleSwiftFileInput( + module: name, + files: [Uri.file(inputFile)], + ), + tempDirectory: Directory(tempDir).uri, + ffigen: FfiGenConfig( + output: Uri.file(outputFile), + outputObjC: Uri.file(outputObjCFile), + objcInterfaces: fg.DeclarationFilters( + shouldInclude: (decl) => decl.originalName.startsWith('Test'), + ), + preamble: ''' +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// ignore_for_file: always_specify_types +// ignore_for_file: camel_case_types +// ignore_for_file: non_constant_identifier_names +// ignore_for_file: unnecessary_non_null_assertion +// ignore_for_file: unused_element +// ignore_for_file: unused_field +// coverage:ignore-file +''', + ), + ).generate(); + + Future generateAndVerifyBindings() async { + // Run the generation pipeline. This produces the swift compatability + // wrapper, and the ffigen wrapper. + await generateBindings(); + + expect(File(inputFile).existsSync(), isTrue); + expect(File(outputFile).existsSync(), isTrue); + expect(File(outputObjCFile).existsSync(), isTrue); + + // The generation pipeline also creates some obj files as a byproduct. + expect(File(objWrapperFile).existsSync(), isTrue); + + // We also need to compile outputObjCFile to an obj file. + await run('clang', [ + '-x', + 'objective-c', + '-fobjc-arc', + '-c', + outputObjCFile, + '-fpic', + '-o', + objObjCFile, + ], tempDir); + expect(File(objObjCFile).existsSync(), isTrue); + + // Link all the obj files into a dylib. + await run('clang', [ + '-shared', + '-framework', + 'Foundation', + objWrapperFile, + objObjCFile, + '-o', + dylibFile, + ], tempDir); + expect(File(dylibFile).existsSync(), isTrue); + } +} + +/// Test files are run in a variety of ways, find this package root in all. +/// +/// Test files can be run from source from any working directory. The Dart SDK +/// `tools/test.py` runs them from the root of the SDK for example. +/// +/// Test files can be run from dill from the root of package. `package:test` +/// does this. +/// +/// https://github.com/dart-lang/test/issues/110 +Uri findPackageRoot(String packageName) { + final script = Platform.script; + final fileName = script.name; + if (fileName.endsWith('.dart')) { + // We're likely running from source in the package somewhere. + var directory = script.resolve('.'); + while (true) { + final dirName = directory.name; + if (dirName == packageName) { + return directory; + } + final parent = directory.resolve('..'); + if (parent == directory) break; + directory = parent; + } + } else if (fileName.endsWith('.dill')) { + // Probably from the package root. + final cwd = Directory.current.uri; + final dirName = cwd.name; + if (dirName == packageName) { + return cwd; + } + } + // Or the workspace root. + final cwd = Directory.current.uri; + final candidate = cwd.resolve('pkgs/$packageName/'); + if (Directory.fromUri(candidate).existsSync()) { + return candidate; + } + throw StateError( + "Could not find package root for package '$packageName'. " + 'Tried finding the package root via Platform.script ' + "'${Platform.script.toFilePath()}' and Directory.current " + "'${Directory.current.uri.toFilePath()}'.", + ); +} + +extension on Uri { + String get name => pathSegments.where((e) => e != '').last; +} diff --git a/pkgs/swiftgen/tool/regen_test_bindings.dart b/pkgs/swiftgen/tool/regen_test_bindings.dart new file mode 100644 index 0000000000..07424cbe68 --- /dev/null +++ b/pkgs/swiftgen/tool/regen_test_bindings.dart @@ -0,0 +1,29 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:io'; + +import 'package:path/path.dart' as path; + +import '../test/integration/util.dart'; + +Future regenIntegrationTestBindings(String name) async { + final gen = TestGenerator(name); + await gen.generateBindings(); + await File(gen.outputFile).copy(gen.actualOutputFile); +} + +const testSuffix = '_test.dart'; +List findAllIntegrationTests() => Directory(TestGenerator('').testDir) + .listSync() + .map((entity) => path.basename(entity.path)) + .where((f) => f.endsWith(testSuffix)) + .map((f) => f.substring(0, f.length - testSuffix.length)) + .toList(); + +Future main(List args) async { + for (final name in args.isEmpty ? findAllIntegrationTests() : args) { + await regenIntegrationTestBindings(name); + } +}