2013-05-31

Patching NSManagedObjects

Previous posts discussed how to create a diff of an NSManagedObject pair. Now we’ll look at how to apply that diff as a patch.

Creating a diff gives us a data structure that looks like this:

(
    {
        attributes: {
            new: {
                city: "Sometown"
            },
            old: {
                city: "Townsville"
            }
        },
        entityName = "Address",
        guid: "123"
    }
)

The diff tells us that the Address entity with the GUID “123” changed its city attribute from “Townsville” to “Sometown”.

The patcher has to be able to do a number of things with a patch like this:

  • It must update the CoreData entity’s attributes to match the new values for each change in the patch.
  • It must be able to traverse both the nested patch structure and the CoreData graph and apply patches to sub-entities.
  • It must be able to apply patch arrays to sets of sub-entities (one-to-many relationships).
  • It must be able to delete entities from the CoreData graph if the patch indicates a entity was deleted.
  • It must be able to create new entities within the CoreData graph if the patch indicates a new entity was created.

The last requirement is tricky. Creating an entity is rarely a case of just calling insertNewObjectForEntityForName: inManagedObjectContext:. Typically, each entity will have associated sub-entities that should be created at the same time, so the patcher will inevitably use the delegate pattern to call back to an external object that can create new entity instances.

It would probably be handy if the patcher could allow manual patching for certain entity types, so that the developer could override its default behaviour for complex or unusual object types. For example, we might receive a patch for a Distance entity that includes two attributes: value and units. If we respect the semantics of the type we can’t update either attribute in isolation. We would expect the patcher to only overwrite one attribute if only one attribute has changed, but we should really overwrite both values to maintain the integrity of the data. (In contrast to the earlier posts about diffing, the current version of the differ in SZManagedObject emits the entire set of old attributes if it detects a change, precisely to cater for this situation.) Allowing for the ability to manually patch objects and override the patcher’s default behaviour lets us do this.

Something else I’m going to bake in is the ability to manually convert types for date and binary attributes. I want to be able to send diffs over the wire as JSON, but JSON doesn’t like date and binary data. They typically get sent as ISO strings or JavaScript date object constructors (for dates) and base64-encoded strings (for binary). A patch will therefore not necessarily have the correct data types for these two attribute types. Whenever they are encountered in the entity I’ll check the type of data in the patch, and if they disagree I’ll call out to a delegate to perform a conversion process.

Patching an entity requires two steps: attributes then relationships. Patching attributes is easy:

  • If there are old values but no new values, the entity has been deleted.
  • Loop through keys in the new dictionary and apply the values to the entity.

Patching relationships is really just a matter of creating new entities if necessary and using recursion to patch the entire graph:

  • If the relationship is one-to-many, patch the relationship as a set.
  • If not:
    • If no sub-entity with the required identifier can be found, call out to the delegate to create it.
    • Recursively patch the entity’s attributes and relationships.

Eventually we’ll hit a point where there are no more sub-patches, which means we have finished patching.

Patching one-to-many relationships is a little more complex, but it isn’t too hard:

  • Loop through sub-patches.
  • If the matching sub-entity does not exist and there is no old data in the patch, call out to the delegate to create it.
  • If there are no new values in the patch, delete the sub-entity.
  • If there are new values, old values and an object to update, recursively update it.

We now have a fully patched object graph.

Again, check out the SZManagedObject project on BitBucket for the sourcecode.

2013-05-28

Diffing NSManagedObjects Part 2

When I last discussed comparing two NSManagedObjects and producing a diff, I said that I’d come back to a few issues later. It turned out to be much later than expected; this post has been floating around on my hard disk for 6 months.

This time around I’ll deal with two of those issues:

  • What do you do if you want to include a related object in the diff if it has its deletion rule set to nullify?
  • What do you do if you want to exclude an attribute or relationship from the diff?

The answers are “whitelist” and “blacklist”.

In the previous post, we used deletion rules to signify object ownership. This works well, but means that we can’t automatically include crucial information from a relationship if the deletion rule indicates a lack of ownership.

For example, imagine we’re a dogfood company and have a Dog class and a Person class:

  • Person (User info: @{ @“id”: @“guid” })

    • Attributes:
      • guid
      • name
      • age
    • Relationships:
      • dog (Dog) (Delete: cascade) (To one)
  • Dog (User info: @{ @“id”: @“guid” })

    • Attributes:
      • guid
      • name
    • Relationships:
      • owner (Person) (Delete: nullify) (To one)

If we delete a person we’ll also delete his dog. We don’t care about the dog if we no longer track the person, presumably because we can’t sell anything directly to a dog. On the other hand, if we delete the dog we don’t necessarily want to delete the person. The person could get a new dog.

If a dog changes owner, it’d be handy if we could represent that information in a diff. Unfortunately, the deletion rule will prevent the automated diff methods from examining this relationship. We need to whitelist this relationship to ensure that it is included in the diff.

The whitelist looks like this:

NSDictionary *whitelistedRelationships = @{
@"Dog": @[ @"owner" ]
};

Each key in the dictionary represents the name of a class. Each value represents an array of properties of that class. In this case, the whitelist says, “If the diff creator encounters an instance of the Dog class, include the owner relationship in the diff regardless of the deletion rule”.

There are two issues here. Firstly, we’re hard-coding information that should really be metadata in the data model. Unfortunately, there’s not much we can do about that if we want to be able to produce a variety of different diffs that can be started in multiple places within the object graph.

The second issue is that we’ve re-introduced a cycle into the graph. The diff creator will repeatedly walk from the Dog to its owner and back, eventually causing a stack overflow. To get around that, we need to introduce the concept of a blacklist. Anything in a blacklist will not be examined. Here’s what the blacklist would look like in this situation:

NSDictionary *blacklistedProperties = @{
@"Owner": @[ @"dog" ]
};

The dictionary format is that same as the blacklist: each key is the name of a class, whilst each value is an array of attribute and relationship names that should be ignored by the diff creator. This particular blacklist says, “Do not traverse the dog relationship of any Owner instances”. This would be counter-productive if we were starting the diff from the owner, due to the direction of the relationship established by the deletion rule, but if we start from the dog it is very useful.

Now that we’ve decided upon workable solutions, we just need to implement them. We’ll start off with a method that can retrieve all of the property names for an NSManagedObject instance that exist in a blacklist or whitelist:

