Implementing a publish/subscribe design using the NSNotificationCenter

My background is mainly in Java, so if I want to create an object which has a set of listeners, I’m used to having to implement that design myself.  In the past, I’ve added a collection of listeners to the observable object, then added some methods to the class’ API to allow other objects to register as listeners for events.

I’m still quite new to iOS, so I haven’t really made a lot of use of NSNotificationCenter up till now, other than for registering for things like keyboard events.  I’ve found that it can really help with the publish/subscribe design pattern, and it reduces the amount of code you have to write.

To register an object to listen for a particular kind of event, you can use the following code:

[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(someMethod) name:kNotificationName object:nil];

You define the method in your listener class.  You pass in the name of notifications to listen for.  This means that whenever you post a notification with that name, the listeners which have registered for those kind of notifications will be notified.

To post a notification, you can use the following code:

[[NSNotificationCenter defaultCenter] postNotification:[NSNotification notificationWithName:kNotificationName object:nil]];

The other thing to remember is to remove your listener classes as observers to the notification center when they are deallocated.

MacRuby and CoreData

As part of a project I’ve been working on, we’ve needed to use CoreData within MacRuby.     MacRuby is an implementation of Ruby which runs on the Objective-C runtime and the CoreFoundation framework.  As such, it allows you to make calls to CoreData within Ruby.

We created a utility class to allow us to make calls to our data store.  It was as follows:

class CoreDataStore
    def create_entity name, props={}, relationships={}
        entity = OSX::NSEntityDescription.insertNewObjectForEntityForName_inManagedObjectContext(name, context)
        props.each do |k,v|
            entity.setValue_forKey v,k
        end

        relationships.each do |k, objects|
            collection = entity.mutableSetValueForKey(k)
            objects.each {|o| collection.addObject o}
        end
        entity
    end

    def get_entity name, key, value
        request = OSX::NSFetchRequest.alloc.init

        description = OSX::NSEntityDescription.entityForName_inManagedObjectContext(name, context)

        request.setEntity(description)

        valueString = "#{value}"
        if (value.is_a? String)
            valueString = "'#{value}'"
        end
        predicateString = "#{key} like[c] #{valueString}"
        predicate = OSX::NSPredicate.predicateWithFormat(predicateString)

        request.setPredicate(predicate)

        error = nil

        result = context.executeFetchRequest_error(request, error)
    end

    def initialize(data_store_path, mom_path)
        @data_store_path = data_store_path
        @mom_path = mom_path
    end

    def context
        @contect ||= OSX::NSManagedObjectContext.alloc.init.tap do |context|
            model = OSX::NSManagedObjectModel.alloc.initWithContentsOfURL(
                OSX::NSURL.fileURLWithPath(@mom_path))
            coordinator = OSX::NSPersistentStoreCoordinator.alloc.initWithManagedObjectModel(model)

            result, error = coordinator.addPersistentStoreWithType_configuration_URL_options_error(
                OSX::NSSQLiteStoreType, nil, OSX::NSURL.fileURLWithPath(@data_store_path), nil)
            if !result
                raise "Add persistent store failed: #{error.description}"
            end
            context.setPersistentStoreCoordinator coordinator
        end
    end

    def save
        res, error = context.save_
        if !res
            raise "Save failed: #{error.description}"
        end
        res
    end
end

The utility class allows us to create a new entity within the persistent store, and to make a simple query on the store.  It is by no means comprehensive, but this did everything we needed in our simple use case.

Updating remote tracking branches on Git

I recently experienced some odd behaviour with Git and Gerrit.  I created a short-lived branch on a project in Gerrit.  Once I was done with it, I deleted the branch using the Gerrit admin page.

When I listed the remote branches for the project (using git branch -a), it was still showing the remote branch which I deleted!  Eventually, after a bit of research online, I found what the problem was.   The branch had been deleted on the remote repository, but I hadn’t yet updated my remote tracking branches yet.  Calling “git fetch” or “git pull” won’t update them either.  To do this, you need to call:

git remote prune origin

(For a remote with a different name, substitute it in for origin).  This deletes any stale remote tracking branches.

Dynamically defining a selector from a string

I’m used to calling a selector in iOS using the following format:

@selector(method_name_here:arguments)

However, I wasn’t aware that you could define a selector by passing in the name of the method at runtime. It turns out you can do it like this:

SEL selector = NSSelectorFromString(some_string);
result = [some_object performSelector:selector];

