Skip to content
C.W.K.
Stream
Lesson 02 of 05 · published

One Wire, Two Surfaces

~12 min · wire, appkit, daemon, ios, shared-types

Level 0Spark
0 XP0/36 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete

The Mac App Is a Client Too

Once a phone exists, it is tempting to treat the Mac app as the real owner and the phone as a special case bolted on. That split recreates the glued factory: desktop types leak into the conversation, the phone becomes a secretly-Mac program, and a type that imports AppKit cannot travel. Smolder's answer is one wire. Shared wire types compile without AppKit, without local process inspection, without a local-filesystem assumption. The Mac app attaches over that wire as a client of the same daemon the phone will attach to. Two surfaces. One conversation. One owner behind both.

"Without AppKit" is not an aesthetic. A type that names NSView cannot build for iOS. A type that asks "what is the local PID of this pane" cannot run on a phone that has no pane PIDs. A type that assumes /Users/... is here cannot describe a folder on a host the phone is only watching. Those assumptions are how a shared module becomes a Mac module with an #if os costume. The costume always tears on the day you need a field the phone cannot fill.

What the Wire Is Allowed to Say

Session identity, pane identity, journal chunks, resize as a control act, input as a leased act, receipts for launches. Things the daemon knows. Not pixels. Not an NSFont. Not a bookmark into the Mac's open-panel. The emulator on each surface turns bytes into glyphs with the same pin; the wire does not ship a screenshot. If you find a screenshot on the wire, you have given up on the emulator and started a remote-desktop, which resizes wrong and copies wrong and cannot tell a spectator from a typist.

The gateway in a later track speaks a bounded WebSocket to approved devices. That is a different mouth, same language. The language has to have been AppKit-free before the gateway exists, or the gateway will start translating desktop types into "phone-safe" shadows, and translation is a second protocol. One protocol. Two mouths (loopback attach for the Mac app, gateway for the phone). Zero AppKit on the types that cross.

Local Inspection Stays Local

The daemon, on the Mac, may inspect its own processes. That inspection does not travel. A phone that needs to show a title asks the wire for the title the daemon already holds, not for a sysctl it cannot run. A Mac window that needs to draw chrome asks AppKit, then sends only the facts the daemon must know (focused pane, requested size from the controlling surface). Mixing those layers is how a resize from a spectator sneaks through as "just a window callback."

This lesson is the type-level version of shells-stay-on-the-host. If the wire cannot compile on iOS, the phone is not a surface. It is a remote-control script pretending to share a model. Keep the compiler honest and the ownership stays honest.

Shared wire types compile without AppKit. The Mac app is a client of the same daemon. Local process inspection and local-filesystem assumptions stay off the wire, or the phone becomes a secretly-Mac program.

Code

Wire types that can travel; Mac types that must not·swift
// Shared wire — must compile for the daemon, the gateway, and iOS.
struct WirePane: Sendable, Codable {
    var id: PaneID
    var title: String
    var cols: Int
    var rows: Int
    var focused: Bool
}

struct WireChunk: Sendable, Codable {
    var pane: PaneID
    var bytes: Data          // journal / live output, not pixels
}

// Illegal on the wire
// import AppKit
// var view: NSView
// var localPID: pid_t
// var bookmark: URL        // assumes this device's filesystem

// Mac app (client, like iOS)
//   attaches, draws with SwiftTerm, sends leased input
// Daemon (owner)
//   may inspect local processes; that inspection does not travel

External links

Exercise

List five facts a phone might want (pane title, glyph pixels, local PID, folder path on the host, current journal chunk). Put each on the wire or keep it off. For every off item, write one sentence naming the lie you would tell if you put it on anyway.
Hint
Pixels and PIDs are the easy refusals. Folder path is legal only as a host-named string the daemon already knew, not as a bookmark from the phone's Files app pretending to be the Mac.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.