Displaying user notifications in your RubyMotion OS X app Nov 27 2013

Notification Center was first announced for Mac OS X 10.8 Mountain Lion, and then it saw some improvements for Mac OS X 10.9 Mavericks. Similar to the notifications on iOS, it provides a convenient way for applications to show information to the user, and is especially useful for applications that run in the background, or that have a background processing component that needs to display and highlight information to the user.

Notify

Using them is fairly straightfward - we'll run through how to add them to an application, and we're going to build upon the small status bar app we created in the last tutorial to demo this. If you weren't following along with that article, it ran through creating a status bar application which talked to a JSON API (the GitHub API) to retrieve commit data and show the latest commits from repositories you have access too. We can now extend that to popup a notification whenever new commits are found. We can also respond to a click on the notification by showing the menu containing the list of commits. All of the changes we'll be making are to the app_delegate.rb file.

First things first, we need to add something to the end of our applicationDidFinishLaunching method:

NSUserNotificationCenter.defaultUserNotificationCenter.setDelegate(self)

This will allow us to intercept events regarding our notifications, and handle the clicks on them, which we'll cover shortly.

Next up, we'll add a method to show new commits as a notification:

def showNotification(new_commits)
  return unless new_commits > 0
  notification = NSUserNotification.alloc.init
  notification.title = "New commits"
  notification.informativeText = "There are #{new_commits} new commits."
  notification.soundName = NSUserNotificationDefaultSoundName
  NSUserNotificationCenter.defaultUserNotificationCenter.scheduleNotification(notification)
end

Here we're making sure we only trigger a new notification when there are new commits, we're setting the title and text, and we're using a default sound (the very familiar tri-tone noise). You can also specify a path to a .caf audio file in the resources bundle of the app, if you so wish. Lastly, we schedule the notification with the user notification center, and as we set no particular scheduled date for the notification, it shows immediately. You could schedule a notification for a specific time by setting the deliveryDate on the notification before adding to the user notification center - this one is scheduled for 10 minutes from now:

notification.deliveryDate = Time.now + (10 * 60)
NSUserNotificationCenter.defaultUserNotificationCenter.scheduleNotification(notification)

Now we need to trigger our notifications method, so we're going to dip into our checkForNewEvents method, which is where our commit processing happened, and towards the end where we used to have the following:

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

We'll modify that to this:

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

What we're doing here is taking the amount of new commits we found (the difference between our @commits and @last_commits arrays), and we're adding it to the unseen counter to display the total number of unseen commits on the menu, as before. We're then however using just the amount of new commits for this particular call, to display the notification in the user notification center. Essentially the menu bar item itself tracks a running tally of unseen commits, but the notifications should only ever let you know of new ones it has found since the last time it showed a notification.

If you run the app now, you'll see that as soon as it starts up and pulls back the initial list of commits, it triggers a notification! However if we click on it in the user notification center, nothing happens. Ideally, we want it to trigger something to show in our app, and also to remove the notification as it has now been seen and processed by the user.

As we've already setup our AppDelegate as a delegate for the user notification center in applicationDidFinishLaunching, all that remains is to handle the approriate event by creating a method as follows:

def userNotificationCenter(center, didActivateNotification: notification)
  @status_item.popUpStatusItemMenu(@status_menu)
  center.removeDeliveredNotification(notification)
end

The didActivateNotification method is what handles the click on a notification created by our app, and so we perform two functions here - the first is to trigger the status bar menu to popup, showing the latest commits, and the second is to remove the delivered notification from the notification center, thereby clearing it out of the users way.

Notified

That's it, a fairly short tutorial this time around, mainly because the NSUserNotification and NSUserNotificationCenter APIs are very clean, tidy and easy to work with. They provide some great additional interaction with your users, making apps more interesting, informative and accessible - and all in just a few lines of code! You can see the above changes in a commit to our existing codebase here, and you can of course grab the entire code for the app here.

If you have any comments, questions or suggestions, please let me know below. Remember to follow @ejdraper on Twitter to hear about the latest articles as we put them up!

Comments

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

Eight things to ask before hiring a web developer Sep 2 2013

Hiring a web developer is an important decision, and a good web developer can make all the difference between a successful, on budget, on time project, and one besieged by delays and issues. So we had a think and have come up with some questions to ask to help get the information you need to make a good choice when discussing a potential project with a web developer.

1. Do they have experience in the same area as the app you're looking to build?

You should be able to view case studies or their portfolio on their site, or if not, then they should be able to provide information about the projects they've worked on upon request (or at the very least, references that back up their claims). You should ask for further detail about their work on projects in a similar area or niche as your own, specifically finding out their role on the project, and what value they delivered.

2. Are they fixed cost, or hourly/day rate, and do they have a method for handling and communicating issues that arise?

This needs to fit in with your expectations and your budget, although billing based on value will almost always be better for everyone involved over fixed cost. Find out how they deal with changes in requirements, new features in the middle of development, changes required as a part of business development, as well as unexpected issues with implementation that can crop up.

3. Do they use source control?

They should be using source control throughout development to track and version their changes. If it's an existing codebase, this might be something you need to give them access to, or if you don't already have it, they should offer to set it up.

