native icon indicating copy to clipboard operation
native copied to clipboard

Add NSAttributedString for object c binding

Open alihassan143 opened this issue 6 months ago • 11 comments

// ignore_for_file: avoid_print

import 'dart:io';

import 'package:ffigen/ffigen.dart';
import 'package:logging/logging.dart';

void main() {
  final iosSdkPath =
      '/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks';

  final generator = FfiGenerator(
    headers: Headers(
      entryPoints: [
        // UIKit and PDFKit from real SDK
        Uri.file('$iosSdkPath/UIKit.framework/Headers/UIKit.h'),
        Uri.file('$iosSdkPath/PDFKit.framework/Headers/PDFKit.h'),
        Uri.file('$iosSdkPath/CoreGraphics.framework/Headers/CoreGraphics.h'),
        Uri.file('$iosSdkPath/Foundation.framework/Headers/Foundation.h'),
      ],
    ),
    objectiveC: ObjectiveC(
      interfaces: Interfaces(
        include: (decl) {
          // Include relevant UIKit + PDFKit classes
          return [
            // UIKit
            "UIView", "UILabel", "UIColor", "UIFont", "UIImage", "UIScreen",
            "UINavigationController", "UIWindow", "UIViewController",
            "UIGraphicsImageRenderer", "UIGraphicsImageRendererContext",
            // PDFKit
            "PDFDocument", "PDFPage", "PDFAnnotation", "PDFOutline",
          ].contains(decl.originalName);
        },
        renameMember: (declaration, member) {
          // Handle naming conflicts if any
          if (member == 'initWithFrame:primaryAction:') {
            return 'initWithFramePrimaryAction';
          }
          return member;
        },
      ),
    ),
    output: Output(
      dartFile: Uri.file('lib/system_bindings.g.dart'),
      objectiveCFile: Uri.file('ios/Classes/system_bindings.m'),
      format: true,
      preamble: '''
// ignore_for_file: unused_element, unused_field, return_of_invalid_type

''',
    ),
  );

  generator.generate(
    logger: Logger('Bindings')..onRecord.listen((r) => print(r.message)),
  );

  // Replace many #imports with clean framework imports
  final objcFile = File('ios/Classes/system_bindings.m');
  var content = objcFile.readAsStringSync();
  content = content.replaceAllMapped(
    RegExp(r'#import "(.*\.h)"\n'),
    (match) => '',
  );
  content = '''#import <UIKit/UIKit.h>
#import <PDFKit/PDFKit.h>
#import <CoreGraphics/CoreGraphics.h>
#import <Foundation/Foundation.h>
$content''';
  objcFile.writeAsStringSync(content);

  print('✅ Generated bindings successfully at lib/system_bindings.g.dart');
}

all binding are generated but NSAtttributedString in missing in Objectc also enums values are also not converted to int automatically

alihassan143 avatar Oct 21 '25 05:10 alihassan143

Are you asking for NSAttributedString to be added to the package:objective_c bindings? Or are you saying that you expect NSAttributedString to be part of your bindings but it isn't?

enums values are also not converted to int automatically

Not sure what you mean by this. Could you explain what behaviour you're expecting, and what you're seeing?

Also, what version of FFIgen are you using? There have been some updates to how ObjC enums are code genned in v20, but it's only in prerelease atm.

liamappelbe avatar Oct 21 '25 22:10 liamappelbe

@liamappelbe objective_c 9.0.0-dev NSAttributedString was not exported from the binding previously, but after running with the main branch, it is now accessible. However, there is an issue with the main branch: the enum conversions have changed. In previous versions of ffigen, enums were converted with their values, but now they are converted to sealed classes. The generated binding contains errors because the native functions expect int values, but the binding is providing sealed class references. Additionally, some protocols are not excluded from generate_file.m.

alihassan143 avatar Oct 22 '25 04:10 alihassan143

I can't really help you if you give me so little information. I'm still not sure what version of ffigen you're using (you gave me the objective_c version, not ffigen), but I'll assume you're using the v20 prerelease.

There are 2 kinds of enum in ObjC, NS_ENUM and NS_OPTION, with the difference being that NS_OPTIONs are designed to be bitwise combined. In ffigen v20 we detect this difference and generate a Dart enum for NS_ENUM and an int for NS_OPTION. So presumably your bindings are declaring a NS_ENUM, but you haven't given me any input or output files, so I'm just guessing.

