Here we go, another article on RubyMotion! For those of you that don’t know what’s that’s all about, it’s a toolchain created by Laurent Sansonetti (of MacRuby fame) allowing you to create native iOS application using Ruby.
I like it a lot, but please keep in mind that while I have a decent knowledge of Ruby, I’m only using RubyMotion on a side project. I worked hard to get everything here to work, but it still might not be the most efficient way of doing it. If you do know a better solution, please share it in the comments and I’ll update the article.
Right now the general feeling is that in order to get the highest level of quality in an app, you have to go native. I’m not disputing that, but there are some cases where you just need to have some HTML, JS and CSS displaying. To do that, the best way is to use the UIWebView element. It allows you to open html file and display them as if you were in a browser.
In this article I’ll try to demonstrate how to:
- Open a given website in a simple web view within a RubyMotion project.
- Open a locally stored HTML file
- Customize a bit the feel of the web view
- Send a signal from the web view back to the application
I won’t get into the details on how to set up a project and will assume you have basic knowledge of how this works. If you never created something with RubyMotion, please take a look at this tutorial before reading the rest of this article.
Setting up the AppDelegate
This is pretty basic, we just set up a controller and assign it to be the root.
Opening A Given URL
We’ll create the structure for a basic controller and set its view to be a UIWebView. Then we’ll build an URL and load it.
EDIT: I moved the UIWebView.alloc.init to loadView to avoid creating a UIView just to throw it away. Thanks to Colin for pointing this out.
If we slow down things a bit, we have this:
Now if we run rake we see my website, and how badly it’s optimized for mobile:
Opening A Local File
It’s good to open a website, but what if you don’t have internet access, or if you don’t need to load anything since you have already everything you need ? That’s when you load your locally stored file.
First, create an
index.html file that you’ll store in
/resources. Put whatever HTML you want inside, maybe a background color or something similar to know if the file loaded properly.
Once it’s done, tweak the controller so that it knows what’s up.
Run rake again, and you should see your file displaying.
At this point you can customize the HTML however you want. Keep in mind that if you use other files (css, js, images), they need to be stored in
/ressources as well in order to be accessible. You might also need to think about how you want to handle retina/non retina images.
Customize The UIWebView
Here what I wanted to do is to forbid scrolling and just have a nice, fixed, piece of HTML. This way it’ll feel more like a native piece of UI than a web browser. To do so, you can change these parameters:
It’s the part where it gets a bit more tricky. Here I want to have a link somewhere in my HTML that triggers something in my RubyMotion code. It could be a “cancel” link, a form… it doesn’t matter.
To do that we’re going to use the logic behind
shouldStartLoadWithRequest and the whole UIWebViewDelegate thing. I’m not an iOS master yet, so please bear with me on the domain-specific vocabulary.
First we’re going to add a delegate to our view. Here I’ll use the current controller because it’s a rather small project, but feel free to use any object you want.
Now we can use the fact that our controller, being the delegate of a UIWebview, will have its shouldStartLoadWithRequest method called before any frame is loaded. Let’s write our own method and see how it goes.
Here we have the bare minimum to get started. You’ll notice we have a couple of parameters. In this article I’ll just use the request, but know that the others can be usefull too. Read the documentation if you want to learn more.
Now if we launch rake one more time and look at the trace, we can see the current path to the local file we’re loading.
This is useless, but now let’s add a link in the
index.html file. Now if you point it to a regular website, it’ll just load it. It’s not what we want right now. Instead we’ll create our own protocol (lol) as such:
By now you must have figured out where we’re heading like that. Since we have our callback thanks to the UIWebViewDelegate protocol, we can parse the URL and do process it how we want. I added a basic example below, but really you can do whatever.
By now you should have noticed how I always return true. It’s because if you return false the frame won’t load. You can use this fact to forbid the user to click certain links on the page, or disable browsing altogether.
A Quick Security Note
We now have an HTML page talking to your iOS app ! Pretty cool, but be carefull on how you allow this to happen, since you really don’t want any website sending funky signals to your app. Make sure you don’t execute any message you receive.
It should be pretty obvious, but it’s still worth mentioning.
As always, you can look up the whole project on github. It is compiling and working as I write this article, but keep in mind that RubyMotion is a fast moving project and it might not be the case in a few months.