Ergonomic and safe weak capturing for Swift closures.
Weak captures in Swift closures often require repetitive boilerplate:
{ [weak self] in
guard let self else { return }
// ...
}While explicit, this pattern adds noise and can obscure the intent of the closure, especially when used frequently.
swift-capture provides a set of helpers that encapsulate this pattern, allowing weakly captured objects to be used safely without repeating the same guard logic.
Note
All NSObject subclasses conform to CapturableObjectProtocol by default.
Custom reference types can conform by inheriting AnyObject.
A common use case for capturing objects is handling asynchronous results:
func loadData() {
apiService.fetchData { [weak self] items in
guard let self else { return }
self.items = items
}
}Can be replaced with:
func loadData() {
apiService.fetchData(completion: capture { _self, items in
_self.items = items
})
}If the object is already deallocated, the closure is simply not executed.
swift-capture helpers utilize variadic generics to automatically support any number of arguments:
networkService.perform(request, completion: capture { _self, response, data, error in
// ...
})For non-Void and non-Optional output closures, a default value must be provided:
dataSource.numberOfItems = capture(orReturn: 0) { _self in
_self.items.count
}Access to properties can be simplified by using key paths as functions:
dataSource.numberOfItems = capture(orReturn: 0, in: \.items.count)When you need a custom capture strategy, it is recommended to use method accessors:
object.capture(as: .strong) { _self in
_self.performCriticalWork()
}The functor accessor also provides a way to override the default (.weak) strategy:
object.capture.as(.strong).orReturn(()) { _self in
_self.performWork()
}Note
There are proper overrides of callAsFunction available as well, so the following examples are also valid:
object.capture.as(.strong)(in: { _self in
_self.performWork()
})object.capture.as(.strong).self { _self in
_self.performWork()
}object.capture.as(.strong).callAsFunction { _self in
_self.performWork()
}However, this will not compile even though the code is valid:
object.capture.as(.strong) { _self in // ❌ Extra trailing closure passed in call
_self.performWork()
}Convenience method orReturn is provided as a workaround for a Swift compiler bug that leads to a compilation issue when callAsFunction is used as a trailing closure.
All function kinds are supported:
try object.capture { _self in
try _self.throwingWork()
}
await object.capture { _self in
await _self.asyncWork()
}
try await object.capture { _self in
try await _self.asyncThrowingWork()
}Including overloads for orReturn and onMainActor functor methods.
- Functors are
Sendablewhen the captured object isSendable. - Sendable functors preserve closure sendability.
onMainActorcan be used to preserve@MainActor.uncheckedSendableis available for explicit opt-out.
object.capture.uncheckedSendable.onMainActor { _self in
_self.updateUI()
}This package does also provide:
WeakStrongUnownedCaptured
You can add swift-capture to an Xcode project by adding it as a package dependency.
- From the File menu, select Swift Packages › Add Package Dependency…
- Enter
https://github.com/capturecontext/swift-captureinto the package repository URL text field - Choose the products you need to link to your project.
If you use SwiftPM for your project structure, add swift-capture to your package file:
.package(
url: "https://github.com/capturecontext/swift-capture.git",
.upToNextMajor(from: "4.0.0")
)Do not forget about target dependencies:
.product(
name: "Capture",
package: "swift-capture"
)This library is released under the MIT license. See LICENSE for details.