rentzsch.tumblr.com

Month

March 2013

2 posts

My Mother Tries an iPhone

My mother tried an iPhone for the first time. Here’s my Hallway Usability Testing report.

Slide to Unlock

Easy Start. Knew to take her finger, touch the “slider” and move it to the right all the way. The instructional text and appearance of a groove that affords sliding the slider along it win the day.

Problem occurred during release. She’s being very deliberate with her actions, which caused an issue here and later. She’s thinking about what she’s seeing and her next action, which causes a delay: she didn’t just quickly lift her finger from the screen to complete the unlock.

Instead, she unintentionally let her finger slide to the left a bit while waiting for the iPhone to do something (she’s used to computers also taking a while to do something, so it may not just be her being tentative but also her waiting to see if the machine needs to “catch up” to her actions” (a sadly common occurrence)).

When nothing happened, she lifted her finger from the screen only to watch the slider spring back to its original virtual resting position.

She tried again, only more slowly since the machine wasn’t operating as expected, so perhaps she has to do things even more slowly. Same result.

I hinted at her to try lifting her finger faster, and then she was able to succeed.

Suggestion: Slide to Unlock would benefit by taking into account time spent with the finger down on the unlock position and/or additional slop as to what’s considered a valid unlock request.

Initial Screen

Seemed fine enough. No problem thinking of icons as touchable buttons. No expressed puzzlement of the two tiers of icons on the top and another set of icons along the bottom (the dock).

I took the liberty of moving all the apps I knew she wouldn’t use to the second page and even the Utility apps (Voice Memos, Calculator, etc) out of their confusing group and onto the normal page.

She mentioned the Clock is wrong. She thought it odd since the other, textual clocks were correct (the first on the Slide to Unlock screen and the uppermost status bar clock). The Calendar app’s date is correct, so she has no reason to believe that the Clock’s icon just happens to be static. I never noticed this inconsistency myself.

Suggestion: while I’d probably be annoyed at it (especially with the second hand), the Clock app’s icon should be live and accurate.

In the same vein, she noted the Weather app must be showing the inside temperature (72°) since it’s currently in the mid-30°s outside when she was looking at it.

Suggestion: the Weather app should display the current location’s weather/temperature or a new, different static icon that’s clear isn’t trying to represent current conditions.

I told her to press the Mail icon. Again she got snagged by her deliberate movements, and accidentally triggered wiggle (icon rearrangement) mode. Apps wouldn’t launch and she didn’t know how to fix it.

Suggestion: wiggle mode is such a bad idea. Better to have a dedicated app to arrange Springboard pages/groups.

Safari

Like many (most?) people, she doesn’t understand URLs, so I typed in Google from her and then puzzled how to navigate to Google News (google.com now hides it under their burger button). She used Google News on her Mac, so I wanted to show her the same information is available on the iPhone. I wanted her to see something that offered the safety of familiarity.

She was immediately thwarted when tapping a headline link opened the page in a new tab. Her Google News page was pushed away and the Los Angeles Times’ page came frontmost. Because it was a new page, the back button wasn’t enabled. She was at a loss at how to go back, not noticing the nondescript tabs button that held the key to her return.

Suggestion: Safari should initially operate in a simpler single-page-at-a-time mode, which is how nonadvanced users use the web or do a better job of visually indicating and managing multiple tabs. Granted, this is a hard design problem.

Miscellaneous

When she opened the Weather app, she wondered whether it was showing the upcoming weather or historical weather.

I showed her Google Maps and had her type in a old address. She informed me that her fingers were too large to possibly hit the little keyboard. I assured her to try anyway, and after wrestling with finger placement to compensate for her nails (see below) she was able to input an address and switch back to the alphabetic keyboard without prompting (I prompted her on how to switch to the numeric keyboard).

I sent her a link via iMessage to look at a page on Amazon. The link was long, and wrapped extensively. For some reason tapping anywhere on the link except the first line didn’t work to open the page. This bug reduced her confidence that blue underlined text is universally tappable on iPhones.

Physicality

I told her a few times to press the home button. She was confused as to which button it was. I considered calling it the Rounded Rectangle Button, but that seemed excessively wordy. I wish Apple would have used a house icon (something like ⌂) instead of the current quite-rounded rectangle. The downside is a house icon suggests an orientation, and could be annoying if your primary use is in a different orientation.

She tried to use her fingernails to touch things, which are long enough that they do a good job of keeping her fingertips away from being detected by the capacitive touch screen. Here a resistive touch screen actually has an edge (not that I’d ever go back).

Mar 17, 201340 notes
#iphone #usablity-test
Semantic Version Branches for Submodules

When you create a new git repo, you get a master branch for free. That’s fine in many cases.

Lately I’ve been using a different branching system for code that’s meant to be used as a library (perchance a git submodule or a subtree).

Here’s how it works:

  • Name the branch based on the code’s Semantic Version. Examples:

    • Brand-new code base that’s still being written: name the branch semver-0.x.

      The x in the branch name denotes a variable, so this branch could house v0.0, v0.1, v0.1.42 and so forth.

      You probably could get away with a simple v0.x or even 0.x, but I think the explicitness is helpful indicator for humans and paves the way for machine recognition of semantic branch names.

    • First version of the code base with a stable public API: semver-1.x.

    • Upcoming not-yet-stable second version of code base: semver-2.x-beta

      Initially I went with semver-2.x-unstable, but I found myself confused when I came back to the project later. Does that mean the code is unstable and crashy? Or just the API is subject to change? Both?

      Whereas with beta it was instantly clear to me this branch holds the work-in-progress for version 2.

    • Second version of the code base with a stable public API: semver-2.x.

  • There’s no master branch.

    “master” is too generic a concept and it’s an attractive nuisance for submodules. Too often I look at a project, see lots of branches and no way to immediately tell what’s going on, so I reach for the familiar: the master branch.

    Learning from my own behavior, I see it’s important to take away that crutch and force perspective library consumers to understand the project’s current state and semver branch structure. Fortunately GitHub makes it easy to select a non-master default branch, so it should be mostly transparent to your users.

    Even better, switching the default branch gives the library’s author the ability to easy start migrating new users to newer versions of your library (while retaining backwards compatibility with your existing users). For example, update your default branch from semver-1.x to semver-2.x.

It’s best if you start off immediately using semver branching, but it’s straightforward to migrate to them even after you publicly release your library using the old fuddy-duddy master branch model. In that case you’ll have to weigh the conceptual clarity and usability advantages of deleting the master branch against breaking the assumed-master branch of your clients.

Mar 16, 20134 notes
#git #github #semver #branch #sub-modules #sub-tree

January 2013

2 posts

NS: Poor Man's Namespacing for Objective-C

Objective-C unfortunately lacks namespaces. This leads to both compile-time woes and runtime woes. You may have been witness to this runtime warning:

Class SUUpdater is implemented in both MyApp.app/Contents/Frameworks/Sparke and MyAppPlugin. One of the two will be used. Which one is undefined.

While Kyle Sluder has been leading the charge towards getting the feature as a first-class citizen in Objective-C, I’ve been successfully using a nine-line preprocessor hack for a few years now that effectively works-around the problem. I call it NS:

#ifndef NS
    #ifdef NS_NAMESPACE
        #define JRNS_CONCAT_TOKENS(a,b) a##_##b
        #define JRNS_EVALUATE(a,b) JRNS_CONCAT_TOKENS(a,b)
        #define NS(original_name) JRNS_EVALUATE(NS_NAMESPACE, original_name)
    #else
        #define NS(original_name) original_name
    #endif
#endif

Instead of writing your classes like so:

@interface Person : NSObject
@property(strong) NSString *name;
@end

Write it like so:

@interface NS(Person) : NSObject
@property(strong) NSString *name;
@end
#define Person NS(Person)

Then use the class like you normally would:

Person *p = [Person new];

Now your code supports compile-time prefix insertion. If you don’t do anything else, your classes will remain unprefixed.

However, you can now prefix all participating classes with one easy flick of a preprocessor definition:

-D NS_NAMESPACE=MyFramework

That turns the code above into:

MyFramework_Person *p = [MyFramework_Person new];

With judicious targeting of your -D NS_NAMESPACE= use, this should enable you to work-around class collisions at both compile-time and runtime.

P. S. I recognize it’s probably a bad idea for me to squat on something as Apple-generic as NS, but I don’t care.

Update: Uli Kusterer points out this hack doesn’t cover XIBs, since they reference classes by name and avoid the preprocessor’s reach.

I’ve only used this technique with pure code libraries, so that limitation hasn’t bitten me.

