D3 / JavaScript Swift & iOS | JavaScriptCore Framework

Why would you ever want to use JavaScript in your Swift Project?

javascript-sucks I am working on a project that started off as a web application first. It has a lot of cool features and some pretty advanced graphs. Their front end developer is using d3.js. D3.js is a JavaScript library for manipulating documents based on data. D3 is an amazing library that can do just about any kind of graph imaginable from circle packing to basic line graphs, and what is even more impressive is it was written by one guy Mike Bostock. Anyway in addition to awesome graphs there are a lot of helpful functions for calculating all kinds of data, and the front end developer on my project was using these. I thought no problem I can just translate these methods into swift and be able to reproduce the same data! Easy peasy lemon cheesy right. Wrong… Mike Bostock is a genius and I found out how incompetent I am at math!

A real world application

So in this application there is a graph that displays your co workers based on how much you interact with them based on email and slack conversations. The data base returns a score with each contact from 1 to 10 and according to that score the user is place in one of three rings the circle closet to you being those that you work with the most and the outer circle being the one that you work with the least. D3 has some really handy Quantitative Scales that the front end developer used to get which circle the co worker should be placed in. So after hours of trying to figure out how the hell Mike wrote these awesome scales I decided to embrace my hate, turn to the dark side and just use javascript in my project.
ain't nobody

JavaScriptCore Framework

Available since iOS 7 JavaScriptCore is the built-in JavaScript engine for WebKit. It currently implements ECMAScript as in ECMA-262 specification. It allows for an easy and fast way to interact with JavaScript.
To get started you first need to load in the d3 library. Also in this first step we are going to add error handling so we are able to see what we are doing wrong with our javascript which can be very handy!

// get a reference to the d3 library in our project
let fileLocation = NSBundle.mainBundle().pathForResource("d3.min", ofType: "js")!
// convert it to a string
let jsSource: String = try! String(contentsOfFile: fileLocation)
// create a javascript context environment
let context = JSContext()
// add exception handler so we can see any error that occur in our javascript
context.exceptionHandler = { context, exception in
    print("JS Error: \(exception)")
}
// load in the javascript into our context
context.evaluateScript(jsSource)

Once d3 is loaded we can access it exactly like you would in a HTML file

// create our javascript function for the linear scale we also mix in swift with the maxAmount variale
context.evaluateScript("circles = d3.scale.quantize().domain([0,\(maxAmount)]).range([3, 2, 1])")

// loop through all the contacts to determine which circle they should be placed in
for contact in contacts {
    // get the circle number based on score it will return 1, 2 or 3
    let circle: JSValue = context.evaluateScript("radius_circles(\(contact.score))");
    //  cast the JSValue to Int
    contact.circle = Int(circle.toInt32())
}
// sort the contacts array based on the new circle value
self.contacts.sortInPlace({ $0.circle < $1.circle })

Success

Thats all there is to it! Now we have data that matches the web app so we can successful show the same rankings and data in the iOS app! I don't think I would use this very often but I think it is very handy to have as an extra tool in your tool box. Let me know in the comments below what you guys think to this approach and if you would ever use javascript in your project

Lets be friends

I love meeting new iOS friends online so follow me on twitter
. Please stop by and say hello!

Active navigation link with javascript / jQuery

Navigation is something that almost every web app has to deal with, and most of the time navigation will have an active state. There are a couple of ways to do this, and I am going to demonstrate using  jQuery as well as plain javascript just in case you don’t have jQuery in your project.

Bootstrap Navigation

So lets take a look at a normal bootstrap navigation


Setting active navigation

What we want to do is get the current url, and then loop through each menu item and check to see if the href is equal to the url. We also want to check if the if the menu item is part of a dropdown menu, if it we want to find the parent menu item and set it to active as well. Here is what the javascript looks like.

$().ready(function(){
  setActiveLink();
});


function setActiveLink(){

  // get the current url
  var url = window.location.pathname;
  // decode the url
  url = decodeURIComponent(url);

  // this is incase your project isn't in the root directory of your url
  // for example localhost/ActiveNavLink/index.html
  projectPrefix = "/ActiveNavLink/";

  // loop through each nav li
  $('.nav li').each(function(){

    // get the menu a tag and get its href attribute
    var href = $(this).find('a').attr('href');

    // href can be undinded if there is a divider
    if(href === undefined)
      return;

    // add the project prefix
    href =  projectPrefix + href;
    
    // check if the menu item is equal to the current url    
    if(url.substring(0, href.length) == href){
      // set active class to li
      $(this).addClass('active');

      // check if there is a parent dropdown
      var parentDropDown = $(this).closest('.dropdown');
      // if there is a parent dropdown set it to active as well
      if(parentDropDown !== undefined){
        parentDropDown.addClass('active');
      }
    }
      
  });

}

Screen Shot 2014-02-11 at 2.49.55 PM
If you want to take a look at a working demo take a look at https://github.com/barrettbreshears/ActiveNavLink. You will probably need to change minor details to make this work in your project, but its pretty straight forward. Hope this is helpful!

Create angularJS filter!

Lately I have been playing with AngularJS and I am in love with it. Angular has these really cool components called filters which are described as:

[custom_blockquote style=” grey”] A filter formats the value of an expression for display to the user. They can be used in view templates, controllers or services and it is easy to define your own filter. [/custom_blockquote]

hodorLets Get Started

So game of thrones season 4 will be here in a couple of months so I thought we should build a Hodor filter to honor the great and noble Hodor. 

Our filter will take a string and translate it into Hodor.

Since this is just a demo, we are going to set up a super simple app that has a controller which looks like this:


	
		My Custom Filter
		
		
	
	

		

{{pageHeading | hodor}}

As you can see this is a pretty simple angular app with a single controller that sets our page heading data. We display our pageHeading property and add our hodor filter. We also attach our pageHeading model to the textarea so we can see the filter in action!

Now lets move on to the filter. We are going to want to use this in all our projects so we need create a new module just for our filter, and call the filter service.

angular.module('hodor-filter', []).filter('hodor', function(){

	return function(input){
		var hodor = [
			'H',
			'o',
			'd',
			'o',
			'r',
			' '
		];

		var stringPos = 0;
		var hodorString = "";
		for (var i = 0; i <input.length; i++) {
			var chr = hodor[i%6];
			hodorString = hodorString + chr;
		}

		return hodorString;

	};

});
  • Lets take a look at whats going on here:
  • – Declare our hodor-filter module and hodor filter
  • – Return a function that gets passed an input parameter
  • – Set up a Hodor array we will use this to replace characters in the input string
  • – Loop through all the characters and replace them with the correlating Hodor character
  • – Return the new hodorString

And thats it! Creating a filter is that easy! If you would like to download the project it is available on GitHub Hodor-Angular-Filter