lukaskollmer / objc

🔮 NodeJS ↔ Objective-C bridge (experimental)
MIT License
98 stars 20 forks source link

How to use NSRange as a Block parameter? #32

Open elliotnash opened 2 years ago

elliotnash commented 2 years ago

I'm looking to create a block that takes an NSRange, and it seems I can't use a struct as a parameter, as I get TypeError: Unable to coerce type from [StructType] when supplying objc.types.NSRange as a type. Any ideas on how to do this? I'm also not sure how to create a block that takes primitives or the BOOL type (I'm trying to create a block of type ^(NSDictionary *attrs, NSRange range, BOOL *stop) {}

lukaskollmer commented 2 years ago

Fixed this, it should now work with the v0.22.0 release. Here's an example of using a block with the signature you mentioned (I also added this to the examples/ folder).

const objc = require('objc');
const ref = require('ref-napi');

objc.import('AppKit'); // for NSLinkAttributeName

const { id, NSRange } = objc.types;
const {
    NSAttributedString,
    NSMutableAttributedString,
    NSLinkAttributeName
} = objc;

let attrString = NSMutableAttributedString.new();
attrString.appendAttributedString_(NSAttributedString.alloc().initWithString_attributes_(
    'abc', { [NSLinkAttributeName]: 'https://example.com' }
));
attrString.appendAttributedString_(NSAttributedString.alloc().initWithString_attributes_(
    'def', { [NSLinkAttributeName]: 'https://example.de' }
));

let block = new objc.Block((arg0, arg1, arg2) => {
    const attrs = objc.wrap(arg0);
    const range = arg1;
    console.log(`block called w/ args attrs: ${attrs}, range: ${[arg1.location, arg1.length]}, stop: ${arg2.deref()}`);
    //ref.set(arg2, 0, 1); // uncomment this to have it stop iterating after the first range.
    return;
}, objc.types.void, [id, NSRange, ref.refType(objc.types.char)]);

attrString.enumerateAttributesInRange_options_usingBlock_(NSRange.new(0, attrString.length()), 0, block);

The block args arg0, arg1, and arg2 are the NSDictionary *, NSRange, and BOOL * args, respectively. You can use the location and length properties to access the fields of the range struct inside the block. The first param (the attributes dict) needs to be wrapped in an objc instance proxy using the objc.wrap function.

This also supports writing to the stop param, you can uncomment the commented line in the block to test that.