objc2 icon indicating copy to clipboard operation
objc2 copied to clipboard

Receiving objective-c pointers through FFI with safe wrappers

Open snowp opened this issue 2 years ago • 5 comments

I've been looking at the docs for this and objc but haven't really found a good answer to how you'd use this library when bridging objective-c code into Rust. For example, given a objective-C function like

void write(const NSArray<const String *> *fields);

what would be the preferred way to implement this as a extern "C" function on Rust and wrap the parameter with appropriate Foundation types? I've been getting by so far by using *const Object and raw msg_send!, but it would be nice to understand how I can use the existing type definitions.

snowp avatar Aug 05 '22 15:08 snowp

Hmm, would be easier to answer if I knew the exact function, can you point me to the documentation for a concrete example? And what is String in this context, did you mean NSString?

In any case, const NSArray<NSString>* can be represented in Rust as *const objc2::foundation::NSArray<objc2::foundation::NSString>, maybe that helps a bit?

madsmtm avatar Aug 05 '22 15:08 madsmtm

Yeah it's supposed to be NSString.

I dug into it a bit more and figured out where I was going wrong: I had tried your suggestion with NSArray<Object> to iterate on the types, but Object does not impl INSObject, so the NSArray<Object> type ended up being pretty useless. After specifying NSArray<NSString> this works out for me.

Here's an example of what I was doing for posterity's sake:

  #[no_mangle]
  pub unsafe extern "C" fn write(ptr: *const NSArray<Object>) {
    let count: usize = msg_send![ptr, count];

    for i in 0 .. count {
      let value: *const Object = msg_send![ptr, objectAtIndex: i];

      // do something with field.
    }
  }

If I want to use a custom type (one not implemented in objc2::foundation) do I have to define my own class? Or is there a way to say NSArray<any INSObject>?

If you have a place where docs around this belongs I'm happy to write something down to make this easier for the next person trying to do this

snowp avatar Aug 05 '22 17:08 snowp

Object does not impl INSObject

Sounds like you're using an older version of the library? INSObject doesn't exist any more, and the stuff you're talking about should be possible with Object now?

an example of what I was doing

So could be simplified to (while keeping the same ABI):

#[no_mangle]
pub unsafe extern "C" fn write(fields: Option<&NSArray<NSString>>) {
    fields.map(|fields| fields.unwrap().iter().map(|obj| {
        // Do something with `obj`
    }))
}

If I want to use a custom type (one not implemented in objc2::foundation) do I have to define my own class? Or is there a way to say NSArray<any INSObject>?

If it's in a generic context, you can do NSArray<T> where T: Message. If not, you can just do NSArray<Object>.

Note that you can define a type that can be used as an interface to a class with the new extern_class! macro.

madsmtm avatar Aug 05 '22 19:08 madsmtm

Got it, yeah I was using the latest stable version per docs.rs (0.2.7 for objc2, 0.1.1 for objc2-foundation) but seems like I'll be better off using the alpha versions that have built on what objc provides.

Thanks for taking the time here, appreciate the help!

snowp avatar Aug 05 '22 19:08 snowp

Yeah, will get a stable release out fairly soon, but you know, to actually promise stability I need to be sure it's at a point where I'd want to keep backwards compatibility (for a while at least).

madsmtm avatar Aug 05 '22 19:08 madsmtm

So, a stable release is still a ways away, since I decided to make icrate (and that will take a lot longer to stabilize).

But I'll close this issue for now, at least

madsmtm avatar Jan 18 '23 15:01 madsmtm