+ (NSArray *)propertyNamesForManagedObject:(NSManagedObject *)managedObject inList:(NSDictionary *)list {

    NSMutableArray *output = [NSMutableArray array];

    for (NSString *className in [list allKeys]) {
        Class class = NSClassFromString(className);

        if ([managedObject isKindOfClass:class]) {
            [output addObjectsFromArray:list[className]];
        }
    }

    return output;
}

We’ve introduced a new metaprogramming technique here: getting a pointer to a metaclass using the NSClassFromString() function. As the name suggests, we can get a class definition from a string representing its name. We can use that to determine the properties of the class.

This function will eventually give us a list of all property names of object managedObject that match a name in the list dictionary. For example, if we passed it an Owner object and the blacklist defined above, we’d get the following output:

@[ @"dog" ]

We know that the dog property of our Owner object is blacklisted and shouldn’t be traversed.

Next we’ll add a little helper method to determine if an array contains a string:

+ (BOOL)isString:(NSString *)string inList:(NSArray *)list {

    for (NSString *item in list) {
        if ([string isEqualToString:item]) {
            return YES;
        }
    }

    return NO;
}

Perhaps a set would have been a better data structure here; I might revisit that.

Now we can modify the diff method so that it checks the blacklist before attempting to examine an attribute or relationship:

...

NSArray *attributeNames = newObject == nil ? [[[oldObject entity] attributesByName] allKeys] : [[[newObject entity] attributesByName] allKeys];

NSArray *blacklist = [self propertyNamesForManagedObject:newObject ? newObject : oldObject inList:blacklistedProperties];

for (NSString *name in attributeNames) {

    if ([self isString:name inList:blacklist]) continue;

    ...

}

We do something similar to ensure that we traverse whitelisted relationships:

...

NSArray *whitelist = [self propertyNamesForManagedObject:newObject ? newObject : oldObject inList:whitelistedRelationships];

for (NSString *name in relationships) {

    NSRelationshipDescription *relationship = [relationships objectForKey:name];

    if (![self isString:name inList:whitelist]) {
        if (relationship.deleteRule != NSCascadeDeleteRule) continue;
    }

    ...
}

It’s a simple check - if the relationship is whitelisted, ignore the deletion rule.

At this point, the code is far too big to post here. I’ll create a BitBucket repository with the diff/patching system as a library with a demo project at some point. The explanation of the patching code will come later.

2013-02-21

Encoding HTML Entities with libXML

iOS doesn’t have a standard, easy-to-use way of encoding UTF-8 strings into HTML entities (ie. from “>” into “>“). One library that can be used to achieve this is libXML, but its API is particularly unpleasant:

int htmlEncodeEntities (unsigned char * out, 
                        int * outlen, 
                        const unsigned char * in, 
                        int * inlen, 
                        int quoteChar)

