Using @discardableResult for Closures in Swift
I don't think there's a way to apply that attribute to a closure. You could capture your closure in another that discards the result:
func discardingResult<T, U>(_ f: @escaping (T) -> U) -> (T) -> Void {
return { x in _ = f(x) }
}
let g = f(5)
g(3) // warns
let h = discardingResult(g)
h(4) // doesn't warn
Ignore unused warning in Swift when use a pointer to func?
Unfortunately, discardableResult
only applies to method/function declarations. What you can do is to wrap the value-returning function in a void-returning closure:
@discardableResult
func f() -> Int { return 1 }
// the type annotation here is important
let someFunc: () -> Void = { f() }
Alternatively, write a function that "erases" the return type of functions:
func discardResult<R>(_ f: @escaping () -> R) -> (() -> Void) {
{ _ = f() }
}
let someFunc = discardResult(f)
The downside is that you need to write an overload of this for each arity:
func discardResult<T, R>(_ f: @escaping (T) -> R) -> ((T) -> Void) {
{ x in _ = f(x) }
}
func discardResult<T, U, R>(_ f: @escaping (T, U) -> R) -> ((T, U) -> Void) {
{ x, y in _ = f(x, y) }
}
func discardResult<T, U, V, R>(_ f: @escaping (T, U, V) -> R) -> ((T, U, V) -> Void) {
{ x, y, z in _ = f(x, y, z) }
}
// and so on
how to squelch result not used warnings
You can discard the result using:
_ = myBSON["key"].string.ifNotNil {
print($0}
}
Or mark your method to not warn for unused results:
extension Optional {
@discardableResult func ifNotNil<T>(_ closure:(Wrapped) -> T) -> T? {
switch self {
case .some (let wrapped):
return closure(wrapped)
case .none:
return nil
}
}
}
Reference : SE-0047
Holding and releasing Closures in an array
So I come up with this solution:
class Wrapper<V> {
var observer: (V) -> Void
public init(_ b: @escaping (V) -> Void) {
observer = b
}
}
class Observable<V> {
public var value: V { didSet {
let enumerator = observers.objectEnumerator()
while let wrapper = enumerator?.nextObject() {
(wrapper as! Wrapper<V>).observer(value)
}
}}
private var observers = NSMapTable<AnyObject, Wrapper<V>>(keyOptions: [.weakMemory], valueOptions: [.strongMemory])
public init(_ initital: V) {
value = initital
}
public func observe(_ subscriber: AnyObject, with closure: @escaping (V) -> Void) {
let wrapper = Wrapper(closure)
observers.setObject(wrapper, forKey: subscriber)
}
}
The final API require subscriber to identify themselves at calling:
Observable.observe(self /* <-- extra param */) { /* closure */ }
Although we cannot weak ref a closure, but with NSMapTable
, we can weak ref the subscriber
object, then use it as a weak key to track observer closure. This allow deallocation of subscriber
thus automatically cleanup outdated observers.
Finally, here's the code for a demo. Expand the snippet and copy-paste to swift playground and see it live.
import Foundation
func setTimeout(_ delay: TimeInterval, block:@escaping ()->Void) -> Timer { return Timer.scheduledTimer(timeInterval: delay, target: BlockOperation(block: block), selector: #selector(Operation.main), userInfo: nil, repeats: false)}
class Wrapper<V> { var observer: (V) -> Void public init(_ b: @escaping (V) -> Void) { observer = b }}
class Observable<V> { public var value: V { didSet { let enumerator = observers.objectEnumerator() while let wrapper = enumerator?.nextObject() { (wrapper as! Wrapper<V>).observer(value) } }} private var observers = NSMapTable<AnyObject, Wrapper<V>>(keyOptions: [.weakMemory], valueOptions: [.strongMemory])
public init(_ initital: V) { value = initital } public func observe(_ subscriber: AnyObject, with closure: @escaping (V) -> Void) { let wrapper = Wrapper(closure) observers.setObject(wrapper, forKey: subscriber) }}
class Consumer { private var id: String
public init(_ id: String, _ observable: Observable<Int>) { self.id = id observable.observe(self) { val in print("[\(id)]", "ok, i see value changed to", val) } } deinit { print("[\(id)]", "I'm out") }}
func demo() -> Any { let observable = Observable(1) var list = [AnyObject]()
list.append(Consumer("Alice", observable)) list.append(Consumer("Bob", observable)) observable.value += 1
// pop Bob, so he goes deinit list.popLast() // deferred setTimeout(1.0) { observable.value += 1 observable.value += 1 }
return [observable, list]}
// need to hold ref to see the effectlet refHolder = demo()
Swift 3 Result of call to (_:parameters:completionHandler:)' is unused warning and Braced block of statements is an unused closure error
You can annotate the method it is warning you about with @discardableResult
.
For example:
@discardableResult
func foobar() -> Bool {
return true
}
Empty closures in Swift 4.0
Note that Void
is just an empty tuple ()
.
I think this is due to Remove implicit tuple splat behavior from function applications (SE-0029)
Basically this change says that you can't pass tuples as parameters anymore. You used to be able to either pass parameters by inserting stuff in the ()
after the function name or pass a tuple representing the parameters
func foo(a: Int, b: Int) {}
let tuple = (a: 1, b: 2)
foo(tuple)
Now you can't.
If we were to transform foo
to swift 4, it would look like this:
func foo(a; Int, b: Int) {}
func foo(_ x: (a: Int, b: Int)) {}
So (Void) -> Void
represents a function taking a Void
parameter. You used to be able to pass nothing because Void
is just an empty tuple, which, after the "splat" becomes "no parameters".
Related Topics
How to Create a Hotspot Network in iOS App Using Swift
How to Capture Multiple Arguments Weakly in a Swift Closure
How to Fix Error: Abort Trap 6 (In Target 'Realmswift' from Project 'Pods')
Swiftui Overlay Blocking List Scroll Events
Selectively Remove and Delete Objects from a Nsmutablearray in Swift
Timer Not Firing Every Second on Watchkit
Swift: Convert String to Hex Color Code
Xcode11 Error "Open(_:Options:Completionhandler:) Is Unavailable in Application Extensions"
Easiest Way to Increment a Data Point in Firebase
Gmail API: How to Send Attachments to The Drafts on Swift
Swift:Pause and Resume Nstimer
Expression Resolves to an Unused Function
Left Aligned Horizontal Stackview and Top Aligned Vertical Stackview
Nsinvocationoperation' Is Unavailable in Xcode 6.1
Array Extension Called from Other Module
Argument of '#Selector' Does Not Refer to an '@Objc' Method, Property or Initializer
How to Get Walking and Running Distance Using Healthkit in Swift