Jan 17, 201318 notes
#cocoa #ns
Nginx Homebrew Support

If you run nginx on OS X via Homebrew, you’ll probably be interested in my nginx-homebrew-support.

Jan 7, 20133 notes
#nginx #homebrew #github

December 2012

1 post

enum => NSString

I wish it were possible in C to access an enum’s name in string form given a type and its integer value. It’s great for logging/debugging.

Until that day arrives, here’s a small Ruby script which automates the process of vending an NSString given typed enum value:

def NSStringFromEnum(input)
  inputArray = input.lines.collect
  typeNameLine = inputArray[-1]
  typeName = typeNameLine.match(/(\w+);/)[1]

  output = "NSString* NSStringFrom#{typeName}(#{typeName} value) {\n    switch (value) {\n";

  constantLines = inputArray[1..-2]
  constantLines.each {|constantLine|
    constantName = constantLine.match(/(\w+)/)[1]
    output += "        case #{constantName}:\n";
    output += "            return @\"#{constantName}\";\n";
    output += "            break;\n";
  }
  output += "        default:\n";
  output += "            return [NSString stringWithFormat:@\"<unknown #{typeName}: %d>\", value];\n";

  output + "    }\n}"
end

Example input:

typedef enum {
    JRStream_Disconnected,
    JRStream_Connecting,
    JRStream_Connected,
    JRStream_Disconnecting
}   JRStreamState;

Example output:

NSString* NSStringFromJRStreamState(JRStreamState value) {
    switch (value) {
        case JRStream_Disconnected:
            return @"JRStream_Disconnected";
            break;
        case JRStream_Connecting:
            return @"JRStream_Connecting";
            break;
        case JRStream_Connected:
            return @"JRStream_Connected";
            break;
        case JRStream_Disconnecting:
            return @"JRStream_Disconnecting";
            break;
        default:
            return [NSString stringWithFormat:@"<unknown JRStreamState: %d>", value];
    }
}

Update: Guess I should mention how I use it. I put this script into an Automator Service. Apparently you can also use TextExpander and probably Typinator as well. Finally, Benedict Cohen wrote a C macro version that looks promising.

Update: I extended Benedict Cohen’s excellent work and I’m pleased to offer JREnum.

Dec 8, 20127 notes

November 2012

4 posts

Xcode Comment Alignment

I finally put my finger on something that’s been bugging me about Xcode’s comment indentation since forever.

For short comments, I typically use //:

// This is a
// short comment.

That’s fine unless comments grow long:

// Lengthy essay
// about the high-level
// overview of the code
// perhaps with parameter-level
// machine-readable API documentation.

Having to prefix each of my lines with an // gets on my nerves, so I switch to classic C comment style:

/*
Lengthy essay
about the high-level
overview of the code
perhaps with parameter-level
machine-readable API documentation.
*/

However, Xcode wants to format it like this:

/*
 Lengthy essay
 about the high-level
 overview of the code
 perhaps with parameter-level
 machine-readable API documentation.
 */

Note each line after the initial comment opening is prefixed by one space.

I think this is because Xcode wants to help you line up the asterisks, like so:

/*
 * Lengthy essay
 * about the high-level
 * overview of the code
 * perhaps with parameter-level
 * machine-readable API documentation.
 */

However the entire reason I use classic C comments is when I don’t want to have to bother with per-line prefixes.

For now I just manually delete the extraneous spaces, but I wish I could easily suppress this behavior.

Nov 28, 20124 notes
#xcode
Functional Cocoa Collections

Tony Arnold / @tonyarnold:

Alrighty guys: best map/reduce/filter categories for NSArray/NSSet/etc — what do you have/use? /cc @atnan @orj @rob_rix

I made a brief aside about this in the latest Edge Cases episode about blocks, so Tony’s question is topical. Here’s the ones I know about, roughly in order of my first-glace preference:

  • RXCollections
  • HBCollections
  • MACollectionUtilities
  • BlocksKit
  • Underscore.m

I haven’t used any of these in anger yet (but have been happy with Underscore.js).

Nov 25, 20128 notes
mogenerator 1.27

What’s New:

  • [NEW] You can now pass .xcdatamodeld paths to mogenerator. mogenerator will look inside the directory, read its hidden .xccurrentversion file and use the “current” .xcdatamodel file. (Alexander Zats)

  • [NEW] Replaced mogenerator’s previous testing system (the test mule) with a new Rakefile-based system that eases building & testing from the current source tree and tests both MRC and ARC. (rentzsch)

  • [NEW] Property declarations generated from attributes can now be qualified as readonly by adding a mogenerator.readonly to an attribute’s userinfo. (crispinb)

  • [NEW] --configuration option that limits generation to the specified configuration. (Sixten Otto)

  • [NEW] --base-class-import option for fine-grained control of base class import statements. (David Aspinall)

  • [CHANGE] Optimized keyPathsForValuesAffectingValueForKey: generated code (returns after first match). (Sean M)

  • [CHANGE] Add default private class extension to human source template. (Jonas Schnelli)

  • [FIX] Align generated code’s pointer asterisks more consistently. (Tony Arnold)

  • [FIX] Missing import when using mogenerator.customBaseClass entity userinfo key. (Thomas Guthrie)

  • [FIX] Handle case in generated fetch request wrapper machine code when predicate variables are repeated. (Sergei Winitzki)

  • [FIX] Explicitly set mogenerator project’s deployment target to 10.6 to avoid segfaulting on 10.8 for some reason. issue 121 (reported by Sixten Otto, diagnosed by Florian Bürger)

  • [FIX] Cast to unsigned in machine source to avoid clang format string warning. (rentzsch)

  • [FIX] Don’t attempt to report errors through -[NSApp reportError:] in generated machine source unless targeting AppKit. (rentzsch)

  • [WORKAROUND] Recent versions of Xcode use an empty string to mark entities that do not have a custom subclass. (Matthias Bauch)

  • [CHANGE] make_installer.command: assume PackageMaker now lives in /Applications/Utilities. (rentzsch)

Nov 14, 20122 notes
#mogenerator
Simple Code

damien katz / @damienkatz:

Invented a design pattern I call “simple code”. When I need the code to do something new I “modify it”.

I’ve wasted too much brain power over the years over-engineering software, bracing my designs for scenarios that fail to ever materialize.

My suggestion, at least for the first cut: do the simplest thing that could possibly work.

If you’re like me, that engages your intellect towards simple solutions instead of over-engineering against invisible foes.

Nov 13, 20124 notes

October 2012

7 posts

OS X 10.8 Apple Software RAID

After my poor experience with Software RAID on Mac OS X 10.7.3, I’m pleased to report that software RAID on OS X 10.8 seems improved over 10.7.

Enabling Software RAID does have some tradeoffs:

  1. You lose OS X’s Recovery Partition. Internet Recovery should still work and you could always build your own Recovery Disk.

  2. FileVault isn’t supported.

For my server purposes, I don’t care about either of these limitations, so I’m happy that OS X Software RAID is realistic option again (the “Repair Disk” button actually works).

Here’s how I set up my new 2x1TB Mac mini Server:

  1. SuperDuper the Mini’s boot drive (“Server HD”) onto an external drive.

  2. Boot from the external drive.

  3. Repartition both drives into three slices each: 50GB Boot, 50GB Boot Backup and the rest Data.

  4. Create three new RAID 1 (Mirroring) sets encompassing those slices, respectively.

  5. SuperDuper the external drive back onto the Boot RAID volume and boot off it.

Here’s my set-up as it appears in Disk Utility:

This set-up enables me to perform system software updates with a bootable backup safety-net in place.

I’m backing up that Data partition offsite, so I’m not backing it up locally on the Mini itself (I’m just SuperDuper’ing the Boot volume to the Boot Backup volume nightly).

Oct 30, 20125 notes
#sysadmin #raid
My Amazon Ebook Insurance Policy

When iTunes Music Store first came out I studied it and decided to buy music off it. Not because I trusted Apple, but because I had a spare Mac that I could use as a DRM lifeboat.

I copied my purchases as I made them to the old Mac. The Mac was also disconnected from the Internet after its initial authorization. Finally, I had software in place to extract the audio I purchased even if Apple pulled the DRM football from me just as I tried to kick it.

