RubyMotion and 3rd Party Native Frameworks: Vendoring vs GemsPosted on 7 Jan 2014 Written by Ryan Grey
We have exciting news! A beta version of our RubyMotion Gem for ShinobiCharts is now available for download. Huzzah!
Providing RubyMotion support for our iOS ShinobiCharts framework raised a lot of questions. This blog post takes a look at how iOS frameworks (including ShinobiCharts) can be used with RubyMotion, including the use of RubyMotion’s ‘vendoring’ mechanism and writing gems that wrap native iOS frameworks.
ShinobiCharts and 3rd Party Frameworks
Here at ShinobiControls we develop UI Controls for Android and iOS. ShinobiCharts iOS is a framework that allows users to embed super-slick and interactive charts into the iOS applications that they are developing.
There is a plethora of other sources for 3rd Party Frameworks that do much more than chart plotting, and using them in app development is second nature to any iOS developer. Objective-C is also second nature when it comes to writing iOS apps, or at least I used to think it was. It turns out that developers love using certain programming languages and technologies so much that they’d kill to be able to do so. As a result there are a whole host of emerging platforms designed to allow you to write your iOS apps in other languages, such as Xamarin, Appcelerator and RubyMotion.
RubyMotion is a toolchain that lets you develop iOS applications using the Ruby programming language. Being able to use a language that is focused on simplicity and productivity to develop iOS applications is very appealing.
That’s only half the battle though – what about all those great 3rd party native frameworks that you probably want to use? Fortunately the people at RubyMotion have realized just how important this is and using native frameworks is supported as a first class concept called ‘vendoring‘.
The idea behind vendoring is that your RubyMotion app can take a native framework and automatically spit out an API that has a RubyMotion interface and will compile correctly with your project. This allows you to use the 3rd Party Framework as though it were a RubyMotion library within all your app’s ruby files!
It’s pretty easy to vendor any native iOS framework for use with RubyMotion. As an example we’ll go through the steps of vendoring and using the native iOS ShinobiCharts framework in a very basic RubyMotion project. Even though we’re using ShinobiCharts.framework as an example, these steps will likely work with most other iOS frameworks.
In order to try out the following you’ll need to have RubyMotion installed and a copy of ShinobiCharts (purchased or trial version) available.
First create your skeleton RubyMotion project on the command line:
$ motion create VendoringShinobiCharts
Our project needs access to the native framework, so place your copy of ShinobiCharts.framework in the new directory VendoringShinobiCharts/vendor/.
We now need to tell the RubyMotion project to vendor the framework found in this new directory. RubyMotion does this at build time so that the framework is available for use at runtime. The Rakefile controls how the RubyMotion project is built, so we’ll be editing that – crank out your favorite text editor! Replace the setup method with the following:
Motion::Project::App.setup do |app| # Use 'rake config' to see complete project settings. app.name = 'VendoringShinobiCharts' app.device_family = :ipad # Link dependencies app.libs += ['/usr/lib/libstdc++.6.0.9.dylib'] app.frameworks += ['QuartzCore.framework', 'OpenGLES.framework', 'CoreText.framework'] #vendor ShinobiCharts so it can be used with a RubyMotion interface app.vendor_project('vendor/ShinobiCharts.framework', :static, :products => ['ShinobiCharts'], :headers_dir => 'Headers') end
This has added a few steps to the projects build process. The app will now:
- Run on an iPad
- Link against ShinobiCharts’ dependencies – it needs OpenGLES to draw the plot area etc.
- Use the vendor_project method to produce a RubyMotion interface to the ShinobiCharts.framework.
It’s that simple! We can now use the ShinobiCharts framework in our project, so let’s go ahead and start writing copying and pasting some code now! Replace your app_delegate.rb with the following:
class AppDelegate def application(application, didFinishLaunchingWithOptions:launchOptions) @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds) @window.rootViewController = VendorViewController.alloc.init @window.makeKeyAndVisible true end end
You can probably guess what we’ll be doing next – we’ll need a VendorViewController class! So go ahead and create a file called vendor_view_controller.rb. The following code contains API calls to a RubyMotion version of iOS ShinobiCharts – this interface is what was produced by the vendor_project method in the Rakefile that we altered above. In order to figure out the API calls to make in order to create and manipulate the chart we need to look at the native docs, and then translate the API using the rules found in the RubyMotion Project Management Guide.
It isn’t an ideal situation having to read from native docs, translate them using a rule-set, and then write in RubyMotion syntax, but once you’ve done this a couple of times it starts to become relatively easy.
Using these translation rules we’ll add some code to vendor_view_controller.rb that creates a really basic ShinobiChart and populates it with data.
class VendorViewController < UIViewController #viewcontroller methods def viewDidLoad super #calls viewDidLoad on super chart = ShinobiChart.alloc.initWithFrame(view.bounds)
#chart.licenseKey = "your license key here if you are a trial user" chart.xAxis = SChartNumberAxis.new chart.yAxis = SChartNumberAxis.new resizeMask = UIViewAutoresizingFlexibleWidth|UIViewAutoresizingFlexibleHeight chart.autoresizingMask = resizeMask chart.datasource = self view.addSubview(chart) end #datasource methods def numberOfSeriesInSChart(chart) 1 end def sChart(chart, seriesAtIndex:index) SChartLineSeries.new end def sChart(chart, numberOfDataPointsForSeriesAtIndex:seriesIndex) 10 end def sChart(chart, dataPointAtIndex:dataIndex, forSeriesAtIndex:seriesIndex) dp = SChartDataPoint.new dp.xValue = dataIndex dp.yValue = dataIndex return dp end end
You should now be able to run the VendoringShinobiCharts project on the command line from the projects top directory using the rake command. When the simulator launches you should see something like this:
What’s Wrong With Vendoring?
Now to address the elephant in the room – if it’s that easy to vendor and use our framework in a RubyMotion project then why would we even think about producing a Gem here at ShinobiHQ? This is where we have to start being critical about vendoring and consider some of the drawbacks and their impact:
- In the above vendoring example we end up with RubyMotion method signatures that are not very descriptive in some cases (see the sChart method).
- All the vendored method signatures are CamelCase, whereas Ruby syntax for methods is normally snake_case.
- There are no docs for the vendored API (we have to translate the native API to work out how to use ShinobiCharts with RubyMotion syntax).
- Vendoring a native framework is alien to Ruby and Obj-C users – meaning it’s one more thing to learn, manage, or go wrong.
- We can’t use all the great Ruby utilities (such as the gem command and bundler) to manage the ShinobiCharts framework as a dependency due to it being native.
From looking at these minor niggles it turns out that as simple as vendoring is, it creates a technical dissonance where we’re constantly having to context switch between the native and RubyMotion worlds in order to write our app. Wouldn’t it be great if we could settle solely for Ruby concepts? After all that’s why you’re using RubyMotion right? – because you want to code and think in Ruby!
There’s a Gem for that!
This is where using the Gem that we’ve produced has advantages over vendoring. Our Gem currently solves two of the above listed drawbacks and has the potential to address all of the issues in the future. At the moment our Gem is very basic and simply provides the auto-generated API which comes for free when vendoring. It still requires that users read the native docs.
Now that we have a Gem (and if we decide to do a full release version) we can address these problems by adding files to it that either subclass or monkey-patch/duck-punch the auto-generated RubyMotion versions of the ShinobiChart classes. This should allow us to wrap the auto-generated API in more Ruby-esque classes/methods. Once we have these ruby files in place we can produce documentation using something like RDoc or YARD.
On top of these potential improvements our Gem currently manages to remove the need to vendor the native iOS framework. Being able to treat ShinobiCharts as a Gem goes some way to making our native framework feel more at home in a RubyMotion project and means that all your favorite Gem tools can now be used to manage it like any other Gem. Installing it is as simple as doing the following on the command line:
$ gem install path/to/shinobicharts-2.5.6-LabsBindings.gem
The gem even works with Bundler, although the steps to do this are slightly different from the most common use cases. This is because we are not hosting a gem server, so the gem needs to be unpacked and then referred to as a local gem in your Gemfile – if you would like more specific instructions on this then please drop us a line at firstname.lastname@example.org. If there is enough interest in using Bundler we might put out a tutorial detailing the steps to follow, update the Gem’s README with instructions or even look into hosting our own Gem server (to remove the need to manage it as a local Gem)! So if a more Ruby-esque API, docs and better support for Bundler are things that you’d like to see then make sure to get in touch!
You know best!
That’s the gist of why we opted to experiment with a beta Gem, but the truth of the matter is that here at ShinobiHQ we’re not writing real-world RubyMotion apps, so our experience of the environment is relatively limited. This means that we can make educated decisions as to what might work best, but the reality is that only you, the users of RubyMotion, can tell us if we got it right. So if you like the Gem or prefer vendoring native frameworks yourself, think we’ve missed something important or have any other general comments, then get in touch and let us know so we can make the right decisions when it comes to ShinobiControls supporting RubyMotion in the future!
Back to Blog