Clipping views using a Bezier path

I had a situation where I was rendering a chart, and I needed to clip a tooltip on the chart so that it didn’t get rendered outside of the chart plotting area.  We can use the CALayer on a view to clip a view, by setting the mask property of the layer like so:

CGRect clippingRect;
// Set clippingRect to the rectangle you wish to clip to

UIBezierPath *maskPath = [UIBezierPath bezierPathWithRect:clippingRect];

// Create a shape layer
CAShapeLayer *maskLayer = [CAShapeLayer layer];
maskLayer.frame = self.frame;

// Set the path of the mask layer to be the Bezier path we calculated earlier
maskLayer.path = maskPath.CGPath;

self.layer.mask = maskLayer;

A key thing to note is that if we just want to clip a particular sub view on a view, we can set the clipping area just for that sub view. This means that any other views are unaffected.

Embedding a hyperlink into CoreText

This was a problem in effectively two parts.  The first part was to style a section of my attributed string as a hyperlink (i.e. underlined, although in other cases I might have wanted to colour it blue).  How to do that is fairly well documented, and I won’t go over it in detail here.  Some useful tutorials on the topic are:

http://www.raywenderlich.com/4147/how-to-create-a-simple-magazine-app-with-core-text

http://www.cocoanetics.com/2011/01/befriending-core-text/

The second part of the problem was less well documented – I had to find some way of responding to user taps on links in the text.  I used a markup parser similar to that used in the Ray Wenderlich tutorial.  It used opening tags, and styled any text following that tag accordingly.  I defined a tag type for links.  Where I came across a link, I stored a custom attribute against that section of the text, which had a key of “link”, and a value of the URL against which to link against.

After rendering my core text, I used the following code to find the bounds on screen of the sections of the text which showed links.  When I found a link, I added a sub view with its bounds to handle taps on that section of the screen:

// Get the lines in our frame
    NSArray* lines = (NSArray*)CTFrameGetLines(frame);
    CFIndex lineCount = [lines count];

    // Get the origin point of each of the lines
    CGPoint origins[lineCount];
    CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), origins);

    for(CFIndex idx = 0; idx < lineCount; idx++)
    {
        // For each line, get the bounds for the line
        CTLineRef line = CFArrayGetValueAtIndex((CFArrayRef)lines, idx);

        // Go through the glyph runs in the line
        CFArrayRef glyphRuns = CTLineGetGlyphRuns(line);
        CFIndex glyphCount = CFArrayGetCount(glyphRuns);
        for (int i = 0; i < glyphCount; ++i)    {
            CTRunRef run = CFArrayGetValueAtIndex(glyphRuns, i);

            NSDictionary *attributes = (NSDictionary*)CTRunGetAttributes(run);
            if ([attributes objectForKey:@"link"])    {
                CGRect runBounds;

                CGFloat ascent;//height above the baseline
                CGFloat descent;//height below the baseline
                runBounds.size.width = CTRunGetTypographicBounds(run, CFRangeMake(0, 0), &ascent, &descent, NULL);
                runBounds.size.height = ascent + descent;

                // The bounds returned by the Core Text function are in the coordinate system used by Core Text.  Convert the values here into the coordinate system which our gesture recognizers will use.
                runBounds.origin.x = CTLineGetOffsetForStringIndex(line, CTRunGetStringRange(run).location, NULL);
                runBounds.origin.y = self.frame.size.height - origins[idx].y - runBounds.size.height;

                // Create a view which will open up the URL when the user taps on it
                LinkTapView *linkTapView = [[[LinkTapView alloc] initWithFrame:runBounds url:[attributes objectForKey:@"link"]] autorelease];
                linkTapView.backgroundColor = [UIColor clearColor];
                [self addSubview:linkTapView];
            }
        }
    }

The LinkTapView class was one I wrote myself. It was a subclass of UIView, to which I added a tap gesture recognizer. In its handleTap method, it opened up the specified URL.

LinkTap:

- (void)handleTap: (UITapGestureRecognizer*)sender  {
    UIApplication *application = [UIApplication sharedApplication];
    NSURL *url = [NSURL URLWithString: linkUrl];
    // If we can open the URL specified by the link, do so in a web browser
    if ([application canOpenURL:url])   {
        [[UIApplication sharedApplication] openURL:url];
    } else {
        NSLog(@"Unable to open URL: %@", linkUrl);
    }
}