How to use below closure variable in Swift - ios

I am working on clouser in swift iOS, I have tried to create variable using clouser and add 2 clouser in single variable, variable declaration I did right, but I don't understand how to access it in my code.
here is the variable declaration.
var multipleClouser: ((_ success: (Bool), _ failer:(Error) -> Void)?

Most probably you meant callback, like
var multipleClouser: ((_ success: (Bool), _ failer: (Error?)) -> Void)?
and usage, like
multipleClouser?(true, nil)
or
multipleClouser?(false, SomeErrorHere)
set up like
multipleClouser = { success, error in
if success {
print("Done!")
} else {
print("Failure")
if let error = error {
print("\t with error: \(error)")
}
}
}

Related

swift question about "#escaping" inside class

I am a newbie and maybe this is a silly question but I need some help.
I have this code like below but I wonder that should I remove "#escaping" inside the checkSignedIn function.
Class A{
public func checkSignedIn(complete: #escaping (Bool) -> Void) {
_ = Amplify.Auth.fetchAuthSession { (result) in
do {
let session = try result.get()
complete(session.isSignedIn)
} catch {
print("Fetch auth session failed with error - \(error)")
complete(false)
}
}
}
I imagine that using "#escaping" will escape the return value from closure if I assign complete() to a variable like below.
Class A{
var complete: (() -> Void)?
public func checkSignedIn(complete: #escaping (Bool) -> Void) {
_ = Amplify.Auth.fetchAuthSession { (result) in
do {
let session = try result.get()
self.complete = complete(session.isSignedIn)
} catch {
print("Fetch auth session failed with error - \(error)")
self.complete = complete(false)
}
}
}
Then I can call A.complete again.
Am I wrong? I appreciate it if you teach me about this.
No, they won't be the same.
The complete: #escaping (Bool) -> Void defines this:
a function (or a callback) that takes 1 argument (Bool), and returns nothing (Void). It's an equivalent of function that looks like this:
func complete(_ value: Bool) { }
this function escapes the scope of the function it's passed to, as it runs asynchronously (that's #escaping keyword meaning)
And then this function is called with complete(session.isSignedIn), where session.isSignedIn is a boolean argument you pass in, just as function's definition states, and function returns nothing
The statement self.complete = complete(session.isSignedIn) won't compile:
You defined self.complete as (() -> Void) type - that is a function or callback that takes no arguments, and returns nothing. It's an equivalent of function:
func y() { }
So complete(session.isSignedIn) returns Void type as we know. Assigning Void type to (() -> Void) type is not going to work.
If you want to save the escaping function / callback to be used elsewhere, you can do this:
// Make sure signature of the variable matches that of a function argument
var complete: ((Bool) -> Void)?
public func checkSignedIn(complete: #escaping (Bool) -> Void) {
// Save callback at the start of the function
self.complete = complete
// Then continue to a asynch part of the code:
_ = Amplify.Auth.fetchAuthSession { (result) in
// Inside this callback, you still can use the function argument
complete(session.isSignedIn)
...
}
// But in another function, you have to use self.complete, e.g.:
func x() {
// Use saved callback. You don't have to say `self` here, just using it for clarity
self.complete(true)
}

Defining type of optional generic closure parameter

I want to create an interface, that can be invoked with a generic and a non generic parameter used in the Result type.
The API would look like the following:
struct NonGenericParameter {}
func taskPreparation<T: Decodable>(onTypedComplete: ((Result<T, Error>) -> Void)?,
onTyplessComplete: ((Result<NonGenericParameter, Error>) -> Void)?) {
// Do the neccessery preparation...
if let onComplete = onTypedComplete {
task(onComplete: onComplete)
}
if let onComplete = onTyplessComplete {
task(onComplete: onComplete)
}
}
func task<T: Decodable>(onComplete: #escaping (Result<T, Error>) -> Void) {
// do task...
}
func task(onComplete: #escaping (Result<NonGenericParameter, Error>) -> Void) {
// do task...
}
However, when i try to invoke the taskPreparation API, specifying onTyplessComplete as nil
taskPreparation(onTypedComplete: nil,
onTyplessComplete: { result in // Do something ... })
I receive the error
Generic parameter 'T' could not be inferred.
I understand, i have to specify the type of the generic parameter. I have tried to create a dummy decodable parameter, and pass it to the closure.
struct DummyDecodable: Decodable {}
taskPreparation(onTypedComplete: { (result: Result<DummyDecodable, Error>) in },
onTyplessComplete: { result in // Do something ... })
But obviously, in this case the onTypedComplete closure is not nil.
Does someone have an idea how could I specify a nil closure and satisfy the type inference too?
You would still need the DummyDecodable for this, which is kind of ugly, but at least you are passing a nil value:
Simply pass ((Result<DummyDecodable, Error>) -> Void)?.none. nil is in fact just a syntactic sugar for Optional<WhateverType>.none.
struct DummyDecodable: Decodable {}
taskPreparation(onTypedComplete: ((Result<DummyDecodable, Error>) -> Void)?.none,
onTyplessComplete: { result in /* Do something ...*/ })

Cannot convert value of type '()' to expected argument type '() -> Void' with a completionHandler

I'm trying to create a completion block where a function can be executed but I keep getting the error:
Cannot convert value of type '()' to expected argument type '() -> Void'
Here is the function:
var labelViews : [LabelViews] = []
private func removeAllLabels(completion: (() -> Void)) {
guard let mapController = viewModel.mapController,
let currentMap = mapController.currentMap else {
return
}
LabelViews.forEach { view in
DispatchQueue.main.async {
mapController.removeComponent(view, on: currentMap)
}
}
completion()
}
But when I try and use it I get error:
self.removeAllCampusLabels(completion: self.labelViews.removeAll()) // error happens here
You need to specify what will happen in multiple completion circumstances:
var labelViews : [LabelViews] = []
private func removeAllLabels(completion: nil) {
guard let mapController = viewModel.mapController,
let currentMap = mapController.currentMap else {
return
}
LabelViews.forEach { view in
DispatchQueue.main.async {
mapController.removeComponent(view, on: currentMap)}
}
func comp(completion: () -> Void) {
print("Success")
completion()
}
}
Assuming removeAllLabels and removeAllCampusLabels are the same methods (ignoring typo), replace the method call
self.removeAllCampusLabels(completion: self.labelViews.removeAll())
with
self.removeAllCampusLabels { self.labelViews.removeAll() }
Your question defines a function named removeAllLabels, but then you say the error occurs when you call removeAllCampusLabels, which your question did not define. You will get better help if you check that the code you put in your question is sufficient to reproduce the error you're asking about.
I'll assume that your function is in fact named removeAllLabels.
Here's your declaration of removeAllLabels:
private func removeAllLabels(completion: (() -> Void))
This is a function that takes one argument, which is called completion. That argument must have the type () -> Void, which means the argument must itself be a function that takes no arguments and returns nothing.
Here's how (I assume) you try to call removeAllLabels:
self.removeAllLabels(completion: self.labelViews.removeAll())
Let's split this up:
let arg = self.labelViews.removeAll()
self. removeAllLabels(completion: arg)
What is the type of arg? We need it to be () -> Void. But if we option-click it in Xcode, we'll see that arg has type () (which is the same as Void). In fact, Swift emits a warning for it:
Constant 'arg' inferred to have type '()', which may be unexpected
What you probably meant to do is wrap the removeAll call in a closure, like this:
let arg = { self.labelViews.removeAll() }
self.removeAllLabels(completion: arg)
And once you have eliminated the error, you can put it back together in a single statement:
self.removeAllLabels(completion: { self.labelViews.removeAll() })

Generic paramater could not be inferred

Here I have a function that creates a DataResponseSerializer with a generic type called T
extension DataResponseSerializer {
static func mappableObjectSerializer<T: Mappable>() -> DataResponseSerializer<T> {...}
}
And another extension with a function to do the request, which returns a generic type called SuccessObjectType, in this one I call the function above.
public extension DataRequest {
#discardableResult func requestObject<SuccessObjectType: Mappable>(onSuccess success: #escaping ((SuccessObjectType?) -> Void),
onFailure failure: #escaping ((NSError?) -> Void),
onCompletion completion: (() -> Void)? = nil) -> Self {
let responseSerializer = DataResponseSerializer<SuccessObjectType>.mappableObjectSerializer()
return response(queue: nil, responseSerializer: responseSerializer, completionHandler: { (response) in
switch response.result {...})
}
}
The idea is to disassociate the serializer from the request itself, so I can implement UnitTest on a legacy project, but for some reason I get the error
Generic parameter 'T' could not be inferred
On the line:
let responseSerializer = DataResponseSerializer<SuccessObjectType>.mappableObjectSerializer()
Thanks to this answer Cannot explicitly specialize a generic function I could find a way around it, it's pretty straight forward, from this:
let responseSerializer = DataResponseSerializer<SuccessObjectType>.mappableObjectSerializer()
To this:
let responseSerializer: DataResponseSerializer<SuccessObjectType> = DataResponseSerializer<SuccessObjectType>.mappableObjectSerializer()
What it does is to type cast the expected return from the function, this way the compile knows what to expect from the function.

How to specify completion handler with one function not return [duplicate]

Error: Cannot convert the expression type (String, MyType) to ()
From the following code
Test(method: {[weak self] (message: String) in self?.callback(message)}, instance: self)
and if I add a return statement, it works, and the error goes away
Test(method: {[weak self] (message: String) in self?.callback(message); return}, instance: self)
Not sure how to handle the above without having to have the dummy return statement, any advise.
Here's my class Test
public class Test {
private var instance: AnyObject?
private var method: ((message: String) -> ())?
public init(method: (String -> ())?, instance: AnyObject) {
}
}
Edit
I've done a playground based minimalistic example (please copy paste for a test)
class Test {
private var _method: ((String) -> ())?
weak private var _instance: AnyObject?
init(method: (String -> ())?, instance: AnyObject?) {
_method = method
_instance = instance
}
}
class Another {
func register() {
//this doesn't need a return
Test(method: {(message: String) in self.callback(message)}, instance: self)
//this needs a return once I add [weak self]
Test(method: { [weak self] (message: String) in self?.callback(message); return}, instance: self)
}
func callback(message: String) {
println(message)
}
}
Not sure how to handle the above without having to have the dummy return statement, any advise.
You have solved the problem beautifully. Anonymous functions automatically use a one-line function body as a return value, so to prevent that from causing a type mismatch with the expected return type (Void) you have to add another line of code so that it is not a one-line function body. The dummy return statement, which itself returns Void, is a great way to handle it; I would just use that and move on. There are some snazzier workarounds but what you have is precisely what I would do.
EDIT: To understand the source of the type mismatch, try this:
struct Test {
func voider() -> Void {}
}
let testMaybe = Optional(Test())
let result = testMaybe?.voider()
Now result is not a Void; it's an Optional wrapping a Void. That is what's happening to you; a Void is expected but your one-line anonymous function returns an Optional wrapping a Void. By adding another line that returns Void explicitly, you solved the problem.
The implicit return is returning the result of your callback() method. That return value conflicts with the closure's return value of void. You thus need an explicit, if ugly, return.

Resources