multipeerLoom
Join the Apple devices in one room into a session, with no Wi-Fi network to sign on to and no server in the middle.
Give it a service name both sides agree on and a name for this device to show up as. One device hosts, the others see it in a nearby list and join; the phones and Macs then talk directly to each other over whatever radio is available. Apple's MultipeerConnectivity does the finding and the carrying — this returns it as a kuilt us.tractat.kuilt.core.Loom.
val loom = multipeerLoom(serviceType = "myapp-play", displayName = "Iain's iPhone")This builds the same loom MultipeerPeerLinkFactory's constructor does. What it adds is the argument order every kuilt fabric factory shares (#1430): the fabric's own required arguments first, then the universal knobs. The constructor keeps its own older order and remains the surface for dependency injection and tests.
selfId is the wire identity, and displayName is only a label. That split is new (#1430) and it is the point of this factory: the id a caller passes here is the PeerId every remote peer sees for this device, so a consumer can name its own peers instead of discovering after the fact what the fabric decided to call it. The identity rides inside the advertised MCPeerID.displayName — Apple exposes no other cross-process handle — which is why the two share one 63-byte budget and why a long display name is trimmed.
Three knobs the convention names are deliberately absent. An argument that is accepted and then ignored — or whose arrival no test can witness — is worse than one that does not exist:
weaveTimeout— no clock boundsweaveon either path. Hosting starts an advertiser and returns immediately; joining sendsinvitePeerand returns immediately, and MC's own 30 s invitation timeout starts afterweavehas already handed back a seam, so it can never fail aweavecall. It is a handshake timeout, not a weave timeout — the distinction:kuilt-nearbydrew in #2333. Renaming it would publish a bound onweavethat no path delivers.policy— genuinely honoured at the layer below:MCSessionLinkandBridgePeerLinkboth take a us.tractat.kuilt.core.DeliveryPolicy and both factories drop it, so every multipeer seam is pinned toReliabletoday. It is omitted here anyway because it cannot yet be shown to arrive: both seams also hardcodeDispatchers.Defaultfor their delivery drain, so a lossy-policy assertion through a woven seam races that drain instead of observing it.policyanddispatchertherefore have to land together, with a test likeNearbyLoomKnobsTest.deliveryPolicyReachesTheWovenSeamsInboundBuffer. Adding an argument whose arrival no test can witness is the same defect as one that never arrives — the reader cannot tell them apart.dispatcher— deferred withpolicy, and additionally awkward on the JVM: the seam lives inside the macOS dylib, so a dispatcher passed here schedules only the JNA-side half of the pipeline; the native half's scope is not reachable across the cdecl ABI.
Parameters
MultipeerConnectivity service-type string. 1–15 ASCII letters, digits or hyphens (Bonjour's _service._tcp. rules minus underscores). Both ends must use the same value; kuilt supplies no default.
What a human sees for this device in a nearby list — a device name is the conventional choice. Cosmetic only. At most 26 bytes of it survive, because it shares Apple's 63-byte name budget with selfId and the identity is kept whole; longer names are trimmed, never rejected.
This peer's identity — the PeerId every remote observes. Defaults to a fresh random one, distinct on every call. Must not contain # and must be short enough to leave room for a display name; a violation throws from this call, not from a later weave.