Swift package

swift-capture

Weak, strong, and unowned closure captures without repetitive guard boilerplate

Put the closure’s work back in focus

Weak captures are correct but repetitive. Capture packages the familiar weak-self guard while preserving callback arguments, return values, throwing and async effects, sendability, and actor isolation.

api.fetch { [weak self] response, items, error in
  guard let self else { return }
  self.handle(
    response: response,
    items: items,
    error: error
  )
}
Before
api.fetch(completion: capture {
  _self, response, items, error in
  _self.handle(
    response: response,
    items: items,
    error: error
  )
})
After

Variadic generics support any callback arity. A Void or optional result needs no fallback; non-optional results make the fallback explicit.

dataSource.numberOfItems = capture(
  orReturn: 0,
  in: \.items.count
)

Weak is the default. Choose .strong only when ownership is intentional, and .unowned only when invocation after deallocation is a programmer error.

let update = object.capture
  .uncheckedSendable
  .onMainActor { capturedObject in
    capturedObject.updateUI()
  }

For stored reference state, the package also provides @Weak, @Strong, @Unowned, and strategy-selected @Captured wrappers.

SurfaceUse it when
capture { ... }A Void or optional-returning callback may skip after release
capture(orReturn:in:)A non-optional callback needs an explicit fallback
.as(.strong) / .as(.unowned)Lifetime semantics intentionally differ from weak
.onMainActorThe produced closure must preserve main-actor isolation
.uncheckedSendableA manually audited callback must cross concurrency boundaries

Custom reference types can conform to CapturableObjectProtocol; NSObject subclasses already receive the instance capture surface.