4. Do they setup a staging or QA system, and deploy regularly from day one to launch day to show progress?

It's no good getting everything delivered in one go, even if it's exactly on the deadline day - for anything but the smallest of projects, it'll require a bit of back and forth in terms of testing and QA, and making sure not just that everything works and is bug-free, but also that everything is optimised for the best user experience.

5. Do they use a project management app to track progress through development?

They should use a tool to define all of the stories/cards that make up the development, and preferably it'd be a web based tool that gives you insight into what is yet to be started, what is currently being worked on, what is finished, and what is delivered ready for testing.

6. Do they do their own automated testing on the code to avoid regressions?

Using a Continuous Integration (CI) server, they should be able to use their automated testing suite to let them know when something breaks elsewhere in the codebase. They should also have tools or metrics available to keep tabs on overall code quality - while somewhat subjective, there are ways and means to find out overall standard metrics that would pick up larger red flags in the code before they become a problem.

7. What experience do they have in deploying and running an application in production, especially with the target traffic you're aiming for?

They should be able to give you an idea of ballpark figures for traffic/usage levels of the projects they've worked on previously, which will give you an idea of whether they've worked on the sort of scale of project that you are estimating for your own app.

8. What do they offer in terms of maintenance after launch, or in terms of handover to internal personnel where applicable?

Are they able to train your own personnel, or help hire personnel for long term maintenance of the app? Do they have examples of documentation they provide for their project that will help with this? In the meantime, are they available to maintain the project after launch, and deal with urgent production issues?

I'm available for freelance work, whether it's for web dev or other projects. Why not get in touch with me today for a free 30 minute consultation where I can get to know your business better?

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

Why launching your product is so important Aug 23 2013

Or how I learned to stop worrying and love the MVP

An MVP, or a minimum viable product, is a key part of what we work with clients to achieve on brand new apps and greenfield projects. The idea behind an MVP is to isolate the smallest amount of work you need to do to get your idea in front of people, so you can see whether it works, and how it works (or doesn't), and see what direction to take it in next. The reason we always look to advise clients to go down this route isn't because we want to work with clients for the smallest time possible (we hope that we'll cultivate a working relationship that continues for lots of development iterations to come!), but because we know how important getting a version of your product out there is, and how important it is to businesses like yours that it can be done quickly and affordably.

If you've been following along with our other posts, you've probably already done some legwork to research and prepare your ideas. You may have even collected a lot of interest on a pre-launch page. But the actual product still might not be as useful or profitable as you think, at least in the incarnation of it you have in your head. The absolute best way to put that to the test is to go ahead and put it in front of people. You'll sometimes see people refer to a prototype, and there are times where a prototype of your idea is the best first step - especially useful with engineering physical products. However prototype implies that it's transient, temporary, something that is there to be replaced with the real thing once the specifications are fully explored and buttoned down. With an MVP, while we're making sure the project stays agile and open to change based on feedback, we're still building the product in a meaningful way, so that it can be built upon and extended for years to come. We're not delivering something to be thrown away, it's simply the first stepping stone.

So how do we go about defining an MVP? The idea is to boil your idea down to its simplest form - the phrase KISS ("Keep It Simple, Stupid") comes to mind. Figure out the core ethos behind your idea, what really makes it tick, and then ascertain the smallest amount of features needed for a user to get that in front of them. With every system, there are some tasks that ideally would be automated, but in the beginning can be manual tasks without too much effort. This means that any dev work to automate them can be excluded, to start with. As soon as the manual work becomes burdensome, then that's a nice problem to have, as it means the app needs to scale, and that's generally as a result of user engagement and growth. No point scaling before then prematurely though, as you're simply burning dev cycles, and you're not even really sure if that feature is something the users will love, hate, or be indifferent to. By focusing on the minimum amount of work to get something in front of a user, you're minimising your exposure if it turns out to be something they don't want, and making sure you collect great feedback to help build on it if it's something that they do find useful.

We'll work with you to pare down the feature set in this way, and with our KickOff packages can schedule work based on how small the idea is, and the length of time required. We can then work with you to build it out very quickly, with a constant feedback loop along the way, and then we help you to launch to your users - whether that's a closed private beta, an open public beta, whether you charge for it or not from day one, those are all things that are up to you. But getting it into users hands, so that their feedback can drive you towards a successful product, that's the key part of the process, and it's what we focus on and deliver when you engage with us to build your idea.

Some people might worry as well that by trimming the feature set to define an MVP, it could result in shipping an imperfect product, and what if that results in your vision not being fully grasped by users, or even worse, small issues or imperfections damaging your reputation? The bottom line however is that no matter how hard you try, how long you work, or how much you spend, your first launch of a new product or idea will almost certainly not be perfect, and more importantly, won't be exactly what users need. You might be in the right ballpark, you might solve a problem for the users, but to do it right, you'll need to work with them to find the sweet spot that your product can reside in. So knowing that, surely it's better to start that iterative customer development process sooner rather than later?

Why not get in touch with me today for a free 30 minute chat about your idea, to see how quickly and affordably I could build your dream app?

Comments

Page 3 of 31 | Next page | Previous page