out: a pointer to an array of bytes to store the result
outlen: the length of @out
in: a pointer to an array of UTF-8 chars
inlen: the length of @in
quoteChar: the quote character to escape (' or ") or zero.

Returns: 0 if success, -2 if the transcoding fails, or -1 otherwise
The value of @inlen after return is the number of octets consumed as
the return value is positive, else unpredictable. The value of
@outlen after return is the number of octets consumed.

What’s wrong with that function signature, you ask? Other than the quality of the documentation, that is? Consider this situation. You need to encode this string:

<p>

That will encode into this string:

&lt;p&gt;

Now consider this braindead catch-22 situation: You must allocate the memory for the “out” parameter before you call the function, but until you call the function you won’t know how much memory you need to allocate.

The raw string in the example above is 4 bytes long; the encoded version is 10 bytes (don’t forget that this is C and all strings are NULL-terminated). If you follow the advice from Stack Overflow and double the initial size you’ll end up with 7 bytes (1 terminator + (3 characters * 2)), which is still too short. Another alternative is to figure out the maximum amount of memory that an encoded string could possibly consume (9 bytes per character) and use that, but then you’ll potentially be wasting massive amounts of memory.

If I were writing the function I’d probably return a pointer to a block of memory allocated inside the function itself. It could re-allocate the memory as needed and users of the function wouldn’t need to guesstimate the buffer size. That would raise my favourite C question, though: Who owns this memory?

I couldn’t find any examples of how to use the httpEncodeEntities() method, so I came up with my own. This solution uses a loop and encodes the string in chunks. It uses an encoded buffer twice the size of the initial string, but will resize it if it finds that the buffer isn’t large enough for a single encoded character. It’s implemented as a category method on NSString.


#import <libxml/htmlparser.h>

@implementation NSString (SZHTMLEncoding)

- (NSString *)stringByEncodingHTMLEntities {

    if (self.length == 0) return;
    
    NSData *data = [self dataUsingEncoding:NSUTF8StringEncoding];
    
    int remainingBytes = data.length;
    int bufferSize = (data.length * 2) + 1;
    const unsigned char *bytes = (const unsigned char *)[data bytes];
    
    // We have to add an extra byte on the end of the encoded string to enable
    // us to add a terminator character.
    unsigned char *buffer = malloc(bufferSize);
    buffer[bufferSize - 1] = '\0';
    
    NSMutableString *output = [NSMutableString stringWithCapacity:remainingBytes];
    
    do {
        
        int outLen = bufferSize - 1;
        int inLen = remainingBytes;
    
        int result = htmlEncodeEntities(buffer, &outLen, bytes, &inLen, '"');
        
        // libXML doesn't append a terminator to the string - presumably because
        // NSString doesn't include one - so we'll have to take care of that in
        // order to convert back to an NSString.  We only add this if we haven't
        // completely filled the buffer.  If we've filled it, we've already
        // added the terminator character.
        if ((NSUInteger)outLen < bufferSize - 1) {
            buffer[outLen] = '\0';
        }
        
        if (result == 0) {
            
            NSString *string = [NSString stringWithCString:(const char *)buffer encoding:NSUTF8StringEncoding];
            
            [output appendString:string];
            
            remainingBytes -= inLen;
            
            if (remainingBytes > 0 && inLen == 0) {
                
                // Oh no!  We've got characters left to encode but they aren't
                // encoding.  This happens if our buffer isn't big enough, so
                // we'll resize it.
                free(buffer);
                bufferSize = ((bufferSize - 1) * 2) + 1;
                buffer = malloc(bufferSize);
                buffer[bufferSize - 1] = '\0';
            }
        } else {
            
            // Something bad happened
            break;
        }

        bytes += inLen;
        
    } while(remainingBytes > 0);

    free(buffer);
        
    return output;
}

@end

2012-11-27

Diffing NSManagedObjects

I’m currently working on a system to calculate the differences between two CoreData objects. Why would you want to do that, you ask? In my case, I’m trying to transmit the changes made to the object to a web server using the least amount of data and I don’t want to bother trying to store the changes as some kind of activity log. As discussed previously, I’m storing two copies of my objects: one that represents the last-known server state, and one that is changed by the user.

Here’s an imaginary CoreData model that we’ll use for all examples:

  • Person

    • firstName (string)
    • lastName (string)
    • age (int)
    • address (Address)
  • Address

    • street (string)
    • city (string)
    • state (string)
    • postalCode (string)

The “Person” entity has a related “Address” object, which gives the address at which the person lives.

In my first attempt, I came up with a particularly brittle solution and created the diff manually. The method takes in two parameters (the new and old objects that represent its different states) and computes the diff like this:


NSMutableDictionary *diff = [NSMutableDictionary dictionary];

if (![newObj.firstName isEqualToString:oldObj.firstName]) {
    diff[@"firstName"] = @{ @"old": oldObj.firstName, @"new": newObj.firstName };
}

Each attribute is compared and, if inequalities are found, the new and old values are stored in the dictionary. We can then transmit this minimal amount of information and the server can figure out what changed and how.

This solution is tied to the structure of the data model. Any changes in the data model mean we have to change the diff method. A better way would be to exploit features of the CoreData API that allow us to use a metaprogramming approach that has no foreknowledge of the data model and figure out the whole thing out on the fly.

CoreData has a number of useful features that allow us to do this. Firstly, all NSManagedObject instances are composed of two types of data: attributes and relationships. Attributes are basic properties of an object, such as ints, strings, doubles, etc. Relationships connect different NSManagedObjects together. To demonstrate this on the example model:

  • Person

    • Attributes:
      • firstName (string)
      • lastName (string)
      • age (int)
    • Relationships:
      • address (Address)
  • Address

    • Attributes:
      • street (string)
      • city (string)
      • state (string)
      • postalCode (string)

Each NSManagedObject has an associated “entity” instance that contains metadata about its attributes and relationships. If we wanted to list all of the names of the attributes for a Person instance, we could do this:


NSArray *attributeNames = [[[person entity] attributesByName] allKeys];

for (NSString *name in attributeNames) {
    NSLog(@"%@", name);
}

The output would be as follows:

age
firstName
lastName

We can do the same with relationship names:


NSArray *relationshipNames = [[[person entity] relationshipsByName] allKeys];

for (NSString *name in relationshipNames) {
    NSLog(@"%@", name);
}

Here’s the output:

address

We’re now able to list all of the properties of an NSManagedObject. Armed with this, and adding in a little Objective-C metaprogramming, we can write a function that can identify differences between two managed objects. Assuming we have two valid NSManagedObject pointers, here’s a simple diff function:


- (NSDictionary *)diffNewObject:(NSManagedObject *)newObject withOldObject:(NSManagedObject *)oldObject {
    
    NSMutableDictionary *diff = [NSMutableDictionary dictionary];

    NSArray *attributeNames = [[[newObject entity] attributesByName] allKeys];

    for (NSString *name in attributeNames) {

        SEL selector = NSSelectorFromString(name);

        id newValue = [newObject performSelector:selector];
        id oldValue = [oldObject performSelector:selector];

        if (![newValue isEqual:oldValue]) {
            diff[name] = @{ @"new": newValue, @"old": oldValue };
        }
    }

    return diff;
}

One of the metaprogramming tricks we’re using is “NSSelectorFromString()”, which can be thought of as giving us a function pointer, though that isn’t really at all accurate. Since entity attributes all map to properties of the NSManagedObject instance, we can treat them as selectors and call them using the “performSelector:” method. Once we’ve extracted the new and old values we can compare them and, if they are different, add them to the diff dictionary.

Let’s see what happens if we run it:


Person *person1 = [self createPerson];
person1.firstName = @"Joe";
person1.lastName = @"McJoe";
person1.age = @23;

Person *person2 = [self createPerson];
person2.firstName = @"Bob";
person2.lastName = @"McJoe";
person2.age = @23;

NSDictionary *diff = [self diffNewObject:person1 withOldObject:person2];

NSLog(@"%@", diff);

The output we get is this:

{
    firstName = {
        new = Joe;
        old = Bob;
    };
};

Now the attributes of the entities in our data model can change without the diff method breaking.

The method is still a little brittle, however. What happens if we’re trying to diff two objects of different types that share some of the same properties? What happens if some of the properties are nil? (NSDictionaries cannot store nil values.) What if one of the objects is nil?

Here’s an updated version of the method that accounts for all of these possibilities:


- (NSDictionary *)diffNewObject:(NSManagedObject *)newObject withOldObject:(NSManagedObject *)oldObject {
    
    NSMutableDictionary *diff = [NSMutableDictionary dictionary];

    NSArray *attributeNames = newObject == nil ? [[[oldObject entity] attributesByName] allKeys] : [[[newObject entity] attributesByName] allKeys];

    for (NSString *name in attributeNames) {

        SEL selector = NSSelectorFromString(name);

        id newValue = nil;
        id oldValue = nil;

        if (newObject != nil && [newObject respondsToSelector:selector]) newValue = [newObject performSelector:selector];
        if (oldObject != nil && [oldObject respondsToSelector:selector]) oldValue = [oldObject performSelector:selector];

        newValue = newValue ? newValue : [NSNull null];
        oldValue = oldValue ? oldValue : [NSNull null];

        if (![newValue isEqual:oldValue]) {
            diff[name] = @{ @"new": newValue, @"old": oldValue };
        }
    }

    return diff;
}

There are a few changes:

  • We ensure that we get the attribute list from a non-nil object;
  • We ensure that the objects respond to the selectors before trying to call them;
  • We replace nil values with NSNull.

Unfortunately, finding the diff between two objects’ attributes is the easy part. Producing a diff of the relationships is rather harder. We made a mistake in our object model, so let’s correct that now and see one of the reasons why diffing relationships is hard:

  • Person

    • Attributes:
      • firstName (string)
      • lastName (string)
      • age (int)
    • Relationships:
      • address (Address)
  • Address

    • Attributes:
      • street (string)
      • city (string)
      • state (string)
      • postalCode (string)
    • Relationships:
      • person (Person)

Apple recommend including an inverse of all relationships. Our Person entity has a relationship to the Address entity, so our Address entity should include the inverse relationship back to the Person entity.

Hopefully the problem we’ve introduced is apparent. We’ve created a cycle in what was previously an acyclic graph. If we try and walk the graph from a Person to an Address, we’ll find that we need to walk back to the Person. And back to the Address. And back to the Person. And now we’ve recursed too far and blown the stack.

CoreData includes another feature that can help mitigate this problem. It isn’t a perfect solution, but it should suffice in most situations. Each relationship includes a deletion rule that tells CoreData what to do with related entities each time an entity is deleted. In this case, if we were to delete a Person we’d want their Address to be deleted. However, if we delete an Address (because the person sells his house), we don’t want to delete the person.

We’ll add that metadata to our data model:

  • Person

    • Attributes:
      • firstName (string)
      • lastName (string)
      • age (int)
    • Relationships:
      • address (Address) (Delete: cascade)
  • Address

    • Attributes:
      • street (string)
      • city (string)
      • state (string)
      • postalCode (string)
    • Relationships:
      • person (Person) (Delete: nullify)

We can use the deletion rule to infer ownership of one entity by another. The Person entity can be said to “own” its related Address entity because deleting the Person deletes the Address. The Address entity can be said to be “owned” by its related Person entity because deleting the Address won’t delete the Person. We still have cycles in the graph, but now we can use its deletion rules as direction indicators that tell a walk algorithm which paths it can take through the graph.

Here’s a graph walk algorithm that uses this new information:


- (void)walkGraph:(NSManagedObject *)managedObject {

    NSLog(@"%@", [[managedObject entity] name]);
    
    NSDictionary *relationships = [[managedObject entity] relationshipsByName];

    for (NSString *name in relationships) {

        NSRelationshipDescription *relationship = relationships[name];

        if (relationship.deleteRule != NSCascadeDeleteRule) continue;

        SEL selector = NSSelectorFromString(name);

        NSManagedObject *child = [managedObject performSelector:selector];

        [self walkGraph:child];
    }
}

We’re using a familiar set of metaprogramming tools: NSSelectorFromString(), performSelector: and relationshipsByName. Here’s what it will output if we feed it a fully-populated Person instance:

Person
Address

Note that an alternative would be to maintain a list of walked objects instead of using the deletion rule/ownership approach, but this could introduce its own set of problems.

At this point, we can add a couple of methods and produce a full diff between two Person objects:


- (NSDictionary *)diffNewObject:(NSManagedObject *)newObject withOldObject:(NSManagedObject *)oldObject {
    
    NSDictionary *attributeDiff = [self diffAttributesOfNewObject:newObject withOldObject:oldObject];

    NSDictionary *relationshipsDiff = [self diffRelationshipsOfNewObject:newObject withOldObject:oldObject];

    NSMutableDictionary *diff = [NSMutableDictionary dictionary];

    if (attributeDiff.count > 0) {
        diff[@"attributes"] = attributeDiff;
    }

    if (relationshipsDiff.count > 0) {
        diff[@"relationships"] = relationshipsDiff;
    }

    return diff;
}

- (NSDictionary *)diffRelationshipsOfNewObject:(NSManagedObject *)newObject withOldObject:(NSManagedObject *)oldObject {

    NSMutableDictionary *diff = [NSMutableDictionary dictionary];
    
    NSDictionary *relationships = newObject == nil ? [[oldObject entity] relationshipsByName] : [[newObject entity] relationshipsByName];

    for (NSString *name in relationships) {

        NSRelationshipDescription *relationship = relationships[name];

        if (relationship.deleteRule != NSCascadeDeleteRule) continue;

        SEL selector = NSSelectorFromString(name);

        id newValue = nil;
        id oldValue = nil;

        if (newObject != nil && [newObject respondsToSelector:selector]) newValue = [newObject performSelector:selector];
        if (oldObject != nil && [oldObject respondsToSelector:selector]) oldValue = [oldObject performSelector:selector];

        NSDictionary *relationshipDiff = [self diffNewObject:newValue withOldObject:oldValue];

        if (relationshipDiff.count > 0) {
            diff[name] = relationshipDiff;
        }
    }

    return diff;
}

- (NSDictionary *)diffAttributesOfNewObject:(NSManagedObject *)newObject withOldObject:(NSManagedObject *)oldObject {

    NSMutableDictionary *diff = [NSMutableDictionary dictionary];

    NSArray *attributeNames = newObject == nil ? [[[oldObject entity] attributesByName] allKeys] : [[[newObject entity] attributesByName] allKeys];

    for (NSString *name in attributeNames) {

        SEL selector = NSSelectorFromString(name);

        id newValue = nil;
        id oldValue = nil;

        if (newObject != nil && [newObject respondsToSelector:selector]) newValue = [newObject performSelector:selector];
        if (oldObject != nil && [oldObject respondsToSelector:selector]) oldValue = [oldObject performSelector:selector];

        newValue = newValue ? newValue : [NSNull null];
        oldValue = oldValue ? oldValue : [NSNull null];

        if (![newValue isEqual:oldValue]) {
            diff[name] = @{ @"new": newValue, @"old": oldValue };
        }
    }

    return diff;
}

We’ve added two new methods. The new diffRelationshipsOfNewObject:withOldObject: method expands on our walkGraph: method by checking for nil, the ability to perform a given selector, and by producing a diff. The new diffNewObject:withOldObject: method collates the attribute and relationship diffs into a single dictionary. It is called recursively by the relationship diff method to ensure that the entire object graph is compared.

Although we can now successfully create a full diff of the graph described by our Person entity, we haven’t yet managed to cover all of the possibilities offered by CoreData. Suppose we modify our data model to accommodate people who own multiple houses?

  • Person

    • Attributes:
      • firstName (string)
      • lastName (string)
      • age (int)
    • Relationships:
      • address (Address) (Delete: cascade) (To many)
  • Address

    • Attributes:
      • street (string)
      • city (string)
      • state (string)
      • postalCode (string)
    • Relationships:
      • person (Person) (Delete: nullify) (To one)

Our relationship diff method no longer works. When we retrieve the value of the relationship we receive an NSSet pointer rather than an NSManagedObject pointer and we haven’t written any code to handle that. Even if we write code to iterate over the set and diff individual items, we run into some nasty problems:

  • If we have two addresses and change one, how do we match up the addresses in the two Person objects?
  • How can we tell if an address has been deleted?
  • How can we tell if an address has been added?

It’s clear that our Address entity needs to include an immutable identifier that is unique within the context of its set, but is shared between copies of a Person. NSManagedObjects include an objectID property, but this is unique within the entire dataset so can’t be used. We’ll need to add a new attribute to the Address entity:

  • Address
    • Attributes:
      • guid (string)
      • street (string)
      • city (string)
      • state (string)
      • postalCode (string)
    • Relationships:
      • person (Person) (Delete: nullify) (To one)

I’ve plumped for a GUID, but you could use a bog-standard string or a number.

Now we can identify Address entities by their GUID, but we’ve got another problem. By design, our diff method is entirely ignorant of the structure of our object model. It doesn’t know that it is trying to diff Address objects and therefore can’t know that it should identify them using their “guid” attribute. To get around this, we’ll use yet another meta-feature of CoreData: user info.

Everything in a CoreData model includes a “user info” dictionary. This is a collection of key/value pairs of user-created metadata that can be examined at run time. By “everything”, I mean precisely that: entities, attributes and relationships all contain a “user info” dictionary.

We need the guid property of the Address entity to be identifiable as a unique identifier at run time, so we can exploit the user info dictionary to do this. We’ll add a single key/value pair to the Address entity’s user info that contains “id” as the key and “guid” as the value. At run time, any time we encounter a one-to-many relationship, we’ll examine the members of the related set and locate their “id” user info key. The value will tell us which property contains that entity’s unique identifier, which we can then use to match up objects in the two sets.

Our Address entity now looks like this:

  • Address (User info: @{ @“id”: @“guid” })
    • Attributes:
      • guid (string)
      • street (string)
      • city (string)
      • state (string)
      • postalCode (string)
    • Relationships:
      • person (Person) (Delete: nullify) (To one)

The method for diffing two sets looks like this:


- (NSArray *)diffNewSet:(NSSet *)newSet withOldSet:(NSSet *)oldSet {
    
    NSMutableArray *changes = [NSMutableArray array];

    // Find all items that have been newly created or updated.
    for (NSManagedObject *newItem in newSet) {

        NSString *idAttributeName = newItem.entity.userInfo[@"id"];

        NSAssert(idAttributeName, @"Entities must have an id property set in their user info.");

        id newItemId = [newItem valueForKey:idAttributeName];

        NSManagedObject *oldItem = nil;

        for (NSManagedObject *setItem in oldSet) {
            id setItemId = [setItem valueForKey:idAttributeName];

            if ([setItemId isEqual:newItemId]) {
                oldItem = setItem;
                break;
            }
        }

        NSDictionary *diff = [self diffNewObject:newItem withOldObject:oldItem];

        if (diff) {
            [changes addObject:diff];
        }
    }

    // Find all items that have been deleted.
    for (NSManagedObject *oldItem in oldSet) {

        NSString *idAttributeName = oldItem.entity.userInfo[@"id"];

        NSAssert(idAttributeName, @"Entities must have an id property set in their user info.");

        id oldItemId = [oldItem valueForKey:idAttributeName];

        NSManagedObject *newItem = nil;

        for (NSManagedObject *setItem in newSet) {
            id setItemId = [setItem valueForKey:idAttributeName];

            if ([setItemId isEqual:oldItemId]) {
                newItem = setItem;
                break;
            }
        }

        if (!newItem) {
            NSDictionary *diff = [self diffNewObject:newItem withOldObject:oldItem];

            if (diff) {
                [changes addObject:diff];
            }
        }
    }

    return changes;
}

We’ll create a pair of example sets and see what we get out of the method. Here are our sets:

NewSet = (
    Address {
        guid = "123";
        street = "Somewhere";
        city = "Sometown";
        postalCode = "54532";
    },
    Address {
        guid = "567";
        street = "Elsewhere";
        city = "Another Town";
        postalCode = "222";
    }
};

OldSet = (
    Address {
        guid = "123";
        street = "Somewhere";
        city = "Townsville";
        postalCode = "54532";
    }
);

You should be able to see that we’ve added a new address and changed the existing address’ city attribute. This is the array produced by the method:

(
    {
        attributes: {
            city: {
                old: "Townsville",
                new: "Sometown"
            }
        }
    },
    {
        attributes: {
            guid: {
                old: "<null>",
                new: "567"
            },
            street: {
                old: "<null>",
                new: "Elsewhere"
            },
            city: {
                old: "<null>",
                new: "Another Town"
            },
            postalCode {
                old: "<null>",
                new: "222"
            }
        }
    }
)

Looks good. We can see that one of the array entries contains just the old and new values for “city” as we’d expect. The other entry contains values for all of the Address object’s attributes as we’ve compared it to nil. It didn’t exist previously. The only issue we have is that we can’t tell which Address object the first list entry represents because its guid attribute isn’t included. We can rectify this omission by modifying the diffNewObject:withOldObject: method:


- (NSDictionary *)diffNewObject:(NSManagedObject *)newObject withOldObject:(NSManagedObject *)oldObject {
    
    NSDictionary *attributeDiff = [self diffAttributesOfNewObject:newObject withOldObject:oldObject];

    NSDictionary *relationshipsDiff = [self diffRelationshipsOfNewObject:newObject withOldObject:oldObject];

    NSMutableDictionary *diff = [NSMutableDictionary dictionary];

    if (attributeDiff.count > 0) {
        diff[@"attributes"] = attributeDiff;
    }

    if (relationshipsDiff.count > 0) {
        diff[@"relationships"] = relationshipsDiff;
    }

    if (diff.count > 0) {
        diff[@"entityName"] = newObject ? newObject.entity.name : oldObject.entity.name;

        NSString *idAttributeName = newObject ? newObject.entity.userInfo[@"id"] : oldObject.entity.userInfo[@"id"];

        if (idAttributeName) {
            id itemId = newObject ? [newObject valueForKey:idAttributeName] : [oldObject valueForKey:idAttributeName];

            if (itemId) {
                diff[idAttributeName] = itemId;
            }
        }
    }

    return diff;
}

We’re fetching the unique identifier attribute and adding its value to the diff. We’ve also added the entity’s name, which may help when dealing with the diff once it has been transmitted and received and needs to be parsed. The output from our previous pair of sets now looks like this:

(
    {
        attributes: {
            city: {
                old: "Townsville",
                new: "Sometown"
            }
        },
        entityName = "Address",
        guid: "123"
    },
    {
        attributes: {
            guid: {
                old: "<null>",
                new: "567"
            },
            street: {
                old: "<null>",
                new: "Elsewhere"
            },
            city: {
                old: "<null>",
                new: "Another Town"
            },
            postalCode {
                old: "<null>",
                new: "222"
            }
        },
        entityName = "Address",
        guid: "567"
    }
)

The final piece of the puzzle is to plug this set diffing method into our relationship diff method. Fortunately, the relationship description class includes an “isToMany” property that will allow us to use the correct diffing method for a given relationship. Here’s the final set of methods required for diffing a pair of managed objects:


- (NSDictionary *)diffNewObject:(NSManagedObject *)newObject withOldObject:(NSManagedObject *)oldObject {
    
    NSDictionary *attributeDiff = [self diffAttributesOfNewObject:newObject withOldObject:oldObject];

    NSDictionary *relationshipsDiff = [self diffRelationshipsOfNewObject:newObject withOldObject:oldObject];

    NSMutableDictionary *diff = [NSMutableDictionary dictionary];

    if (attributeDiff.count > 0) {
        diff[@"attributes"] = attributeDiff;
    }

    if (relationshipsDiff.count > 0) {
        diff[@"relationships"] = relationshipsDiff;
    }

    if (diff.count > 0) {
        diff[@"entityName"] = newObject ? newObject.entity.name : oldObject.entity.name;

        NSString *idAttributeName = newObject ? newObject.entity.userInfo[@"id"] : oldObject.entity.userInfo[@"id"];

        if (idAttributeName) {
            id itemId = newObject ? [newObject valueForKey:idAttributeName] : [oldObject valueForKey:idAttributeName];

            if (itemId) {
                diff[idAttributeName] = itemId;
            }
        }
    }

    return diff;
}

- (NSDictionary *)diffRelationshipsOfNewObject:(NSManagedObject *)newObject withOldObject:(NSManagedObject *)oldObject {

    NSMutableDictionary *diff = [NSMutableDictionary dictionary];
    
    NSDictionary *relationships = newObject == nil ? [[oldObject entity] relationshipsByName] : [[newObject entity] relationshipsByName];

    for (NSString *name in relationships) {

        NSRelationshipDescription *relationship = relationships[name];

        if (relationship.deleteRule != NSCascadeDeleteRule) continue;

        SEL selector = NSSelectorFromString(name);

        id newValue = nil;
        id oldValue = nil;

        if (newObject != nil && [newObject respondsToSelector:selector]) newValue = [newObject performSelector:selector];
        if (oldObject != nil && [oldObject respondsToSelector:selector]) oldValue = [oldObject performSelector:selector];

        if (relationship.isToMany) {

            NSArray *changes = [self diffNewSet:newValue withOldSet:oldValue];

            if (changes.count > 0) {
                diff[name] = changes;
            }

        } else {

            NSDictionary *relationshipDiff = [self diffNewObject:newValue withOldObject:oldValue];

            if (relationshipDiff.count > 0) {
                diff[name] = relationshipDiff;
            }
        }
    }

    return diff;
}

- (NSDictionary *)diffAttributesOfNewObject:(NSManagedObject *)newObject withOldObject:(NSManagedObject *)oldObject {

    NSMutableDictionary *diff = [NSMutableDictionary dictionary];

    NSArray *attributeNames = newObject == nil ? [[[oldObject entity] attributesByName] allKeys] : [[[newObject entity] attributesByName] allKeys];

    for (NSString *name in attributeNames) {

        SEL selector = NSSelectorFromString(name);

        id newValue = nil;
        id oldValue = nil;

        if (newObject != nil && [newObject respondsToSelector:selector]) newValue = [newObject performSelector:selector];
        if (oldObject != nil && [oldObject respondsToSelector:selector]) oldValue = [oldObject performSelector:selector];

        newValue = newValue ? newValue : [NSNull null];
        oldValue = oldValue ? oldValue : [NSNull null];

        if (![newValue isEqual:oldValue]) {
            diff[name] = @{ @"new": newValue, @"old": oldValue };
        }
    }

    return diff;
}

- (NSArray *)diffNewSet:(NSSet *)newSet withOldSet:(NSSet *)oldSet {
    
    NSMutableArray *changes = [NSMutableArray array];

    // Find all items that have been newly created or updated.
    for (NSManagedObject *newItem in newSet) {

        NSString *idAttributeName = newItem.entity.userInfo[@"id"];

        NSAssert(idAttributeName, @"Entities must have an id property set in their user info.");

        id newItemId = [newItem valueForKey:idAttributeName];

        NSManagedObject *oldItem = nil;

        for (NSManagedObject *setItem in oldSet) {
            id setItemId = [setItem valueForKey:idAttributeName];

            if ([setItemId isEqual:newItemId]) {
                oldItem = setItem;
                break;
            }
        }

        NSDictionary *diff = [self diffNewObject:newItem withOldObject:oldItem];

        if (diff.count > 0) {
            [changes addObject:diff];
        }
    }

    // Find all items that have been deleted.
    for (NSManagedObject *oldItem in oldSet) {

        NSString *idAttributeName = oldItem.entity.userInfo[@"id"];

        NSAssert(idAttributeName, @"Entities must have an id property set in their user info.");

        id oldItemId = [oldItem valueForKey:idAttributeName];

        NSManagedObject *newItem = nil;

        for (NSManagedObject *setItem in newSet) {
            id setItemId = [setItem valueForKey:idAttributeName];

            if ([setItemId isEqual:oldItemId]) {
                newItem = setItem;
                break;
            }
        }

        if (!newItem) {
            NSDictionary *diff = [self diffNewObject:newItem withOldObject:oldItem];

            if (diff.count > 0) {
                [changes addObject:diff];
            }
        }
    }

    return changes;
}

That’s it: diff created! At this point, if you find that you don’t like the way old and new values are grouped together, or want to change any other aspect of the way the diff is produced, you should have enough information about how to use metaprogramming with NSManagedObjects to be able to change things around.

There are still two unsolved problems that I’ll tackle in another post:

  • What do you do if you want to include a related object in the diff if it has its deletion rule set to nullify?
  • What do you do if you want to exclude an attribute or relationship from the diff?

Once those are solved, there’s another big one: how do you apply the diff to an object? I’ll tackle that in another post, too.

2012-11-17

Super Foul Egg - iPad

Soon:

Here’s what’s left to do:

  • Pause/exit game;
  • Replace any bitmaps that mention keys with bitmaps that mention touches;
  • Test it on real hardware.

I don’t currently have an iPad to test on. I had a quick try on the office iPad and discovered that the initial control system I’d written was unresponsive and unusable, so I’ve just finished a re-write of that.

I had two options for the control system. The obvious solution was to use an on-screen joypad. However, I detest iOS games that use an on-screen joypad to such a degree that I don’t even bother trying them. A more interesting solution was to try and work with the touchscreen instead of working against it and use gestures.

This is what I’ve implemented:

  • Tap with one finger to rotate the current shape clockwise;
  • Tap with two fingers to rotate the current shape anticlockwise;
  • Swipe downwards with two fingers to auto-drop the current shape (I had to concede that the hated auto-drop makes sense in the context of an iPad);
  • Touch and drag with one finger to move the current shape left and right (it follows the movements of the finger).

These gestures can be performed anywhere on the screen, which means the player’s fingers won’t get in the way of the action.

Unfortunately, I had to remove the two-player mode in order to accommodate this control system. I’m hoping that I’ll get around to using GameKit to allow multiplayer on several devices. Perhaps I’ll get an 8-player mode implemented.

Getting SFE working on a new platform has allowed me to look over the code again. Back in August 2011 I said this about the codebase:

My code probably looks hideous to experienced Objective-C coders as I’ve trampled conventions all over the place.

Just over a year later, I’m pleased to say that I agree with myself. The SFE code is pretty nasty in places. Here are a few mistakes I’ve spotted in the last few minutes:

  • Using #define instead of constants.

C guys use #define. C++ guys use consts. I went with #define because objc is clearly C-with-classes, but consts are better style.

  • Using blocks instead of protocols and delegates.

Blocks are cool! They’re also finicky when doing manual memory management and are an exceptionally good way to end up with cyclic references. For the most part I used blocks to separate out the game engine from the I/O code, which made the code vaguely MVC-like. Great idea (and wonderful for getting the game working on the iPad) but using delegates and protocols would have simplified things immensely and been more conventional.

  • Not cleaning up references after releasing objects.

Try doing something to the live block references in a Grid object when it doesn’t have live blocks. Ooops: crash.

  • Not following standard Objective-C naming conventions.

Pretty much everything should have been prefixed with “SZ” but wasn’t.

  • Following standard Objective-C naming conventions.

The enum names follow Apple’s official enum naming style (aside from the missing two character prefix), but then Apple have two styles and the official style sucks. It’s impossible to use autocompletion to determine the contents of an enum if the values are named “NSUTF16BigEndianStringEncoding” instead of “NSStringEncodingUTF16BigEndian”.

  • Using “SomeType* variable” instead of “SomeType *variable”.

Nitpicking, I know. I think the former makes the most sense semantically - the full type definition is “a pointer to SomeType”, so the asterisk is grouped with the type name - and is what I use in C/C++. However, I’ve been following Apple guidelines and now use the latter in objc. Apple don’t always follow this themselves, though. The fact that their templates don’t consistently place the opening brackets for a code block in the same place irritates me, too.

  • Protocol names.

“ControllerProtocol”: It’s like backwards Hungarian notation.

  • Fear of singletons.

It’s widely acknowledged that singletons are a Bad Pattern. They can’t be tested! They can’t be mocked! Every time you use a singleton a child dies! Look through most of my code and I’m pretty sure you’ll find that I try to avoid singletons wherever possible. Part of this was a response to the irrational level of hate levelled at the pattern, and part of it was due to the number of issues that singletons cause when working on web applications in .NET (hint: static variables have an application-level scope, not a client-level scope).

On the other hand, singletons are used extensively in Apple’s objc frameworks, and I now use them everywhere too. TDD crowd be damned. The “BlockFactory” could easily be a singleton, accessed via [SZBlockFactory sharedFactory], instead of being injected into every class that needs it like I’m writing some kind of enterprisey abomination.

  • “SZPoint”.

I’m hoping that this was a holdovee from the C++ version, because I can’t think of a valid reason why I’d create a class with identical functionality to CGPoint.

  • Reference counting.

I haven’t seen any examples yet, but this sentence from the August blogpost sounds extremely dumb:

For the most part, I’ve used what Objective-C calls “weak references” - I’ve got pointers to objects without retaining them, the same as I would in C++.

I might try to tidy some of this mess up. On the other hand, it ain’t broken…

2012-11-08

Barcodes in iOS

Recently I’ve been investigating methods for getting iOS apps to read one and two dimensional barcodes. There’s a huge variety of formats, but the three I’m mainly interested in are UPCA and EAN-13 (linear barcodes you’ll find on boxes in shops) and PDF417 (matrix barcodes you’ll find on some US driving licences).

There appear to be three main competitors for your attention if you’re trying to read two-dimensional barcodes in an iOS app:

ZXing is an open-source offering that supports a smorgasbord of formats for a variety of platforms and languages, including iOS, Android, Java, C#, ActionScript and others. Unfortunately, its PDF417 support is listed as “alpha quality” and the iOS port only supports QR codes (another matrix format). The iOS port hasn’t been updated in about 3 years, so ZXing is out.

Manatee Works is a closed-source library that claims to be smaller and more efficient than ZXing. It supports Windows, Android and iOS, and supports all of the bar codes I’m interested in. Looks good! How much does it cost? And can I see some code so I get an idea of how well the API was put together? No. No I can’t.

Manatee Works appears to be one of those companies that believes ardently that keeping its prices secret will encourage people to contact its sales team so they can presumably engage in the hard sell. Either that, or it’s like one of those restaurants that don’t put prices on the menus because, if you have to ask how much it costs, you probably can’t afford it. In any case, their product does indeed cost more than I’d pay.

Lastly, there’s SD-Toolkit. Again, they support multiple platforms: Windows, Android, iOS, Mac OSX and Linux, and again it supports all of the barcodes I need. Their relatively reasonable prices are published on their website and they even provide a trial SDK. My only issue with SD-Toolkit was that their trial SDK doesn’t yet support the armv7s architecture used in the iPhone 5.

If you’re trying to include a barcode reader in your iOS app, these are your options:

  • An extremely limited open-source effort that’s been abandoned;
  • An extremely expensive closed-source library that you can’t see until you talk to the sales team;
  • A more reasonably-priced closed-source library, with a trial SDK, that’s a little behind the times.

At this point I came up with an alternative solution: don’t try to read barcodes on an iOS device at all. Instead:

  • Grab a photo of the barcode using the phone;
  • Rotate, scale, crop and compress the image down to ~40K;
  • Post the image to a web server running a barcode reader SDK behind a RESTful web service;
  • Perform all of the barcode parsing on the web server;
  • Return the parsed data as JSON objects to the iOS device.

The downsides to this are obvious. Barcode parsing will only work on an iOS device that has an internet connection, and parsing times will include data transmission time. However, the advantages are compelling. The average web server is so much faster than an iPhone that the time taken to transmit in both directions and decode via a web service appears to be no longer than the time to parse directly on an iPhone. ZXing becomes an option again if you’re a Java shop. If you can’t use ZXing, you should be able to find an SDK for your prefered language for - at most - the cost of Manatee Works’ library, but it will work on all devices. Yep, a web service will work with Android, iOS, Windows, Linux, BSD or anything else with an internet connection.

That’s the option I’ve plumped for. It’s working nicely so far. The most troublesome part was rotating, scaling and cropping the images from the iOS camera correctly on all iOS devices.

One final note: Don’t bother trying to read barcodes on an iOS device that doesn’t have an autofocusing camera. It just doesn’t work reliably. Stick to the iPhone 3GS+, iPod Touch 5g+ or the iPad 3+.

2012-11-06

Yet Another iOS JSON Date Parser

Here’s yet another fix for my JSON date parser for Objective-C. This version includes support for dates with a timezone offset, which Jeff requested a while ago and which I’ve finally run into (Grumpydev updated Nancy to produce this date format back in April).

#import <Foundation/Foundation.h>

@interface SZJSONDate : NSObject

+ (NSObject *)dateToJson:(NSDate *)date;
+ (NSDate *)jsonStringToNSDate:(NSString *)string;

@end


#import "SZJSONDate.h"

@implementation SZJSONDate

+ (NSObject *)dateToJson:(NSDate *)date {
    if (date == nil) return [NSNull null];

    NSString *value = [NSNumberFormatter localizedStringFromNumber:[NSNumber numberWithDouble:date.    timeIntervalSince1970 * 1000] numberStyle:NSNumberFormatterNoStyle];

    return [NSString stringWithFormat:@"/Date(%@)/", value];
}

+ (NSDate *)jsonStringToNSDate:(NSString *)string {

    if (string == nil) return nil;

    // Extract the numeric part of the date.  Dates should be in the format
    // "/Date(x)/", where x is a number.  This format is supplied automatically
    // by JSON serialisers in .NET.  Alternatively, dates can be supplied with
    // the time zone suffixed after x as a 5 character timezone offset,
    // consisting of a positive/negative indicator ('+' or '-'), two digit hour
    // offset, and two digit minute offset (ie. '-0700' for MST).
    NSRange range = NSMakeRange(6, [string length] - 8);
    NSString* substring = [string substringWithRange:range];

    NSTimeInterval seconds;

    NSNumberFormatter* formatter = [[NSNumberFormatter alloc] init];

    // We need to parse out the date portion and the timezone portion
    // separately.  It's possible that we won't have a timezone and the date is
    // negative, so we have to be more specific when searching for the 
    // separator.
    unichar timezoneSeparator = [substring characterAtIndex:substring.length - 5];

    NSString *date = nil;

    if (timezoneSeparator == '+' || timezoneSeparator == '-') {

        date = [substring substringToIndex:substring.length - 5];

        NSString *timeZone = [substring substringFromIndex:substring.length - 4];
        NSString *timeZoneHours = [timeZone substringToIndex:2];
        NSString *timeZoneMinutes = [timeZone substringFromIndex:2];

        NSNumber* milliseconds = [formatter numberFromString:date];

        int hours = [[formatter numberFromString:timeZoneHours] intValue];
        int minutes = [[formatter numberFromString:timeZoneMinutes] intValue];
        int offset = (minutes + (hours * 60)) * 60;

        // NSTimeInterval is specified in seconds, with milliseconds as
        // fractions.  The value we get back from the web service is specified
        // in milliseconds.  Both values are since 1st Jan 1970 (epoch).
        seconds = [milliseconds longLongValue] / 1000.0;

        if (timezoneSeparator == '+') {
            seconds += offset;
        } else {
            seconds -= offset;
        }
    } else {

        date = substring;

        NSNumber* milliseconds = [formatter numberFromString:date];
        seconds = [milliseconds longLongValue] / 1000.0;
    }

    [formatter release];

    // If we've encountered .NET's minimum date value we treat it as nil.
    if ([date isEqualToString:@"-59011459200000"]) return nil;

    return [NSDate dateWithTimeIntervalSince1970:seconds];
}

@end

2012-07-31

JSON, .NET and NSDate Redux

Here’s a version of my JSON date to NSDate conversion function updated to convert correctly to the local timezone.


/**
 * Converts a .NET JSON date in the format "/Date(x)/" to an NSDate object.
 * @param string The NSString to convert.
 * @return the NSDate object.
 */
(NSDate*)jsonStringToNSDate(NSString* string) {

    if (string == nil) return nil;

    // If we encounter .NET's minimum date value we treat it as nil.
    if ([string isEqualToString:@"/Date(-59011459200000)/"]) return nil;

    // Extract the numeric part of the date.  Dates should be in the format
    // "/Date(x)/", where x is a number.  This format is supplied automatically
    // by JSON serialisers in .NET.
    NSRange range = NSMakeRange(6, [string length] - 8);
    NSString* substring = [string substringWithRange:range];

    NSNumberFormatter* formatter = [[NSNumberFormatter alloc] init];
    NSNumber* milliseconds = [formatter numberFromString:substring];

    [formatter release];

    // NSTimeInterval is specified in seconds.  The value we get back from the
    // web service is specified in milliseconds.  Both values are since 1st Jan
    // 1970 (epoch).
    NSTimeInterval seconds = [milliseconds longLongValue] / 1000.0;

    seconds += [[NSTimeZone localTimeZone] secondsFromGMT];

    return [NSDate dateWithTimeIntervalSince1970:seconds];
}

Edit: Fixed to return milliseconds as the fractional part of the “seconds” value.

2012-07-28

Objective-C Literals

Xcode 4.4 ships with a new version of LLVM that supports a host of new syntactic sugar around collections. You used to have to write this:


NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:@"value1", @"key1", @"value2", @"key2", nil];

Now you can write this:


NSDictionary *dict = @{ @"key1": @"value1", @"key2": @"value2" };

There’s a similar syntax for arrays. There’s also an easier way of subscripting. This is the old way:


id obj = [dict objectForKey:@"key1"];

This is the new way:


id obj = dict[@"key1"];

This all sounds wonderful in theory. Less typing! However, in practice:

  • Apple state in the release notes for Xcode 4.4 that this new syntax only works in OSX, but I’ve successfully used array, number and dictionary literals in an iOS app.

  • Attempts at subscripting arrays or dictionaries in iOS fail with a “bad receiver type” compiler error.

  • Subscripts work in OSX when compiled for “running”, but fail with the same compiler error when compiled for “archiving”.

As none of this seems to be documented anywhere (except on the LLVM website, which of course makes no mention of OSX or iOS and has no information on which bits of the syntax are available on which platform), I’m going to assume that subscripting is, at least for the moment, broken and unusable.