If you want an NS_ENUM to generate as ints, you can use FfiGenerator.enums.style.

Additionally, some protocols are not excluded from generate_file.m.

You'll need to give me more info if you want me to help. What are your inputs and outputs, and what's the expected output.

liamappelbe avatar Oct 22 '25 04:10 liamappelbe

I can't really help you if you give me so little information. I'm still not sure what version of ffigen you're using (you gave me the objective_c version, not ffigen), but I'll assume you're using the v20 prerelease.

There are 2 kinds of enum in ObjC, NS_ENUM and NS_OPTION, with the difference being that NS_OPTIONs are designed to be bitwise combined. In ffigen v20 we detect this difference and generate a Dart enum for NS_ENUM and an int for NS_OPTION. So presumably your bindings are declaring a NS_ENUM, but you haven't given me any input or output files, so I'm just guessing.

If you want an NS_ENUM to generate as ints, you can use FfiGenerator.enums.style.

Additionally, some protocols are not excluded from generate_file.m.

You'll need to give me more info if you want me to help. What are your inputs and outputs, and what's the expected output.

@liamappelbe pdf_compressor.zip that is sample you can check the bindings also how i can exclude the macos protocols in that ?

alihassan143 avatar Oct 22 '25 04:10 alihassan143

Which protocols did you want to exclude? I see a bunch of stubbed protocols. These stubs are generated because other APIs depend on them. In a sense, stubs are already excluded, since we only generate a minimal amount of code for them to get the bindings to compile.

For example, the PDFDocumentDelegate stub exists because it's the return type of PDFDocument.delegate. To avoid generating these stubs entirely you'd need to filter out any method that depends on them, like using method filtering to remove PDFDocument.delegate. And even if you did that you'd still get stubs for all the protocols that your interfaces implement.

I recommend you just ignore the stubs. Removing them from the bindings is difficult, and pointless. These stubs aren't causing any problems.

liamappelbe avatar Oct 22 '25 23:10 liamappelbe

@liamappelbe, what about sealed classes that should be generated as enums because native method accepts values like int and sealed classes cannot be directly pass in that function? That's why the analyzer is showing an error on that

alihassan143 avatar Oct 23 '25 03:10 alihassan143

@liamappelbe, what about sealed classes that should be generated as enums because native method accepts values like int and sealed classes cannot be directly pass in that function? That's why the analyzer is showing an error on that

I need more details. Can you give me an example of the problematic code? Post the analyzer error message? Create a minimal repro example? Did you try the FfiGenerator.enums.style suggestion I gave earlier?

liamappelbe avatar Oct 23 '25 07:10 liamappelbe

@liamappelbe, what about sealed classes that should be generated as enums because native method accepts values like int and sealed classes cannot be directly pass in that function? That's why the analyzer is showing an error on that

I need more details. Can you give me an example of the problematic code? Post the analyzer error message? Create a minimal repro example? Did you try the FfiGenerator.enums.style suggestion I gave earlier?

can you provide sample how to do that?

alihassan143 avatar Oct 25 '25 08:10 alihassan143

@liamappelbe can proivde some sample how i can set FfiGenerator.enums.style

alihassan143 avatar Oct 29 '25 04:10 alihassan143

@liamappelbe can proivde some sample how i can set FfiGenerator.enums.style

Set the enums options in your FfiGenerator constructor. Something like this:

FfiGenerator(
  ...
  enums: Enums(
    style: (Declaration declaration, EnumStyle? suggestedStyle) => EnumStyle.intConstants,
  ),
)

liamappelbe avatar Nov 03 '25 03:11 liamappelbe

Hi @liamappelbe, thanks for your guidance. I'm currently updating maplibre to ffigen 20 and noticed that EnumStyle is not exported by the package.

I worked around that issue with a src import for now.

import 'package:ffigen/ffigen.dart';
import 'package:ffigen/src/config_provider/config.dart' show EnumStyle;

edit: Found a few more missing exports, opened https://github.com/dart-lang/native/issues/2765 for it.

josxha avatar Nov 08 '25 00:11 josxha