Fortunately Apple’s iTMS DRM dog never barked. But after today’s Amazon Ebook controversy I decided it’s time to share my similar Amazon Ebook Insurance Policy:

  1. Have the Kindle’s network turned off most of the time. I do this to conserve battery on my Kindle 3, but it has the nice side-effect that Amazon can’t remotely delete content at its whim.

  2. Buy Chronosync and set it up to copy purchased Ebooks to my Mac whenever I connect+charge my Kindle. That way I can recover even from an unexpected remote wipe from Amazon.

  3. Use (and hopefully donate to) Calibre and then install plugins to strip Amazon’s DRM from your purchased Ebook files. Calibre can then export your .azw and .azw1 files to unencumbered .mobi and .epub formats.

Update: Zachary West offers specific steps on converting Amazon to ePub using Calibre.

Oct 22, 201230 notes
#amazon #ebook
Xcode 4 Splits

One of the features Apple took away in Xcode 4 was splits.

rentzsch / @rentzsch:

predicted headline feature of Xcode 5: splits

But it turns out you can clumsily emulate them with Xcode 4’s Assistant Editors:

kongtomorrow / @kongtomorrow:

@rentzsch like text splits? already there. View > Assistant Editor > Add Assistant Editor (and various shortcuts)

rentzsch / @rentzsch:

@kongtomorrow that only works when you have an assistant editor showing. Very rarely that I do. Need it for standard editor

Troy Gaul / @tgaul:

@rentzsch @kongtomorrow You can open the same file in the assistant editor and then stack the editors vertically in your window.

kongtomorrow / @kongtomorrow:

@tgaul @rentzsch ooh, thanks, thought that was there somewhere. View > Assistant Editor > All Editors Stacked Vertically

rentzsch / @rentzsch:

@kongtomorrow @tgaul ah that’s clumsy, but it will work when I’m desperate. Thanks!

Oct 19, 20124 notes
#xcode
Wherein I Write Apple's Technote About OpenSSL on OS X For Them

Technical Note TNNaN: OpenSSL on OS X

Long story short: we screwed up when we included OpenSSL (libcrypto) in OS X in the first place.

(We learned our lesson and didn’t repeat the mistake with iOS.)

Now there’s some transitionin’ to do.

Recommendations

  • New Code Should Use SecTransform. SecTransform is cool: it’s high-level, rather declarative, fast and even leverages GCD. It solves OpenSSL’s issues described below in the Backgrounder section.

  • Code That Uses OpenSSL Just for Hashing Should Switch to CommonCrypto. CommonCrypto provides a thin OpenSSL compatibility shim for common cryptographic message digests. It’s called COMMON_DIGEST_FOR_OPENSSL and lives in CommonDigest.h.

  • Apps That Need OpenSSL Should Include Their Own Copy. You should stop using the system’s supplied libcrypto.dylib, build your own and link it into your app.

    We know that’s a pain, but this project should help you out.

    Add -isystem "$(SRCROOT)/openssl-1.0.1c/include" to OTHER_C_FLAGS to pick up your project’s local OpenSSL headers instead of the system’s headers (which are outdated and throb with deprecation attributes).

Backgrounder

The crux of the problem is OpenSSL doesn’t offer API compatibility between versions.

We’d love to ship updated versions of OpenSSL, but there’s only two feasible routes, both of which are seriously problematic:

  • Apps link to /usr/lib/libcrypto.dylib symlink. In theory we should be able to always point that to the latest version of libcrypto.dylib and apps get free security + capability updates.

    In practice we’d break shipping apps since the APIs are different. So it’s kinda stuck. (Notice we’re still shipping v0.9.8r when v1.0.1c is the latest.)

  • Apps link to a specific version of libcrypto. For example: /usr/lib/libcrypto.0.9.8.dylib.

    Now your app doesn’t get security + capability updates for free. When there’s a security hole, it’s now our job to backport the fixes in the latest OpenSSL into the old-ass version your app is linked against.

    Nobody wins in this case.

Ideally the OpenSSL project would do a better job at API compatibility, but it’s really not in their unixy source-code-oriented worldview. Sure we could get big into the project to try to improve it, but we’d rather put the resources into making OS X rock harder.

Both SecTransform and lowly CommonCrypto offer API compatibility, allowing us to add functionality and fix security problems even in shipping apps, which is awesome for users.

Oct 16, 201222 notes
#openssl
Working Around Apple's Deprecation of OpenSSL

Apple has started deprecating OpenSSL on Mac OS X 10.7 SDK and later. I guess they’d prefer you use SecTransform or something. Unfortunately SecTransform isn’t on iOS yet and even it were, some of us have codebases that aren’t Apple-platform exclusive. Feels kind of like a dick move to me.

Anyway, I continued to use OpenSSL in my code — I just stuck a big fat

#pragma clang diagnostic ignored "-Wdeprecated-declarations"

on the top of my OpenSSL-using source files.

Unfortunately that turns off deprecation warnings for the entire file. I tried downloading OpenSSL (/usr/include/openssl/opensslv.h’s OPENSSL_VERSION_TEXT indicates you want v0.9.8r) and using their header files directly but was stumped:

rentzsch / @rentzsch:

lazytwitter: how do I get Xcode to prefer my local openssl header dir to the SDK’s (that’s littered with with deprecation attributes)?

rentzsch / @rentzsch:

(setting ALWAYS_SEARCH_USER_PATHS, HEADER_SEARCH_PATHS & USER_HEADER_SEARCH_PATHS doesn’t seem to be enough or I’m doing it wrong)

Fortunately Christopher Lloyd answered my query:

Christopher Lloyd / @cjwl:

@rentzsch -isystem <myincludedir> added using other c flags should do it

I just tried it and it works. Thanks again, Christopher!

Update: I take back my “feels kind of like a dick move” statement. There are good reasons for Apple to deprecate OpenSSL, I just wish they wrote a Technote about it. So I wrote one for them as penance. Apologies, Apple peeps.

Oct 15, 20124 notes
#openssl
Lich: simple, general, human-sympathetic binary data format

Lich is my new mostly-human-readable binary data format. Check it out if you like bencode or need a space-efficient JSON.

Oct 4, 20122 notes
#lich
Apple Maps and OpenStreetMaps

Buzz Andersen / @buzz:

Second vintage tactic I wish Apple used with Maps: align Apple resources (a la WebKit) with a promising open source effort (OpenStreetMaps).

I agree with Buzz here: feels like a missed opportunity for Apple and indeed humanity.

Apple uses OpenStreetMaps in iPhoto for iOS, so it’s not like they never heard of the project. I can only surmise somehow OpenStreetMaps wasn’t a fit (or Apple knew it was about to pour a billion dollars into a project and didn’t want to share the project’s fruits with competitors).

See also: llvm and Facebook’s Open Compute Project.

Oct 2, 20123 notes

June 2012

2 posts

Edge Cases Podcast

I started a new Apple-developer-related podcast with my friend Andrew Pontious called Edge Cases.

To kick off the show we’re going to catch up with our backlog and post an episode each day this week.

Click here to subscribe to the show.

You can also follow the show on twitter: @edgecasesshow.

Special thanks to Victoria Wang for the website design and coding.

Jun 18, 20121 note
#edgecases
Mac App Store vs Buying Direct

Since the launch of the Mac App Store, a common question potential customers ask developers is “Should I buy your app directly or through the Mac App Store?”

Developers have been remarkably cagey, mostly replying with the non-answer “choose whichever is better for you”.

Fortunately Apple now only accepts sandboxed Mac apps, clarifying the situation: customers should buy Mac apps directly unless there’s a good reason not to.

Here are some reasons why it’s preferable to buy non-sandboxed apps directly from developers:

  • Better App User Experience: Non-sandboxed apps can auto-navigate the user to correct folders in Open/Save panels, run user-written AppleScripts, control iTunes* and install PDF Services automatically.

  • More features: Non-sandboxed BBEdit can directly edit files that require administrator privileges, non-sandboxed OmniFocus can automatically determine the selected document in the Finder when creating a clipping. There’s lots more.

  • Better Data Integrity: Document-based Core Data apps are incompatible with Sandboxing. One work-around is to disable disk-based journaling, increasing the risk of data corruption. It appears sandboxed Core Data Mac apps will need to switch to packages for a long-term solution, changing file formats for no good reason (and packages are less convenient for data sharing than flat files).

  • More and Faster Updates: Different developers follow different update schedules, but typically direct apps get updated more often and with less latency than Mac App Store apps. Some developers intentionally throttle down their direct update schedule to match Apple’s delays and avoid customer confusion, but that’s the developer’s decision.

  • Less Risk of Losing Your Software Investments: I bought Divvy via the Mac App Store. Unfortunately Divvy relies on Apple’s Accessibility APIs, which aren’t allowed for sandboxed apps. That means aside from minor bug fixes, I will no longer receive updates for the application I purchased.

    Some developers are going out of their way to allow seamless cross-grading from Mac App Store versions of their apps to direct apps, which is commendable and helps alleviate somewhat the situation Apple has created.

    Sandboxing is just the latest App Store rule change, I’m sure there’s more to come. All things being equal, it’s safer to buy directly instead of being cut off from your own software based on an arbitrary Apple policy change.

  • More Money Goes to the Developer: For a $10 application, only $7 goes to the developer when you buy it through the Mac App Store. For a direct purchase, it’s more on the order of $9.

    The fact more developers weren’t encouraging customers to buy directly is a testament to how much Mac developers care about User Experience — they’re willing to forego the extra profit of selling direct to make things easier for their customers.

