Subjective-Script makes Objective-C more scripty!
Objective-C C Ruby
Switch branches/tags
Nothing to show
Latest commit d6c93cf Dec 7, 2013 @kmalakoff kmalakoff Release 0.1.2
Failed to load latest commit information.
Config Preparation for CocoaPods update Aug 8, 2012
.gitignore Updated to default CocoaPods implementation Aug 3, 2012
Podfile.lock Upgrading to new Cocoapods Dec 7, 2013
Rakefile Flattened hierarchy due to pod lint failure Dec 7, 2013


Version Platform

Subjective-Script makes Objective-C more scripty!

Note: Subjective-Script requires ARC to be enabled on your Objective-C project.

The Idea

My language of preference is CoffeeScript and whenever I develop in Objective-C, I find myself often having to look up [NSSomething reallyLongFunctionName:YES withAVerboseParameterName:YES and:[NSSomethingElse whichAddsMoreBrackets]] and it isn't very enjoyable or speedy!

While I was porting a test for _.m from the original Underscore.js that was easy to read:

var people = [{name : 'curly', age : 50}, {name : 'moe', age : 30}];
people = _.sortBy(people, function(person){ return person.age; });
equal(_.pluck(people, 'name').join(', '), 'moe, curly', 'stooges sorted by age');

It looked like this in Objective-C:

