3 minutes

# Implementing Unit conversion library

Last week I decided to implement unit conversion library for Objective-C called MKUnits. I was fully aware that there were many open-sourced implementation already available, but unfortunately none of them fitted my needs.

I required a library that:

- is easily extendable
- has built-in rounding functionality
- is precise up to at least 10 decimal places
- allows manipulation between units of the same group, i.e. kg and pounds, without the need of conversion

### Quantity Pattern

Before I started coding, I had a quick read through the chapter of Analysis Patterns by Martin Fowler, which describes the **Quantity Pattern**. This pattern is extremely useful when you want to represent dimensioned quantities.

The idea behind this pattern is straightforward; it is a class that represents both the `amount`

and the `unit`

, and allows arithmetic behaviour, e.g. addition and subtraction, and conversion to other quantities.

The initial interface for the `MKQuantity`

was:

```
@class MKUnit;
@interface MKQuantity : NSObject
@property (nonatomic, copy) NSDecimalNumber *amount;
@property (nonatomic, strong) MKUnit *unit;
- (id)initWithAmount:(NSNumber *)amount withUnit:(MKUnit *)unit;
- (instancetype)add:(MKQuantity *)other;
- (instancetype)subtract:(MKQuantity *)other;
- (instancetype)convertTo:(MKUnit *)unit;
- (NSComparisonResult)compare:(MKQuantity *)other;
@end
```

Also, it’s worth to mention that `MKQuantity`

is a **value object**.

A

value objectis a simple object whose equality is not based on identity.

In other words, two value objects are equal if all their attributes are equal. In this case, it is `amount`

and `unit`

.

```
- (BOOL)isEqual:(id)object {
if (![object isKindOfClass:[self class]]) return NO;
if (![[object unit] isEqual:self.unit]) return NO;
return [self.amount isEqual:[object amount]];
}
- (NSUInteger)hash {
return [[NSString stringWithFormat:@"%@%@%@",
[self class], self.unit.symbol, self.amount] hash];
}
```

`MKQuantity`

is associated with `MKUnit`

:

```
@interface MKUnit : NSObject<NSCopying>
@property (nonatomic, strong) NSString *name;
@property (nonatomic, strong) NSString *symbol;
@property (nonatomic, strong) NSDecimalNumber *ratio;
- (id)initWithName:(NSString *)name
withSymbol:(NSString *)symbol
withRatio:(NSDecimalNumber *)ratio;
@end
```

The `ratio`

property is used to convert unit to base unit, eg. for Length a base unit is `meter`

, therefore, the ratio for `kilometer`

unit is 1000 as `amount_in_meters = 1000 * amount_in_kilometers`

.

### Unit conversion

Since this is a unit conversion library, the first type of a behaviour to be added is conversion.

```
- (instancetype)convertTo:(MKUnit *)unit {
[self _assert_that_is_convertible_with_unit:unit];
id converted = [self.unit convertAmount:self.amount to:unit];
return [[self class] createWithAmount:converted withUnit:unit];
}
```

The conversion process of unit A to unit B consists of:

- asserting that unit A is convertible with unit B

```
- (void)_assert_that_is_convertible_with_unit:(MKUnit *)unit {
NSAssert(
[self.unit isConvertibleWith:unit],
UNITS_NOT_CONVERTIBLE
);
}
```

The aim is to perform conversion and arithmetic operations not only on the same units, but also on the other units of the same quantity, eg. adding 500 pounds to 2.5 kilograms should be allowed, but adding 500 grams to 1 meter should not.

```
- (BOOL)isConvertibleWith:(MKUnit *)unit {
return [unit isMemberOfClass:[self class]];
}
```

- converting
`amount`

of unit A to`amount`

of unit B

Firstly the `amount`

of unit A must be converted to `amount`

of base unit, and then the result is converted to `amount`

of unit B.

```
- (NSNumber *)convertAmount:(NSNumber *)amount to:(MKUnit *)unit {
return [self convertAmount:amount from:self to:unit];
}
- (NSNumber *)convertAmount:(NSNumber *)amount
from:(MKUnit *)from to:(MKUnit *)to {
NSAssert([from isConvertibleWith:to], UNITS_NOT_CONVERTIBLE);
id baseAmount = [from convertToBaseUnit:amount];
id converted = [to convertFromBaseUnit:baseAmount];
return converted;
}
```

- creating new
`Quantity`

object of unit B with converted`amount`

This is due general consensus that a `Value Object`

should be entirely immutable.

### Arithmetic

Addition and subtraction operations of two quantities are allowed as long as their units are convertible.

```
- (instancetype)add:(MKQuantity *)other {
MKQuantity *converted = [other convertTo:self.unit];
id amount = [self.amount decimalNumberByAdding:converted.amount];
return [[self class] createWithAmount:amount withUnit:self.unit];
}
- (instancetype)subtract:(MKQuantity *)other {
MKQuantity *converted = [other convertTo:self.unit];
id amount = [self.amount decimalNumberBySubtracting:converted.amount];
return [[self class] createWithAmount:amount withUnit:self.unit];
}
```

**NB** As this is a unit conversion library, we should only allow multiplication and division by scalar numbers.

If you would like to know more, please visit MKUnits GitHub repository.

I am @michalkonturek on Twitter.

Looking forward to your feedback.