save escaping method reference in swift - swift

I am a Java/Kotlin programmer and new to swift. I want to pass a method reference in a constructor to save it for later use. The method I want to pass looks like this:
func refresh(completion: #escaping (Error?) -> ()) {
...
}
What I want to do is instantiate an object and pass this method as a parameter like this:
refreshControl = Refresher() {
compl -> Void in
self.refresh(completion: compl)
}
The class I want to pass this function to looks like this:
class Refresher {
let refresh: (#escaping (Error?) -> ()) -> Void
init(refresh: (#escaping (Error?) -> ()) -> Void) {
self.refresh = refresh
}
// call refresh somewhere later
}
This does not compile with error "Assigning non-escaping parameter 'refresh' to an #escaping closure. Not entirely sure what escaping does but I know I need it in the actual refresh function. I am not sure how to syntax this right. Any help would be appreciated.

But Xcode tells you what to do. It offers you a Fix-It:
init(refresh: #escaping (#escaping (Error?) -> ()) -> Void) {
Personally I would then get rid of the other #escaping stuff you've put in, as it is not needed. So:
class Refresher {
let refresh: ((Error?) -> ()) -> Void
init(refresh: #escaping ((Error?) -> ()) -> Void) {
self.refresh = refresh
}
}
And elsewhere:
func refresh(completion: (Error?) -> ()) {
// ...
}
func f() {
let refreshControl = Refresher() {
compl -> Void in
self.refresh(completion: compl)
}
// ...
}

Related

Is there a way to nullify a escaping closure without calling it?

I'm looking for a way to nullify an escaping closure without calling it.
func should(completion: #escaping () -> Void) {
if something {
completion = nil
} else {
completion()
}
}
As long as I understand, the escaping closure can be called after the should function is finished, it will alive until it is get called.
Is there a way to nullify the closure(and captured values) without calling it?
In your example, the closure is in fact not escaping, since you're not assigning it to anything outside the function, so there's no need to nullify:
func should(completion: () -> Void) {
if !something {
completion()
}
}
But if it was escaping, say by assigning it to a property, then you could nullify the property to release it:
class Foo {
let fn: (() -> Void)?
func should(completion: #escaping () -> Void) {
fn = completion
}
func executeAndRelease() {
fn?()
fn = nil
}
}

Having trouble returning a user token from StoreKit as a string

import StoreKit
class AppleMusicAPI {
let developerToken = "ABC123"
func getUserToken() -> String {
var userToken = String()
SKCloudServiceController().requestUserToken(forDeveloperToken: developerToken) { (receivedToken, error) in
userToken = receivedToken!
}
return userToken
}
}
I'm trying to basically return the userToken by doing AppleMusicAPI().getUserToken() however nothing gets returned (literally just blank/empty).
How can I output the token as a string?
If you take a look a the method signature that you're calling, it looks like this:
open func requestUserToken(forDeveloperToken developerToken: String, completionHandler: #escaping (String?, Error?) -> Void)
Notice the #escaping keyword which indicates that the completion block is not necessarily called right away (asynchronous). To fix your issue, I'd suggest using something like this:
class AppleMusicAPI {
let developerToken = "ABC123"
func getUserToken(completion: #escaping (String?, Error?) -> Void) {
SKCloudServiceController().requestUserToken(forDeveloperToken: developerToken, completionHandler: completion)
}
}

Passing closure in userInfo to NotificationCenter, getting cryptic messages from compiler at runtime

I'm trying to pass a closure in userInfo through NotificationCenter. The closures works as expected but I'm getting weird messages that I do not understand in runtime. What is the cause of this message?
The message is:
0x000000010292b350 [ProjectName]`partial apply forwarder for reabstraction thunk helper from #escaping #callee_guaranteed () -> (#out ()) to #escaping #callee_guaranteed () -> () at <compiler-generated>
This is how I post the notification.
let closure: (() -> Void) = {
print("TEST")
}
NotificationCenter.default.post(name: .test,
object: nil,
userInfo: ["closure" : closure ])
This is how I consume the notificaiton:
#objc private func test(_ notification: Notification) {
let closure = notification.userInfo?["closure"] as? (() -> Void)
closure?()
}
I'm using Swift 5.
Unfortunately I can't reproduce the error, but I would suggest wrapping the closure in a wrapper class. Things that need to round-trip into and out of Cocoa do better when they are NSObjects:
class Wrapper : NSObject {
let closure : () -> Void
init(closure: #escaping () -> Void) {
self.closure = closure
}
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let closure: (() -> Void) = {
print("TEST")
}
let wrapper = Wrapper(closure: closure)
let test = Notification.Name("Test")
NotificationCenter.default.addObserver(self,
selector: #selector(testing), name: test, object: nil)
NotificationCenter.default.post(name: test,
object: nil, userInfo: ["wrapper" : wrapper ])
}
#objc private func testing(_ notification: Notification) {
let wrapper = notification.userInfo?["wrapper"] as? Wrapper
wrapper?.closure()
}
}

Class Initializer with #escaping parameter

I'm setting up a class that can ideally store an async method for later use. I would like to pass a function inside of class initializer but I'm running into a weird compiler issue.
Cannot convert value of type '(#escaping (Int) -> Void) -> ()' to expected argument type '((Int) -> Void) -> Void'
If the method is not escaping/sync this works fine. The compiler also suggest to force cast the parameter as! (((Int) -> Void) -> Void). Gave that a shot but it crashes.
Here's an example I've been messing with in a playground:
class NumberTwo {
let numberTwoMethod: ((Int) -> Void) -> Void
init(numberTwoMethod: #escaping ((Int) -> Void) -> Void) {
self.numberTwoMethod = numberTwoMethod
}
func callNumberTwoMethod() {
numberTwoMethod { myNum in
print(myNum)
}
}
}
func getNumberTwoMethod(completion: #escaping (Int) -> Void) {
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
completion(2)
}
}
func getNumberTwoMethodSync(completion: (Int) -> Void) {
completion(2)
}
NumberTwo(numberTwoMethod: getNumberTwoMethod) // error: cannot convert value of type '(#escaping (Int) -> Void) -> ()' to expected argument type '((Int) -> Void) -> Void'
NumberTwo(numberTwoMethod: getNumberTwoMethodSync) // Works
Any suggestions on what's going on here or alternative ways of storing an async function as a variable in a class?
You are missing #escaping on the inner closure:
class NumberTwo {
let numberTwoMethod: (#escaping (Int) -> Void) -> Void
init(numberTwoMethod: #escaping (#escaping (Int) -> Void) -> Void) {
self.numberTwoMethod = numberTwoMethod
}
func callNumberTwoMethod() {
numberTwoMethod { myNum in
print(myNum)
}
}
}
or slightly simplified:
class NumberTwo {
typealias CompletionHandler = (Int) -> Void
let numberTwoMethod: (#escaping CompletionHandler) -> Void
init(numberTwoMethod: #escaping (#escaping CompletionHandler) -> Void) {
self.numberTwoMethod = numberTwoMethod
}
func callNumberTwoMethod() {
numberTwoMethod { myNum in
print(myNum)
}
}
}
Also note this will probably create a memory leak since there is no weak anywhere.

Generic understanding issue

I was reading Alamofire example, below is the function declaration is not clear to me what does this mean: Response<T, NSError> -> Void
public func responseObject<T: ResponseJSONObjectSerializable>
(completionHandler: Response<T, NSError> -> Void) -> Self {
////
}