Articles tagged 'technical'

How to integrate your RubyMotion OS X app with a JSON based web API Nov 22 2013

In our last article on RubyMotion OS X apps, we built a status bar app that fetched data in the background to keep the menu item continually updated and showing the latest information. Our data source was from the local system, fetching the CPU usage statistics. In this article we'll look at how to integrate with a JSON web API, making HTTP calls from your RubyMotion app, processing the response, and showing that in the menu.

You have to commit

The little utility app we'll build will be a very basic and straightforward one to show the latest commits from projects you have access to on GitHub. GitHub has a wonderful JSON API, complete with very good documentation, and for developers, utility apps integrating with GitHub can be very useful indeed. You can follow along with the code in the article, and the app is open source and available in full here too.

Familiar start

By now if you've been following along with the RubyMotion on OS X series of articles, you'll know our starting point. We'll use the template gem from our first article to spin up a new app. Let's fire up a blank status bar app now:

motion create --template=osx-status-bar-app CommitTrackerMenu

Dependencies

First thing we'll do is add the gems we'll need, BubbleWrap, and motion-yaml. Your Gemfile should look something like this:

source 'https://rubygems.org'

gem 'rake'
gem 'bubble-wrap'
gem 'motion-yaml'

Remember to run this when you're done too:

bundle install

Configuration

We'll come to BubbleWrap in a minute, but we're using motion-yaml for the configuration for the app - to load our config file and ready it for use in our code, add the following to your AppDelegate:

class AppDelegate
  CONFIG = YAML::load(NSMutableData.dataWithContentsOfURL(
    NSBundle.mainBundle.URLForResource("config", withExtension: "yml")).to_s)

This finds the config in the root of the resource bundle, and loads the YAML contents into the constant. You'll see an example config file in the GitHub repo for the code, you just need to rename config.yml.example to config.yml and fill in your GitHub personal access token - you can get one by visiting here. You can also amend the timer config to specify how often to check for new updates. It should end up looking something like this:

timer: 30
github_token: TOKEN

GitHub integration

Now, let's work on integration with the API itself - this is where BubbleWrap comes in handy. This gem is a fantastic collection of all sorts of great functionality, nice Ruby wrappers around more complicated Cocoa APIs, shortcuts for often used features, and helpers that take advantage of Ruby language features to provide nicer ways of doing things. It's a veritable Swiss Army Knife for iOS and OS X development.

We'll be using BW::HTTP and BW::JSON to grab the data we need and to parse it. We've separated out our GitHub integration into a separate class to make it easier to read, so here it is in full:

class GitHub
  attr_accessor :token, :user

  def initialize(token, &block)
    @token = token
    self.user do |json|
      @user = json
      block.call
    end
  end

  def user(&block)
    self.get("https://api.github.com/user", &block)
  end

  def events(&block)
    self.get(@user['received_events_url'], &block)
  end

  def get(url, &block)
    BW::HTTP.get(url, {:credentials => {:username => @token, :password => ""}}) do |response|
      block.call(BW::JSON.parse(response.body))
    end
  end
end

There isn't a lot to this, essentially we initialize the client with our personal access token. It immediately uses the token to do a quick check, and load the authenticated user JSON - this way we don't need further user details such as username to make additional calls. It'll call any block passed into that initialization method when that process is complete, allowing you to wait until the client is ready before doing anything else.

We have a method to retrieve events for the user which we'll be using as our data source in the status bar itself in a minute, but you'll see both that and the authenticated user call are wrappers themselves around the get method, which uses the BubbleWrap HTTP methods to easily call to the URL, passing the provided credentials (using the token over basic auth for GitHub means providing the token as the username, and leaving the password blank), and then processing the response body. In this case, we use the BubbleWrap JSON support to parse the body and return an array or hash parsed from the API.

Lastly, you'll notice too that in the events method, we're using something provided by the response from the authenticated user check as the URL (received_events_url returned within @user). The GitHub API helpfully returns other API call URLs within the authenticated user response, which we can then parse and use for further calls. This way, if their API changes and they move or update URLs, our code won't break as it's using the pointer to the right method provided by GitHub themselves, rather than a hard-coded URL!

With that out of the way, we can now focus back on our status bar app. First, we need to setup our menu items, so our applicationDidFinishLaunching method should look like this:

def applicationDidFinishLaunching(notification)
  @app_name = NSBundle.mainBundle.infoDictionary['CFBundleDisplayName']

  @status_menu = NSMenu.new
  @status_menu.delegate = self

  @status_item = NSStatusBar.systemStatusBar.statusItemWithLength(NSVariableStatusItemLength).init
  @status_item.setMenu(@status_menu)
  @status_item.setHighlightMode(true)
  @status_item.setTitle(@app_name)

  @http_state = createMenuItem("Loading...", '')
  @http_state.enabled = false
  @status_menu.addItem @http_state

  @status_menu.addItem createMenuItem("About #{@app_name}", 'orderFrontStandardAboutPanel:')
  @status_menu.addItem createMenuItem("Quit", 'terminate:')

  @unseen = 0
  @github = GitHub.new(CONFIG[:github_token]) do
    self.checkForNewEvents
  end
end

This should be fairly familiar if you've been following along with the previous articles. A few key differences, we're setting the delegate on the status menu to the app delegate itself - we're doing that so we can look out for a specific event, but we'll come back to that a little later.

We're creating a disabled menu item, @http_state, used just to display information on the state of the app, so we can update it to show when we're doing different things. We instantiate it and set it to show "Loading…" to start with.

We then instantiate the GitHub client, and pass in a block that will get called when the initial authenticated user JSON is returned - we need to wait for this, as any subsequent calls won't work until that has completed. We can then kick off our check for new events. This is where we check with our data source, in this case the GitHub API, and it's where the meat of the app lies.

Events

The method that checks for and processes events from the GitHub API does quite a lot, so we'll step through it and explain how it works - if you want to see the entire thing, you can do so here.

def checkForNewEvents
  begin
    @http_state.title = "Working..."
    @github.events do |events|
      @http_state.title = "Processing..."

We start by setting our @http_state menu item to show us as working, and then as soon as we get the response from GitHub, we switch that to processing.

unless events.empty?
  @events.each { |item| @status_menu.removeItem(item) } unless @events.nil?
  @events = []
  @urls = {}
  @last_commits = @commits || []
  @commits = []

