Last active
February 5, 2021 12:05
-
-
Save maximkrouk/7e44b2ce67c731b57c6469405a975d43 to your computer and use it in GitHub Desktop.
A mechanism for safe capturing & weakifying objects
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
/// Weakly captures an object | |
/// | |
/// Actually the same as `weak` but initializable | |
/// | |
/// Usage: | |
/// ``` | |
/// Weak(object) | |
/// Weak(self) | |
/// Weak<FutureObject>(nil) | |
/// ``` | |
/// or | |
/// ``` | |
/// protocol SomeClassProtocol: AnyObject {} | |
/// | |
/// class AnotherClass { | |
/// @Weak var someClass: SomeClassProtocol? | |
/// } | |
/// ``` | |
@dynamicMemberLookup | |
@propertyWrapper | |
public struct Weak<Object: AnyObject> { | |
public weak var wrappedValue: Object? | |
public var projectedValue: Object? { | |
get { wrappedValue } | |
set { wrappedValue = newValue } | |
} | |
public init(_ object: Object?) { | |
self.init(wrappedValue: object) | |
} | |
public init() {} | |
public init(wrappedValue: Object? = nil) { | |
self.wrappedValue = wrappedValue | |
} | |
public subscript<T>(dynamicMember keyPath: KeyPath<Object, T>) -> T? { | |
wrappedValue?[keyPath: keyPath] | |
} | |
public var isReleased: Bool { wrappedValue == nil } | |
public var isRetained: Bool { wrappedValue != nil } | |
public func ifReleased(execute: @autoclosure () -> (() -> Void)) { | |
guard isReleased else { return } | |
execute()() | |
} | |
public func ifRetained(execute: @autoclosure () -> ((Object) -> Void)) { | |
guard let object = wrappedValue else { return } | |
execute()(object) | |
} | |
} | |
extension Weak { | |
/// Provides a closure with a weak self as a first parameter for non-returning functions | |
/// | |
/// Usage: | |
/// ``` | |
/// DispatchQueue.main.async(execute: Weak(object).captured { object in | |
/// object.titleLabel = "Task has been finished" | |
/// }) | |
/// ``` | |
public func capture(in closure: @escaping (Object) -> Void) -> (() -> Void) { | |
return { [weak wrappedValue] in | |
guard let object = wrappedValue else { return } | |
closure(object) | |
} | |
} | |
/// Provides a closure with a weak self as a first parameter for non-returning functions | |
/// | |
/// Usage: | |
/// ``` | |
/// fetchSomething(completion: Weak(object).captured { object, result in | |
/// object.handleFetch(result) | |
/// } | |
/// ``` | |
public func capture<T0>(in closure: @escaping (Object, T0) -> Void) -> ((T0) -> Void) { | |
return { [weak wrappedValue] params in | |
guard let object = wrappedValue else { return } | |
closure(object, params) | |
} | |
} | |
public func capture<T0, T1>(in closure: @escaping (Object, T0, T1) -> Void) -> ((T0, T1) -> Void) { | |
return { [weak wrappedValue] t0, t1 in | |
guard let object = wrappedValue else { return } | |
closure(object, t0, t1) | |
} | |
} | |
public func capture<T0, T1, T2>(in closure: @escaping (Object, T0, T1, T2) -> Void) -> ((T0, T1, T2) -> Void) { | |
return { [weak wrappedValue] t0, t1, t2 in | |
guard let object = wrappedValue else { return } | |
closure(object, t0, t1, t2) | |
} | |
} | |
public func capture<T0, T1, T2, T3>(in closure: @escaping (Object, T0, T1, T2, T3) -> Void) -> ((T0, T1, T2, T3) -> Void) { | |
return { [weak wrappedValue] t0, t1, t2, t3 in | |
guard let object = wrappedValue else { return } | |
closure(object, t0, t1, t2, t3) | |
} | |
} | |
public func capture<T0, T1, T2, T3, T4>(in closure: @escaping (Object, T0, T1, T2, T3, T4) -> Void) -> ((T0, T1, T2, T3, T4) -> Void) { | |
return { [weak wrappedValue] t0, t1, t2, t3, t4 in | |
guard let object = wrappedValue else { return } | |
closure(object, t0, t1, t2, t3, t4) | |
} | |
} | |
public func capture<T0, T1, T2, T3, T4, T5>(in closure: @escaping (Object, T0, T1, T2, T3, T4, T5) -> Void) -> ((T0, T1, T2, T3, T4, T5) -> Void) { | |
return { [weak wrappedValue] t0, t1, t2, t3, t4, t5 in | |
guard let object = wrappedValue else { return } | |
closure(object, t0, t1, t2, t3, t4, t5) | |
} | |
} | |
public func capture<T0, T1, T2, T3, T4, T5, T6>(in closure: @escaping (Object, T0, T1, T2, T3, T4, T5, T6) -> Void) -> ((T0, T1, T2, T3, T4, T5, T6) -> Void) { | |
return { [weak wrappedValue] t0, t1, t2, t3, t4, t5, t6 in | |
guard let object = wrappedValue else { return } | |
closure(object, t0, t1, t2, t3, t4, t5, t6) | |
} | |
} | |
public func capture<T0, T1, T2, T3, T4, T5, T6, T7>(in closure: @escaping (Object, T0, T1, T2, T3, T4, T5, T6, T7) -> Void) -> ((T0, T1, T2, T3, T4, T5, T6, T7) -> Void) { | |
return { [weak wrappedValue] t0, t1, t2, t3, t4, t5, t6, t7 in | |
guard let object = wrappedValue else { return } | |
closure(object, t0, t1, t2, t3, t4, t5, t6, t7) | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Foundation | |
public protocol Weakifiable: AnyObject {} | |
extension Weakifiable { | |
/// Provides a closure with a weak self as a first parameter for non-returning functions | |
/// | |
/// Usage: | |
/// ``` | |
/// DispatchQueue.main.async(execute: captured { _self in | |
/// _self.titleLabel = "Task has been finished" | |
/// } | |
/// ``` | |
public func capture(in closure: @escaping (Self) -> Void) | |
-> (() -> Void) { Weak(self).capture(in: closure) } | |
/// Provides a closure with a weak self as a first parameter for non-returning functions | |
/// | |
/// Usage: | |
/// ``` | |
/// fetchSomething(completion: captured { _self, result in | |
/// _self.handleFetch(result) | |
/// } | |
/// ``` | |
public func capture<T0>(in closure: @escaping (Self, T0) -> Void) | |
-> ((T0) -> Void) { Weak(self).capture(in: closure) } | |
public func capture<T0, T1>(in closure: @escaping (Self, T0, T1) -> Void) | |
-> ((T0, T1) -> Void) { Weak(self).capture(in: closure) } | |
public func capture<T0, T1, T2>(in closure: @escaping (Self, T0, T1, T2) -> Void) | |
-> ((T0, T1, T2) -> Void) { Weak(self).capture(in: closure) } | |
public func capture<T0, T1, T2, T3>(in closure: @escaping (Self, T0, T1, T2, T3) -> Void) | |
-> ((T0, T1, T2, T3) -> Void) { Weak(self).capture(in: closure) } | |
public func capture<T0, T1, T2, T3, T4>(in closure: @escaping (Self, T0, T1, T2, T3, T4) -> Void) | |
-> ((T0, T1, T2, T3, T4) -> Void) { Weak(self).capture(in: closure) } | |
public func capture<T0, T1, T2, T3, T4, T5>(in closure: @escaping (Self, T0, T1, T2, T3, T4, T5) -> Void) | |
-> ((T0, T1, T2, T3, T4, T5) -> Void) { Weak(self).capture(in: closure) } | |
public func capture<T0, T1, T2, T3, T4, T5, T6>(in closure: @escaping (Self, T0, T1, T2, T3, T4, T5, T6) -> Void) | |
-> ((T0, T1, T2, T3, T4, T5, T6) -> Void) { Weak(self).capture(in: closure) } | |
public func capture<T0, T1, T2, T3, T4, T5, T6, T7>(in closure: @escaping (Self, T0, T1, T2, T3, T4, T5, T6, T7) -> Void) | |
-> ((T0, T1, T2, T3, T4, T5, T6, T7) -> Void) { Weak(self).capture(in: closure) } | |
} | |
extension NSObject: Weakifiable {} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Usage
Todo: Make hashable
https://twitter.com/mobileunderhood/status/1357587809369006081?s=20
Back to index