Textual Representation for Classes (Swift)

Swift make it easy to convert a custom class to a string (e.g. for use with print) via the CustomStringConvertible protocol. (a clear improvement over earlier implementations)

class X : CustomStringConvertible  {
  var description: String{
    return "This is class X"
  }
}

print(X()) //output is: "This is class X"

A Groovy lib for Xcode pbxproj files

See github

// snippet from the test (spock) spec:

    def "object types and keys are ok"( String key, String klass ) {
        expect:
        proj.objects[ key ].class.simpleName == klass

        where:
        key                        | klass
        'F98F991811A4A86400D21E1F' | 'PBXBuildFile'
        '0597689803D6472D00C9149F' | 'PBXFileReference'
        '059768A803D6494200C9149F' | 'PBXFileReference'
        '05CA34F70433CFDF00C9149F' | 'PBXFileReference'
        'F98F991611A4A85000D21E1F' | 'PBXFileReference'
        'F98F991411A4A85000D21E1F' | 'PBXFrameworksBuildPhase'
        '0597688C03D6465000C9149F' | 'PBXGroup'
        '0597689703D646C100C9149F' | 'PBXGroup'
        'F98F991511A4A85000D21E1F' | 'PBXNativeTarget'
        '0597689003D6465000C9149F' | 'PBXProject'
        'F98F991311A4A85000D21E1F' | 'PBXSourcesBuildPhase'
        '05B1F3D8089068690080B6E2' | 'XCBuildConfiguration'
        'F98F991711A4A85100D21E1F' | 'XCBuildConfiguration'
        '05B1F3D7089068690080B6E2' | 'XCConfigurationList'
        'F98F991911A4A8B900D21E1F' | 'XCConfigurationList'
    }

    def "file references are ok"( def key, def path ) {
        expect:
        proj.objects[ key ].path == path

        where:
        key                        | path
        "0597689803D6472D00C9149F" | "keymgr.c"
        "059768A803D6494200C9149F" | "keymgr.h"
        "05CA34F70433CFDF00C9149F" | "testcases/basic-eh-app.cc"
        "F98F991611A4A85000D21E1F" | "libkeymgr.dylib"
    }

    def "verify product group -- PBXRef links"( def index, def path ) {
        given:
        def children = proj.objects[ "0597688C03D6465000C9149F" ].children

        expect:
        children[ index ].path == path

        where:
        index | path
        1     | "keymgr.c"
        2     | "keymgr.h"
        3     | "testcases/basic-eh-app.cc"
    }

CGContext Syntactic Sugar

Tired of typing CGContextBlah(context, ...)? There’s an extension for that: context.blah(...)

//  CGContextABCD(context!, ...) becomes context?.ABCD(...)
 
import Cocoa
 
extension CGContext {
    func saveGState() { CGContextSaveGState(self) }
    func restoreGState() { CGContextRestoreGState(self) }
    func scaleCTM( sx: CGFloat, sy: CGFloat) { CGContextScaleCTM(self, sx, sy) }
    func translateCTM( tx: CGFloat, ty: CGFloat) { CGContextTranslateCTM(self, tx, ty) }
    func rotateCTM( angle: CGFloat) { CGContextRotateCTM(self, angle) }

Get CGContextExt.swift.

Text Aliasing in Custom Drawn OSX Status Bar Items

Its fairly straightforward for an OSX app to add a status bar item:

// -1: variable length
var statusBarItem = NSStatusBar.systemStatusBar().statusItemWithLength(-1)

As of OSX 10.10, most drawing modes have been “softly” deprecated, including custom views. To quote the docs:

Custom views should not be set on a status item. The button property with a template image will allow proper styling of the status item in various states and contexts and should be used instead.

This isn’t a major issue for static status items which can set text via the button’s title or an image via the button’s image. Non-static status items will likely miss the ability to set custom views.

One workaround is to install a drawing handler for button’s image. The handler will be called within the appropriate drawing context. Note that this method is preferable over creating a “rendered” NSImage (lockFocus & draw) since lockFocus() will ignore text antialiasing.

let image = NSImage(size: mySize, flipped: false, drawingHandler: drawImage)
image.cacheMode = .Never //don't cache, always draw
statusItem.button.image = image

func drawImage(rect: NSRect) -> Bool {
  var context = NSGraphicsContext.currentContext()?.CGContext
  CGContextSaveGState(context!)

  //draw

  CGContextRestoreGState(context!)
}

Beginning with Yosemite, status items are required to deal with dark modes. If we supply a template image, Yosemite will automatically handle the display mode (a template image comprises only clear or greyscale pixels).

let image = NSImage(size: mySize, flipped: false, drawingHandler: drawImage)
image.setTemplate(true)
...

However, text aliasing isn’t optimal when template mode is turned on. Turns out that for best performance, text anti-aliasing uses a dash of color pixels which isn’t possible in pure-greyscale template mode. Note the differences in the images below:

label-with-template

setTemplate(true)

label-template-zoomed
label-no-template
setTemplate(false)
label-no-template-zoomed

In dark mode:

dark-label-template

setTemplate(true)

dark-label-template-zoomed
dark-label-no-template

setTemplate(false)
dark-label-no-template-zoomed

The solution? Don’t set template mode and handle display mode changes appropriately.

Update May 04

The app which prompted this study is now live on the app store. 

Using CSON with nconf (nodejs)

nconf for nodejs offers hierarchical configuration with files, environment variables, command-line arguments, and atomic object merging. Out of the box, nconf supports JSON and ini file formats. Getting nconf to support custom formats, e.g. CSON is straightforward: provide a format handler as an option to nconf.

nconf.file file : "somefile.cson", format : { stringify : cson.stringifySync, parse : cson.parseSync }

See gist for a longer example.

REST Command Line Tool

https://github.com/venkatperi/node-restit

restit is a command line tool (CLI) for talking to RESTful APIs. It’s intended to reduce some of the repetition and verbosity that comes with using general purpose CLI tools such as curl with RESTful APIs.

NodeJS Custom Start Script on OpenShift

The nodejs cartridge provides Node.JS on OpenShift. The default entry point for the nodejs cartridge is /server.js, which can be changed with the following package.json entry:

"scripts": { "start": "supervisor <relative-path-from-repo-to>/app.js" },

However, we couldn’t get node to cooperate without this additional package.json entry:

  "scripts" : { "start" : "supervisor app.coffee" },
  "main" : "app.coffee",

Of course, use of coffeescript requires a corresponding entry in the dependencies section.