diff --git a/Package.swift b/Package.swift index 1310037..625b973 100644 --- a/Package.swift +++ b/Package.swift @@ -3,26 +3,35 @@ import PackageDescription let package = Package( - name: "swift-capture", - products: [ - .library( - name: "Capture", - targets: ["Capture"] - ) - ], - dependencies: [ - .package( - url: "https://github.com/apple/swift-docc-plugin.git", - from: "1.3.0" - ), - ], - targets: [ - .target(name: "Capture"), - .testTarget( - name: "CaptureTests", - dependencies: [ - .target(name: "Capture") - ] - ), - ] + name: "swift-capture", + products: [ + .library( + name: "Capture", + targets: ["Capture"] + ) + ], + dependencies: [ + .package( + url: "https://github.com/apple/swift-docc-plugin.git", + from: "1.4.0" + ), + ], + targets: [ + .target(name: "Capture"), + .testTarget( + name: "CaptureTests", + dependencies: [ + .target(name: "Capture") + ] + ), + ] ) + +#if compiler(>=6) +for target in package.targets where target.type != .system && target.type != .test { + target.swiftSettings = target.swiftSettings ?? [] + target.swiftSettings?.append(contentsOf: [ + .enableUpcomingFeature("InferSendableFromCaptures") + ]) +} +#endif diff --git a/Package@swift-6.0.swift b/Package@swift-6.0.swift new file mode 100644 index 0000000..481729d --- /dev/null +++ b/Package@swift-6.0.swift @@ -0,0 +1,37 @@ +// swift-tools-version:6.0 + +import PackageDescription + +let package = Package( + name: "swift-capture", + products: [ + .library( + name: "Capture", + targets: ["Capture"] + ) + ], + dependencies: [ + .package( + url: "https://github.com/apple/swift-docc-plugin.git", + from: "1.4.0" + ), + ], + targets: [ + .target(name: "Capture"), + .testTarget( + name: "CaptureTests", + dependencies: [ + .target(name: "Capture") + ] + ), + ], + swiftLanguageModes: [.v6] +) + +for target in package.targets where target.type == .system || target.type == .test { + target.swiftSettings?.append(contentsOf: [ + .swiftLanguageMode(.v5), + .enableExperimentalFeature("StrictConcurrency"), + .enableUpcomingFeature("InferSendableFromCaptures"), + ]) +} diff --git a/README.md b/README.md index 2504edc..0f7ad76 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # swift-capture -[![test](https://github.com/CaptureContext/swift-capture/actions/workflows/Test.yml/badge.svg)](https://github.com/CaptureContext/swift-capture/actions/workflows/Test.yml) [![SwiftPM 5.9](https://img.shields.io/badge/📦_swiftpm-5.9-ED523F.svg?style=flat)](https://github.com/CaptureContext/swift-declarative-configuration/actions/workflows/Test.yml) ![Platforms](https://img.shields.io/badge/platforms-iOS_|_macOS_|_tvOS_|_watchOS_|_Catalyst-ED523F.svg?style=flat) +[![test](https://github.com/CaptureContext/swift-capture/actions/workflows/Test.yml/badge.svg)](https://github.com/CaptureContext/swift-capture/actions/workflows/Test.yml) ![SwiftPM 5.9](https://img.shields.io/badge/📦_swiftpm-5.9_|_6.0-ED523F.svg?style=flat) ![Platforms](https://img.shields.io/badge/platforms-iOS_|_macOS_|_tvOS_|_watchOS_|_Catalyst-ED523F.svg?style=flat) [![docs](https://img.shields.io/badge/docs-spi-ED523F.svg?style=flat)]([https://twitter.com/capture_context](https://swiftpackageindex.com/CaptureContext/swift-capture/3.0.1/documentation)) [![@capture_context](https://img.shields.io/badge/contact-@capture__context-1DA1F2.svg?style=flat&logo=twitter)](https://twitter.com/capture_context) A mechanism for ergonomic and safe capturing & weakifying objects in Swift. @@ -47,7 +47,7 @@ capture { _self, a, b, c in } ``` ---- +---- Methods @@ -77,6 +77,13 @@ let object.dataSource = { [weak self] in let object.dataSource = capture(orReturn: [], in: \.data) ``` +---- + +Sendable closures (_beta_) + +> Prefix `capture` methods with an underscore to use explicit sendable closures +> Tho closures should implicitly infer sendable conformance (see [Package.swift](./Package.swift#L34)) + ## Installation ### Basic @@ -94,7 +101,7 @@ If you use SwiftPM for your project, you can add `weak` to your package file. Al ```swift .package( url: "git@github.com:capturecontext/swift-capture.git", - .upToNextMajor("3.0.0") + .upToNextMajor("4.0.0-beta") ) ``` diff --git a/Sources/Capture/Capture.swift b/Sources/Capture/Capture.swift index 605bedf..c3b61ee 100644 --- a/Sources/Capture/Capture.swift +++ b/Sources/Capture/Capture.swift @@ -5,27 +5,55 @@ import Foundation /// Weakly captures an object in non-parametrized void result closure. @inlinable public func capture( - _ object: Object, - in closure: @escaping (Object) -> Void + _ object: Object, + in closure: @escaping (Object) -> Void ) -> () -> Void { - return Weak(object).capture(in: closure) + return Weak(object).capture(in: closure) } /// Weakly captures an object in non-parametrized lazy void result closure. @inlinable public func capture( - _ object: Object, - in closure: @escaping (Object) -> () -> Void + _ object: Object, + in closure: @escaping (Object) -> () -> Void ) -> () -> Void { - Weak(object).capture(in: closure) + Weak(object).capture(in: closure) } /// Weakly captures an object in parametrized void result closure. public func capture( - _ object: Object, - in closure: @escaping (Object, repeat each Arg) -> Void + _ object: Object, + in closure: @escaping (Object, repeat each Arg) -> Void ) -> (repeat each Arg) -> Void { - Weak(object).capture(in: closure) + Weak(object).capture(in: closure) +} + +// MARK: Sendable + +/// Weakly captures an object in non-parametrized void result closure. +@inlinable +public func capture( + _ object: Object, + in closure: @escaping @Sendable (Object) -> Void +) -> @Sendable () -> Void { + return Weak(object)._capture(in: closure) +} + +/// Weakly captures an object in non-parametrized lazy void result closure. +@inlinable +public func capture( + _ object: Object, + in closure: @escaping @Sendable (Object) -> () -> Void +) -> @Sendable () -> Void { + Weak(object)._capture(in: closure) +} + +/// Weakly captures an object in parametrized void result closure. +public func _capture( + _ object: Object, + in closure: @escaping @Sendable (Object, repeat each Arg) -> Void +) -> @Sendable (repeat each Arg) -> Void { + Weak(object)._capture(in: closure) } // MARK: - Non-void result closures @@ -33,30 +61,61 @@ public func capture( /// Weakly captures an object in non-parametrized non-void result closure. @inlinable public func capture( - _ object: Object, - orReturn defaultValue: @escaping @autoclosure () -> Output, - in closure: @escaping (Object) -> Output + _ object: Object, + orReturn defaultValue: @escaping @autoclosure () -> Output, + in closure: @escaping (Object) -> Output ) -> () -> Output { - Weak(object).capture(orReturn: defaultValue(), in: closure) + Weak(object).capture(orReturn: defaultValue(), in: closure) } /// Weakly captures an object in non-parametrized lazy non-void result closure. @inlinable public func capture( - _ object: Object, - orReturn defaultValue: @escaping @autoclosure () -> Output, - in closure: @escaping (Object) -> () -> Output + _ object: Object, + orReturn defaultValue: @escaping @autoclosure () -> Output, + in closure: @escaping (Object) -> () -> Output ) -> () -> Output { - Weak(object).capture(orReturn: defaultValue(), in: closure) + Weak(object).capture(orReturn: defaultValue(), in: closure) } /// Weakly captures an object in parametrized non-void result closure. public func capture( - _ object: Object, - orReturn defaultValue: @escaping @autoclosure () -> Output, - in closure: @escaping (Object, repeat each Arg) -> Output + _ object: Object, + orReturn defaultValue: @escaping @autoclosure () -> Output, + in closure: @escaping (Object, repeat each Arg) -> Output ) -> (repeat each Arg) -> Output { - Weak(object).capture(orReturn: defaultValue(), in: closure) + Weak(object).capture(orReturn: defaultValue(), in: closure) +} + +// MARK: Sendable + +/// Weakly captures an object in non-parametrized non-void result closure. +@inlinable +public func _capture( + _ object: Object, + orReturn defaultValue: @escaping @autoclosure () -> Output, + in closure: @escaping (Object) -> Output +) -> () -> Output { + Weak(object).capture(orReturn: defaultValue(), in: closure) +} + +/// Weakly captures an object in non-parametrized lazy non-void result closure. +@inlinable +public func _capture( + _ object: Object, + orReturn defaultValue: @escaping @autoclosure () -> Output, + in closure: @escaping (Object) -> () -> Output +) -> () -> Output { + Weak(object).capture(orReturn: defaultValue(), in: closure) +} + +/// Weakly captures an object in parametrized non-void result closure. +public func _capture( + _ object: Object, + orReturn defaultValue: @escaping @autoclosure @Sendable () -> Output, + in closure: @escaping @Sendable (Object, repeat each Arg) -> Output +) -> @Sendable (repeat each Arg) -> Output { + Weak(object)._capture(orReturn: defaultValue(), in: closure) } // MARK: - Non-void optional result closures @@ -64,10 +123,10 @@ public func capture( /// Weakly captures an object in non-parametrized non-void optional result closure. @inlinable public func capture( - _ object: Object, - in closure: @escaping (Object) -> Output? + _ object: Object, + in closure: @escaping (Object) -> Output? ) -> () -> Output? { - Weak(object).capture(in: closure) + Weak(object).capture(in: closure) } /// Weakly captures an object in non-parametrized lazy non-void optional result closure. @@ -81,8 +140,37 @@ public func capture( /// Weakly captures an object in parametrized non-void optional result closure. public func capture( - _ object: Object, - in closure: @escaping (Object, repeat each Arg) -> Output? + _ object: Object, + in closure: @escaping (Object, repeat each Arg) -> Output? ) -> (repeat each Arg) -> Output? { - Weak(object).capture(in: closure) + Weak(object).capture(in: closure) } + +// MARK: Sendable + +/// Weakly captures an object in non-parametrized non-void optional result closure. +@inlinable +public func _capture( + _ object: Object, + in closure: @escaping @Sendable (Object) -> Output? +) -> @Sendable () -> Output? { + Weak(object)._capture(in: closure) +} + +/// Weakly captures an object in non-parametrized lazy non-void optional result closure. +@inlinable +public func _capture( + _ object: Object, + in closure: @escaping @Sendable (Object) -> () -> Output? +) -> @Sendable () -> Output? { + Weak(object)._capture(in: closure) +} + +/// Weakly captures an object in parametrized non-void optional result closure. +public func _capture( + _ object: Object, + in closure: @escaping @Sendable (Object, repeat each Arg) -> Output? +) -> @Sendable (repeat each Arg) -> Output? { + Weak(object)._capture(in: closure) +} + diff --git a/Sources/Capture/OptionalReferenceContainerProtocol.swift b/Sources/Capture/OptionalReferenceContainerProtocol.swift index 4a46fde..8adab57 100644 --- a/Sources/Capture/OptionalReferenceContainerProtocol.swift +++ b/Sources/Capture/OptionalReferenceContainerProtocol.swift @@ -2,324 +2,644 @@ import Foundation /// Protocol to generalize Weak and Weak.Box APIs public protocol OptionalReferenceContainerProtocol { - associatedtype Object: AnyObject - var object: Object? { get } + associatedtype Object: AnyObject + var object: Object? { get } } // MARK: - Void result closures extension OptionalReferenceContainerProtocol { - /// Weakly captures an object in non-parametrized void result closure. - /// - /// Creates `() -> Void` handler from `(Object) -> Void` closure - /// so you can access weakly captured Object in your handler if the object is present. - /// - /// Unwrapping happens on the call of returned closure. - /// - /// Example: - /// ```swift - /// class MyClass: Weakifiable { - /// func observe( - /// _ publisher: some Publisher - /// ) -> AnyCancellable { - /// publisher.sink( - /// receiveValue: capture { _self in - /// // ... - /// } - /// ) - /// } - /// } - /// ``` - /// - /// - Parameter closure: `(Object) -> Void`, where Object is unwrapped weakly captured object. - /// - Returns: `() -> Void` handler, created from `(Object) -> Void` closure by weakly capturing object. - @inlinable - public func capture( - in closure: @escaping (Object) -> Void - ) -> () -> Void { - return { [weak object] in - guard let object else { return } - closure(object) - } - } + /// Weakly captures an object in non-parametrized void result closure. + /// + /// Creates `() -> Void` handler from `(Object) -> Void` closure + /// so you can access weakly captured Object in your handler if the object is present. + /// + /// Unwrapping happens on the call of returned closure. + /// + /// Example: + /// ```swift + /// class MyClass: Weakifiable { + /// func observe( + /// _ publisher: some Publisher + /// ) -> AnyCancellable { + /// publisher.sink( + /// receiveValue: capture { _self in + /// // ... + /// } + /// ) + /// } + /// } + /// ``` + /// + /// - Parameter closure: `(Object) -> Void`, where Object is unwrapped weakly captured object. + /// - Returns: `() -> Void` handler, created from `(Object) -> Void` closure by weakly capturing object. + @inlinable + public func capture( + in closure: @escaping (Object) -> Void + ) -> () -> Void { + return { [weak object] in + guard let object else { return } + closure(object) + } + } + + /// Weakly captures an object in non-parametrized lazy void result closure. + /// + /// Primary idea behind this method is to be able to pass methods without referring to an object. + /// + /// Example: + /// ```swift + /// class MyClass: Weakifiable { + /// func handleCompletion() { + /// // ... + /// } + /// + /// func observe( + /// _ publisher: some Publisher + /// ) -> AnyCancellable { + /// publisher.sink( + /// receiveValue: capture(in: MyClass.handleCompletion) + /// ) + /// } + /// } + /// ``` + /// + /// - Parameter closure: `(Object) -> () -> Void`, most likely it's `ObjectType.someInstanceMethod` + /// - Returns: `() -> Void` handler, created from `(Object) -> () -> Void` closure by weakly capturing object + @inlinable + public func capture( + in closure: @escaping (Object) -> () -> Void + ) -> () -> Void { + return { [weak object] in + guard let object else { return } + closure(object)() + } + } + + /// Weakly captures an object in parametrized void result closure. + /// + /// > In context of this doc "`A, B, C...`" is a shortcut for 1 or more types. + /// + /// Creates `(A, B, C...) -> Void` handler from `(Object, A, B, C...) -> Void` closure + /// so you can access weakly captured Object in your handler if the object is present. + /// + /// Example: + /// ```swift + /// class MyClass: Weakifiable { + /// func observe( + /// _ publisher: some Publisher + /// ) -> AnyCancellable { + /// publisher.sink( + /// // number of params comes from publisher, if it was + /// // some completion with multiple args, all the args + /// // would've been passed to the capture method + /// receiveValue: capture { _self, value in + /// // ... + /// } + /// ) + /// } + /// } + /// ``` + /// + /// - Parameter closure: `(Object, A, B, C...) -> Void`, where Object is unwrapped weakly captured object. + /// - Returns: `(A, B, C...) -> Void` handler, created from `(Object, A, B, C) -> Void` closure by weakly capturing object. + public func capture( + in closure: @escaping (Object, repeat each Arg) -> Void + ) -> (repeat each Arg) -> Void { + return { [weak object] (arg: repeat each Arg) in + guard let object else { return } + closure(object, repeat each arg) + } + } +} + +// MARK: Sendable + +extension OptionalReferenceContainerProtocol where Object: Sendable { + /// Weakly captures an object in non-parametrized void result closure. + /// + /// Creates `() -> Void` handler from `(Object) -> Void` closure + /// so you can access weakly captured Object in your handler if the object is present. + /// + /// Unwrapping happens on the call of returned closure. + /// + /// Example: + /// ```swift + /// class MyClass: Weakifiable { + /// func observe( + /// _ publisher: some Publisher + /// ) -> AnyCancellable { + /// publisher.sink( + /// receiveValue: capture { _self in + /// // ... + /// } + /// ) + /// } + /// } + /// ``` + /// + /// - Parameter closure: `(Object) -> Void`, where Object is unwrapped weakly captured object. + /// - Returns: `() -> Void` handler, created from `(Object) -> Void` closure by weakly capturing object. + @inlinable + public func _capture( + in closure: @escaping @Sendable (Object) -> Void + ) -> @Sendable () -> Void { + return { [weak object] in + guard let object else { return } + closure(object) + } + } - /// Weakly captures an object in non-parametrized lazy void result closure. - /// - /// Primary idea behind this method is to be able to pass methods without referring to an object. - /// - /// Example: - /// ```swift - /// class MyClass: Weakifiable { - /// func handleCompletion() { - /// // ... - /// } - /// - /// func observe( - /// _ publisher: some Publisher - /// ) -> AnyCancellable { - /// publisher.sink( - /// receiveValue: capture(in: MyClass.handleCompletion) - /// ) - /// } - /// } - /// ``` - /// - /// - Parameter closure: `(Object) -> () -> Void`, most likely it's `ObjectType.someInstanceMethod` - /// - Returns: `() -> Void` handler, created from `(Object) -> () -> Void` closure by weakly capturing object - @inlinable - public func capture( - in closure: @escaping (Object) -> () -> Void - ) -> () -> Void { - return { [weak object] in - guard let object else { return } - closure(object)() - } - } + /// Weakly captures an object in non-parametrized lazy void result closure. + /// + /// Primary idea behind this method is to be able to pass methods without referring to an object. + /// + /// Example: + /// ```swift + /// class MyClass: Weakifiable { + /// func handleCompletion() { + /// // ... + /// } + /// + /// func observe( + /// _ publisher: some Publisher + /// ) -> AnyCancellable { + /// publisher.sink( + /// receiveValue: capture(in: MyClass.handleCompletion) + /// ) + /// } + /// } + /// ``` + /// + /// - Parameter closure: `(Object) -> () -> Void`, most likely it's `ObjectType.someInstanceMethod` + /// - Returns: `() -> Void` handler, created from `(Object) -> () -> Void` closure by weakly capturing object + @inlinable + public func _capture( + in closure: @escaping @Sendable (Object) -> () -> Void + ) -> @Sendable () -> Void { + return { [weak object] in + guard let object else { return } + closure(object)() + } + } - /// Weakly captures an object in parametrized void result closure. - /// - /// > In context of this doc "`A, B, C...`" is a shortcut for 1 or more types. - /// - /// Creates `(A, B, C...) -> Void` handler from `(Object, A, B, C...) -> Void` closure - /// so you can access weakly captured Object in your handler if the object is present. - /// - /// Example: - /// ```swift - /// class MyClass: Weakifiable { - /// func observe( - /// _ publisher: some Publisher - /// ) -> AnyCancellable { - /// publisher.sink( - /// // number of params comes from publisher, if it was - /// // some completion with multiple args, all the args - /// // would've been passed to the capture method - /// receiveValue: capture { _self, value in - /// // ... - /// } - /// ) - /// } - /// } - /// ``` - /// - /// - Parameter closure: `(Object, A, B, C...) -> Void`, where Object is unwrapped weakly captured object. - /// - Returns: `(A, B, C...) -> Void` handler, created from `(Object, A, B, C) -> Void` closure by weakly capturing object. - public func capture( - in closure: @escaping (Object, repeat each Arg) -> Void - ) -> (repeat each Arg) -> Void { - return { [weak object] (arg: repeat each Arg) in - guard let object else { return } - closure(object, repeat each arg) - } - } + /// Weakly captures an object in parametrized void result closure. + /// + /// > In context of this doc "`A, B, C...`" is a shortcut for 1 or more types. + /// + /// Creates `(A, B, C...) -> Void` handler from `(Object, A, B, C...) -> Void` closure + /// so you can access weakly captured Object in your handler if the object is present. + /// + /// Example: + /// ```swift + /// class MyClass: Weakifiable { + /// func observe( + /// _ publisher: some Publisher + /// ) -> AnyCancellable { + /// publisher.sink( + /// // number of params comes from publisher, if it was + /// // some completion with multiple args, all the args + /// // would've been passed to the capture method + /// receiveValue: capture { _self, value in + /// // ... + /// } + /// ) + /// } + /// } + /// ``` + /// + /// - Parameter closure: `(Object, A, B, C...) -> Void`, where Object is unwrapped weakly captured object. + /// - Returns: `(A, B, C...) -> Void` handler, created from `(Object, A, B, C) -> Void` closure by weakly capturing object. + public func _capture( + in closure: @escaping @Sendable (Object, repeat each Arg) -> Void + ) -> @Sendable (repeat each Arg) -> Void { + return { [weak object] (arg: repeat each Arg) in + guard let object else { return } + closure(object, repeat each arg) + } + } } // MARK: - Non-void result closures extension OptionalReferenceContainerProtocol { - /// Weakly captures an object in non-parametrized non-void result closure. - /// - /// Creates `() -> Output` handler from `(Object) -> Output` closure. - /// so you can access weakly captured Object in your handler if the object is present. - /// - /// Unwrapping happens on the call of returned closure. - /// - /// Example: - /// ```swift - /// class MyClass: Weakifiable { - /// var value: Int = 0 - /// - /// func createZeroDataSource() -> () -> Int { - /// return capture(orReturn: -1) { _self in - /// return 0 - /// } - /// } - /// - /// func createValueDataSource() -> () -> Int { - /// return capture(orReturn: -1, in: \.value) - /// } - /// } - /// ``` - /// - /// - Parameters: - /// - defaultValue: Default value to be returned if object was deinitialized when returned handler was called. - /// - closure: `(Object) -> Output`, where Object is unwrapped weakly captured object. - /// - Returns: `() -> Output` handler, created from `(Object) -> Output` closure by weakly capturing object. - @inlinable - public func capture( - orReturn defaultValue: @escaping @autoclosure () -> Output, - in closure: @escaping (Object) -> Output - ) -> () -> Output { - return { [weak object] in - guard let object else { return defaultValue() } - return closure(object) - } - } + /// Weakly captures an object in non-parametrized non-void result closure. + /// + /// Creates `() -> Output` handler from `(Object) -> Output` closure. + /// so you can access weakly captured Object in your handler if the object is present. + /// + /// Unwrapping happens on the call of returned closure. + /// + /// Example: + /// ```swift + /// class MyClass: Weakifiable { + /// var value: Int = 0 + /// + /// func createZeroDataSource() -> () -> Int { + /// return capture(orReturn: -1) { _self in + /// return 0 + /// } + /// } + /// + /// func createValueDataSource() -> () -> Int { + /// return capture(orReturn: -1, in: \.value) + /// } + /// } + /// ``` + /// + /// - Parameters: + /// - defaultValue: Default value to be returned if object was deinitialized when returned handler was called. + /// - closure: `(Object) -> Output`, where Object is unwrapped weakly captured object. + /// - Returns: `() -> Output` handler, created from `(Object) -> Output` closure by weakly capturing object. + @inlinable + public func capture( + orReturn defaultValue: @escaping @autoclosure () -> Output, + in closure: @escaping (Object) -> Output + ) -> () -> Output { + return { [weak object] in + guard let object else { return defaultValue() } + return closure(object) + } + } + + /// Weakly captures an object in non-parametrized lazy non-void result closure. + /// + /// Primary idea behind this method is to be able to pass methods without referring to an object. + /// + /// Example: + /// ```swift + /// class MyClass: Weakifiable { + /// // `MyClass.getData` signature is `(MyClass) -> () -> Int` + /// func getData() -> Int { + /// return 0 + /// } + /// + /// func createDataSource() -> () -> Int { + /// return capture(orReturn: -1, in: MyClass.getData) + /// } + /// } + /// ``` + /// + /// - Parameter closure: `(Object) -> () -> Output`, most likely it's `ObjectType.someInstanceMethod`. + /// - Returns: `() -> Output` handler, created from `(Object) -> () -> Output` closure by weakly capturing object. + @inlinable + public func capture( + orReturn defaultValue: @escaping @autoclosure () -> Output, + in closure: @escaping (Object) -> () -> Output + ) -> () -> Output { + return { [weak object] in + guard let object else { return defaultValue() } + return closure(object)() + } + } + + /// Weakly captures an object in parametrized non-void result closure. + /// + /// > In context of this doc "`A, B, C...`" is a shortcut for 1 or more types. + /// + /// Creates `(A, B, C...) -> Output` handler from `(Object, A, B, C...) -> Output` closure + /// so you can access weakly captured Object in your handler if the object is present. + /// + /// Example: + /// ```swift + /// class MyClass: Weakifiable { + /// func createDataSource() -> (Bool) -> Int { + /// // number of params comes from publisher, if it was + /// // some completion with multiple args, all the args + /// // would've been passed to the capture method + /// return capture(orReturn: -1) { _self, isZero in + /// return isZero ? 0 : 1 + /// } + /// } + /// } + /// ``` + /// + /// - Parameter closure: `(Object, A, B, C...) -> Output`, where Object is unwrapped weakly captured object. + /// - Returns: `(A, B, C...) -> Output` handler, created from `(Object, A, B, C) -> Output` closure by weakly capturing object. + public func capture( + orReturn defaultValue: @escaping @autoclosure () -> Output, + in closure: @escaping (Object, repeat each Arg) -> Output + ) -> (repeat each Arg) -> Output { + return { [weak object] (arg: repeat each Arg) in + guard let object else { return defaultValue() } + return closure(object, repeat each arg) + } + } +} + +// MARK: Sendable + +extension OptionalReferenceContainerProtocol where Object: Sendable { + /// Weakly captures an object in non-parametrized non-void result closure. + /// + /// Creates `() -> Output` handler from `(Object) -> Output` closure. + /// so you can access weakly captured Object in your handler if the object is present. + /// + /// Unwrapping happens on the call of returned closure. + /// + /// Example: + /// ```swift + /// class MyClass: Weakifiable { + /// var value: Int = 0 + /// + /// func createZeroDataSource() -> () -> Int { + /// return capture(orReturn: -1) { _self in + /// return 0 + /// } + /// } + /// + /// func createValueDataSource() -> () -> Int { + /// return capture(orReturn: -1, in: \.value) + /// } + /// } + /// ``` + /// + /// - Parameters: + /// - defaultValue: Default value to be returned if object was deinitialized when returned handler was called. + /// - closure: `(Object) -> Output`, where Object is unwrapped weakly captured object. + /// - Returns: `() -> Output` handler, created from `(Object) -> Output` closure by weakly capturing object. + @inlinable + public func _capture( + orReturn defaultValue: @escaping @autoclosure @Sendable () -> Output, + in closure: @escaping @Sendable (Object) -> Output + ) -> @Sendable () -> Output { + return { [weak object] in + guard let object else { return defaultValue() } + return closure(object) + } + } - /// Weakly captures an object in non-parametrized lazy non-void result closure. - /// - /// Primary idea behind this method is to be able to pass methods without referring to an object. - /// - /// Example: - /// ```swift - /// class MyClass: Weakifiable { - /// // `MyClass.getData` signature is `(MyClass) -> () -> Int` - /// func getData() -> Int { - /// return 0 - /// } - /// - /// func createDataSource() -> () -> Int { - /// return capture(orReturn: -1, in: MyClass.getData) - /// } - /// } - /// ``` - /// - /// - Parameter closure: `(Object) -> () -> Output`, most likely it's `ObjectType.someInstanceMethod`. - /// - Returns: `() -> Output` handler, created from `(Object) -> () -> Output` closure by weakly capturing object. - @inlinable - public func capture( - orReturn defaultValue: @escaping @autoclosure () -> Output, - in closure: @escaping (Object) -> () -> Output - ) -> () -> Output { - return { [weak object] in - guard let object else { return defaultValue() } - return closure(object)() - } - } + /// Weakly captures an object in non-parametrized lazy non-void result closure. + /// + /// Primary idea behind this method is to be able to pass methods without referring to an object. + /// + /// Example: + /// ```swift + /// class MyClass: Weakifiable { + /// // `MyClass.getData` signature is `(MyClass) -> () -> Int` + /// func getData() -> Int { + /// return 0 + /// } + /// + /// func createDataSource() -> () -> Int { + /// return capture(orReturn: -1, in: MyClass.getData) + /// } + /// } + /// ``` + /// + /// - Parameter closure: `(Object) -> () -> Output`, most likely it's `ObjectType.someInstanceMethod`. + /// - Returns: `() -> Output` handler, created from `(Object) -> () -> Output` closure by weakly capturing object. + @inlinable + public func _capture( + orReturn defaultValue: @escaping @autoclosure @Sendable () -> Output, + in closure: @escaping @Sendable (Object) -> () -> Output + ) -> @Sendable () -> Output { + return { [weak object] in + guard let object else { return defaultValue() } + return closure(object)() + } + } - /// Weakly captures an object in parametrized non-void result closure. - /// - /// > In context of this doc "`A, B, C...`" is a shortcut for 1 or more types. - /// - /// Creates `(A, B, C...) -> Output` handler from `(Object, A, B, C...) -> Output` closure - /// so you can access weakly captured Object in your handler if the object is present. - /// - /// Example: - /// ```swift - /// class MyClass: Weakifiable { - /// func createDataSource() -> (Bool) -> Int { - /// // number of params comes from publisher, if it was - /// // some completion with multiple args, all the args - /// // would've been passed to the capture method - /// return capture(orReturn: -1) { _self, isZero in - /// return isZero ? 0 : 1 - /// } - /// } - /// } - /// ``` - /// - /// - Parameter closure: `(Object, A, B, C...) -> Output`, where Object is unwrapped weakly captured object. - /// - Returns: `(A, B, C...) -> Output` handler, created from `(Object, A, B, C) -> Output` closure by weakly capturing object. - public func capture( - orReturn defaultValue: @escaping @autoclosure () -> Output, - in closure: @escaping (Object, repeat each Arg) -> Output - ) -> (repeat each Arg) -> Output { - return { [weak object] (arg: repeat each Arg) in - guard let object else { return defaultValue() } - return closure(object, repeat each arg) - } - } + /// Weakly captures an object in parametrized non-void result closure. + /// + /// > In context of this doc "`A, B, C...`" is a shortcut for 1 or more types. + /// + /// Creates `(A, B, C...) -> Output` handler from `(Object, A, B, C...) -> Output` closure + /// so you can access weakly captured Object in your handler if the object is present. + /// + /// Example: + /// ```swift + /// class MyClass: Weakifiable { + /// func createDataSource() -> (Bool) -> Int { + /// // number of params comes from publisher, if it was + /// // some completion with multiple args, all the args + /// // would've been passed to the capture method + /// return capture(orReturn: -1) { _self, isZero in + /// return isZero ? 0 : 1 + /// } + /// } + /// } + /// ``` + /// + /// - Parameter closure: `(Object, A, B, C...) -> Output`, where Object is unwrapped weakly captured object. + /// - Returns: `(A, B, C...) -> Output` handler, created from `(Object, A, B, C) -> Output` closure by weakly capturing object. + public func _capture( + orReturn defaultValue: @escaping @autoclosure @Sendable () -> Output, + in closure: @escaping @Sendable (Object, repeat each Arg) -> Output + ) -> @Sendable (repeat each Arg) -> Output { + return { [weak object] (arg: repeat each Arg) in + guard let object else { return defaultValue() } + return closure(object, repeat each arg) + } + } } + // MARK: - Non-void optional result closures extension OptionalReferenceContainerProtocol { - /// Weakly captures an object in non-parametrized non-void optional resilt closure. - /// - /// Creates `() -> Output?` handler from `(Object) -> Output?` closure. - /// so you can access weakly captured Object in your handler if the object is present. - /// - /// Unwrapping happens on the call of returned closure. - /// Handler will return nil if object was already deinitialized. - /// - /// Example: - /// ```swift - /// class MyClass: Weakifiable { - /// var value: Int = 0 - /// - /// func createZeroDataSource() -> () -> Int? { - /// return capture { _self in - /// return _self.value - /// } - /// } - /// } - /// ``` - /// - /// - Parameters: - /// - closure: `(Object) -> Output?`, where Object is unwrapped weakly captured object. - /// - Returns: `() -> Output?` handler, created from `(Object) -> Output?` closure by weakly capturing object. Handler will return nil if object was already deinitialized. - @inlinable - public func capture( - in closure: @escaping (Object) -> Output? - ) -> () -> Output? { - return { [weak object] in - guard let object else { return nil } - return closure(object) - } - } + /// Weakly captures an object in non-parametrized non-void optional resilt closure. + /// + /// Creates `() -> Output?` handler from `(Object) -> Output?` closure. + /// so you can access weakly captured Object in your handler if the object is present. + /// + /// Unwrapping happens on the call of returned closure. + /// Handler will return nil if object was already deinitialized. + /// + /// Example: + /// ```swift + /// class MyClass: Weakifiable { + /// var value: Int = 0 + /// + /// func createZeroDataSource() -> () -> Int? { + /// return capture { _self in + /// return _self.value + /// } + /// } + /// } + /// ``` + /// + /// - Parameters: + /// - closure: `(Object) -> Output?`, where Object is unwrapped weakly captured object. + /// - Returns: `() -> Output?` handler, created from `(Object) -> Output?` closure by weakly capturing object. Handler will return nil if object was already deinitialized. + @inlinable + public func capture( + in closure: @escaping (Object) -> Output? + ) -> () -> Output? { + return { [weak object] in + guard let object else { return nil } + return closure(object) + } + } + + /// Weakly captures an object in non-parametrized lazy non-void optional result closure. + /// + /// Primary idea behind this method is to be able to pass methods without referring to an object. + /// + /// Handler will return nil if object was already deinitialized. + /// + /// Example: + /// ```swift + /// class MyClass: Weakifiable { + /// // `MyClass.getData` signature is `(MyClass) -> () -> Int?` + /// func getData() -> Int? { + /// return 0 + /// } + /// + /// func createDataSource() -> () -> Int? { + /// return capture(orReturn: -1, in: MyClass.getData) + /// } + /// } + /// ``` + /// + /// - Parameter closure: `(Object) -> () -> Output?`, most likely it's `ObjectType.someInstanceMethod`. + /// - Returns: `() -> Output?` handler, created from `(Object) -> () -> Output?` closure by weakly capturing object. + @inlinable + public func capture( + in closure: @escaping (Object) -> () -> Output? + ) -> () -> Output? { + return { [weak object] in + guard let object else { return nil } + return closure(object)() + } + } + + /// Weakly captures an object in parametrized non-void optional result closure. + /// + /// > In context of this doc "`A, B, C...`" is a shortcut for 1 or more types. + /// + /// Creates `(A, B, C...) -> Output?` handler from `(Object, A, B, C...) -> Output?` closure + /// so you can access weakly captured Object in your handler if the object is present. + /// + /// Handler will return nil if object was already deinitialized. + /// + /// Example: + /// ```swift + /// class MyClass: Weakifiable { + /// func createDataSource() -> (Bool) -> Int? { + /// // number of params comes from publisher, if it was + /// // some completion with multiple args, all the args + /// // would've been passed to the capture method + /// return capture(orReturn: -1) { _self, isZero in + /// return isZero ? 0 : 1 + /// } + /// } + /// } + /// ``` + /// + /// - Parameter closure: `(Object, A, B, C...) -> Void`, where Object is unwrapped weakly captured object. + /// - Returns: `(A, B, C...) -> Output?` handler, created from `(Object, A, B, C) -> Output?` closure by weakly capturing object. + public func capture( + in closure: @escaping (Object, repeat each Arg) -> Output? + ) -> (repeat each Arg) -> Output? { + return { [weak object] (arg: repeat each Arg) in + guard let object else { return nil } + return closure(object, repeat each arg) + } + } +} + +// MARK: Sendable - /// Weakly captures an object in non-parametrized lazy non-void optional result closure. - /// - /// Primary idea behind this method is to be able to pass methods without referring to an object. - /// - /// Handler will return nil if object was already deinitialized. - /// - /// Example: - /// ```swift - /// class MyClass: Weakifiable { - /// // `MyClass.getData` signature is `(MyClass) -> () -> Int?` - /// func getData() -> Int? { - /// return 0 - /// } - /// - /// func createDataSource() -> () -> Int? { - /// return capture(orReturn: -1, in: MyClass.getData) - /// } - /// } - /// ``` - /// - /// - Parameter closure: `(Object) -> () -> Output?`, most likely it's `ObjectType.someInstanceMethod`. - /// - Returns: `() -> Output?` handler, created from `(Object) -> () -> Output?` closure by weakly capturing object. - @inlinable - public func capture( - in closure: @escaping (Object) -> () -> Output? - ) -> () -> Output? { - return { [weak object] in - guard let object else { return nil } - return closure(object)() - } - } +extension OptionalReferenceContainerProtocol where Object: Sendable { + /// Weakly captures an object in non-parametrized non-void optional resilt closure. + /// + /// Creates `() -> Output?` handler from `(Object) -> Output?` closure. + /// so you can access weakly captured Object in your handler if the object is present. + /// + /// Unwrapping happens on the call of returned closure. + /// Handler will return nil if object was already deinitialized. + /// + /// Example: + /// ```swift + /// class MyClass: Weakifiable { + /// var value: Int = 0 + /// + /// func createZeroDataSource() -> () -> Int? { + /// return capture { _self in + /// return _self.value + /// } + /// } + /// } + /// ``` + /// + /// - Parameters: + /// - closure: `(Object) -> Output?`, where Object is unwrapped weakly captured object. + /// - Returns: `() -> Output?` handler, created from `(Object) -> Output?` closure by weakly capturing object. Handler will return nil if object was already deinitialized. + @inlinable + public func _capture( + in closure: @escaping @Sendable (Object) -> Output? + ) -> @Sendable () -> Output? { + return { [weak object] in + guard let object else { return nil } + return closure(object) + } + } - /// Weakly captures an object in parametrized non-void optional result closure. - /// - /// > In context of this doc "`A, B, C...`" is a shortcut for 1 or more types. - /// - /// Creates `(A, B, C...) -> Output?` handler from `(Object, A, B, C...) -> Output?` closure - /// so you can access weakly captured Object in your handler if the object is present. - /// - /// Handler will return nil if object was already deinitialized. - /// - /// Example: - /// ```swift - /// class MyClass: Weakifiable { - /// func createDataSource() -> (Bool) -> Int? { - /// // number of params comes from publisher, if it was - /// // some completion with multiple args, all the args - /// // would've been passed to the capture method - /// return capture(orReturn: -1) { _self, isZero in - /// return isZero ? 0 : 1 - /// } - /// } - /// } - /// ``` - /// - /// - Parameter closure: `(Object, A, B, C...) -> Void`, where Object is unwrapped weakly captured object. - /// - Returns: `(A, B, C...) -> Output?` handler, created from `(Object, A, B, C) -> Output?` closure by weakly capturing object. - public func capture( - in closure: @escaping (Object, repeat each Arg) -> Output? - ) -> (repeat each Arg) -> Output? { - return { [weak object] (arg: repeat each Arg) in - guard let object else { return nil } - return closure(object, repeat each arg) - } - } + /// Weakly captures an object in non-parametrized lazy non-void optional result closure. + /// + /// Primary idea behind this method is to be able to pass methods without referring to an object. + /// + /// Handler will return nil if object was already deinitialized. + /// + /// Example: + /// ```swift + /// class MyClass: Weakifiable { + /// // `MyClass.getData` signature is `(MyClass) -> () -> Int?` + /// func getData() -> Int? { + /// return 0 + /// } + /// + /// func createDataSource() -> () -> Int? { + /// return capture(orReturn: -1, in: MyClass.getData) + /// } + /// } + /// ``` + /// + /// - Parameter closure: `(Object) -> () -> Output?`, most likely it's `ObjectType.someInstanceMethod`. + /// - Returns: `() -> Output?` handler, created from `(Object) -> () -> Output?` closure by weakly capturing object. + @inlinable + public func _capture( + in closure: @escaping @Sendable (Object) -> () -> Output? + ) -> @Sendable () -> Output? { + return { [weak object] in + guard let object else { return nil } + return closure(object)() + } + } + + /// Weakly captures an object in parametrized non-void optional result closure. + /// + /// > In context of this doc "`A, B, C...`" is a shortcut for 1 or more types. + /// + /// Creates `(A, B, C...) -> Output?` handler from `(Object, A, B, C...) -> Output?` closure + /// so you can access weakly captured Object in your handler if the object is present. + /// + /// Handler will return nil if object was already deinitialized. + /// + /// Example: + /// ```swift + /// class MyClass: Weakifiable { + /// func createDataSource() -> (Bool) -> Int? { + /// // number of params comes from publisher, if it was + /// // some completion with multiple args, all the args + /// // would've been passed to the capture method + /// return capture(orReturn: -1) { _self, isZero in + /// return isZero ? 0 : 1 + /// } + /// } + /// } + /// ``` + /// + /// - Parameter closure: `(Object, A, B, C...) -> Void`, where Object is unwrapped weakly captured object. + /// - Returns: `(A, B, C...) -> Output?` handler, created from `(Object, A, B, C) -> Output?` closure by weakly capturing object. + public func _capture( + in closure: @escaping @Sendable (Object, repeat each Arg) -> Output? + ) -> @Sendable (repeat each Arg) -> Output? { + return { [weak object] (arg: repeat each Arg) in + guard let object else { return nil } + return closure(object, repeat each arg) + } + } } + diff --git a/Sources/Capture/Weak.swift b/Sources/Capture/Weak.swift index 38ffa0d..2395784 100644 --- a/Sources/Capture/Weak.swift +++ b/Sources/Capture/Weak.swift @@ -13,116 +13,156 @@ import Foundation /// But there is a recommended usecase: workarounds for poorly designed APIs with ``capture(_:)-swift.type.method`` 😅 @propertyWrapper public struct Weak: OptionalReferenceContainerProtocol { + + /// Weak reference to an object + public weak var wrappedValue: Object? + + /// Convenience property to access wrappedValue with better semantics when used inline + @inlinable + public var object: Object? { + get { wrappedValue } + set { wrappedValue = newValue } + } + + /// Creates a reference-type box with a weak reference the object + @inlinable + public var projectedValue: Box { + return .init(wrappedValue) + } + + /// Convenience property to access projectedValue with better semantics when used inline + @inlinable + public var box: Box { + return projectedValue + } + + public init() {} + + public init(wrappedValue: Object?) { + self.wrappedValue = wrappedValue + } + + @inlinable + public init(_ object: Object?) { + self.init(wrappedValue: object) + } + + /// Passes capturable box in a closure, box object is set to returned value after return + /// + /// Recommended usecase: workarounds for poorly designed APIs 😅 + /// ```swift + /// return Weak.capture { box in + /// return createFancyBottomSheet(onLinkTapped: { url in + /// guard let controller = box.object else { return } + /// openWebView(url, in: controller) + /// }) + /// } + /// ``` + /// instead of + /// ```swift + /// var controller: UIViewController + /// controller = createFancyBottomSheet(onLinkTapped: { [weak controller] url in + /// guard let controller else { return } + /// openWebView(url, in: controller) + /// }) + /// return controller + /// ``` + @inlinable + public static func capture( + _ initializer: (Box) -> Object + ) -> Object { + let box = Box() + let object = initializer(box) + box.wrappedValue = object + return object + } - /// Weak reference to an object - public weak var wrappedValue: Object? - - /// Convenience property to access wrappedValue with better semantics when used inline - @inlinable - public var object: Object? { - get { wrappedValue } - set { wrappedValue = newValue } - } - - /// Creates a reference-type box with a weak reference the object - @inlinable - public var projectedValue: Box { - return .init(wrappedValue) - } - - /// Convenience property to access projectedValue with better semantics when used inline - @inlinable - public var box: Box { - return projectedValue - } - - public init() {} - - public init(wrappedValue: Object?) { - self.wrappedValue = wrappedValue - } - - @inlinable - public init(_ object: Object?) { - self.init(wrappedValue: object) - } - - /// Passes capturable box in a closure, box object is set to returned value after return - /// - /// Recommended usecase: workarounds for poorly designed APIs 😅 - /// ```swift - /// return Weak.capture { box in - /// return createFancyBottomSheet(onLinkTapped: { url in - /// guard let controller = box.object else { return } - /// openWebView(url, in: controller) - /// }) - /// } - /// ``` - /// instead of - /// ```swift - /// var controller: UIViewController - /// controller = createFancyBottomSheet(onLinkTapped: { [weak controller] url in - /// guard let controller else { return } - /// openWebView(url, in: controller) - /// }) - /// return controller - /// ``` - @inlinable - public static func capture( - _ initializer: (Box) -> Object - ) -> Object { - let box = Box() - let object = initializer(box) - box.wrappedValue = object - return object - } + /// Passes capturable box in a closure, box object is set to returned value after return + /// + /// Recommended usecase: workarounds for poorly designed APIs 😅 + /// ```swift + /// return Weak._capture { box in + /// return createFancyBottomSheet(onLinkTapped: { url in + /// guard let controller = box.object else { return } + /// openWebView(url, in: controller) + /// }) + /// } + /// ``` + /// instead of + /// ```swift + /// var controller: UIViewController + /// controller = createFancyBottomSheet(onLinkTapped: { [weak controller] url in + /// guard let controller else { return } + /// openWebView(url, in: controller) + /// }) + /// return controller + /// ``` + @inlinable + public static func _capture( + _ initializer: (UncheckedSendableBox) -> Object + ) -> Object { + let box = UncheckedSendableBox() + let object = initializer(box) + box.wrappedValue = object + return object + } } extension Weak { - /// Value-type container for capturing objects weakly - /// - /// We believe that most APIs should not be too opinionated and this package provides - /// various ways to capture objects, so feel free to use it as property wrapper or plain property/variable. - /// - /// However opinionated approach removes cognitive load while working with APIs, so we do also provide recomendations. - /// - /// It's recommended to conform your objects to ``Weakifiable`` protocol instead of using this type if possible - /// or use `_capture(...)` methods directly if not. - /// - /// But there is a recommended usecase: workarounds for poorly designed APIs with ``capture(_:)-swift.type.method`` 😅 - @propertyWrapper - public final class Box: OptionalReferenceContainerProtocol { - /// Weak reference to an object - public weak var wrappedValue: Object? = nil + /// Value-type container for capturing objects weakly + /// + /// We believe that most APIs should not be too opinionated and this package provides + /// various ways to capture objects, so feel free to use it as property wrapper or plain property/variable. + /// + /// However opinionated approach removes cognitive load while working with APIs, so we do also provide recomendations. + /// + /// It's recommended to conform your objects to ``Weakifiable`` protocol instead of using this type if possible + /// or use `_capture(...)` methods directly if not. + /// + /// But there is a recommended usecase: workarounds for poorly designed APIs with ``capture(_:)-swift.type.method`` 😅 + @propertyWrapper + public class Box: OptionalReferenceContainerProtocol { + /// Weak reference to an object + public weak var wrappedValue: Object? = nil + + /// Convenience property to access wrappedValue with better semantics when used inline + @inlinable + public var object: Object? { + get { wrappedValue } + set { wrappedValue = newValue } + } + + /// Creates a value-type ``Weak`` container with a weak reference the object + @inlinable + public var projectedValue: Weak { + return .init(wrappedValue) + } + + /// Convenience method to access ``projectedValue`` with better semantics when used inline + /// + /// - Returns: Value-type weak reference to an object + @inlinable + public func unboxed() -> Weak { projectedValue } + + public init() {} + + public init(wrappedValue: Object?) { + self.wrappedValue = wrappedValue + } + + @inlinable + public convenience init(_ object: Object?) { + self.init(wrappedValue: object) + } + } - /// Convenience property to access wrappedValue with better semantics when used inline - @inlinable - public var object: Object? { - get { wrappedValue } - set { wrappedValue = newValue } - } - - /// Creates a value-type ``Weak`` container with a weak reference the object - @inlinable - public var projectedValue: Weak { - return .init(wrappedValue) - } - - /// Convenience method to access ``projectedValue`` with better semantics when used inline - /// - /// - Returns: Value-type weak reference to an object - @inlinable - public func unboxed() -> Weak { projectedValue } - - public init() {} - - public init(wrappedValue: Object?) { - self.wrappedValue = wrappedValue - } - - @inlinable - public convenience init(_ object: Object?) { - self.init(wrappedValue: object) - } - } + @propertyWrapper + public class UncheckedSendableBox: Box, @unchecked Sendable { + public override var wrappedValue: Object? { + get { super.object } + set { super.object = newValue } + } + } } + +extension Weak: Sendable where Object: Sendable {} diff --git a/Sources/Capture/Weakifiable.swift b/Sources/Capture/Weakifiable.swift index 5a9ec96..b0bee47 100644 --- a/Sources/Capture/Weakifiable.swift +++ b/Sources/Capture/Weakifiable.swift @@ -10,290 +10,290 @@ extension NSObject: Weakifiable {} // MARK: - Void result closures extension Weakifiable { - /// Weakly captures an object in non-parametrized void result closure. - /// - /// Creates `() -> Void` handler from `(Object) -> Void` closure - /// so you can access weakly captured Object in your handler if the object is present. - /// - /// Unwrapping happens on the call of returned closure. - /// - /// Example: - /// ```swift - /// class MyClass: Weakifiable { - /// func observe( - /// _ publisher: some Publisher - /// ) -> AnyCancellable { - /// publisher.sink( - /// receiveValue: capture { _self in - /// // ... - /// } - /// ) - /// } - /// } - /// ``` - /// - /// - Parameter closure: `(Object) -> Void`, where Object is unwrapped weakly captured object. - /// - Returns: `() -> Void` handler, created from `(Object) -> Void` closure by weakly capturing object. - @inlinable - public func capture( - in closure: @escaping (Self) -> Void - ) -> () -> Void { - return Weak(self).capture(in: closure) - } - - /// Weakly captures an object in non-parametrized lazy void result closure. - /// - /// Primary idea behind this method is to be able to pass methods without referring to an object. - /// - /// Example: - /// ```swift - /// class MyClass: Weakifiable { - /// func handleCompletion() { - /// // ... - /// } - /// - /// func observe( - /// _ publisher: some Publisher - /// ) -> AnyCancellable { - /// publisher.sink( - /// receiveValue: capture(in: MyClass.handleCompletion) - /// ) - /// } - /// } - /// ``` - /// - /// - Parameter closure: `(Object) -> () -> Void`, most likely it's `ObjectType.someInstanceMethod` - /// - Returns: `() -> Void` handler, created from `(Object) -> () -> Void` closure by weakly capturing object - @inlinable - public func capture( - in closure: @escaping (Self) -> () -> Void - ) -> () -> Void { - return Weak(self).capture(in: closure) - } - - /// Weakly captures an object in parametrized void result closure. - /// - /// > In context of this doc "`A, B, C...`" is a shortcut for 1 or more types. - /// - /// Creates `(A, B, C...) -> Void` handler from `(Object, A, B, C...) -> Void` closure - /// so you can access weakly captured Object in your handler if the object is present. - /// - /// Example: - /// ```swift - /// class MyClass: Weakifiable { - /// func observe( - /// _ publisher: some Publisher - /// ) -> AnyCancellable { - /// publisher.sink( - /// // number of params comes from publisher, if it was - /// // some completion with multiple args, all the args - /// // would've been passed to the capture method - /// receiveValue: capture { _self, value in - /// // ... - /// } - /// ) - /// } - /// } - /// ``` - /// - /// - Parameter closure: `(Object, A, B, C...) -> Void`, where Object is unwrapped weakly captured object. - /// - Returns: `(A, B, C...) -> Void` handler, created from `(Object, A, B, C) -> Void` closure by weakly capturing object. - public func capture( - in closure: @escaping (Self, repeat each Arg) -> Void - ) -> (repeat each Arg) -> Void { - return Weak(self).capture(in: closure) - } + /// Weakly captures an object in non-parametrized void result closure. + /// + /// Creates `() -> Void` handler from `(Object) -> Void` closure + /// so you can access weakly captured Object in your handler if the object is present. + /// + /// Unwrapping happens on the call of returned closure. + /// + /// Example: + /// ```swift + /// class MyClass: Weakifiable { + /// func observe( + /// _ publisher: some Publisher + /// ) -> AnyCancellable { + /// publisher.sink( + /// receiveValue: capture { _self in + /// // ... + /// } + /// ) + /// } + /// } + /// ``` + /// + /// - Parameter closure: `(Object) -> Void`, where Object is unwrapped weakly captured object. + /// - Returns: `() -> Void` handler, created from `(Object) -> Void` closure by weakly capturing object. + @inlinable + public func capture( + in closure: @escaping (Self) -> Void + ) -> () -> Void { + return Weak(self).capture(in: closure) + } + + /// Weakly captures an object in non-parametrized lazy void result closure. + /// + /// Primary idea behind this method is to be able to pass methods without referring to an object. + /// + /// Example: + /// ```swift + /// class MyClass: Weakifiable { + /// func handleCompletion() { + /// // ... + /// } + /// + /// func observe( + /// _ publisher: some Publisher + /// ) -> AnyCancellable { + /// publisher.sink( + /// receiveValue: capture(in: MyClass.handleCompletion) + /// ) + /// } + /// } + /// ``` + /// + /// - Parameter closure: `(Object) -> () -> Void`, most likely it's `ObjectType.someInstanceMethod` + /// - Returns: `() -> Void` handler, created from `(Object) -> () -> Void` closure by weakly capturing object + @inlinable + public func capture( + in closure: @escaping (Self) -> () -> Void + ) -> () -> Void { + return Weak(self).capture(in: closure) + } + + /// Weakly captures an object in parametrized void result closure. + /// + /// > In context of this doc "`A, B, C...`" is a shortcut for 1 or more types. + /// + /// Creates `(A, B, C...) -> Void` handler from `(Object, A, B, C...) -> Void` closure + /// so you can access weakly captured Object in your handler if the object is present. + /// + /// Example: + /// ```swift + /// class MyClass: Weakifiable { + /// func observe( + /// _ publisher: some Publisher + /// ) -> AnyCancellable { + /// publisher.sink( + /// // number of params comes from publisher, if it was + /// // some completion with multiple args, all the args + /// // would've been passed to the capture method + /// receiveValue: capture { _self, value in + /// // ... + /// } + /// ) + /// } + /// } + /// ``` + /// + /// - Parameter closure: `(Object, A, B, C...) -> Void`, where Object is unwrapped weakly captured object. + /// - Returns: `(A, B, C...) -> Void` handler, created from `(Object, A, B, C) -> Void` closure by weakly capturing object. + public func capture( + in closure: @escaping (Self, repeat each Arg) -> Void + ) -> (repeat each Arg) -> Void { + return Weak(self).capture(in: closure) + } } // MARK: - Non-void result closures extension Weakifiable { - /// Weakly captures an object in non-parametrized non-void result closure. - /// - /// Creates `() -> Output` handler from `(Object) -> Output` closure. - /// so you can access weakly captured Object in your handler if the object is present. - /// - /// Unwrapping happens on the call of returned closure. - /// - /// Example: - /// ```swift - /// class MyClass: Weakifiable { - /// var value: Int = 0 - /// - /// func createZeroDataSource() -> () -> Int { - /// return capture(orReturn: -1) { _self in - /// return 0 - /// } - /// } - /// - /// func createValueDataSource() -> () -> Int { - /// return capture(orReturn: -1, in: \.value) - /// } - /// } - /// ``` - /// - /// - Parameters: - /// - defaultValue: Default value to be returned if object was deinitialized when returned handler was called. - /// - closure: `(Object) -> Output`, where Object is unwrapped weakly captured object. - /// - Returns: `() -> Output` handler, created from `(Object) -> Output` closure by weakly capturing object. - @inlinable - public func capture( - orReturn defaultValue: @escaping @autoclosure () -> Output, - in closure: @escaping (Self) -> Output - ) -> () -> Output { - return Weak(self).capture(orReturn: defaultValue(), in: closure) - } - - /// Weakly captures an object in non-parametrized lazy non-void result closure. - /// - /// Primary idea behind this method is to be able to pass methods without referring to an object. - /// - /// Example: - /// ```swift - /// class MyClass: Weakifiable { - /// // `MyClass.getData` signature is `(MyClass) -> () -> Int` - /// func getData() -> Int { - /// return 0 - /// } - /// - /// func createDataSource() -> () -> Int { - /// return capture(orReturn: -1, in: MyClass.getData) - /// } - /// } - /// ``` - /// - /// - Parameter closure: `(Object) -> () -> Output`, most likely it's `ObjectType.someInstanceMethod`. - /// - Returns: `() -> Output` handler, created from `(Object) -> () -> Output` closure by weakly capturing object. - @inlinable - public func capture( - orReturn defaultValue: @escaping @autoclosure () -> Output, - in closure: @escaping (Self) -> () -> Output - ) -> () -> Output { - return Weak(self).capture(orReturn: defaultValue(), in: closure) - } - - /// Weakly captures an object in parametrized non-void result closure. - /// - /// > In context of this doc "`A, B, C...`" is a shortcut for 1 or more types. - /// - /// Creates `(A, B, C...) -> Output` handler from `(Object, A, B, C...) -> Output` closure - /// so you can access weakly captured Object in your handler if the object is present. - /// - /// Example: - /// ```swift - /// class MyClass: Weakifiable { - /// func createDataSource() -> (Bool) -> Int { - /// // number of params comes from publisher, if it was - /// // some completion with multiple args, all the args - /// // would've been passed to the capture method - /// return capture(orReturn: -1) { _self, isZero in - /// return isZero ? 0 : 1 - /// } - /// } - /// } - /// ``` - /// - /// - Parameter closure: `(Object, A, B, C...) -> Output`, where Object is unwrapped weakly captured object. - /// - Returns: `(A, B, C...) -> Output` handler, created from `(Object, A, B, C) -> Output` closure by weakly capturing object. - public func capture( - orReturn defaultValue: @escaping @autoclosure () -> Output, - in closure: @escaping (Self, repeat each Arg) -> Output - ) -> (repeat each Arg) -> Output { - return Weak(self).capture(orReturn: defaultValue(), in: closure) - } + /// Weakly captures an object in non-parametrized non-void result closure. + /// + /// Creates `() -> Output` handler from `(Object) -> Output` closure. + /// so you can access weakly captured Object in your handler if the object is present. + /// + /// Unwrapping happens on the call of returned closure. + /// + /// Example: + /// ```swift + /// class MyClass: Weakifiable { + /// var value: Int = 0 + /// + /// func createZeroDataSource() -> () -> Int { + /// return capture(orReturn: -1) { _self in + /// return 0 + /// } + /// } + /// + /// func createValueDataSource() -> () -> Int { + /// return capture(orReturn: -1, in: \.value) + /// } + /// } + /// ``` + /// + /// - Parameters: + /// - defaultValue: Default value to be returned if object was deinitialized when returned handler was called. + /// - closure: `(Object) -> Output`, where Object is unwrapped weakly captured object. + /// - Returns: `() -> Output` handler, created from `(Object) -> Output` closure by weakly capturing object. + @inlinable + public func capture( + orReturn defaultValue: @escaping @autoclosure () -> Output, + in closure: @escaping (Self) -> Output + ) -> () -> Output { + return Weak(self).capture(orReturn: defaultValue(), in: closure) + } + + /// Weakly captures an object in non-parametrized lazy non-void result closure. + /// + /// Primary idea behind this method is to be able to pass methods without referring to an object. + /// + /// Example: + /// ```swift + /// class MyClass: Weakifiable { + /// // `MyClass.getData` signature is `(MyClass) -> () -> Int` + /// func getData() -> Int { + /// return 0 + /// } + /// + /// func createDataSource() -> () -> Int { + /// return capture(orReturn: -1, in: MyClass.getData) + /// } + /// } + /// ``` + /// + /// - Parameter closure: `(Object) -> () -> Output`, most likely it's `ObjectType.someInstanceMethod`. + /// - Returns: `() -> Output` handler, created from `(Object) -> () -> Output` closure by weakly capturing object. + @inlinable + public func capture( + orReturn defaultValue: @escaping @autoclosure () -> Output, + in closure: @escaping (Self) -> () -> Output + ) -> () -> Output { + return Weak(self).capture(orReturn: defaultValue(), in: closure) + } + + /// Weakly captures an object in parametrized non-void result closure. + /// + /// > In context of this doc "`A, B, C...`" is a shortcut for 1 or more types. + /// + /// Creates `(A, B, C...) -> Output` handler from `(Object, A, B, C...) -> Output` closure + /// so you can access weakly captured Object in your handler if the object is present. + /// + /// Example: + /// ```swift + /// class MyClass: Weakifiable { + /// func createDataSource() -> (Bool) -> Int { + /// // number of params comes from publisher, if it was + /// // some completion with multiple args, all the args + /// // would've been passed to the capture method + /// return capture(orReturn: -1) { _self, isZero in + /// return isZero ? 0 : 1 + /// } + /// } + /// } + /// ``` + /// + /// - Parameter closure: `(Object, A, B, C...) -> Output`, where Object is unwrapped weakly captured object. + /// - Returns: `(A, B, C...) -> Output` handler, created from `(Object, A, B, C) -> Output` closure by weakly capturing object. + public func capture( + orReturn defaultValue: @escaping @autoclosure () -> Output, + in closure: @escaping (Self, repeat each Arg) -> Output + ) -> (repeat each Arg) -> Output { + return Weak(self).capture(orReturn: defaultValue(), in: closure) + } } // MARK: - Non-void optional result closures extension Weakifiable { - /// Weakly captures an object in non-parametrized non-void optional resilt closure. - /// - /// Creates `() -> Output?` handler from `(Object) -> Output?` closure. - /// so you can access weakly captured Object in your handler if the object is present. - /// - /// Unwrapping happens on the call of returned closure. - /// Handler will return nil if object was already deinitialized. - /// - /// Example: - /// ```swift - /// class MyClass: Weakifiable { - /// var value: Int = 0 - /// - /// func createZeroDataSource() -> () -> Int? { - /// return capture { _self in - /// return _self.value - /// } - /// } - /// } - /// ``` - /// - /// - Parameters: - /// - closure: `(Object) -> Output?`, where Object is unwrapped weakly captured object. - /// - Returns: `() -> Output?` handler, created from `(Object) -> Output?` closure by weakly capturing object. Handler will return nil if object was already deinitialized. - @inlinable - public func capture( - in closure: @escaping (Self) -> Output? - ) -> () -> Output? { - return Weak(self).capture(in: closure) - } - - /// Weakly captures an object in non-parametrized lazy non-void optional result closure. - /// - /// Primary idea behind this method is to be able to pass methods without referring to an object. - /// - /// Handler will return nil if object was already deinitialized. - /// - /// Example: - /// ```swift - /// class MyClass: Weakifiable { - /// // `MyClass.getData` signature is `(MyClass) -> () -> Int?` - /// func getData() -> Int? { - /// return 0 - /// } - /// - /// func createDataSource() -> () -> Int? { - /// return capture(orReturn: -1, in: MyClass.getData) - /// } - /// } - /// ``` - /// - /// - Parameter closure: `(Object) -> () -> Output?`, most likely it's `ObjectType.someInstanceMethod`. - /// - Returns: `() -> Output?` handler, created from `(Object) -> () -> Output?` closure by weakly capturing object. - @inlinable - public func capture( - in closure: @escaping (Self) -> () -> Output? - ) -> () -> Output? { - return Weak(self).capture(in: closure) - } - - /// Weakly captures an object in parametrized non-void optional result closure. - /// - /// > In context of this doc "`A, B, C...`" is a shortcut for 1 or more types. - /// - /// Creates `(A, B, C...) -> Output?` handler from `(Object, A, B, C...) -> Output?` closure - /// so you can access weakly captured Object in your handler if the object is present. - /// - /// Handler will return nil if object was already deinitialized. - /// - /// Example: - /// ```swift - /// class MyClass: Weakifiable { - /// func createDataSource() -> (Bool) -> Int? { - /// // number of params comes from publisher, if it was - /// // some completion with multiple args, all the args - /// // would've been passed to the capture method - /// return capture(orReturn: -1) { _self, isZero in - /// return isZero ? 0 : 1 - /// } - /// } - /// } - /// ``` - /// - /// - Parameter closure: `(Object, A, B, C...) -> Void`, where Object is unwrapped weakly captured object. - /// - Returns: `(A, B, C...) -> Output?` handler, created from `(Object, A, B, C) -> Output?` closure by weakly capturing object. - public func capture( - in closure: @escaping (Self, repeat each Arg) -> Output? - ) -> (repeat each Arg) -> Output? { - return Weak(self).capture(in: closure) - } + /// Weakly captures an object in non-parametrized non-void optional resilt closure. + /// + /// Creates `() -> Output?` handler from `(Object) -> Output?` closure. + /// so you can access weakly captured Object in your handler if the object is present. + /// + /// Unwrapping happens on the call of returned closure. + /// Handler will return nil if object was already deinitialized. + /// + /// Example: + /// ```swift + /// class MyClass: Weakifiable { + /// var value: Int = 0 + /// + /// func createZeroDataSource() -> () -> Int? { + /// return capture { _self in + /// return _self.value + /// } + /// } + /// } + /// ``` + /// + /// - Parameters: + /// - closure: `(Object) -> Output?`, where Object is unwrapped weakly captured object. + /// - Returns: `() -> Output?` handler, created from `(Object) -> Output?` closure by weakly capturing object. Handler will return nil if object was already deinitialized. + @inlinable + public func capture( + in closure: @escaping (Self) -> Output? + ) -> () -> Output? { + return Weak(self).capture(in: closure) + } + + /// Weakly captures an object in non-parametrized lazy non-void optional result closure. + /// + /// Primary idea behind this method is to be able to pass methods without referring to an object. + /// + /// Handler will return nil if object was already deinitialized. + /// + /// Example: + /// ```swift + /// class MyClass: Weakifiable { + /// // `MyClass.getData` signature is `(MyClass) -> () -> Int?` + /// func getData() -> Int? { + /// return 0 + /// } + /// + /// func createDataSource() -> () -> Int? { + /// return capture(orReturn: -1, in: MyClass.getData) + /// } + /// } + /// ``` + /// + /// - Parameter closure: `(Object) -> () -> Output?`, most likely it's `ObjectType.someInstanceMethod`. + /// - Returns: `() -> Output?` handler, created from `(Object) -> () -> Output?` closure by weakly capturing object. + @inlinable + public func capture( + in closure: @escaping (Self) -> () -> Output? + ) -> () -> Output? { + return Weak(self).capture(in: closure) + } + + /// Weakly captures an object in parametrized non-void optional result closure. + /// + /// > In context of this doc "`A, B, C...`" is a shortcut for 1 or more types. + /// + /// Creates `(A, B, C...) -> Output?` handler from `(Object, A, B, C...) -> Output?` closure + /// so you can access weakly captured Object in your handler if the object is present. + /// + /// Handler will return nil if object was already deinitialized. + /// + /// Example: + /// ```swift + /// class MyClass: Weakifiable { + /// func createDataSource() -> (Bool) -> Int? { + /// // number of params comes from publisher, if it was + /// // some completion with multiple args, all the args + /// // would've been passed to the capture method + /// return capture(orReturn: -1) { _self, isZero in + /// return isZero ? 0 : 1 + /// } + /// } + /// } + /// ``` + /// + /// - Parameter closure: `(Object, A, B, C...) -> Void`, where Object is unwrapped weakly captured object. + /// - Returns: `(A, B, C...) -> Output?` handler, created from `(Object, A, B, C) -> Output?` closure by weakly capturing object. + public func capture( + in closure: @escaping (Self, repeat each Arg) -> Output? + ) -> (repeat each Arg) -> Output? { + return Weak(self).capture(in: closure) + } } diff --git a/Tests/CaptureTests/CaptureTests.swift b/Tests/CaptureTests/CaptureTests.swift index 056f16b..9741520 100644 --- a/Tests/CaptureTests/CaptureTests.swift +++ b/Tests/CaptureTests/CaptureTests.swift @@ -2,7 +2,7 @@ import XCTest @testable import Capture final class WeakTests: XCTestCase { - class Object: Weakifiable { + class Object: Weakifiable, @unchecked Sendable { init(onDeinit: (() -> Void)? = nil) { self._onDeinit = onDeinit } @@ -138,7 +138,7 @@ final class WeakTests: XCTestCase { } func testPassingMethods() { - class LocalObject: Object { + class LocalObject: Object, @unchecked Sendable { private(set) var didSomething: Bool = false func doSomething() {