A quick SQL digression

Just a brief break from Swift programming — I stumbled across an interesting SQL shortcut the other day.

I was exploring a SQL table, trying to understand what kind of data was contained therein.  I was doing things of the form:

select count(*) from thetable where description like ‘%thing%';
count
-------
60
(1 row)

(I’m using PostgreSQL here, but I’m pretty sure this would work anywhere).  And this was then often followed by

select count(*) from thetable where description not like ‘%thing%';
count
-------
16
(1 row)

 Which would have been OK, except that I remembered that 

select count(*) from thetable;
count
-------
80
(1 row)

 Where were the missing rows?  Oh, right!  Nulls are special — they are neither equal nor not-equal to regular values.  So that meant:

select count(*) from thetable where description is null;
count
-------
4
(1 row)

 I was constantly running the same comparison repeatedly to get the equal / not-equal / null breakdowns for various columns.   And then I realized that:

select description like ‘%thing%’ as f, count(*) from thetable group by f;
 f | count
---+-------
∅ | 4
t | 16
f | 60
(3 rows) 

I could create the comparison as a field, then group by that field.  There would be three possible values (true, false, and null) — so one pass would give me my counts. 

I also prefer the way it reads.  The thing I’m counting is at the beginning of the phrase, not the end — where it feels more natural.  And this way, I don’t forget to check for the null case (which I often forget) — I get it for free.

Failing to Succeed

At OSCON 2006, I gave a talk titled “Failing to Succeed” in which I made a series of observations about how the value of open source software was due to its willingness to acknowledge (and thereby, fix) bugs.   The point being that learning happens in the gap between making an error and correcting it.

I realized that I have neglected to act on this realization.  I’ve been writing blog posts (as so many tutorials do) explaining a solution for the task I’m describing.  But I realized as I was readying a post on creating the status item for ClickSliver, that I spent most of my time figuring out why the thing I was doing wasn’t working — and yet the post was just explaining the solution I eventually settled on, without describing the strange behavior I observed, and what I eventually realized I was doing wrong.

I need to fix that.

Going forward, I intend to spend more time talking about what didn’t work, and what I learned therefrom — and, yes, eventually describing a solution.  Hopefully the description of the failure will be more valuable than the description of the success.  

Having created an icon and customized XCode to create Swift files the way I prefer (my last two posts), it is time to start writing code.

ClickSliver starts as a “status item” — a utility which sits in the Mac menu bar.   So step one is — write the code to create a status item and put it in the menu bar.  I’ll start with a dummy menu — and I find some sample code which does just that.  In the applicationDidFinishLaunching method of my App Delegate, I create the menu to attach to the status item.  