However, as alluded to above, there are downsides to buying directly. Buying through the Mac App Store offers these benefits:

  • Better Purchasing Experience: Apple probably already has the customer’s credit card on file. No .zip to unpackage and drag into the right place. It Just Works.

  • Better Maintenance Experience: Buy a new Mac, launch App Store.app and enter your Apple credentials. Ta-da, a list of the apps you purchased ready to reinstall. No trying to remember what apps you previously had installed, who wrote them, what the developer’s website is or what your license key was.

  • iCloud Access: Apple has decided only Mac App Store apps (and thus now sandboxed apps) can access iCloud. Fortunately this isn’t much of an issue since the smart folks at SmileOnMyMac have already demonstrated a work-around of having a “bridge” Mac App Store application which allows its directly-sold PDFPen to access iCloud as well.

  • Vetted By Apple: I don’t hold this benefit in very high regard, but it does add a safety net versus downloading and running arbitrary software off the web. For example, I’m looking forward to 10.8’s Gatekeeper as a way of limiting the software my mother downloads and attempts to run on her Mac (though this is apparently possible since 10.6).

The bottom line is that sandboxing has effectively collapsed the ambiguity and customers should now purchase their apps directly instead of through the Mac App Store.

* Update: Originally I wrote here “send Growl notifications”. I was corrected by Growl Project CEO Chris Forsythe that the version of Growl in the Mac App Store appears to now listen on a machine-local network port, which works around sandbox-disallowed Mach messaging.

Jun 4, 201244 notes
#appstore

April 2012

4 posts

Apps I Love: git-subtree

My thinking was fuzzy in regards to what’s wrong with git submodules. So I finally opened up an editor window and started typing how I think an Ideal Submodule System should work. I wound up with this:

  1. The subproject is copied into the super-project’s repo. At least a snapshot of it, if not the entire history. It’s a fact of life external resources have a habit of disappearing — this helps makes your project resilient.

  2. git clone foo is enough. None of this git clone --recursive foo or git submodule init && git submodule update business.

  3. Can easily pull subproject updates.

  4. Can easily push subproject updates.

  5. Can easily handle super-project branching.

  6. Super-project’s commits don’t wind up in subproject’s history.

  7. Subproject’s commits don’t wind up in super-project’s history.

Armed with clearer thinking, I reexamined my options and have settled on Avery Pennarun’s git-subtree.

Unfortunately its CLI UX is lacking (you have to specify the subproject’s entire remote repo URL each time you pull or push) and Avery hasn’t accepted pull requests or budged the project for a year. Fortunately Helmo forked Avery’s repo, added .gittrees support and push-all and pull-all subcommands, and generally seems to be keeping on top of pull requests and moving the project forward.

I just started using git-subtree, but so far it’s making my life better than before.

P.S. Here’s a short primer on using git-subtree.

Update: Hmm, I discovered and turned to Helmo’s fork after I got tired of reentering my remote repo URL each time I wanted to push or pull a subtree. Turns out Helmo’s fork is currently unstable — the tests don’t pass and there’s a definite bug in it when adding a repo as a subtree that doesn’t show up until you attempt push it back. So I recommend using Avery’s original repo until it’s fixed.

Update 2: I fixed the bug and tests are passing in my git-subtree fork. git subtree push-all away!

Apr 30, 20125 notes
#git #sub-modules #appsilove #subtree
Backup Chrome tabs with JSTalk

Sometimes Chrome has trouble restoring its windows and tabs upon launch (the windows and tabs open, but their content never loads). The worst thing about this bug is that it tends to happen only when I have large number of windows and tabs to restore.

I’ve added the following JSTalk script to my nightly backup routine. It writes out the names and URLs of all open tabs to a text file:

var chrome = [SBApplication application:'Google Chrome'];
var windows = SBElementArrayToJSArray([chrome windows]);
var output = '';
windows.forEach(function(window){
    var windowName = [window name];
    if (windowName != 'New Tab') {
        var tabs = SBElementArrayToJSArray([window tabs]);

        output += windowName + '\n';
        tabs.forEach(function(tab){
            output += '\t' + [tab title] + '\n';
            output += '\t' + [tab URL] + '\n\n';
        });
    }
});

Date.prototype.getShortWeekdayName = function(){
    return ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'][this.getDay()];
};
var tabsBackupFileName = 'tabs_' + (new Date()).getShortWeekdayName() + '.txt';
var tabsBackupFilePath = [@"~/Documents/backup/browser" stringByExpandingTildeInPath];
tabsBackupFilePath = [tabsBackupFilePath stringByAppendingPathComponent: tabsBackupFileName];

[[NSString stringWithString:output] writeToFile:tabsBackupFilePath atomically:NO encoding:NSUTF8StringEncoding error:nil];

function SBElementArrayToJSArray(sbArray) {
    var sbIdx = 0;
    var sbCount = [sbArray count];
    var result = [];
    for (; sbIdx < sbCount; sbIdx++) {
        result.push([sbArray objectAtIndex:sbIdx]);
    }
    return result;
}

It uses Cocoa’s Scripting Bridge to enable JSTalk to talk to Chrome via its AppleScript interface.

Apr 22, 20122 notes
#chrome #jstalk
mogenerator 1.26

What’s New:

  • [FIX] Missing space in transformable attribute codegen. issue 89 (Daniel Tull, Kris Markel, Whitney Young)

  • [NEW] mogenerator’s standard templates are now bundled into the mogenerator binary itself. This should solve the problem of templates growing out of sync with the intended version of mogenerator (exacerbated by the now-popular homebrew installer). You can still use your own templates with the --template-path and --template-group parameters. issue 79 (Ingvar Nedrebo, rentzsch).

  • [NEW] Support for per-entity custom base classes, set via mogenerator.customBaseClass key in the entity’s user info. (Trevor Squires)

  • [CHANGE] mogenerator installer no longer installs separate template files (but it won’t touch those already installed).

  • [CHANGE] mogenerator’s .pkg installer no longer includes Xmo’d since 1) Xmo’d doesn’t work with Xcode 4 yet and 2) Xcode.app now lives in /Applications, so the installer needs to get smarter to cope.

Apr 13, 20123 notes
#mogenerator
WWDC-less WWDC

Last year Victoria and I tried something different: visiting San Francisco during WWDC, but not actually attending WWDC. Here’s a quick rundown of our experience.

Pros:

  • Ticket Price. Buy yourself another MacBook Air.

  • Recordings: Since I didn’t spring for a ticket, there’s no guilt about missing a live session. Ever since Apple phased out after-session Q & A, the online videos tend to be better than the live sessions.

    Additionally, Apple has come a long way from the days where you’d get a box of DVDs in the mail many many months after the conference. The last couple of years Apple aggressively edited the recordings during WWDC itself and generally made them available 2-3 weeks after the conference.

  • Sleep Schedule: Wake up late sans guilt that you’re missing an Important Session. Dovetails nicely with late nights typical of WWDC.

  • Out of SOMA: There’s much less of a need to stay in SOMA since you won’t need to make those early-morning sessions. We stayed in a bed and breakfast in Pacific Heights and enjoyed many new establishments.

Cons:

  • Lessened Hallway: The best part of WWDC is the hallway. Since you can’t get past the Moscone lobby, such interaction opportunities are lessened. That said, we found great success in just hanging out outside the Moscone. We didn’t do it enough, but we immediately bumped into folks we knew and had several nice chats.

  • No Badge: A lot of parties require a WWDC badge to get in. Fortunately there are parties with specific invitation lists and undiscriminating bars.

  • Lab Time: I’m listing this one for completeness since friends tell me they’ve received valuable debugging and feedback in the labs. Unfortunately in all my years of WWDC, I’ve only managed to stump engineers assigned to the Lab, so I don’t use it any more with the exception of…

  • UI Design Lab: Finally, a serious con. I’ve gotten benefit out of this lab (when I remember to reserve a slot before they’re all booked up).

  • Stump the Experts: another serious con. This one’s always a blast.

