Smart Open Xcode

August 2, 2023

Multiple versions of Xcode in the macOS Command Tab switcher

If you’re like me, you often have multiple versions of Xcode installed. One or two beta versions, a stable version, and maybe another version in case the most recent stable version has something weird about it.

I also really like mapping my Caps Lock key to something more useful, and after reading Brett Terspstra’s excellent article on making a Hyper key many years ago, I’ve gotten used to hitting Caps Lock + X to jump to Xcode thanks to Karabiner Elements and Alfred. It works a lot like Command + Tab, but doesn’t require hitting Tab until you find it. It basically maps Caps Lock to holding down Command, Option, Control, and Shift all at once, a modifier that is very unlikely to conflict with anything else.

Anyway, this setup works by mapping a hotkey to a specific app, which works great 99% of the time, but if you’re working in a beta version of Xcode, and you have that keyboard shortcut mapped to the stable version, it opens the wrong app and can even sometimes get Xcode confused about accessing a file in multiple locations. Not good!

So, we need a smarter version rather than just hardcoding the app. Lots of options exist, like AppleScript (had a small delay though) and Keyboard Maestro, probably Shortcuts too, but I like a little Lua scripting utility called Hammerspoon. Just download it and drop it in your Applications folder.

At that point, we can add a short script to our init.lua file that uses Hammerspoon’s find() API to, well, find the version of Xcode that’s currently running, and open that, rather than a hardcoded one. Nice! (If you keep multiple versions of Xcode open at once, I don’t know what to tell you, weirdo.)

hs.hotkey.bind({"cmd", "alt", "ctrl", "shift"}, "X", function()
    -- Use the bundle ID rather than just 'Xcode' to prevent it from trying to open utility apps like 'Xcodes'
    local xcode = hs.application.find("com.apple.dt.Xcode")
    
    -- If nothing is found then just alert the user to open one, it would be very hard to guess which one they want if none are open!
    if xcode == nil then 
        hs.notify.new({title="Xcode is not open! 🫨", informativeText="Manually launch an Xcode instance and then I’ll be able to work!"}):send()
        return
    end

    --- If not the frontmost app, make it the frontmost! If it already is, hide it (makes it more toggle-y).
    if xcode:isFrontmost() then
        xcode:hide()
    else
        xcode:activate()
    end
end)

Voilà! Works like a charm. Here’s an addition that you can add as well that extends it to Simulators:

hs.hotkey.bind({"cmd", "alt", "ctrl", "shift"}, "A", function()
    -- Use the bundle ID rather than just 'Simulator' to prevent it from trying to open random background processes, and yes 'iphonesimulator' works for iPads as well
    local simulator = hs.application.find("com.apple.iphonesimulator")
    
    -- If nothing is found then just alert the user to open one, it would be very hard to guess which one they want if none are open!
    if simulator == nil then 
        hs.notify.new({title="Simulator is not open! 🫨", informativeText="Run your project in Xcode to launch it in a simulator, then I'll know what to open!"}):send()
        return
    end

    --- If not the frontmost app, make it the frontmost! If it already is, hide it (makes it more toggle-y).
    if simulator:isFrontmost() then
        simulator:hide()
    else
        simulator:activate()
    end
end)