Here we make sure that we have something to process, and then we are removing any menu items that we had previously pertaining to events. Each time this runs it'll clear out the event menu items, and recreate them based on the data found. In addition to the variable to track those menu items (@events), we also have a hash to track URLs (@urls), which comes into play when we're handling menu item clicks for these events, as well as a couple of variables to help us track what's new between calls (@commits and @last_commits).

counter = 0
events.reverse.each do |event|
  next unless event["type"] == "PushEvent"
  next if event["payload"].nil? || event["payload"]["commits"].nil?

  commits = event["payload"]["commits"].select { |c| c["distinct"] }

  next if commits.length == 0

We're now going to loop through all the events returned - we do so in reverse order, as we'll be adding each menu item in at a specific index, which will in turn push all of those before it further down. So that we end up with date descending (newest first) order in the end, we reverse things to begin with.

We're skipping over any event that isn't a push event (code being pushed up to a repository), and that doesn't have a payload or commits.

Lastly, we also ignore any commits that aren't distinct - this is a very handy flag provided by GitHub from their API, and by distinct they mean if a commit has been seen in the repo before, i.e. if something was pushed as part of a branch, it'd be distinct, but then merging it into master, it's the same commit, and it is no longer distinct. We only want to be notified of entirely new commits, so we filter out anything that isn't distinct.

actor = event['actor']['login']
repo = event['repo']['name']

commits.each do |commit|
  message = commit['message'].split("\n").first
  message = "#{message[0...30]}..." if message.length > 35
  @urls[counter] = commit['url'].gsub("api.", "").gsub("/repos", "").gsub("/commits", "/commit")
  item = self.createMenuItem("[#{actor}]: #{repo} - #{message}", 'pressEvent:')

Now we loop through the commits we have left to look at, and we'll do a bit of processing on the message and URL. We want to truncate the message to a reasonable uniform length, and we need to mess with the URL a bit as the URL returned from GitHub points to the commit within the API, and we want a URL we can open in a browser to show the commit - the always useful String gsub method comes in handy there as we stash the URL in our @urls hash for looking up later.

We then setup the menu item, similarly to how we've done before. Each item will use the same event handler, which we'll look at shortly. One thing to note is that by specifying pressEvent:, instead of just pressEvent (note the trailing colon), then our event handler will be sent an argument, the sender of the event, in this case the item itself. As we're handling all of the item events with the same event handler method, and using the tag on the item to lookup the URL to show, it'll be quite handy to be sent the clicked item!

    item.tag = counter
    @events << item
    @status_menu.insertItem(item, atIndex: 1)
    counter += 1
    @commits << commit['sha']
  end
end

Here we set a tag on the item, and this is where our counter comes in - we can only use an integer for the tag, but we need a reference to our URL for use in the event handler. So we've used the counter as a numeric pointer to the URL in our URLs hash (@urls), and we set it on the item so we can match the two together. We are then keeping track of our event menu item (so we can remove it from the menu next time we re-process), and then insert the item into the menu at the same index for each item. We're doing it at index 1 instead of index 0 so as to always leave the @http_state menu item at the top of the menu as the others get pushed down with each new item added.

We then increment our counter for the next commit, and stash the unique SHA reference for the commit in @commits so we can compare which commits we've seen this time with last time for working out which commits are new.

  @unseen += (@commits - @last_commits).length
  self.showUnseenCommits
end

Here we use our arrays of the commit references (@commits for this time, @last_commits for the last time around) to deduce how many new commits there are in this run, and add that to our unseen total tracking variable, @unseen. The method to show those we'll get into below.

    @http_state.title = "Ready"
  end
rescue
  @http_state.title = "Error, retrying again shortly"

If everything finishes as expected, we set the @http_state menu item to show things as ready for the next call - and if we experienced an error, we make that clear too.

  ensure
    NSTimer.scheduledTimerWithTimeInterval(CONFIG[:timer],
      target: self,
      selector: 'checkForNewEvents',
      userInfo: nil,
      repeats: false)
  end
end

Whatever happens though, the ensure part of the begin/rescue/ensure block will always end by triggering the next call. We're using our timer configuration to schedule the same method to be called again. We do this rather than simply call it once at the beginning but with repeats set to true, because the call itself and subsequent processing will take a variable amount of time. For the most part it's very quick, but network slowdowns, poor connections, or timeouts could cause it to take a bit longer. We want our delay between calls to be just that - between calls, not between the time each call is kicked off, regardless of result. From the time our processing ends, to the time it runs again, will be the value we've configured.

Mark all as read

We have a couple of steps left to flesh out. Above we called showUnseenCommits after incrementing how many unseen commits there were - it's a fairly simple update to the title of the main system menu item for our app:

def showUnseenCommits
  @status_item.setTitle("#{@unseen.zero? ? 'No' : @unseen} new #{@unseen == 1 ? 'commit' : 'commits'}")
end

When we click on the menu, it'd be good to clear the message, sort of a mark all as read. This is the reason we set the delegate for the status menu to be the app delegate itself - luckily we can easily handle the event of the menu opening (in response to the click) by implementing menuWillOpen, and in there we can clear the message by setting @unseen to zero and updating the status menu text:

def menuWillOpen(menu)
  @unseen = 0
  self.showUnseenCommits
end

Click to view

Lastly, when we click on an item, it'd be great to go to that commit on GitHub for viewing. We've already stashed the URLs when processing the events above, and so we now just need to lookup the particular URL, which we can do with the numeric tag property on the item. That's our pointer into the URLs hash, @urls, and so our event handler looks like this:

def pressEvent(item)
  NSWorkspace.sharedWorkspace.openURL(NSURL.URLWithString(@urls[item.tag]))
end

NSWorkspace.sharedWorkspace.openURL is a nice way to use your system default way of opening a URL, i.e. your default browser. Easy!

You can now fire up the app to see it all working together.

Wrapping up - questions?

And that's about it - much like last time, we've built a status bar app that runs in the background, updating from a data source. This time though we used HTTP calls and JSON processing to interact with a 3rd party API to make the data a bit more interesting, we used YAML for file configuration, we used a timer instead of a background thread to periodically check for new data, and we dynamically updated our menu list based on the data we brought back, bringing even greater interactivity into our fairly basic app. Remember you can check out all the code here.