func applicationDidFinishLaunching(_ notification: Notification) {
NSApp.mainMenu = NSMenu()

let mb = NSApp.mainMenu!.addSubmenu(withTitle:
NSLocalizedString("ClickSliver", tableName: "Menu", value: "ClickSliver", comment:"Application name")
)

mb += R0MenuItem(title: NSLocalizedString("About ClickSliver", tableName: "Menu",
             value: "About ClickSliver", comment: "about clicksliver"), keyEquivalent: "n") {_ in 
AboutWindowController.open()

The code shown above won’t make sense yet — I’ll explain it in the next post — but lets assume that the net effect is that the variable mb contains the desired menu.  The next step, then, is to create the status item and attach the menu.   The code looks like:

 let sb = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
let im = NSImage(named: NSImage.Name("AppIcon"))

let x = NSStatusBar.system.thickness - 4
sb.image = im?.resize(withSize: NSSize(width: x, height: x))

sb.menu = mb
sb.isVisible = true

The middle two lines resize the AppIcon to fit in the status item — the resize method comes from R0Kit.  One can see the behavior by just doing 

 sb.image = im

instead of those two middle lines (the icon will be big — but the behavior is the same).  It seems simple enough:

  • create the status item
  • get the image
  • set the image
  • set the menu
  • make it visible

Nothing happens.

Actually, if I run it again and watch closely, I see the icon appear on the menu bar, then disappear.  Definitely.  It appears, then disappears.

Perhaps it needs to be explicitly enabled.  Maybe I need to add 

sb.isEnabled = true

Nope.  No difference in behavior.  I literally copied this code from the sample — how could it not work?

Well, technically I didn’t copy the code, I changed the variable names.  Should that matter?  Does it matter what the variable names are?

This is where most of my programming time goes: matching my mental model of what is going on, and what things matter, with what is actually going on and what actually matters.   I’m pretty sure that the names of the variables don’t matter.   But what if they did?

Nah!  Those of you who have seen this before, and know what is going on can probably spot the error immediately.  It turns out I not only changed the name, but I had to add a “let” in front of the first line in order to create “sb” as a variable — otherwise it wouldn’t even compile.  You see, the sample code was using an instance variable — so it didn’t need a let.  But I didn’t have such an instance variable, so I added the let, and ran the code.  Since the variable is a local variable, it gets garbage collected when the method ends, and when the status item gets garbage collected, it gets removed from the menu bar.  Hence the appear/disappear behavior.

OK.  Explained that way, it makes sense.  Presumably that means if I create a window with a view, any subviews that I add to the view will disappear unless I store them all in persistent instance variables.  Actually, no.  If I create subviews and attach them to a view, then if I retain the view, all the subviews will not be garbage collected — even if I only used local variable references to create them.

Hence my cognitive dissonance.  It looks to me like a status item is kind of like a subview in the menu bar — and as long as the menu bar hasn’t been garbage collected, its subviews won’t be garbage collected either.  Except, apparently, that’s not how it works for the menu bar.  Incidentally, it is how it works for the menus.   Note that mb is a local variable containing the menu.  I attach the menu to the status item, then the local variable containing the menu goes out of scope.  But as long as the status item hangs around, the menu hangs around.

So, when creating UI elements, they will not be garbage collected if they are attached to other UI elements — unless they are status items — in which case they will be garbage collected even if they are attached to the menu bar.

The solution then is, I create an instance variable in my AppDelegate, and stuff sb into that (although the code shown below needs to be in the reverse order in order to work.  Why?  Well, I need to terminate the method in order enter the class scope in order to create the instance variable. )

statusBarItem = sb
}

var statusBarItem : NSStatusItem?

And now the status item remains in the menubar.

I skipped over:

  • creating the menu — that looked weird
  • resizing the image
  • defining the AppDelegate
  • writing the main function

All these will be covered in the next few posts.  But I wanted to explain why I’ll be talking so much more about things that didn’t work — and in particular, the thing that didn’t work which started this whole train of thought.

 

Fixing the file header

First thing that happens when I create a Swift file is I get something that looks like this:

File Header

The file gets created with a comment that names the file and the project.   But if I’m using Xcode (or AppCode), and probably any other editor, the file name and project are in the title bar of the editor window.  In this example, R0Kit and Menu.   Including these as comments seems to be entirely superfluous.  The first few times, I manually delete these extra lines every time I create a file.  But the right thing to do is fix it so that these lines never get inserted in the first place.

I’m indebted to Keith Harrison for explaining how to do this (here: https://useyourloaf.com/blog/changing-xcode-header-comment/ ).  The magic phrase is “Xcode text macro”.  In my case that means creating the file called ~/Library/Developer/Xcode/UserData/IDETemplateMacros.plist with the following content:

<?xml version=”1.0″ encoding=”UTF-8″?>

<!DOCTYPE plist PUBLIC “-//Apple//DTD PLIST 1.0//EN” “http://www.apple.com/DTDs/PropertyList-1.0.dtd”&gt;

<plist version=”1.0″>

<dict>

        <key>FILEHEADER</key>

        <string>  Created by r0ml on ___DATE___

//  ___COPYRIGHT___</string>

</dict>

</plist>

 

And that’s it!

Well, almost.  Now when I create a Swift file, I get

// Created by r0ml on 4/16/18.
// Copyright © 2018 Semasiology. All rights reserved.

import Foundation

but what I really want is to import R0Kit.  Here’s my solution:

The definition of the file template is at 

/Applications/Xcode.app/Contents/Developer/Library/Xcode/Templates/File\ Templates/Source/Swift\ File.xctemplate

I don’t want to modify my Xcode bundle, because that will get over-written every tim I upgrade.   I can duplicate that template to my home directory, but then the sheet for selecting file types will list “Swift File” twice.  I don’t know how to change or remove the existing definition, but I can add a new one.  I copy the Xcode template to my home directory, renaming it to “R0Swift File”  (yes, R0 is my prefix).

ditto /Applications/Xcode.app/Contents/Developer/Library/Xcode/Templates/File\ Templates/Source/Swift\ File.xctemplate ~/Library/Developer/Xcode/Templates/File\ Templates/Source/R0Swift\ File.xctemplate

Then, edit the file named ___FILEBASENAME___.swift , replacing Foundation with R0Kit .  Now when I create a new file, I see

New File

and if I select R0Swift File, my file looks like 

// Created by r0ml on 4/16/18.
// Copyright © 2018 Semasiology. All rights reserved.

import R0Kit

And THAT’S it!

Icon

First things first.

ClickSliver needs an icon.

After creating an Xcode project, pretty much the first thing one needs is to create an application icon.

I had resolved to try to make things code rather than data as much as possible — so I had been trial’ing PaintCode — an app which takes vector graphics and generates code therefrom.  It has very rudimentary editing capabilities; the idea is to import some graphics and then spit out code.

It turns out that Graphic.app (from Picta) — a vector drawing app — has a hidden feature:  under the Edit menu, there is a Copy As… submenu which contains “Core Graphics Code”.  Yes, that’s right.  Draw something, select it, pick “Copy As Core Graphics Code” and paste into Xcode.  Don’t look for this feature under “Export As…” or “Save As…”.   Picta also has an iOS version of the app — I can use the iPad as a design tool.  I decide that’s the way to go.

So, armed with my new copy of Graphic, I

  • create a 1024×1024 document,
  • take a large C and S (for Click Sliver) in Bookman Old Style,
  • pick Modify > Convert Text to Outlines,
  • reposition the letters for make them “iconic”,
  • select both letters,
  • pick the first “Combine Paths” option (Union) on the Properties Window: PathTools
  • apply a gradient to the resulting shape,
  • and, voila!

ClickSliver

I have an icon.

Well, almost.  I can export my Graphic document as a png, but I need to convert it to an icon by generating a bunch of different sizes.  There are lots of apps for that.  I settle on Asset Catalog Creator.  Drop my png into the app, pick “Mac App Icon” and export an asset catalog containing my appicon asset which I can drag into my Xcode project.

Now I have an icon.

I go ahead and “Copy As Core Graphics Code” and paste into Xcode.  The Preferences… in Graphic let one select Mac or iOS, Swift or Objective-C.  (Strangely, the preference is under “import/export”, but the actual menu item to do it is not.)  

NewImage

I get about 200 lines of Swift code.

I’m not ready for this yet, but when I want to create some effect or animation in which I morph this image, I’ll have a place to start.

ClickSliver

Time to learn more about macos programming.  In order to do so, rather than work through a bunch of small tutorial-sized projects, I’m going to pick a large project that will have lots of interesting nooks and crannies to learn about.  The project is a reboot of QuickSilver.   QuickSilver is Mac utility which was written about fifteen years ago.  Eventually it got “open sourced”.  There’s a lot of Carbon APIs still in use, and lots of other deprecated APIs.  QuickSilver is like LaunchBar or Alfred — a productivity app to browse, launch, and execute commands with only a few keystrokes.  If I were interested in productivity, I would get one of those apps.  But this is about learning to program — so I’m going to write one.

Why QuickSilver?  I remember it had interesting visual effects, and graphics programming / animation is high on my list of things to learn.  The objective is to expose all the functionality on the machine with a few keystrokes — so it is an opportunity to explore widely.  Also, the original architecture was plugin based — which made sense before it was an open source project — and one needed an interface to allow plugins to be developed separately.  In an open source project, pull requests take the place of plugins.   So I’m going to try to have similar functionality with a completely different design.  That way, rather than start with the existing codebase and trying to modify it, I’m going to start with a blank sheet of paper (or blank screen, I should say) and work my way up.

Here are some of the design objectives:

  1. No plugins.  All the functionality is built in.
  2. Preserve and enhance all the visual effects.  And add new ones.
  3. Replace data with code.  Because of the plugin architecture, a lot of the behavior of QuickSilver was driven by plists.
  4. Swift, not Objective-C.  Learn the “swiftish” way of doing things.  Avoid all those constructs that used “perform:” and “respondsToSelector:”
  5. Golf.  Strive for conciseness.  If, after trial and error, I get something to work, spend the time to remove all the unnecessary steps.
  6. No third party libraries.  This is about learning — so if there is a feature that needs to be implemented, I need to figure out how to implement it — not link in some library written by somebody else who has learned how to do it.  If there is a macos provided library that implements the feature, I can use that.  Often, just finding the macos library is hard enough — never mind looking elsewhere.
  7. Eye on the future.  Avoid old ways of doing things.  Learn new ways of doing things.
  8. Build my toolkit.  Extract those bits of learning which I might use in future projects into a single framework (R0Kit).  This project will be the R0Kit framework and the app itself.

The project is called ClickSliver to evoke the debt to QuickSilver.  It is an app to use keystrokes more, and clicks less; to make clicks be a sliver of the user’s activity.