ObjectiveC.jl - Bridge package for using Objective-C in Julia

I just released ObjectiveC.jl 0.1, a package for working with Objective-C in Julia. The package was created by @MikeInnes back in 2014, but wasn’t registered, or functional anymore on recent versions of Julia. That has been fixed, and you can use ObjectiveC.jl to interface with Objective-C frameworks using Julia 1.6 - 1.10, although for now only macOS is supported.

Demo

The core feature of the package is the ability to call Objective-C methods from Julia, and to build a Julia type hierarchy that matches Objective-C. For example, you can create an Objective-C NSString object, and then call methods on it:

julia> using ObjectiveC

# create a wrapper class that will hold an object pointer
julia> @objcwrapper NSString

# call a NSString class method to create an NSString object
julia> str = NSString(@objc [NSString stringWithUTF8String:"test"::Ptr{Cchar}]::id{NSString})
NSStringInstance (object of type NSTaggedPointerString)

# call an instance method to get back the contents of our NSString
julia> unsafe_string(@objc [str::id{NSString} UTF8String]::Ptr{Cchar})
"test"

# or, easier, you can have ObjectiveC.jl generate property methods
julia> @objcproperties NSString begin
           @autoproperty UTF8String::Ptr{Cchar}
       end
julia> unsafe_string(str.UTF8String)
"test"

For more examples, see the README. ObjectiveC.jl also provides a Foundation submodule that already contains definitions for NSString and a few other essential types:

julia> NSArray([str,str])
(
    test,
    test
)

julia> length(ans)
2

# etc

People familiar with Objective-C might note that the @objc call syntax almost looks like native Objective-C, with the exception of type information (e.g. id{NSString}). This is for performance reasons, as it allows us to generate fully static code that performs reasonably well:

julia> @benchmark @objc [$str::id{NSString} UTF8String]::Ptr{Cchar}
BenchmarkTools.Trial: 10000 samples with 985 evaluations.
 Range (min … max):  57.360 ns …  1.825 μs  ┊ GC (min … max): 0.00% … 0.00%
 Time  (median):     62.903 ns              ┊ GC (median):    0.00%
 Time  (mean ± σ):   65.431 ns ± 34.296 ns  ┊ GC (mean ± σ):  0.00% ± 0.00%

      ▂▄▆▅▇▇▇▆▇█▂
  ▂▃▄▅████████████▅▅▄▄▃▃▃▃▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▂▁▂▂▂▁▂▂▂▂▂▂▂▂▂▂▂ ▄
  57.4 ns         Histogram: frequency by time        88.8 ns <

 Memory estimate: 0 bytes, allocs estimate: 0.

There’s a couple of more advanced features, like @objcblock to generate callable blocks for Julia functions, but those are also demonstrated in the README.

Current status

As the package has recently been revamped, APIs may still change, so it’s only at v0.1 right now. However, Metal.jl uses it for all interactions with the Metal APIs, so the functionality seems to be working pretty well.

In the process of revamping the package, some functionality also got lost, like the ability to create Objective-C classes from Julia. I personally don’t have a need for that, so if you’re interested, PRs to reinstate that functionality are most welcome :slightly_smiling_face:

17 Likes

This is great to see, excellent work Tim! Metal.jl is very exciting too.

4 Likes

Would this help to support iOS (eventually)? I’m not sure if Objective-C is still very relevant there, I even though Swift had also taken over for macOS…

This wouldn’t help to make Julia itself iOS compatible (insofar it isn’t), but it would make it possible to call iOS APIs, as AFAIU those are still Objective-C compatible APIs that you can just call from Swift. Native Swift APIs cannot be used with Objective-C.jl, but I’m not sure to what extent those are used in iOS.

1 Like

Would this make it possible to interact somehow with objects generated via applescript? I know that one can include objective c APIs in applescripts, but the syntax is so annoying that I often wished I could manipulate objects via Julia :slight_smile:

This is just a far out question, I’m only interested if it could be technically possible with a bridge like this to access the data structures of an applescript. Not if it’s possible now (which I’m sure it’s not)

I’m not familiar with AppleScript or the ObjC APIs to interact with them, sorry. I found NSAppleScript, but that doesn’t seem to expose a way to interact with AppleScript objects.

I have looked around a little more and there seems to be a way forward. There’s something called the “Scripting Bridge”, which is an Objective C framework which enables one to call some application’s apple script API in Objective C. The headers needed for that can be generated using the binaries sdef and sdp. sdef extracts an XML description of the scripting API defined for a given application. sdp creates an Objective C header file from that, which one would normally include in an Objective C application, which also references the ScriptingBridge framework. So I would have to define both elements from ScriptingBridge framework and from the sdp output of whatever application I was interested in on the Julia side somehow.

Now, I haven’t yet tried anything, only read information here and there on the internet. Is there an automated way, for example using the Clang.jl generator to turn the header files I’ll get from sdp into Julia code, or is that a manual transformation process? How did you do it for Foundation @maleadt?

Ok I’ve managed the start, which is to start an application via the scripting bridge. I think from here on out I should make it work :slight_smile:

using ObjectiveC
using ObjectiveC.Foundation

load_framework("ScriptingBridge")

@objcwrapper SBObject <: NSObject

@objcwrapper SBApplication <: SBObject

# @objcproperties SBApplication begin
#     @autoproperty classNamesForCodes::id{NSDictionary}
# end

function SBApplication(bundle_identifier::String)
    x = @objc [SBApplication applicationWithBundleIdentifier:bundle_identifier::id{NSString}
                    ]::id{SBApplication}
    return SBApplication(x)
end

app = SBApplication("com.captureone.captureone16")

activate(app::SBApplication) = @objc [app::id{SBApplication} activate]::Cvoid

activate(app) # now Capture One starts
1 Like