Smarter Numbers

When it comes to iOS app Development, I find one of the most commonly used framework class is NSNumber.

Which is ironic, because it’s a pretty dumb class. Really… there’s not much you can do with it except store numbers and check for equality. Albeit it being all over the place, it’s a pretty useless wrapper object. Whenever we need to edit the wrapped value we need to go through overly complicated statements like this:


number1 = NSNumber(float: number1.floatValue + number2.floatValue)

Fortunately for us, Swift is AWESOME.

We can leverage the flexibility of the language to extend classes in useful ways using conventions that will feel natural for any developer. Using advanced language features like inout, prefix, postfix and infix functions mixed with category extensions, we can refactor the framework to transform statements like the one above to something more readable, like this:


number1 += number2

Feels a lot more natural, doesn’t it? That’s because it’s cleaner, less prone to errors and is reducing our written code by over 50%. Excited to know more? Great!

Let’s jump to the tutorial →

Modifying the swift language

Swift comes with a pretty awesome secret weapon called Operator Overloading. This allow us to change the behaviour for language operations like +, -, =, *=, etc… Which is incredibly powerful, and incredibly scary at the same time. It allows you to change the functionality of the symbols if they have already been implemented on objects, or to add new symbol functions if there is non already defined (you can even create your own symbols, though I would strongly recommend against that unless there is a VERY clear logic to them).

Like any other secret weapon, this should be used with caution whilst using your common sense. It’s up to you to understand when it makes sense to add a + operator to a class, you want to avoid seeing  *= symbols into your “Car” or “TableViewController” classes, but it would make perfect sense to add them to Vectors, Matrices and, of course… NSNumbers.

So what symbols do we want to add? Well… As many as make sense.

So let’s start with some simple algebra functions. It’s actually surprisingly easy. If we wanted to add an addition to NSNumber, we would add a global function (on the top of our swift class file, above the declaration of our extension), that looks like this:


func + (left:NSNumber, right:NSNumber) -> NSNumber {
  return NSNumber(double: left.doubleValue + right.doubleValue)
}

And that’s it! Now you can declare statements like this in your code:


let number3 = number1 + number2
let number4 = number1 + 10.0 //just because swift is awesome

But wait, what if my NSNumber object is not made by a number type object (say char or boolean)?!

Unfortunately, NSNumber’s “objCType” property does not map to an enum in swift, but we can could potentially do a workaround to check for number compatibility, by writing the following extension to our earlier method:


//MARK: - algebra functions
func + (left:NSNumber, right:NSNumber) -> NSNumber {
  assert(left.isNumberType && right.isNumberType, "you can only perform arithmetics on numbers that are of numeric type")
  return NSNumber(double:(left.doubleValue + right.doubleValue))
}

//MARK: - private static vars
private let booleanNumber : NSNumber = true

//MARK: - class extensions
extension NSNumber {
  var isNumberType : Bool {
    return self.objCType != booleanNumber.objCType
  }
}

By creating a private NSNumber variable equivalent to a boolean number, we can test our numbers against booleans and char numbers by using a custom computed property: “isNumberType”. This is safe because both char and boolean numbers share the same objCType value (no idea why)*, while unassigned numbers will return nil for this value.

Having said this, having this equality check is actually unnecessary since our functions will work around these type limitations anyway, as they will be returning new NSNumber objects of numeric types regardless.

Using this as a base, we can now add custom operator functions for the following symbols: +, -, *, /, %, <, >, <= and >=. We don’t need to add operator functions for == and != because there are already provided by the framework.

In-Out Functions

Sometimes, we want our operator to be directly editing one of our variables. This is the case for symbols like +=, -=, *=, /= and %=. We can leverage the use of inout functions for this purpose, which are declared like this:


func *= (inout left: NSNumber, right: NSNumber) {
  left = left + right
}

Since our left variable is declared as inout, its value can be reassigned to a new value from within the function. This now allows us to make calls like the following (note that calling an inout function will throw a compiler error if the variable is defined as a let):


number1 *= 0.05

Prefix Functions

We can also use prefix functions to add operators in front of variables. This is useful for when you want to get the inverse of an NSNumber easily:


prefix func - (number:NSNumber) -> NSNumber {
  return NSNumber(double: -number.doubleValue)
}

This now allows us to make calls like the following:


let inverse = -number

Postfix Functions

Similar to prefix functions, postfix functions allow us to add symbols after a variable. This is useful for when you want to increment or decrement the value of an NSNumber easily:


postfix func ++ (inout number:NSNumber) -> NSNumber {
  number = NSNumber(double: number.doubleValue + 1.0)
  return number
}

Notice that we declare the function as an inout that returns a value. This is to keep the consistency with the behaviour expected from the ++ operator on basic data types. Now we can declare the following in our code:


number++

Taking it one step further

Using these powerful new techniques we can get creative and create utilities for performing batch operations. Suppose we have an array of NSNumbers and we want to get the modulus of 10 of all numbers in the array. We can further extend our framework by adding methods like the following:


//MARK: - some funky functionality
func % (left:[NSNumber], right:NSNumber) -> [NSNumber] {
  return NSNumber.batchModifyNumbers(left) { (number) -> NSNumber in number % right }
}

//MARK: - class extensions
extension NSNumber {
  private class func batchModifyNumbers(var array:[NSNumber], block:(number:NSNumber) -> NSNumber) -> [NSNumber]  {
    for var i = 0; i < array.count; i++ {
      array[i] = block(number: array[i])
    }
  return array
  }
}

Batch operations on NSNumbers are now available to us, like so:


let array : [NSNumber] = [1, 2, 3]
let modifiedArray = array % 2
println(modifiedArray) //prints [1, 0, 1]

We’ve uploaded a sample project with the end product of this tutorial, a full category on NSNumber available to everyone on GitHub. As always, feel free to use this code in your own projects, it’s free and always will be. 🙂

Happy coding!


*Char and bool are the smallest possible type you can declare in C. Both are 1 byte therefore they can be functionally interchangeable although not logically.
– Thank you, Tim Storey, for the explanation!

Author: Danny Bravo

Director @ EPIC

1 thought on “Smarter Numbers”

  1. Hi bigbeno37, first off, thank you for your feedback!
    It’s not necessary to perform arithmetics using the NSNumber class in Swift or Obj-C, you can use basic datatypes for that (like most programming languages). NSNumbers is a common object wrapper for these basic data types, and the goal of this article is to explore how to leverage the capabilities of the Swift language in order to perform arithmetics directly on these wrapper objects.

    Like

Comments are closed.