Swift & Ruby Interoperability

For the past years I favored Ruby for developing web applications. Ruby has tons of great frameworks that allows me to quickly develop and deploy for the web. With Swift for backend gaining momentum, I wondered: what If I wanted to migrate some code developed in Ruby — some shared business logic — into a shiny new Swift web app? Or what if I wanted to create some kind of framework in Swift and use it also in a Ruby project? On this blog post I’m going to set two examples on how I can you interoperate these two languages.

Yes I understand, this can sound a bit of nonsense, but still, I was curious to see how could I validate the idea. Moreover how could I bridge these two languages.


1. Calling Ruby from Swift

Ruby has a C API, that allows you to instantiate a Ruby VM, read *.rb files, call methods and much more. Technically what’s missing is a way to import ruby.h into Swift, the good news is that it can be easily done with Swift Package Manager, for instance take the CRuby package. Its Package.swift is empty, the only thing that’s actually doing is pointing where your Ruby is installed on the module.modulemap:

Using Ruby 2.2.5 installed with rbenv

Now, let’s create a project and use the CRuby package:

And add the hello.rb file with a method that takes an argument and returns a string:

Finally, create some Swift code to call hello.rb using Ruby’s C API.

And compile:

RUBY_225_PATH=$RBENV_ROOT/versions/2.2.5 
swift build -Xcc -IRUBY_225_PATH/lib/include/ruby-2.2.0 \ 
-Xcc -IRUBY_225_PATH/lib/include/ruby-2.2.0/x86_64-darwin15 \
-Xlinker -L$RUBY_225_PATH/lib

Note: The flag -Xcc is used to pass an option during all C compiler invocation, and -Xlinker is used pass an option through all linker invocations. This is only available on Swift 3.

If everything worked you should be able to see:

Hello Swift from Ruby!

2. Calling Swift from Ruby

For the second part, where I want to call Swift from Ruby, I will use Fiddle, that is nothing more than a libffi wrapper. Libffi is a popular C library which provides a portable interface that allows code written in one language to call code written in another language.

With Fiddle we can call a dynamic library from a Ruby code. And for the Swift part, I can simply use the Swift Package Manager, that recently introduced an option to compile to dynamic libraries (*.dylib).

Example:

And on Ruby

Calling Swift from Ruby

You will notice that I’m using the function’s mangled name, which I’ve found by running:

nm -a .build/release/libPonyScale.dylib

At this point, I’m not sure how can I improve, in order to have a readable function name.

Then, if you execute pony_scale.rb you will see the pony’s weight:

Your pony weighs: 176.4

Conclusion

So distant yet so close. As we know Swift and Ruby are two distinct languages. However, you can connect them without using any type of hack nor special framework — mainly because they share a common C ancestor. You can be happy if you encounter this issue on a future, where you have to create some bridge between your legacy Ruby project to a brand new Swift app.

References