In our next few articles we'll look at integrating notifications within our apps to make notifying the user of a change more prominent and useful, and we'll also start to explore custom user interfaces, to improve the experience for users of your Mac OS X apps, both for status bar apps as we've been focusing on thus far, but also for window based apps.

Hopefully you're enjoying my articles on RubyMotion development - as always, let me know your thoughts, comments and questions below, or contact me on Twitter @ejdraper!

Comments

How to build a RubyMotion status bar app that updates in the background Nov 7 2013

In our last post we looked at how easy it is to create an OS X menu bar app using RubyMotion. This time around, we'll look at one of the ways we can make status bar apps more useful - having it fetch and display data from an external source in the background. In our specific example, we'll be wrapping the top commandline tool, to pull out CPU usage and display it. It should demonstrate how you could replace that and roll in calls to other system commands for different data, or even API calls to web services to pull in live data from there too.

The code is open source and available here. It's based on using osx-status-bar-app-template that we detailed in the last post, so if you want to build this yourself as we go along in the article, start by installing the osx-status-bar-app-template gem and creating a new app using that template.

Extensions

First of all, we have one other code file besides the app delegate in the codebase. In app/extensions/ns_menu_item.rb, we have the following:

class NSMenuItem
  def checked
    self.state == NSOnState
  end

  def checked=(value)
    self.state = (value ? NSOnState : NSOffState)
  end
end

If you've worked with Ruby before, you'll know that you can re-open classes to add methods to them, and if you've worked with Objective-C much you'll know that you can do something similar with categories. All we're essentially doing here is adding some convenience short-hand methods to menu items to allow us to check and uncheck them, and to see if they're checked. You'll see how this is used shortly.

Setting up our menu

Let's setup the menu items we need in app/app_delegate.rb. The idea is we'll have three options, Total to show total CPU usage, User to show the user CPU usage, and System to show the sys CPU usage value. In our applicationDidFinishLaunching method, alongside the default menu items created for about and quit, add our three new menu items in:

@total_item = createMenuItem("Total", 'clickTotal')
@total_item.checked = true
@status_menu.addItem(@total_item)

@user_item = createMenuItem("User", 'clickUser')
@user_item.checked = false
@status_menu.addItem(@user_item)

@system_item = createMenuItem("System", 'clickSystem')
@system_item.checked = false
@status_menu.addItem(@system_item)

We'll also add the following to applicationDidFinishLaunching:

@user = 0
@sys = 0
self.updateStatus

We'll be implementing updateStatus shortly, but it's what sets the title text in the menu bar based on the current values, which we've defaulted to 0 above.

Next up, we need to implement the event handlers for each menu item click. We're going to get a bit funky with the selection, so that if you check Total, it only shows that, and unchecks the other two. You can select either or both of the other two to show, and if you deselect both, it goes back to having Total selected. Our three event handlers look like this:

def clickTotal
  @user_item.checked = false
  @system_item.checked = false
  @total_item.checked = true
  self.updateStatus
end

def clickUser
  @total_item.checked = false
  @user_item.checked = !@user_item.checked
  self.mustSelectSomething
  self.updateStatus
end

def clickSystem
  @total_item.checked = false
  @system_item.checked = !@system_item.checked
  self.mustSelectSomething
  self.updateStatus
end

def mustSelectSomething
  @total_item.checked = true if !@user_item.checked && !@system_item.checked
end

If you select the total one, it automatically unchecks the other two, and you can't click to unselect it. If you select either user or system, it will uncheck the total one, and either uncheck or check itself, depending upon its current state. They both also use the mustSelectSomething method to double check and ensure that if after the click the result is that neither is selected, then the total becomes selected again. All three event handlers finish up by triggering a call to update the status text. Let's look at how that is implemented next:

def updateStatus
  if @total_item.checked
    @status_item.setTitle("CPU: #{sprintf("%.2f", @user + @sys)}%")
  else
    text = []
    text << "User: #{sprintf("%.2f", @user)}%" if @user_item.checked
    text << "Sys: #{sprintf("%.2f", @sys)}%" if @system_item.checked
    @status_item.setTitle(text.join(", "))
  end
end

Here is where we're using the status of each menu item, checked or unchecked, to decide what to show. If total is checked, we're showing the sum of both the user and sys values as a total CPU usage figure. If not, then we're showing user, sys, or both, based on which ones are checked. Hopefully by now it's obvious why we added those convenience methods to the menu items, to make seeing if an item is checked a bit less verbose!

If you run the app, you'll see it works, showing 0.0% for all figures, but allowing you to toggle between the various different displays. So what we're now missing is the external source to give us data, and a way to have it run in the background to update the status bar all on its own.

Driven by data

We're going to use the top command as the source for our values, and we're going to use IO.popen to open up a pipe to the command sampling values continuously. top produces a lot of data, so we'll be filtering it out and looking for the specific line that contains CPU values, then parsing those out. It's not too tricky, and the whole thing looks like this:

def startTop
  IO.popen("top -l 0") do |f|
    while true
      unless((line = f.gets).nil?)
        if line[0...10] == 'CPU usage:'
          line.gsub!("CPU usage: ", "")
          line.split(", ")
          @user, @sys = line.split(", ").map { |p| p.split("%").first.to_f }
          self.updateStatus
        end
      end
    end
  end
end

Running top -l 0 is what gives us the output in a format we can work with from a script - by default top is pretty clever, and replaces the output every sample so that when you run it in a terminal, it seems like it updates on screen (rather than scrolling in new data for each sample). By passing -l we're asking it to run for a specific amount of samples, giving us the output in a raw form, and by specifying 0 rather than a set amount, we're telling it we want it to run infinitely. With a pipe open to that command running endlessly, we can then read each line, check to see if it's the line we're looking for, and then parse out the data. The lines we want look like this:

CPU usage: 3.79% user, 3.12% sys, 93.8% idle

