Class Initializer with #escaping parameter - swift

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.

Related

Protocol with associatedtype conformance with generic gives compiler error

I'm trying to make a DB mock to test some UI implementations, but compiler keeps giving me the following error:
Type 'DBClientMock<T>' does not conform to protocol 'DBClient'
This is my code...
protocol DBClient {
associatedtype Model
func get(id: UUID, completion: #escaping ((Model?, Error?)) -> Void)
func insert(_ model: Model, completion: #escaping (Error?) -> Void)
func delete(_ model: Model, completion: #escaping (Error?) -> Void)
}
final class DBClientMock<T>: DBClient where T: Identifiable, T: Equatable {
typealias Model = T
private let queue: DispatchQueue
private var modelDB = [T]()
enum Error: Swift.Error {
case notFound
}
init(queue: DispatchQueue = DispatchQueue.global()) {
self.queue = queue
}
private func getModel(id: UUID) -> T? {
let results = modelDB.filter({ $0.id as? UUID == id })
guard results.count > 0 else { return nil }
return results[0]
}
// Extension
func get(id: UUID, completion: #escaping ((T?, Error?)) -> Void) {
let record = getModel(id: id)
queue.asyncAfter(deadline: .now() + .milliseconds(1500), execute: {
if let model = record {
completion((model, nil))
} else {
completion((nil, Error.notFound))
}
})
}
func insert(_ model: T, completion: #escaping (Error?) -> Void) {
modelDB.append(model)
queue.asyncAfter(deadline: .now() + .milliseconds(1000), execute: {
completion(nil)
})
}
func delete(_ model: T, completion: #escaping (Error?) -> Void) {
modelDB.removeAll(where: { $0 == model })
queue.asyncAfter(deadline: .now() + .milliseconds(800), execute: {
completion(nil)
})
}
}
XCode: Version 12.4 (12D4e)
Swift: 5.0
What am I doing wrong? Do I have to be more explicit with the generic type in some way? I tried replacing T with Model but had the same result.
Thanks for your help!
It doesn't conform because you declared another Error type inside the class, so everywhere where you use Error in the required methods, it uses DBClientMock.Error instead of the protocol-required Swift.Error.
Either rename DBClientMock.Error to something else, or change the Error in methods to Swift.Error, like below:
// Extension
func get(id: UUID, completion: #escaping (T?, Swift.Error?) -> Void) {
//...
}
func insert(_ model: T, completion: #escaping (Swift.Error?) -> Void) {
//...
}
func delete(_ model: T, completion: #escaping (Swift.Error?) -> Void) {
//...
}

save escaping method reference in 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)
}
// ...
}

Swift: Convert a Sync function to Async

If you have a sync function, how would you convert it to an async function?
func syncFunc() -> Int {
//Do something
}
Would this work?
func asyncFunc(_ syncFunc:()->Int, _ completion:(Int)->()) -> Int {
DispatchQueue.background.async{
completion( syncFunc() )
}
}
No, functions containing an asynchronous task cannot return any value from the closure body and both closures must be marked as #escaping
func asyncFunc(_ syncFunc: #escaping ()->Int, completion: #escaping (Int)->()) {
DispatchQueue.global().async {
completion( syncFunc() )
}
}

RxCocoa: Cannot invoke 'bind' with an argument list of type '(to: (UITapGestureRecognizer) -> Void)'

I use RxCocoa, I have code like this,
func debug(){
isUserInteractionEnabled = true
let tap = UITapGestureRecognizer()
tap.numberOfTapsRequired = 2
addGestureRecognizer(tap)
tap.rx.event.bind { (event) in
if self.phone.text == Phone.one{
self.phone.text = Phone.two
}
else{
self.phone.text = Phone.one
}
self.verification.text = ""
}.disposed(by: rx.disposeBag)
}
I want to do some encapsulation, turn the above to this:
func debug( _ event: (UITapGestureRecognizer) -> Void){
isUserInteractionEnabled = true
let tap = UITapGestureRecognizer()
tap.numberOfTapsRequired = 2
addGestureRecognizer(tap)
tap.rx.event.bind(to: event).disposed(by: rx.disposeBag)
}
Xcode reports:
Cannot invoke 'bind' with an argument list of type '(to: (UITapGestureRecognizer) -> Void)'
I try
func debug( _ event: #escaping (UITapGestureRecognizer) -> Void){
Xcode reports:
Cannot invoke 'bind' with an argument list of type '(to: #escaping (UITapGestureRecognizer) -> Void)'
What matters? Rx name space?
When I type eventin the former, Xcode tips event is UITapGestureRecognizer
I think bind(onNext: #escaping (E) -> Void) is what you look for, rather than bind<O: ObserverType>(to observer: O).
Compare implementations:
public func bind<O: ObserverType>(to observer: O) -> Disposable where O.E == E {
return self.subscribe(observer)
}
public func bind(onNext: #escaping (E) -> Void) -> Disposable {
return subscribe(onNext: onNext, onError: { error in
rxFatalErrorInDebug("Binding error: \(error)")
})
}

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 {
////
}