How can I call DCSCopyTextDefinition(_:_:_:) function?

HelloTaxiHelloTaxi Member ✭✭

In Swift, I use following code to translate a word from system dicts:

var word = "hello"
if let result = DCSCopyTextDefinition(nil, word as CFString, CFRange(location: 0, length: word.count)){
    var translation = result.takeRetainedValue() as String
}

Now, how can I call the DCSCopyTextDefinition function in Xamarin?
Thanks.

Posts

  • ChrisHamonsChrisHamons USForum Administrator, Xamarin Team Xamurai

    So that function is a C API:

    https://developer.apple.com/documentation/coreservices/1446842-dcscopytextdefinition?language=objc

    So you need to write a p/invoke. It's a bit involved, let me write this one with a description.

    First here's the prototype:

    CFStringRef DCSCopyTextDefinition(DCSDictionaryRef dictionary, CFStringRef textString, CFRange range);

    and our doc tell us it's in CoreServices.

    So we start with a class:

    static class TextServices
    {
    

    and then add an attribute telling C# where to find our function:

            [DllImport ("/System/Library/Frameworks/CoreServices.framework/CoreServices")]
    

    now we translate the function into the C# equivalent:

            public static extern IntPtr DCSCopyTextDefinition (IntPtr dictionary, IntPtr textString, CFRange range);
    

    All of the "Refs" are pointers, so IntPtr. Xamarin.Mac has a CFRange struct, so we can use that.

    Now that's a nightmare to call, so let's wrap it:

            public static string CopyTextDefinition (IntPtr dictionary, string text, CFRange range)
            {
                using (NSString t = (NSString)text) {
                    return ObjCRuntime.Runtime.GetNSObject<NSString> (DCSCopyTextDefinition (dictionary, t.Handle, range));
                }
            }
    

    The dictionary is some random thing you get elsewhere, so we have to pass the pointer around. We can turn a C# string into an NSString, and NSString and CFString are toll free bridged.

    • We use a using block to make sure the lifetime of our NSString is long enough.
    • We use ObjCRuntime.Runtime.GetNSObject to convert the IntPtr we get back to an NSString (from CFString).

    Now, since I have no way of getting a DCSDictionaryRef, I can't test it, so I don't know if I made any mistakes. That's where I'd start from though.

    P/invokes can be tricky in some cases (callbacks, pointers) and if you mess up it will crash your app in "fun" ways. I'd suggest lots of testing to make sure you get it right.

    Since this is a system framework, you can always request that we bind it at some point via an issue.

  • HelloTaxiHelloTaxi Member ✭✭
    edited July 11

    @ChrisHamons So pretty cool! And now I learned how to call an OC func also, many many thanks.
    Now here's my code in C#, it works fine:

    var word = "hello";
    var translation = TextServices.CopyTextDefinition(IntPtr.Zero, word, new CoreFoundation.CFRange(0, word.Length));
    
Sign In or Register to comment.