CaptureContext/swift-capture
A mechanism for safe capturing & weakifying objects in Swift.
Table of Contents
Motivation
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.
Usage
[!NOTE]
All
NSObjectsubclasses conform toCapturableObjectProtocolby default. Custom reference types can conform by inheritingAnyObject.
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)Overriding strategy
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
callAsFunctionavailable 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
orReturnis provided as a workaround for a Swift compiler bug that leads to a compilation issue whencallAsFunctionis used as a trailing closure.
Async / throwing variants
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.
Sendability
- 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()
}Containers
This package does also provide:
WeakStrongUnownedCaptured
Installation
Basic
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.
Recommended
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"
)License
This library is released under the MIT license. See LICENSE for details.
Package Metadata
Repository: CaptureContext/swift-capture
Homepage: https://swiftpackageindex.com/CaptureContext/swift-capture/3.0.1/documentation
Stars: 11
Forks: 3
Open issues: 0
Default branch: main
Primary language: swift
License: MIT
Topics: arc, memory-management, spm, swift, swift-package-manager
README: README.md