Zettelkasten for Programmers: Documenting Confusing with Swift.SendableMetatype

I was reading the SendableMetatype docs because the new NotificationCenter messages I was catching up on use that in the API headers.

It sounded straightforward and I was nodding along as I read the example, but this revealed that I must have one crucial misunderstanding about global actor isolation and sendability, because while every next step in the explanation sounded sensible, the concluding line was the exact opposite of what I expected.

I don’t yet know the answer, but since I needed to record my current interpretation anyway (I won’t remember in a day, will take my daughter outside in a couple of minutes, and context will be lost, etc.), I figured I should share this pattern with you.

So below is the full text of the note 202601290813 SendableMetatype expresses sendable static member access with the surprise/confusion explained in the end. I will update this, and when I do, I will update you on the change. But as of 2026-01-29 08:38, this reflects my current understanding and helps me ask others for info and interpetation:


SendableMetatype can express sendability on a protocol (a non-concrete type) to make static members Sendable.

Any T: Sendable automatically also satisfy the requirement T: SendableMetatype because Sendable inherits from SendableMetatype.

The API docs have an illustrative example:

protocol P {
  static func f()
}

You cannot use the type in a concurrent context to access the static f():

func useFromAnotherTask<T: P>(_: T.Type) {
  Task { @concurrent in
    T.f()   // error: capturing non-Sendable type `T.Type` in a concurrently-executing task
  }
}

Instead, you need to mark the metatype as sendable:

func useFromAnotherTask<T: P & SendableMetatype>(_: T.Type) {
  Task { @concurrent in
    T.f()   // okay, T.Type is Sendable
  }
}

Confusion

Global actor isolated types are Sendable[[202511110735]] with #SE-0316 – and I expected the API docs example to satisfy the SendableMetatype constraint just fine, but instead it says:

@MainActor
class MyModel: @MainActor P {
  /*implicitly @MainActor*/
  static func f() {
    /* on the main actor */
  }
}

useFromAnotherTask(MyModel.self) // error: cannot use main-actor-isolated conformance `MyModel: P` for a `SendableMetatype`-conforming type parameter `T`