A tiling window manager sounds simple: put every window in its place and keep it there. On a Mac that turns into five jobs. It has to size and place windows, know which Space each window is on, move windows between Spaces, switch Spaces, and focus the right window when it gets there. macOS gives you a public API for the first job and none for the other four.
This post walks through each job, how Spacetile does it, what that costs, and what it still can’t do. The source is public, so every claim here points at code you can read.
Placing windows: Accessibility
Most of the work goes through the public Accessibility API, which is why Spacetile asks for that one permission and checks for it before it tiles anything. Every window’s position and size is an attribute Spacetile can read and write. A layout pass computes each Tile’s frame, writes the size, then the position, then the size again (shrinking first stops a window being pushed back on screen at its old size), and reads the frames back. A window that stays bigger than its Tile has revealed its minimum size, so the layout adjusts and writes again.
Spacetile watches each app for new windows and focus changes, and each window for moves, resizes, minimising and closing. Those move and resize events also let it notice when you’ve used macOS’s own tiling from the green button, and re-tile the other windows round it. Native full screen and Split View go through Accessibility as well: the AXFullScreen attribute or the green button, and the app’s own Window ▸ Full-Screen Tile menu items, pressed like any other control.
An app’s window list through Accessibility only holds its windows on the current Space. But a window handle Spacetile already has keeps working after the window leaves that Space, and that’s what lets it lay out a Space you aren’t looking at.
Everything to do with Spaces: SkyLight
Accessibility knows nothing about Spaces. For the other four jobs, Spacetile calls SkyLight, the private framework behind the window server. None of the calls below needs System Integrity Protection (SIP) turned off.
Reading Spaces. SLSCopyManagedDisplaySpaces returns each display’s Spaces in Mission Control order, which one is showing, and which are full-screen app Spaces rather than Desktops. SLSCopySpacesForWindows says which Space a window is on, and SLSCopyActiveMenuBarDisplayIdentifier says which display has the menu bar. Spacetile reads these fresh each time, because Spaces and displays change under it, and it handles “Displays have separate Spaces” on or off.
Moving a window to another Space. ⌥⇧1 builds an SLSBridgedMoveWindowsToManagedSpaceOperation and hands it to SkyLight. The function that performs it has internal linkage, so dlsym can’t find it. The bridge, adapted from WhichSpace under its MIT licence, walks SkyLight’s symbol table for it instead, checking every address it computes so a changed layout gives up instead of crashing. The move is asynchronous and lands within about 15 ms. Spacetile then lays the window out on its new Space through the handle it kept, so the window is already in its Tile when you get there.
Switching Spaces. ⌥1 doesn’t call a Space API at all. It posts the events a trackpad swipe would send to the Dock, one swipe per Space, with an IOHID payload attached, since macOS 27 ignores the swipe without it. The event fields and the payload layout come from WhichSpace as well. A fast swipe switches with no slide animation. Swipes act on the display under the pointer, so when the target is on another display Spacetile moves the pointer there first. If the switch hasn’t landed after 0.3 seconds, it falls back to pressing the Mission Control “Move left a space” and “Move right a space” shortcuts, whatever you’ve bound them to.
Focusing a window. Since macOS 14, asking a background app to activate is a request the app can ignore, and it can’t choose which window becomes key. Spacetile calls _SLPSSetFrontProcessWithOptions with the window it wants, then posts the key-window event records whose byte layout comes from yabai. Focus follows mouse uses the same call with a flag that makes the app frontmost without raising any of its windows, so a floating window stays on top of the Tile under the pointer.
The full table of calls is in How it works without SIP.
Other answers to the same jobs
Every Mac tiler has to answer the Spaces jobs somehow, and the two best known answer them differently.
yabai is the most capable tiler on the Mac, and it tiles the real Spaces too. Its features that create, destroy and move Spaces, and those that set a window’s opacity or layer or show it on every Space, need SIP partly off so its scripting addition can load into the Dock. A good part of Spacetile’s focus code follows paths that yabai worked out first, and the source says so where it does.
AeroSpace takes the other route. It runs with SIP on and keeps workspaces of its own, i3-style, and it parks the windows of hidden workspaces in a corner of the screen. That makes switching instant and needs no changes to the system. The cost is that Mission Control doesn’t know those workspaces exist, and full-screen apps sit outside them.
Both are free and open source, and each does its job well. Spacetile takes a third route: the Desktops Mission Control already shows, tiled, with SIP left alone, using the calls above. Spacetile compared puts the three side by side, row by row, with Amethyst and others.
What that costs
Private calls can change in any macOS update, and Apple owes me no warning. Spacetile’s are checked on macOS 27.0. The window move looks up its symbols the first time it runs, never at launch, so if Apple removes it Spacetile loses that one ability and keeps tiling. The other SkyLight calls link when the app loads, so losing one of those would take a new release to fix.
Linking a private framework also rules out the Mac App Store. Spacetile ships as a signed DMG from the website instead.
And switching by synthetic swipe has an edge. When you land on a Desktop with none of the front app’s windows, macOS activates another app, and with “When switching to an application, switch to a Space with open windows for the application” on, it then follows that app to its Space. Spacetile watches for that bounce for a second after each switch and switches back once, unless you pressed a key or clicked in the meantime.
What it can’t do
Some things stay out of reach without turning SIP off:
- It can’t create, delete or reorder Spaces. You add Desktops in Mission Control.
- It can’t change another app’s window opacity or layer, or show a window on every Space. Dimming the windows you aren’t using works another way: Spacetile orders its own click-through windows just above and just below the focused one, which the public
NSWindowAPI allows. - It can’t lift a floating window back above another app’s window once you click a Tile in front of it. A background app’s window only rises among its own app’s windows. Click the floating window to bring it forward.
- It doesn’t tile full-screen Spaces. macOS lays those out itself, and Spacetile counts them when it numbers Spaces and puts a window back in its Tile when it leaves full screen.
If you need any of the first three and you’re happy to turn SIP partly off, yabai is the better choice.
Check it yourself
All of the above is in the source at Spacetile/Spacetile.app: the SkyLight declarations and the move bridge in Sources/CSkyLight, Space reading, moving and switching in Sources/Spacetile/Spaces.swift, focus in Focus.swift, and the Accessibility wrapper in AXWindow.swift. The tiling logic in SpacetileCore makes no OS calls and has its own unit tests. You can build it yourself and run your own build next to a downloaded one.
Spacetile is source-available, auditable, not open source: the Source licence lets anyone read the code, and using the app still needs a Licence after the Trial. A Licence costs £20 (about $27), one-time, includes VAT. Every install starts with a 14-day Trial with every feature on, and the download is a signed DMG. It needs a Mac with Apple silicon and macOS 26 or later.
If a claim here doesn’t match the code, or a macOS update breaks one of these calls before I catch it, email support@spacetile.app.