Once we strip out the CPU usage: heading, we can then split on the comma to separate our values, and split on the % to separate the value from the name. We can then parse it as a float, and we just pull out the first two as our user and sys values (we don't use the idle value). This is an example of how Ruby's superior string handle really comes in handy, you can see what our breakdown and parsing of that line looks in a Ruby console:

> line = "CPU usage: 3.79% user, 3.12% sys, 93.8% idle"
=> "CPU usage: 3.79% user, 3.12% sys, 93.8% idle"
> line.gsub!("CPU usage: ", "")
=> "3.79% user, 3.12% sys, 93.8% idle"
> line.split(", ")
=> ["3.79% user", "3.12% sys", "93.8% idle"]
> line.split(", ").map { |p| p.split("%") }
=> [["3.79", " user"], ["3.12", " sys"], ["93.8", " idle"]]
> line.split(", ").map { |p| p.split("%").first }
=> ["3.79", "3.12", "93.8"]
> line.split(", ").map { |p| p.split("%").first.to_f }
=> [3.79, 3.12, 93.8]

We fairly effortless and reliably go from a string output from top, to pulling out the two values we need. From there, it calls updateStatus to reflect the updated values, and we're golden. Except, how does this method get called? If you try to call startTop directly from within applicationDidFinishLaunching, then it will fire up and continuously run to pull out values - but you can't interact with the menu bar app at all, and it won't update beyond the initial value being shown. This is because our startTop method runs endlessly, and thus blocks the main thread when called in that manner. Any UI interactions or any further interaction with the app doesn't work. We need a way to explicitly call that method in the background of our app, so it happens on a separate thread. Luckily, this is fairly straightforward - add this to the end of applicationDidFinishLaunching:

self.performSelectorInBackground('startTop', withObject: nil)

This call returns immediately, firing up that method in a background thread, and meaning that interaction with the app continues as normal, and our startTop method keeps running in the background, parsing out the latest values and updating the status bar title text. Voila, we have an app that is talking to a data source in the background, and updating the status bar all on its own. Replacing the startTop method with whatever data source you want to regularly query to return data to show in the status bar would be fairly trivial.

If you fire up the app now, you can see it update in real-time:

CPUTrackerMenu

What next?

What data sources can you think of that'd be useful to ping and show data from in your status bar? Chances are there are a bunch of things you'd like to keep an eye on, and now with just a small amount of code, and RubyMotion, you can! With CPUTrackerMenu too, you can even make sure that your own app isn't taking up too much CPU by running in the background!

In a future post we'll look at integrating with web service APIs from within your RubyMotion app - in the meantime, any questions related to this article, a previous article, or RubyMotion dev in general, let me know in the comments below, or catch me on Twitter @ejdraper!

Comments

How to quickly get started creating status bar apps for Mac OS X using RubyMotion Oct 18 2013

Recently here at KickCode we've been working on our first Mac OS X application, due for submission to the App Store shortly, and using RubyMotion. We'll be unveiling it shortly, but it's a fairly simple utility app that lives in the status bar. Status bar apps are quite useful, whethere it's a utility for day to day productivity tasks, or whether it's a connection or window into an online service, that provides background syncing, notifications, or other information.

New gem

The good news is, they're fairly simple to get up and running using RubyMotion! Indeed, as with a lot of work in RubyMotion, it's a lot less effort than building the equivalent in Xcode and Objective-C! And we've just made it a bit easier still, with a small gem called osx-status-bar-app-template that provides you with a new RubyMotion app template.

Simply run:

gem install osx-status-bar-app-template

And then once installed, you can go ahead and create your new RubyMotion app using the template:

motion create --template=osx-status-bar-app TestStatusBarApp

Then simply fire it up to see the basic skeleton app in action, ready for building upon:

cd ./TestStatusBarApp
rake

At this point you'll see an entry called "TestStatusBarApp" appear in your status bar, and clicking it will show you three options - a working about window, a custom action to show off how you handle menu item events, and a quit command. Note that the app doesn't appear in the dock or the cmd+tab window, as it resides solely in the status bar!

RubyMotion app in OS X status bar

How it works

As this is a RubyMotion template, it basically just creates the app skeleton for you, so we can delve into the code to understand better how it's working, and how we can build on it.

To make the app status bar only, and have the dock and cmd+tab window ignore it as a targetable window, we have added the following to our Rakefile:

app.info_plist['LSUIElement'] = true

We've also updated the spec/main_spec.rb to reference and test the status bar menu items instead of windows by default - you'll want to update this as you make changes to your menus.

The rest of the good stuff happens inside app/app_delegate.rb as you might expect - and it's surprisingly little code. In our applicationDidFinishLaunching method, we're setting up a new NSMenu, and then creating a status item to sit in the system status bar:

@status_menu = NSMenu.new

@status_item = NSStatusBar.systemStatusBar.statusItemWithLength(NSVariableStatusItemLength).init
@status_item.setMenu(@status_menu)

We're using NSVariableStatusItemLength so that the status item can match the content. It's easier to setup this way, but bear in mind that you probably don't want the content that'll appear in the status bar to be changing width too dramatically or too often, as that could be quite annoying!

Next up, we just set the highlight mode to true so that when you click the status bar item you get the blue highlight to show that you've clicked it, and we're also setting the title to the app name:

@status_item.setHighlightMode(true)
@status_item.setTitle(@app_name)

After that we can setup some of our menu items, but we're using a helper method to make things a little less verbose:

def createMenuItem(name, action)
  NSMenuItem.alloc.initWithTitle(name, action: action, keyEquivalent: '')
end

This just sets up a new menu item with the specified title and action (more on actions in a minute), and returns it. We can then use it like this:

@status_menu.addItem createMenuItem("About #{@app_name}", 'orderFrontStandardAboutPanel:')
@status_menu.addItem createMenuItem("Custom Action", 'pressAction')
@status_menu.addItem createMenuItem("Quit", 'terminate:')

Actions are just references to methods that will act as event handlers for the menu item being clicked. The first and third items have actions that are "built-in" to OS X apps - namely with the default app structure, the about panel is already there and ready to use, and "orderFrontStandardAboutPanel:" shows that window. It contains a list of credits derived from a text file in the "resources" folder of your project ("Credits.rtf"). Within our new template, this is still the same as the one that comes with the default OS X template for RubyMotion, and should be edited to reflect your own details. The third item, with action "terminate:", as you might expect refers to a method that is already accessible and that shuts the app down. It's important to provide a way for the user to shutdown your status bar app, as it doesn't appear in the dock or the "Force Quit" list!

The second menu item there is more interesting - "pressAction" is our own defined method, and acts as our event handler for a click on that item:

def pressAction
  alert = NSAlert.alloc.init
  alert.setMessageText "Action triggered from status bar menu"
  alert.addButtonWithTitle "OK"
  alert.runModal
end

This too is fairly basic - we're just popping up an NSAlert dialog to show that the menu item has been clicked, and the event handler has correctly received the event. That's all there is to creating a basic status bar app in RubyMotion!

Extending our app

Now let's extend our default app skeleton provided by the gem to do something a little more interesting. We'll have a menu item action that updates the content of the status bar item itself. First of all, let's update our spec to expect a fourth menu item:

it "has four menu items" do
  @app.delegate.status_menu.itemArray.length.should == 4
end

Then, in our app delegate, lets setup a new menu item above the "quit" item, called increment:

@status_menu.addItem createMenuItem("Increment", 'pressIncrement')

Lastly, let's implement our event handler:

def pressIncrement
  @count ||= 0
  @count += 1

  @status_item.setTitle("#{@count} times")
end

Here we're establishing a variable to keep track of our count, if one isn't already set, then we're incrementing it. Finally we're referencing our main status item (which remember refers to the item that sits in the status bar itself, not any of the child menu items that show when you click on the status bar item), and we're updating the title with the count. Now if you fire up the app, and click "Increment" from the status bar app menu, you'll see the value in the status bar update!

RubyMotion app in OS X status bar extended with increment action

From here you can see how it'll now be fairly straightforward to start building slightly more useful functionality into the app, either hooking into system stats or calls to provide data, or perhaps calling out to a third party API to do interesting things, making it all available from the status bar. In a future article we'll look at how to run things in the background to do something interesting that updates the status bar without direct user interaction.

Feedback welcome

In the meantime, check out the gem, and let me know what you think! Any questions or comments on the above are welcome, either below or @ejdraper on Twitter!

Comments

How to refresh your RubyMotion app for iOS 7 in 30 minutes or less Oct 9 2013

With iOS 7 now launched, and with one of the highest adoption rates ever seen amongst mobile phone owners (estimates put it at around 200 million inside the first few days, compared with 100 million in a similar timeframe for iOS 6), it's a great time to upgrade any apps you might have built to really look snazzy on the wildly revamped OS. Your apps will work of course, regardless of whether you take the time to update them, but if you want them to fit in with the much improved user interface, there are a few quick wins available.

Upgrading RubyMotion

You'll need to make sure you're running the latest RubyMotion, and make sure your app is building with it (if you've locked into a previous version before in your Rakefile, either lock in to the latest version, or remove the version to use the latest one you have installed, after running the update). Now the nice thing is that with Xcode 5, iOS 7 SDK, and RubyMotion 2.8+ installed, rebuilding your app will let you see your app running with the iOS 7 style UI on the iOS 7 simulator. If you're using default controls such as navigation controllers, tab bars, and table views etc, your app will get a nice visual upgrade, that is completely backwards compatible (i.e. still uses the familiar old styles) on iOS 6 and previous.

New shiny

Having iOS 7 skins for default controls is all well and good, and puts you above a vast majority of apps that for whatever reason will never see another update (an app has to be rebuilt with the iOS 7 SDK and re-submitted, even if there are no other changes, to get the new default look for iOS 7, otherwise it'll look like an iOS 6 blast from the past!). But what about nice new features, like the status bar colour tinting? With a small little snippet, you can get the super sweet colour way tinting that effortlessly melts into the status bar, and which serves two distinct purposes. The first is that the visual effect gives your app the feel of having more real estate and room to play with, as there isn't an abrupt cut-off for the status bar any longer. The second is that it can help to reinforce branding, with your app or company colours now nailed to the mast for the header of their phones screen, with no status bar looming over everything else. Here is how we go about doing that:

For an app that has the root view controller set to a UINavigationController, then from within our AppDelegate we need to add the following:

def application(application, didFinishLaunchingWithOptions:launchOptions)
  ...

  @window.rootViewController.navigationBar.barTintColor = UIColor.colorWithRed(0.64, green: 0.64, blue: 0.64, alpha: 1.0)
  @window.rootViewController.navigationBar.translucent = true
  @window.rootViewController.navigationBar.tintColor = UIColor.blackColor

  ...
  @window.makeKeyAndVisible
  true
end

Now obviously you can substitute your own colors in, but essentially we've picked a nice color for the status bar heading, and we've set translucent to true to let us see the shadow of things passing through underneath it, which is a nice effect. Lastly we set the tint color to black so that the icons in the status bar look right with our color, but again you can find a pair that matches with your colors and branding.

I'm testing this out on our app AlbumAlert, and you can see that when you fire it up, you end up with something like this:

iOS 7

So that all works fine, however I imagine at this point that you probably don't want to support iOS 7 solely. You may in time make that switch, or at least offer features that depend upon iOS 7, but if you're using mainly default UI controls, you can easily ensure you still support iOS 6 and previous too. Let's see what the situation is with our changes on iOS 6 by running our app with the 6.1 simulator (you will need to make sure it's installed from within the Xcode Downloads section if you're having issues, as well as any other simulators you want to test with). To fire up a specific simulator version:

rake target=6.1

Replace 6.1 with whatever version you want to test with - after it builds and runs, you'll see however that we have an error:

Terminating app due to uncaught exception 'NoMethodError', reason: ' 315:in `application:didFinishLaunchingWithOptions:':
undefined method `barTintColor=' for #<UINavigationBar:0xab390c0> (NoMethodError)

This is because the barTintColor method is new to iOS 7 - so how do we ensure we do one thing for our UI in iOS 7, and stick to the older way of doing things for past versions? We can query the version number of the OS in this particular case, which changes our code as follows:

if UIDevice.currentDevice.systemVersion.floatValue >= 7.0
  @window.rootViewController.navigationBar.barTintColor = UIColor.colorWithRed(0.64, green: 0.64, blue: 0.64, alpha: 1.0)
  @window.rootViewController.navigationBar.translucent = true
  @window.rootViewController.navigationBar.tintColor = UIColor.blackColor
else
  @window.rootViewController.navigationBar.tintColor = UIColor.colorWithRed(0.64, green: 0.64, blue: 0.64, alpha: 1.0)
  @window.rootViewController.navigationBar.translucent = false
end

Here we are using our specific color as the tintColor for pre-iOS 7 versions, which will keep our color branding, but isn't quite as snazzy as the new iOS 7 UI. We're then only setting barTintColor when the version is 7.0 or greater. For other feature differences you may find specific flags you can check to determine if the feature is available, rather than relying on the overall OS version number, but in this case it's an easy check to make to differentiate between iOS 7, and previous OS versions.

Now if we fire up our app in 6.1 it'll look as follows:

iOS 6

You can re-test in iOS 7 too to ensure it still has our nice new snazzy style navigation bar, which it does. And that's it! We're taking advantage of a new design feature to make our app feel more at home on iOS 7, while maintaining backwards compatibility for users on older versions of the OS.

Final thoughts

This is just a quick peek at the low hanging fruit on offer, where you can spend maybe 30 minutes on each RubyMotion iOS app you have, re-submit them all for approval, and take advantage of the wave of iOS 7 app updates happening as users update their devices. Instead of being left behind and reminding your users of a forgotten user interface, you can be at the forefront of Apple's new interface revolution.

Remember, if you need any help with your RubyMotion app, I'm available by the hour, or by the week, and can take your app to the next level. Contact us today to find out how.

Comments

Unity Render Texture Sep 13 2013

UPDATE: All engine features are now available in both Personal and Pro editions of Unity, so you no longer need a paid Unity license to use this feature!

This is a fairly brief tutorial on how to setup a Render Texture. This feature makes it trivial (you don't even need any code) to show the live feed from a secondary camera on a screen within your game. I used this to great effect in Velocity Ball for 7DFPS, to show goal line views above each endzone!

I've made a video which covers using this technique on a basic Render Texture - check it out now to see how easy it is to use this feature in your games!

One thing I forgot to mention at the end of the video is that the scene being rendered on the render texture can come out a little dark - you can try different shaders to find one that works for your use, specifically something like "Unlit/Texture" will end up a lot closer to the "Camera Preview" for your secondary camera, but you might find that there is a specific shader you want to use to achieve a certain effect, in the same way that you can apply different scripts and effects to your secondary camera, and they too will be shown as part of the renderered view on the texture.

Here is how I put the technique to use in my 7DFPS game, Velocity Ball:

If you have any comments, let me know on this post, on the YouTube video, or you can catch me on Twitter @ejdraper. If you have suggestions for other tutorial screencasts, let me know - I'm looking to do more, fun little 5 minute videos on various topics to give you tips, tricks and insight into different Unity features!

Comments

The Power of Prototyping Sep 5 2013

Recently I've been spending some time prototyping different game ideas. Partly this was so that I could get to grip with a wide variety of features of Unity - different types of game prototypes have lent themselves to me learning and polishing my knowledge on different areas of the engine. But also, it's a great way to quickly identify the ideas that work, and the ideas that don't.

Prototyping power

What's so great about prototyping then? You're still building games, perhaps smaller versions to test out, but it takes time, and you still need to decide which idea to work on first. That means working out which one you think might be most enjoyable to play. The difference is though that when you're prototyping you are quite often using temporary or transient placeholders for the majority of the structure of the game. The aim is to do the minimum amount possible in order to be able to play your idea, to test to see if it works, and to see if it is fun. That means that in terms of art and design assets, you can look to use stock assets, or temporary art. In terms of code, you can treat it very much as a hackday - use a game jam mentality, where you want to get the mechanics for the game up and running as quickly as possible, and you can look to extract the logic and tidy things up later if you pursue the idea.

Constraints are good

Speaking of game jams and hackdays, quite often constraints like that fuel creativity. Ordinarily, most of us are probably used to writing code in the nicest, cleanest way possible, and building assets in a way that makes them easy-to-use, re-usable, and flexible. But the constraints of a time limited game jam or hackday means that we're free to forget some of those things, and instead just let our creativity flow. We know that the end result is simply a playable validation (hopefully!) of our idea, rather than a shippable product (even if you do actually put it online so people can play it), so we can cut a few corners to test our theories, and then simply revisit and improve those things if we decide to pursue it.

From prototype to finished product

In fact, if we do decide to turn a prototype in to a real game we want to ship, we might well end up rewriting or rebuilding most of it. A prototype by definition is an experimental, primitive model on which something else is based, most often made out of placeholder parts. It is designed to test the theory behind a product in practice, and to help build specifications for then making the real product. Our prototype therefore serves a purpose not just in establishing if our idea is any good, but also, if it is, in setting out how we want to build the real deal - but this doesn't mean getting things right on the first go. It might be that during the prototype construction, we stumbled upon some pretty neat solutions that we'll want to keep - or it might be that we know we have the right idea, but it can be built better, and the prototype shows us the things we should be doing differently and improving. In terms of code, that might equate to making it more performant and smarter, and in terms of art that might be making something more detailed and more intricate, or even throwing it out entirely and changing the style while keeping the mechanics. Either way, a prototype paves the way for success - and the ones that don't make it, we can be safe in the knowledge that we didn't spend ages figuring out that they won't work!

Reflection

One other thing about prototypes is that you can get the idea that's in your head out there and into a playable form much quicker, as we're all probably guilty of thinking up ideas but then not implementing them in any form to test them out. After the prototype is finished, then you can even forget about it for a bit - you could work on another project, and just let the ideas that you've put into practice stew for a while, to see if time makes it better or worse. It's fairly natural for us to think that something new is better, because it's fresh, and it's our most recent work. Time helps to level that playing field, and then you can revisit your prototype with a more even handed and impartial view on things, determined to root out what works, and replace what doesn't. It might even be that you decide not to pursue it - but perhaps there is something in the prototype that you can keep and carry over to another project. If not, then you've almost certainly grown and taught yourself something in the process at least, so usually there is always something to be gained from knocking together a prototype!

Coming soon

We have another couple of prototypes I've worked on recently that I'll put up for playtesting soon! Follow me on Twitter so you don't miss out!

Comments

Throwing Shapes Aug 23 2013

I wanted an excuse to play around with Futile, the 2D framework for Unity by Matt Rix, especially since I read that it's quite easy to pick up if you're familiar with Cocos2d, which I am. I figured a simple puzzle or shape game would be best, so I didn't have to spend too long messing around with assets, so I came up with the idea for Throwing Shapes.

It's designed for touch screen devices (although right now you can just play the prototype in your browser) and specifically asks you to throw shapes, either to the left of the screen, or to the right of the screen. Throwing them to the left "scores" them, removing them from play, and incrementing your score. Throwing them to the right makes them split into three new shapes from the left. You start with a single shape, and a 30 second countdown, and the idea is to score as many shapes as you can in the timeframe. It's a fairly straightforward risk/reward dynamic, with you spending time splitting the shapes into more, and having to figure out when you bank the shapes you have to boost your score.

I have a fair few other ideas to extend it, as it isn't particularly challenging or providing of much entertainment more than a couple of plays right now. It'd be nice to introduce levels and increasing difficulty - starting with what the prototype has now, building up to only scoring shapes when thrown through a smaller and smaller gap on the left hand side, maybe even leading to sorting shapes by type or colour when scoring, making it a bit more than a frantic mashing of dragging fingers. It also lends itself to GameCenter leaderboards to compete with friends, given the score based nature.

The music as well is just a placeholder at the minute, me messing around with Garageband and the excellent 8-bit plugin, as well as just having the music shift to suit the context (more frantic in the last eight seconds to represent the final countdown).

Working with Futile was awesome, especially as Owen Goss released a starter script to help you get a blank Unity 4.2 project up and running with Futile incredibly easily. It certainly takes the focus out of the Unity editor and lets you concentrate on the code - whether that is a good thing or not is probably based on how much you like the Unity editor. I find it very useful for 3D games, but for 2D I prefer the structure of setting out my sprites and layers in code, and again this comes from working with Cocos2d a lot. Futile is very similar, and overall it doesn't take a lot of code to do some cool stuff. I'd recommend playing with it if you have an interest in cross-platform 2D games for sure.

So check out the video above, and then play the game for yourself - I'm thinking I might add a few more challenges and levels to Throwing Shapes, add GameCenter support, and then tidy it up a bit to get it on the App Store for iOS soon - what do you all think?

Comments

7DFPS: after the dust has settled Aug 21 2013

I thought it might be fun to look back at 7DFPS, now the dust has settled, to look at what went right and what went wrong with the development of Velocity Ball. The observations here might be useful to other people doing a time limited game jam in future, and certainly will help me to solidify my experiences to make next time around even better.

Positives

  • I learned a lot about Unity, in a very short space of time
  • I shipped a pretty much complete (if a bit buggy, and short on content) game concept in 7 days
  • 7DFPS gives you a spotlight, so a bunch of people have played the web and Mac versions!
  • I think the game idea has a lot of potential, and I'm left with a lot of ideas on how to improve it and extend it

Improvements

  • I probably spent too long on a few bells and whistles, then had a few late breaking physics related bugs that were tough to fully nail down and fix before the end - specifically I was messing around with the image effects to create the splash screen loading into the main game, as well as alternate camera angles for the main menu, and the goal line cams that show on big screens above each endzone
  • The game doesn't have much longevity, and isn't particularly taxing as the AI is fairly one directional at the minute

On the whole though, it was an enjoyable exercise, and Velocity Ball is definitely something I'd like to continue working on and building out from the prototype it is now, into something bigger and better.

Other entries

Another bonus of course was being able to take a look at what other people can get done in a week - and it's pretty humbling and awesome to see some of the amazing things people did. Some clearly focused on short, polished experiences, while others worked on a broader, more ambitious vision that had a little less polish, but there were some gems in there. Here are a few of my favourites, after having gone through just a small percentage of the 167 games that were finished.

Escape Sequence

Really polished, with the design fitting the concept perfectly - the idea is to escape each maze, with them seemingly getting tougher and tougher, on your way to the escape pods

Root Arena

Old school style fragfest, frenetic multiplayer action - a great variety of weaponry, and it even has it's own stats server and leaderboards, great for bragging rights!

Hunting Anubis

Nice change of pace, aerial combat - good style, was hard to test the actual combat as no one else was on when I tested it, but I can imagine it'd be quite tense!

Beyond Perception

Another innovative idea, this time a puzzle game set inside museum paintings, 2D puzzles in 3D - nice to see such an abstract, fresh concept squeezed into a game produced in such a short space of time

Superhot

Incredibly stylish aesthetic, with a great unique selling point in that time only moves forward in the game when you're moving - would make a fantastic full game

Unity

Worth noting that all of the above games were built using Unity, just like Velocity Ball - I'm not sure exactly, but it seems like a good majority of the games were built with Unity, quite possibly as it lends itself so well to being able to get something up and running in a very short space of time, without really having to sacrifice the quality in visuals or fidelity that you might have expected from such a rapid tool in the past.

Over to you

How about your 7DFPS? What was good, what was bad, and what would you do differently next time? And what are your favourite games from the jam? Sound off in the comments, or hit me up on Twitter.

Comments

Velocity Ball (#7DFPS): Dev Blog #3 Aug 19 2013

7DFPS finished a couple of days ago, and I just about wrapped up a version of Velocity Ball I was happy with. It then took me a few days to sort out a video demoing the game, and also a downloadable build.

You can check out more about the game, including the Mac download and controls (keyboard/mouse or PS3 controller) for the game here - Windows build hopefully coming soon, and I might well get a build running on the OUYA too.

I'm pretty pleased that in a week I was able to get 2v2 gameplay with (fairly limited) AI in, working arena, some nice special effects, and proper game timers, scoring and restarting. I had a fair few other ideas beyond that, but 7 days isn't quite as long as it seems! Below is the video run through of what the build looks like right now:

Check out the video - and if you're on Mac, download it and give it a go yourself! You can let me know what you think in the comments on this post, on Twitter, or via the contact form. I'm going to pause and take stock of the project a little bit now (and work on some other prototype ideas), but then will return to it and do another iteration on the idea with feedback and refinements as I think it could be a lot of fun with a bit more time and attention!

If you've worked on a 7DFPS game too then please let me know - I'm checking out lots on the 7DFPS site, but don't hesitate to shout about your game so I know to take a look!

UPDATE: After a few requests, I've added the ability to play the game in browser using the Unity web player. Click here to check it out!

Comments

Velocity Ball (#7DFPS): Dev Blog #2 Aug 14 2013

Just a couple of days to go now for 7DFPS, and since the last dev blog I've only really had time to improve the arena and presentation a bit, and add a menu.

Here are a few screenshots of those features:

  • Main menu
  • Bot gameplay
  • Bot opposition score
  • Home endzone
  • Player score

So I'm refining the list of what is possible in the remaining couple of days (multiplayer is definitely a post-7DFPS task now), and I'm hoping to get the following done:

  • Sound effects (the game is entirely silent right now!)
  • Countdown restart and position reset after each goal to make it more tactical and less hectic
  • Improved AI and ball physics
  • Getting games going solely with AI, for a spectator mode, and/or to make the main menu more interesting
  • Improving the UI and presentation a bit further
  • Dual stick controller support

The next dev blog will definitely be a video run through, most likely fairly close to the end of 7DFPS in the hope that I can show off as many of the above features in there as possible! I'll then probably package the game up for download on Mac (Windows too if I get time to test it).

A few more resources that have been useful in the last couple of days:

  • NGUI: Next-Gen UI, great asset for building user interfaces on top of and around your games
  • NGUI Basic Menu, a cracking little tutorial video on putting together a basic menu using NGUI

Hope everyone else is making good progress, looking forward to spending some time looking at other entries over the weekend!

Comments

Velocity Ball (#7DFPS): Dev Blog #1 Aug 12 2013

3 days into my 7DFPS game, and things are progressing quite well. I've learned the basics of blocking out arenas in Blender, and setting them up properly with UV unwrapping for texturing them in Unity.

I've got working ball physics (with a few quirks to be ironed out), and possession/throwing of the ball working. I've also implemented the two endzones, and got some basic AI working with teammates and opposition players in a 2v2, as well as scoring (red vs blue teams, naturally). The bot characters even have swish idle to run animation transitions after messing around with Mecanim.

I'm planning on doing a proper run through video once I add a few more bits in, namely I need to add a time limit to games, and also fine tune some of the mechanics for scoring, resetting after a score, and some of the ball physics and AI edge cases. At the minute it's only working on keyboard/mouse too, so I'd like to really getting it working on a controller (either OUYA or PS3 I think). After that, I can start to flesh out the content a bit more, sound effects, special effects, different arenas and team selection, and if I can improve the AI, it might be playable with more than just 2v2 as well (right now the bots go a little loopy if there are too many!). And who knows, maybe I'll get on to some multiplayer even which would be really great!

Some of the great tutorials and resources I've used so far:

Hoping to make enough progress now for a post like this each day until 7DFPS is up, but we'll see - how is everyone else getting on?

Comments

More than meets the eye Aug 8 2013

There has been a lot of discussion surrounding the fundamental changes happening in the next version of iOS, iOS 7. For a long time the user interface for iOS has stayed largely the same, with small, incremental improvements (mostly) made with each release. More recently, a lot of people expressed wishes for more of an overhaul, something to drastically change the user interface, to better represent how people are using their devices these days, but also to change some of the things that can't easily be done with incremental iterations.

Why then, now it's been announced, shown off, and it is in developers hands, is there such an outcry over the direction it has taken? Given that it is still in beta and thus only registered iOS application developers can download it, it's fair to say that a lot of people are making their minds up based on screenshots - especially as a lot of these opinions stem from the minutes and hours after the announcement! Having seen a lot of the same screenshots myself before updating my device to iOS 7 with the latest beta, it struck me that the new design is very nuanced - initially it does look a little jarring, however, it's actually incredible natural when in motion, on the device.

This got me thinking - are there a lot of scenarios where something gives off the wrong impression when you take a stale, single frame of it? After all, Apple wouldn't have designed the new user interface solely with the intention of it looking good in screenshots. It's pretty clear from how they announced it and the features they added, that it's geared towards it being a natural experience in the palm of your hand. Something that feels right to use, something less clunky than its predecessors. So why is it being so harshly judged, mainly on screenshots, and mainly based on those icons?

It's incredible how quickly all of that fades away when using it - the icons are bright and in your face at first, but it's just because they are different, and you soon get used to them. They will almost certainly continue to be tweaked and changed anyway, but by and large they aren't as terrible as a lot of people imply. Instead what you do notice after a day or two of playing with it is all of the transitions between apps and screens are so much more natural. Digging into folders of apps from the home screen, now using a zoom in instead of the sort of accordion expansion it had before, is so much nicer. Switching apps gives you a preview of each open app that you can swipe through. The whole experience now is so much more cohesive and feels right - this almost certainly is down to the fact that the OS is now designed expressly for all of the things you use it for, rather than the older versions which had features and user interface extensions bolted on as new capabilities were unveiled.

Besides being left with a very nice new experience for our iDevices, what else does the whole process that Apple have gone through teach us? One of the key things, I think, is that it's always better to see things in motion, if that's what you are designing for. Of course you want it to look nice in screenshots and promotional materials, but the proof is in the pudding, and experiencing something first hand needs to be so sweet, that it's the last selling point you'll need for someone to be in full agreement with you that your app/service/site is something they can't live without (as well as convincing them that it does what they need functionally, of course). Why do we spend so much time in wireframes and mockups then? Essentially static, stale versions of our visions - we should be looking to do the bare minimum in terms of boring, lifeless designs, and instead look to get something functional and in motion as soon as possible. That is where we'll see if our vision is flawed, or brilliant, and we can iterate on a moving product, honing in on the best experience.

I think most of us would rather figure out how best to show off an incredible product in screenshot form later, than worry that while our screenshots look good, our actual product experience doesn't match it. I'm not saying mockups and wireframes have no place of course, but the concept of making sure there is a mockup for every page, and that it is signed off and locked down, doesn't mesh well with fast, agile, iterative development, and with the idea of an MVP (minimum viable product) to find your audience, and your market fit. Get your branding right with a traditional design process, then bring the app to life with the smallest, simplest functionality you can. Iterating from there on a living, breathing idea, which you and your testers can use and fine tune, will result in a much better product. You may even ship a beta version to users and continue iterating the design there too. And if, like Apple, you get to the point where the design community at large feels the need to debate every facet of your newest design at length, then you've most likely done quite an effective job at building something innovative and fresh!

Comments

Page 2 of 3 | Next page | Previous page