Non-cons:

  • Apple Design Awards: I prefer to review the list of winners and runners-up instead of attending this event.

  • Lunchtime presentations: Worth going to when you’re there, but I wasn’t broken up about missing them.

  • Bash: The Bash is fun and good for mingling but there’s plenty of that already during WWDC.

Bottom line: we consider our WWDC-less WWDC a rousing success and plan to do it again.

Apr 10, 201211 notes
#wwdc

March 2012

1 post

The Business of Conferences

Mikeal Rogers:

You realize pretty fast that, if you do things a certain way, there is a lot of money to be made on events like this. It’s honestly not that hard to run huge profits. When you have this kind of demand, where tickets to the first NodeConf sold out in 10 minutes and tickets to JSConf sold out in one second, it’s hard not to make money. This is hard work and there’s nothing wrong with making money.

But, here’s the rub. I don’t want to go to the conference that is being run to make someone money. I don’t want to be a product. I want to attend, or speak, at a conference to build community. That’s what I get off on. I’ve got money, I make a good living, so I don’t need to run conferences that I wouldn’t like to attend myself.

Once you run a conference, you can’t look at conferences the same way again. For better and for worse.

Mar 9, 20122 notes
#c4 #conf #conferences

February 2012

2 posts

mogenerator 1.25

What’s New:

  • [NEW] Support for Xcode 4.3 and it’s relocation of momc into its bundle. Only supports /Applications/Xcode.app for now. (Matt McGlincy)

  • [CHANGE] Now generates size-specific scalar types (int16_t, int32_t, int64_t) instead of size-variable types (short, int, long long). bug 2 (Rob Rix)

  • [NEW] Can now generate NSFetchedResultsController creation code for to-many relationships (use --template-var frc=true). (Daniel Tull)

  • [DOC] Link to John Blanco’s Getting Started with Mogenerator.

Feb 17, 20123 notes
#mogenerator
Ease of Use is User Respect

Brad Larson / @bradlarson:

@cieslak People seem to think that making stuff easy to use is only for the benefit of stupid users. Expertise comes in different flavors.

chris cieslak / @cieslak:

@bradlarson The same “stupid users” who fix your car, or perform surgery on you, or teach your kid a foreign language. Drives me nuts.

Feb 8, 20128 notes

December 2011

3 posts

Klotzbach & Gray Discontinue December Hurricanes Prediction

via Russ Roberts:

Two top U.S. hurricane forecasters, famous across Deep South hurricane country, are quitting the practice of making a seasonal forecast in December because it doesn’t work.
William Gray and Phil Klotzbach say a look back shows their past 20 years of forecasts had no predictive value.

I love the intellectual honesty this must have taken. I wish practitioners of software development could look back and evaluate what doesn’t work, but that’s challenging from both a pragmatic and human-nature standpoint.

Dec 26, 20112 notes
#pretenseofknowledge
mogenerator 1.24

What’s New:

  • [FIX] Was incorrectly using -mutableSetValueForKey: for ordered relationships instead of -mutableOrderedSetValueForKey:. bug 75 (Martin Schürrer)

  • [NEW] Now generates Mike Ash-style constant structures for attributes, relationships and fetched properties. This allows you to write code like [obj valueForKey:PersonMOAttributes.age]. Tip: you’ll need to enable ARC generation (--template-var arc=true) if you’re using ARC. (Daniel Tull)

  • [NEW] --base-class-force option, for specifying a base class even if the model file’s entities don’t specify one. (Joe Carroll)

  • [NEW] PONSO: NSSet-based templates, improved inverse relationship logic and plug memory leak. (Tyrone Trevorrow)

  • [FIX] PONSO: Added import for super entity in machine headers. (Tyrone Trevorrow)

  • [FIX] Migrate from deprecated -[NSString initWithContentsOfFile:] and fix a MiscMerge warning where an immutable object was assigned to a mutable ivar. (Joshua Smith)

Dec 8, 2011
#mogenerator
Death and Resurrection of an SSD

Summary: SSDs live fast, die young, and pretend to be OK even while they’re dying. Don’t use one without awesome backups.

And sometimes, they come back from the dead.

※ ※ ※

On November 30 2010, I received my first SSD: a 240 GB OWC Mercury Extreme Pro.

On Thursday, November 10 2011, the drive “died”.

I claimed on twitter there was no warning: there weren’t any I/O errors logged to Console.app > All Messages (a standard technique to recognize a drive going bad). Looking back on it now, there were hints: Alfred corrupting its SQLite database, EyeTV losing its schedules and a recording, pbs (Pasteboard Server) crashing, mds throwing a hissy-fit (not uncommon) and finally a kernel panic (an uncommon event).

Last week I came back from lunch to discover my machine frozen. It was still pingable, but everything that touched the disk locked up. I held down the power key to force a hard reboot.

My machine bounced back, but with kernel BootCache warnings in the Console log. After a bit of googling, I decided to restart the machine in Safe Mode, which I understood would rebuild the BootCache. Turns out it also runs fsck, putting up a nice little progress bar. It was taking a long time, so I went to the gym. I came back an hour later, and the progress bar was where I left it: around 30%.

Uh-oh.

I booted off my nightly SuperDuper backup and launched Disk Utility.

My internal SSD fell off the bus: wouldn’t even appear on the device list. My SSD was gone.

I powered down my MacBook Pro and prepared to yank the drive for replacement from OWC. I didn’t expect any issues, they’ve happily replaced two traditional failed drives for me in the past. On a hunch, after I yanked the battery, I counted to down 10 and plugged the machine back in.

It successfully booted off the SSD.

So my SSD was “back”. My guess is the drive firmware simply turns off its SATA connection when it gets backed into an unrecoverable corner. Removing power seemed to “unlock” the drive.

This is kind of a worst-case scenario, since I didn’t trust the drive anymore but it seemed to be working and OWC may not want to replace it.

fsck came back with a couple of invalid inodes, and indicated successful repair. Still not trusting it, I tried a traditional way to force drive failure: a reformat with writing zeros. ~45 minutes later, the drive mounts sans any reported trouble.

If this was a traditional drive, I might have started to trust it again. However, I know about an extra trick some SSDs have up their sleeve: block-level de-duplication.

So I wrote a small C program that fills a file or device with random data. Note to Unix pedants: I know I could have done this with shell commands or your $FAVORITE_LANGUAGE, but I wanted to get close to the kernel on this one and reduce variables for ease of reproduction.

Random data defeats deduping, and I ran my program with parameters to fill my SSD. I went to bed.

This morning I discovered my SSD, which happily survived a complete filling with zeros, failed when I tried to fill it with random data. It fell off the bus again, and wouldn’t show up in Disk Utility.

I removed the drive from the MacBook Pro and plugged it into my Mac Pro via a drive toaster and reran the stress test.

It passed. I reran it again. It passed again.

My SSD had resurrected itself.

I have since reinstalled the SSD and am happily using it again. I’ve also rewritten and enhanced my initial C program into a better, faster one I’ve entitled stressdrive. It now passes my SSD with flying colors:

$ sudo ./stressdrive /dev/rdisk0
blockSize: 512
blockCount: 468862128
speedScale: 16x
scaled blockSize: 8192
scaled blockCount: 29303883
writing random data to /dev/rdisk0
writing 100% (block 29303002 of 29303883)
1779f30a231c1d07c578b0e4ee49fde159210d95 : SHA-1 of written data
verifying written data
reading 100% (block 29302306 of 29303883)
1779f30a231c1d07c578b0e4ee49fde159210d95 : SHA-1 of read data
SUCCESS

My current hypothesis is that my SSD wore out a flash block and attempted to mark the block as bad and recruit a fresh block from its overprovisioning reserve. This path has a bug, causing the controller to panic. Maybe the supposedly fresh block also had issues, maybe a few of them did. I’m thinking restarting the SSD by removing power helped it make progress in the recovery until it succeeded.

I did have the SSD mysteriously drop off my internal bus again today right before the stressdrive test, so I’m keeping an eye on it — I may not be out of the woods yet.

By the way, through all of this, my SSD’s SMART status has remained “Verified”. Ugh.

This week two friends of mine with a Sandforce controller also had SSD failures similar to mine, where the drive fell off the bus. At least one had the same experience as me where the drive was able to “resurrect” itself and pass “surface scans” (whatever those are). Anecdotes aren’t data, but there you go.