NSArray *people = [NSArray arrayWithObjects:
  [NSDictionary dictionaryWithObjectsAndKeys: @"moe", @"name", [NSNumber numberWithInt:30], @"age", nil],
  [NSDictionary dictionaryWithObjectsAndKeys: @"curly", @"name", [NSNumber numberWithInt:50], @"age", nil],

STAssertEqualObjects([_.pluck(people, @"name") componentsJoinedByString:@", "],
                     @"moe, curly",
                     @"stooges sorted by age");

The keys were in the wrong order, there was too much typing involved, and it became unreadable. So I wrote Subjective-Script and ported QUnit.m to end up with this:

A* people = AO(OKV({@"name", @"curly"}, {@"age", N.I(50)}), OKV({@"name", @"moe"}, {@"age", N.I(30)}));
people = _.sortBy(people, ^(O* person){ return person.get(@"age"); });
equal(_.pluck(people, @"name").join(@", "), @"moe, curly", @"stooges sorted by age");

Much better! And best of all, I can reuse my knowledge of JavaScript for function names so I can stop looking things up and get stuff done!

The Syntax


While JavaScript allows flexibility in variable types, Objective-C requires explicit variable types. To try to keep it brief, I tried to condense types down to the shortest name possible:

typedef BOOL            B;
typedef NSInteger       I;
typedef NSUInteger      UI;
typedef float           F;
typedef double          D;
typedef id              KV[2]; // key-value pair

#define NSO             NSObject
#define A               NSMutableArray
#define NSA             NSArray
#define O               NSMutableDictionary
#define NSD             NSDictionary
#define S               NSMutableString
#define NSS             NSString
#define Date            NSDate
#define N               NSNumber
#define E               NSException

and I tried to shorten NSNumbers, too, using the above abbreviations (B, I, UI, F, D):

N* value = N.B(true);
equal(value.B, true, @"YES it is!")

Using Aggregate Types

I tried many different ways, but this seemed to be easy-to-read and brief (with key-value pair matching checked by the compiler):

A* arrayOfInts = AI(1, 2, 3);
A* arrayOfObjects = AO(N.I(1), N.F(2.0), @"hello", nil,;
I intValue = arrayOfInts.getAt(0).I;    // or: intValue = arrayOfInts.get(@"1").I;
arrayOfInts.setAt(0, N.I(intValue+1));  // or: arrayOfInts.set(@"1", N.I(intValue+1));

O* obj = OKV({@"int", N.I(1)}, {@"float", N.F(2.0)}, {@"string", @"hello"}, {@"nil", nil}, {@"dictionary",});
intValue = obj.get(@"int").I;
obj.set(@"int", N.I(intValue+1));

would be equivalent to the following Javascript:

var arrayOfInts = [1, 2, 3];
var arrayOfObjects = [1, 2.0, 'hello', null, {}];
var intValue = arrayOfInts[0];
arrayOfInts[0] = intValue+1;

var obj = {int: 1, float: 2.0, string: 'hello', nil: null, dictionary:{}};
intValue =;       // or: intValue = obj['int']; = intValue+1;     // or: obj['int'] = intValue+1;

and if you are interested, Objective-C may make the syntax even lighter using Objective-C literals in the future:

NSA* arrayOfInts = @[@1, @2, @3];
NSA* arrayOfObjects = @[@1, @2.0, @"hello", NSNull.null, @{}];
NSD* obj = @{@int: @1, @float: @2.0, @string: @"hello", @nil: NSNull.null, @dictionary:@{}};

The JavaScript-Inspired Library

To be able to reuse my JavaScript knowledge and not have to look through StackOverflow for really basic things that are hard to remember in Objective-C, I've ported some common JavaScript functions.

All Objects

// true

// true

// @"true"

OKV({@"key1", @"value1"}).toString();
// @"[object Object]"

AO(AI(1,2,3),N.F(4.5),OKV({@"five", @"5"})).toString();
// @"[[1,2,3],4.5,[object Object]]


// 3

// true

// N.I(3)

// [1,2,3,1,2,4]

AO(@"Banana", @"Orange", @"Lemon", @"Apple", @"Mango").slice(-3,-1);
// [Lemon,Apple]

// [3,2,1]

// [1,2,3]

// N.I(3)

AI(1,2,3).push(N.I(4)).push(@"out the door");
// [1,2,3,4,out the door]

// [1,2,2,3]

AO(@"Banana", @"Orange", @"Apple", @"Lemon").splice(2,1, @"Kiwi", @"Mango", nil);
// [Banana,Orange,Kiwi,Mango,Lemon]

// [1,2,3,4,5]

// [2,3,4,5]


OKV({@"int", N.I(1)}, {@"float", N.F(2.0)}).hasOwnProperty(@"int");
// true

OKV({@"int", N.I(1)}, {@"float", N.F(2.0)}).delete_(@"int").hasOwnProperty(@"int");
// false

@"int".in(OKV({@"int", N.I(1)}, {@"float", N.F(2.0)}));
// true
delete obj.key or delete obj['key'] -> obj.delete_(@"key") -> what is the delete resevered word for..can it be used?


@"hello world!".split(@" ")
// [@"hello", @"world!"]

@"hello world!".split(@"");
// [@"h", @"e", @"l", @"l", @"o", @"w", @"o", @"r", @"l", @"d", @"!"]

// @"hello world!"

S* message = S.newS(@"hello"); message.append(@" world!");
// message == @"hello world!"


SS.stringify(Date.newYMD_JS(2012, 7, 31));
// @"2012-08-30T15:00:00.000Z"


O* obj = OKV({@"mirror", ^(NSS* string){return string.add(@" mirror"); }});
@"mirror".call(obj, @"hello", nil);
// @"hello mirror"

@"mirror".apply(obj, AO(@"hello"));
// @"hello mirror"


There are a few utilities on the Subjective-Script base object rather than making them global as in JavaScript.

SS.stringify(AO(N.I(1), N.F(2.0), N.F(3.1), @"hello", nil,;
// @"[1,2,3.1,\"hello\",null,{}]"

// N.I(123)

// @"boolean"

// @"string"

// @"object"

__block BOOL called1 = false;
SSTimeout* timeout1 = SS.setTimeout(^{ called1 = true; }, NSEC_PER_SEC*1);
// not called

__block BOOL called2 = false;
SS.setTimeout(^{ called2 = true; }, NSEC_PER_SEC*2);
// called

Just One More Thing

You can also easily add named properties on your own objects:

@interface MyObject : O
  @property (strong) NSS* name;
@implementation MyObject : O
  @dynamic name;

MyObject* obj =; = @"Steve";
// they call me Steve

So you can write the initial example like:

@interface Stooge : O
  @property (strong) NSS* name;
  @property (strong) N* age;
@implementation Stooge
  @dynamic name, age;

A* people = AO(OTKV(Stooge, {@"name", @"curly"}, {@"age", N.I(50)}), OTKV(Stooge, {@"name", @"moe"}, {@"age", N.I(30)}));
people = _.sortBy(people, ^(Stooge* person){ return person.age; }); // no get(@"age") required
equal(_.pluck(people, @"name").join(@", "), @"moe, curly", @"stooges sorted by age");


To run the example project; clone the repo, and run pod install from the Project directory first.



SubjectiveScript.m is available through CocoaPods, to install it simply add the following line to your Podfile:

pod "SubjectiveScript.m"


Kevin Malakoff,


SubjectiveScript.m is available under the MIT license. See the LICENSE file for more info.

Please Contribute to SubjectiveScript.m

Currently, this is still early days and there is a lot to add and test. Please help out!

If there is anything else you would like added, just implement it in a good place, write the appropriate tests, and submit a pull request on GitHub.

Also, feel free to submit your favorite features from other languages as long as they are easy to remember and help speed up development!