I should also mention OWC has been a champ, proactively finding my original order with them and emailing me when I originally mentioned my failure on twitter. They’ve offered to replace my drive, but I’m keeping it for now. What can I say, running a fast drive that may die at any moment makes me feel alive.

Dec 4, 201151 notes
#sysadmin #ssd

November 2011

2 posts

Nearly Hygienic C Macros via __COUNTER__

A few months back Benjamin Stiglitz taught me that it’s finally possible to build practically-hygienic macros with modern plain ol’ C #defines. Here’s his example:

#define MIN_PASTE(A,B) A##B
#define MIN_IMPL(A,B,L) ({ \
    __typeof__(A) MIN_PASTE(__a,L) = (A); \
    __typeof__(B) MIN_PASTE(__b,L) = (B); \
    MIN_PASTE(__a,L) < MIN_PASTE(__b,L) ? MIN_PASTE(__a,L) : MIN_PASTE(__b,L); \
})
#define MIN(A,B) MIN_IMPL(A,B,__COUNTER__)

So this C code:

int a = 11;
int b = 42;
int c = MIN(a, b);

preprocesses into this:

int a = 11;
int b = 42;
int c = ({
    __typeof__(a) __a0 = (a);
    __typeof__(b) __b0 = (b);
    __a0 < __b0 ? __a0 : __b0;
});

Here’s how __COUNTER__ works:

A new predefined macro __COUNTER__ has been added. It expands to sequential integral values starting from 0. In conjunction with the ## operator, this provides a convenient means to generate unique identifiers.

It’s still not gensym-quality, since the generated variable name can shadow other variable names:

int a = 11;
int b = 42;
int __a0 = a * b; // uh oh
int c = MIN(a, b);
assert(__a0 == 462); // passes though

This is contrived, since you probably aren’t going to prefix your variable names with double-underscores. That said, this code does compile and execute correctly, although enabling -Wshadow will reveal all is not well.

__COUNTER__ is supported by GGC 4.3, clang and even MSVC.

Nov 18, 201113 notes
Copy OmniFocus Task Outline Text to Clipboard

Here’s a quick script that will take the selected tasks in OmniFocus 1.9.4 and put a simple space-indented text representation on the clipboard:

tell application "OmniFocus"
    tell first document window of front document
        set selectedItems to selected trees of content
        if ((count of selectedItems) < 1) then
            error "Please first select a task"
        end if
        set output to ""
        repeat with itemItr in selectedItems
            set output to output & my printTree(itemItr, 0)
        end repeat
    end tell
end tell
set the clipboard to output

on printTree(node, indent)
    set output to ""
    repeat indent times
        set output to output & "    "
    end repeat
    set output to output & name of node & (ASCII character 13)
    tell application "OmniFocus"
        repeat with treeItr in trees of node
            set treeItr to treeItr as any
            set output to output & my printTree(treeItr, indent + 1)
        end repeat
    end tell
    return output
end printTree
Nov 8, 20115 notes
#applescript #omnifocus

October 2011

1 post

Scott Adams on Systems

Scott Adams:

The trick is that I didn’t have an exercise goal in the first place, so I couldn’t have failed to reach it. What I do have is an exercise system, and I was completely successful at the system. My philosophy is that losers have goals and winners have systems.

Building systems is far more effective than merely aiming at goals.

Oct 22, 201111 notes

September 2011

7 posts

Swap Word Instances in BBEdit

Sometimes I want to swap all instances of “foo” with “bar”. That is, I want every instance of “foo” turned into “bar” and vice-versa.

This may sound odd, but I encounter often it enough that I formed a strategy long ago, three replacement operations:

  1. Replace “foo” with something unique (a sentinal) in the document. I often use a bullet (•) for this.

  2. Replace “bar” with “foo”.

  3. Finally replace the sentinel string with “bar”.

Encountering this again tonight, I decided to finally automate the three-operation replacement:

set uuid to "1DB2FC05-1011-4467-BA2C-A8F3A8B530BC"
set findPasteboard to do shell script "pbpaste -pboard find"
set findPasteboard to findPasteboard & "↔"
display dialog "Swap a↔b" default answer findPasteboard
set swapSet to split of (text returned of result) by "↔"
set term1 to item 1 of swapSet
set term2 to item 2 of swapSet
tell application "BBEdit"
    tell text window 1
        replace term1 using uuid
        replace term2 using term1
        replace uuid using term2
    end tell
end tell

to split of textToSplit by splitter
    set oldTextItemDelimiters to AppleScript's text item delimiters
    if the class of splitter is list then
        set AppleScript's text item delimiters to splitter
    else
        set AppleScript's text item delimiters to {splitter}
    end if
    set theResult to text items of textToSplit
    set AppleScript's text item delimiters to oldTextItemDelimiters
    return theResult
end split

I now use a hard-coded UUID instead of a bullet for my sentinel, but the idea is the same.

AppleScript’s simple display dialog doesn’t allow two separate text input fields, so I delimit the input with ↔. Hacky.

Sep 24, 20119 notes
#applescript #bbedit
Switch Between Tabs and Spaces in BBEdit

Here’s a script I wrote to make it easy to switch between tab-based and space-based indention in BBEdit 10:

tell application "BBEdit"
    tell text window 1
        if expand tabs then
            set currentMode to "Spaces (" & tab width & ")"
        else
            set currentMode to "Tabs"
        end if
        display dialog "Current Mode: " & currentMode & ".
Enter new mode (blank for tabs):" default answer "4"
        set newTabWidth to text returned of result as number
        if newTabWidth = 0 then
            set expand tabs to false
        else
            set expand tabs to true
            set tab width to newTabWidth
        end if
    end tell
end tell

Indention control in BBEdit is painful. Whether tabs or spaces are used is in the Edit > Text Options sheet (confusingly named “Auto-expand tabs”) and the amount of spaces inserted in space-indention mode in stuck to the bottom of the Fonts panel (accessible via View > Text Display > Show Fonts).

This script makes it one action to figure out the current mode and modify it if necessary.

Sep 21, 20114 notes
#applescript #bbedit
Apps I Love: DTerm

DTerm is a free app that uses the Accessibility API to figure out the current document you’re looking at. When you hit a hotkey (mine is bound to command-shift-return), it pops up a window with a shell command line with the working directory already set to the frontmost document’s folder.

DTerm works awesomely with Finder, BBEdit, TextMate, Xcode, GitX and a bunch of other Mac apps.

Protip: instead of typing return to execute your DTerm command, type command-return to open a new Terminal.app window with your command. Great if you plan a few follow-up commands or want colored output.

Sep 21, 20115 notes
#appsilove
Google Storage Economics

Scott McNulty / @blankbaby:

You can add 1 TB of storage to your Google account for $256 a year. Who knew? bit.ly/pDO7aS

At first glance it seems excessive that Google would charge you $256/year for an extra 1TB of storage when raw 1TB drives run ~$60-80 on Amazon today.

But you need to keep in mind a lot of extra factors:

  • Redundancy. You’ll need at least two drives to match google’s data safety. Probably more like three with offsite rotation.

  • Energy. Those Amazon drives are bare. Spin them for a year and see what it costs you on your electricity bill. Not to mention that i5 machine you have wrapped around it.

  • Bandwidth. Data’s gotta hit the platters, and that will go over wires Google pays for. Both ways.

  • Humans. Maintaining all of Google’s servers and hard drives takes world-leading sysadmin skills and raw headcount. Part of that $256/year are humans watching monitors at 3:30 in the morning on your behalf.

All that said, I’m not saying purchasing an extra 1TB/year for $256 is a good deal, I’m just saying it’s not as bad a deal as it may seem initially.

Related.

Sep 17, 201124 notes
Mac Illustrator Alternatives

Jon Stokes / @jonst0kes:

So the answer to my Illustrator question seems to be that there is no Pixelmator for vectors. You just have to shell out for AI.

I purchased LineForm a while back, however since Freeverse was bought out by ngmoco I doubt for its future. Here’s some other options, mostly gathered by Chris Foresman:

  • Artboard

  • iDraw

  • Intaglio

  • Sketch and DrawIt

  • VectorDesigner

Sep 17, 20115 notes
[NSScreen mainScreen] != screen with menubar

Karsten Kusche:

for years i’d thought that [NSScreen mainScreen] returns the screen with menu. It returns the screen with the window that has keyboard focus

My testing doesn’t agree with +mainScreen’s documentation:

The main screen is not necessarily the same screen that contains the menu bar or has its origin at (0, 0). The main screen refers to the screen containing the window that is currently receiving keyboard events. It is the main screen because it is the one with which the user is most likely interacting.

In my testing, +mainScreen is constant and doesn’t change depending on which window on which screen is receiving keyboard events.

But I’ll follow its advice anyway:

The screen containing the menu bar is always the first object (index 0) in the array returned by the screens method.

Sep 13, 20115 notes
Transformation Matrix Funhouse

I wrote Transformation Matrix Funhouse to make is easier to understand and play with -webit-transform: matrix3d(). It’s also cool for grokking transformation matrices in general.

Sep 9, 20117 notes

August 2011

7 posts

JRTruthTable

JRTruthTable is my new small+simple Objective-C class for resolving what state you’re in given a condition set.

I have a Cocoa app I wrote back in 2004 for generating truth tables. I was going to modernize it an release it, but now I think I’ll just turn it into a Cappuccino app — there’s no need to tie it to Mac OS X. The rewrite is a little while away, however.

Aug 31, 2011
HOWTO Use UTF-8 Throughout Your Web Stack

Good is the enemy of Great
Latin-1 is the enemy of UTF-8

You write web apps. You understand the web is global, and want to support internationalization. You want UTF-8.

UTF-8 is extremely sane. Well, as sane as an encoding can be that features backwards-compatibility with ASCII.

Everything you care about supports UTF-8. Trust me: you want it everywhere.

Problem is, every last part of the web-application stack will fight you on your quest towards UTF-8 purity. What follows is a playbook to win your pervasive-UTF-8 battle.

First, you’re going to need diagnostic tools. There are two main weapons:

  1. A hex editor and traffic dumper.

    The programs you use to view text, be it dynamic from a tool’s output (Console.app) or a static file like a database dump (TextEdit, BBEdit, TextMate), have encoding logic. They will attempt to auto-detect encoding and paint you a pretty picture.

    Avoid them. When debugging, you don’t want a pretty picture, you want The Truth. You need to be able to see raw byte-streams to debug this stuff.

    A common problem is mixed encodings. That is, a file or stream that says it’s UTF-8 but has a chunk of Latin-1 in it. This is invisible corruption since most software won’t alert you when it hits mixed encodings (BBEdit is a notable exception).

    Using a hex editor or viewing raw hex streams allows you to spot when a character that should be taking up three bytes (UTF-8) is only taking one (Latin-1).

  2. A Unicode Canary-in-a-Coal-Mine.

    You need a chunk of data that exercises the Unicode system: a sentinel value that you can push through your stack and make sure it survives a round-trip intact.

    Initially I went with something like “tésting”, but it turns out that’s not enough — it will losslessly survive undesired transcoding to Latin-1 and back again.

    No, you need something hard-core: “Iñtërnâtiônàlizætiøn” (complete with curly quotes).

    (If you can’t read that word in your browser, it looks like the word “Internationalization” that’s had an umlaut omelet thrown in its face, and you’ve discovered an yet another encoding error somewhere between where I’m typing this and where you’re reading it.)

    “Iñtërnâtiônàlizætiøn” is a great word to push through your systems because it can’t be represented in Latin-1 and will catch all sorts of hidden failure scenarios. Coupled the viewing raw hex, there’s no place for encoding bugs to hide.

    (For the record, “Iñtërnâtiônàlizætiøn” looks like E2 80 9C 49 C3 B1 74 C3 AB 72 6E C3 A2 74 69 C3 B4 6E C3 A0 6C 69 7A C3 A6 74 69 C3 B8 6E E2 80 9D in UTF-8 in hex.)

※ ※ ※

OK, those are your weapons. Now for some concrete tips, starting from the bottom-up:

  • MySQL DDL: MySQL uses Latin1 by default. You need to set default charset to utf8 and collate to utf8_unicode_ci:

    drop table if exists t_my_table;
    create table t_my_table (
        ...
    ) engine=innodb default charset=utf8 collate=utf8_unicode_ci;
    

    The major gotcha here is that if you fail to specify default charset=utf8 in your DDL, it will default to Latin1 but simple storing and retrieval of UTF-8 will still work.

    This is because there are no invalid characters in Latin-1 (well, except for NUL (0x00)). You can jam anything in there and MySQL will dutifully store it for you and give it back when asked.

    No errors, no warnings.

  • MySQL Importing/Restoration: Consider the following file, myutf8.sql:

    drop table if exists myutf8_table;
    create table myutf8_table (
        demo varchar(255)
    ) engine=innodb default charset=utf8 collate=utf8_unicode_ci;
    insert into myutf8_table values ('“Iñtërnâtiônàlizætiøn”');
    

    Now let’s load it up:

    mysql -e 'drop database if exists myutf8_db;create database myutf8_db;'
    mysql myutf8_db < myutf8.sql
    

    You’ve already failed.

    You see, myutf8.sql’s file encoding was UTF-8, but no one told mysql that. So mysql assumed Latin-1 and corrupted the data.

    Looking at myutf8_table’s lonely single row in Querious, I see it has a value of “Iñtërnâtiônàlizætiøn” — a far cry from the “Iñtërnâtiônàlizætiøn” value we intended.

    Fortunately it’s easy to instruct mysql that an input file has a specific encoding:

    mysql -e 'drop database if exists myutf8_db;create database myutf8_db;'
    mysql --default-character-set=utf8 myutf8_db < myutf8.sql
    

    That --default-character-set=utf8 makes all the difference. I recommend using it all the time — I’ve gotten to the point where I’m nervous if I spot an invocation of mysql that lacks an explicit --default-character-set=utf8.

  • MySQL Exporting/Backup: Use --default-character-set=utf8 like you do when importing:

    mysqldump --user=root --opt --default-character-set=utf8 myutf8_db
    

    Relately, Mo McRoberts has a nice post on when MySQL encodings go bad.

  • JDBC Connection URL: It’s been a while since I’ve used Java, but it looks like you want to set two options, useUnicode and characterEncoding:

    jdbc:mysql:///myutf8_db?useUnicode=true&characterEncoding=UTF-8
    
  • HTTP Headers: Your web server should vend a Content-Type of text/html;charset=utf-8.

  • HTML Documents: In theory your web server should be configured to declare all your HTML content as UTF-8 with its Content-Type HTTP header, but unfortunately that’s not always something you can control. You can also declare your UTF-8 conformance in the HTML document itself with a meta tag:

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
        "http://www.w3.org/TR/html4/strict.dtd">
    <html>
    <head>
        <meta http-equiv="content-type" content="text/html; charset=utf-8">
    </head>
    <body>
    </body>
    </html>
    
  • HTML Forms: Specify accept-charset in your <form> tag to tell the browser to submit user-entered data encoded in UTF-8:

    <form action="foo" accept-charset="UTF-8">...</form>
    
  • Ajax/XHR/XMLHTTPRequest: Don’t sweat it, the W3C XMLHTTPRequest standard specifies POST data will always be encoded with the UTF-8 charset.

Aug 19, 2011217 notes
#utf-8
QuickPick Pulled From App Store

You may recall Seth Willits, whose app QuickPick was rejected from the Mac App Store for being “confusingly similar” to 10.7’s Launchpad. Even though QuickPick has been shipping for years before Launchpad and also runs on 10.6.

Seth submitted a formal appeal to Apple’s App Review Board on April 7 2011. After seven weeks Apple denied his appeal. Seth asked for a supervisor, was promised contact info, but never received it.

While QuickPick stayed still available for sale on the Mac App Store, it was stuck on version 2.0.2 — Apple wouldn’t let him update it.

That didn’t stop Seth from continuing to fix bugs and add new features to the direct-sale version of his app, which now stands at 2.0.7.

Unfortunately once Lion was released, it uncovered a bug in QuickPick 2.0.2 on the new OS which prevents screen corner activation from working.

Facing a widening feature gap between his direct-sale version and a bug that Apple won’t allow him to fix, Seth has done the best thing he can do: remove QuickPick from the Mac App Store.

It’s safe to say everyone loses here: Seth, his customers, Apple and Apple’s developers.

Aug 16, 201121 notes
#appstore #macappstore #quickpick
Drove the Chevy to the Levy

bitcartel / @bitcartel:

Today: 40th Anniversary of Nixon ending Gold Standard. 1 USD was worth 1/35th of an ounce of gold, now just 1/1750th. http://t.co/m3v2LeM

In 1971 you have $105.

$35 goes under the mattress, $35 into a savings account at 0.80% monthly and you buy a single 1oz gold coin.

Satisfied with your investments, you hop in your Corvair, get distracted upon hearing American Pie and wind up sideswiped by a bus.

In 2011 you awake from your coma and do some math to see about paying off that largish hospital bill.

Thanks to inflation, the buying power of the $35 under your mattress has been significantly reduced — most stuff that used to cost $35 in 1971 now costs about $250*.

Checking the balance on your bank savings account, it has $48.25 in there. Better than the $35 under the mattress, but it didn’t even keep up with inflation.

Checking your pockets, you find your gold coin. It’s now worth $1,700.

※ ※ ※

I used to believe gold wasn’t a good investment. I still believe that — gold doesn’t give good returns in real terms.

However, when the world is devaluing its currency like mad, just holding your head above water is an accomplishment.

*Thanks to Frink for the inflation calculation and BankRate for the savings account calculator.

Aug 15, 201113 notes
BART Disables Cell Phone Service

San Francisco BART:

BART temporarily interrupted [cell phone] service at select BART stations as one of many tactics to ensure the safety of everyone on the platform.

I’m glad this happened — it provides real-world evidence to the extent mainstream communication channels operate at the whim of nameless bureaucrats.

Aug 12, 20116 notes
Cable Subscriber Inflation

Peter Svensson writing for the Associated Press:

The U.S. subscription-TV industry first showed a small net loss of subscribers a year ago. This year, that trickle has turned into a stream.

I pay Comcast $65/month for Cable Internet and Basic Cable.

I don’t need Basic Cable — I don’t even have a TV attached to it. However Comcast would charge me an additional $5/month for Internet-only service.

That’s right, Comcast would charge me more if I declined a service.

I initially thought it had something to do with Universal Service-style municipal regulations, but now I suspect Comcast wants to prop up its “TV subscribers” number, the way magazines will give away subscriptions to inflate their readership numbers.

I’m impressed a single customer is worth ~$60/year to Comcast to keep them on their subscriber books. Seems high.

Aug 11, 20112 notes
Hickey on Values, Identity and State

Rich Hickey delivers a must-watch presentation.

I have a hard time watching presentations on my computer, but this one is very worth it.

In an approachable and high-level manner, Hickey effectively attacks variables as insufficient abstractions, making a compelling argument and offering effective replacements.

Though I’m a Lisp fan, I haven’t been too interested in Clojure since I viewed it as just a Lisp-on-the-JVM. I now understand Hickey and friends are doing tremendous work.

(link via Dave Dribin)

Aug 7, 201172 notes
#variables #values #state #lisp #clojure

July 2011

10 posts

Survival of the Fattest

Olaf Storbeck:

The poor learning ability of the banks yields another important insight: We cannot rely upon the idea that market forces punish badly managed banks. In the financial industry the principle of the “survival of the fittest” does not apply. In this sector we have “survival of the fattest” instead.

I much prefer “Survival of the Fattest” as a descriptive term of the disease than “Too Big To Fail”.

Jul 26, 20113 notes
"I wish I was born later"

Marco Arment / @marcoarment:

Grandpa: “I wish I was born later.”

Me too. In particular, I wish I were born where born around 1990, so I could take advantage of the web in my formative years for technical information instead of spending lots of money on now-obsolete books.

This also strikes me as a good metric for humanity’s progress. There’s not a time in the past I’d rather have been born in. It makes me think the “best” time to be born is still in our future.

Jul 25, 20119 notes
Using M4 to Compose Your .gitignore File

Chris Wanstrath has a cool project to collect folks’ .gitignore files.

Chris breaks these down by platform and language/environment, which is great. What’s less great is you can’t pass multiple files to core.excludesfile, so you can’t easily compose them.

The ideal situation would to extend git to support multiple ignore files, but that’s a lot of pain (mostly because git isn’t on github itself).

So I wrote .gitignore.m4:

dnl cd && m4 -I ~/code/ext/gitignore ~/.gitignore.m4 > ~/.gitignore

.metadata
bin
dist
tmp

include(`Global/OSX.gitignore')

include(`C++.gitignore')

include(`Objective-C.gitignore')
build
*~.nib

include(`Autotools.gitignore')

This is my first time using M4 in anger, and apparently that dn1 is a way to make a single-line comment. I know, weird.

Anyway, that comment contains the invocation you need to run to (re)generate your composited .gitignore:

cd && m4 -I ~/code/ext/gitignore ~/.gitignore.m4 > ~/.gitignore

Here’s the breakdown:

  • First we switch to the users home directory, where .gitignores usually live.

  • That -I ~/code/ext/gitignore tells M4 to add ~/code/ext/gitignore to its file search path. Change this part to where-ever you git-clone’d Chris’s gitignore repo on your local machine.

  • The rest just reads the template file and overwrites its output to ~/.gitignore.

Jul 24, 201111 notes
#git #m4 #github #gitignore
mogenerator 1.23

What’s New:

  • [NEW] Support for Mac OS X 10.7 Lion’s ordered relationships (generated relationship code uses NSOrderedSet and NSMutableOrderedSet). (rentzsch 1 2 3)

  • [NEW] Optional support for ARC: pass --template-var arc=true to mogenerator. bug 63 (Adam Cox)

  • [NEW] New template that dumps a binary .xcdatamodel into a pseudo-ASCII-plist format perfect for diffing. A great way to compare two versions of a data model. (Brian Webster)

  • [NEW] Attributes and relationships are now sorted for generation. This should eliminate spurious changes to source files when unrelated model entities are changed. After upgrading to 1.23 you probably want to regenerate all your source files without a model change, just to let things settle in before your next real model change. (Nikita Zhuk)

  • [NEW] Support for generation of PONSOs: Plain Old NSObjects. These are in-memory, typesafe non-CoreData classes generated from your Xcode data models. Generate reams of ObjC classes from a single data model. Supports relationships and basic serialization. See contributed templates/Nikita Zhuk/ponso/README.txt for details. (Nikita Zhuk)

  • [NEW] Support for momc error-reporting options: MOMC_NO_WARNINGS, MOMC_NO_INVERSE_RELATIONSHIP_WARNINGS and MOMC_SUPPRESS_INVERSE_TRANSIENT_ERROR. (Nikita Zhuk)

  • [NEW] Now generates output directories if they don’t already exist or presents an error message if they cannot be created. (Scott Little)

  • [CHANGE] Change #include to #import in include.m. (Zac Bowling)

  • [NEW] You can now use --template-var to pass arbitrary command-line options through to templates. (Adam Cox)

  • [NEW] Update MiscMerge to NS(U)Integer for 64-bit compatibility. (Nikita Zhuk)

  • [FIX] Memory leaks in MiscMerge. (Nikita Zhuk)

  • [CHANGE] mogeneratorTestMule’s mogenerate.command upgraded to use double-dash option names. (rentzsch)

  • [FIX] Set mogeneratorTestMule’s mogenerate.command executable bit. (rentzsch)

Jul 23, 20117 notes
#mogenerator
Apps I Love: Yojimbo

I accidentally stumbled across my notes for what would become C4, and I realized I’ve been using Yojimbo for a very long while.

Yojimbo is such a great app, it hard for me to think of the time before I used to use it.

I use Yojimbo as a digital junk drawer, storing programming snippets, shell invocations, SQL nuggets, notes on different projects & processions and archives of web pages that I fear will vanish (most of which have).

It does a little bit too much for me, but it’s easy to ignore the things I don’t used it for (like storing serial numbers).

I’m actually still using Yojimbo 1.5.2 since I love its original icon so much. I’m planning on upgrading to Yojimbo 4 once the iCloud dust settles, but I’ll have to copy and paste 1.x’s icon onto the new version. Can’t live without it.

Jul 23, 20116 notes
#appsilove
Next page →
2012 2013
  • January 2
  • February
  • March 2
  • April
  • May
  • June
  • July
  • August
  • September
  • October
  • November
  • December
2011 2012 2013
  • January
  • February 2
  • March 1
  • April 4
  • May
  • June 2
  • July
  • August
  • September
  • October 7
  • November 4
  • December 1
2010 2011 2012
  • January 4
  • February 2
  • March 10
  • April 6
  • May 1
  • June 1
  • July 10
  • August 7
  • September 7
  • October 1
  • November 2
  • December 3
2009 2010 2011
  • January 13
  • February 11
  • March 19
  • April 7
  • May 7
  • June 8
  • July 11
  • August 5
  • September 3
  • October 2
  • November 11
  • December 6
2009 2010
  • January
  • February
  • March
  • April
  • May
  • June
  • July
  • August
  • September
  • October 3
  • November 38
  • December 29