iOS Swift function with type () -> () - ios

Im unable to create function of type () -> () in Swift with xCode...
so far im at
func successBlock() -> Void {
//code
return ()
}
but this is only () not () -> ()
I want this function to fit the type () -> () because of this function:
func loginToFacebookWithSuccess(callingViewController: UIViewController, successBlock: () -> (), andFailure failureBlock: (NSError?) -> ()) {
gonna pass the successBlock() func as variable in there.I have nearly the same problem with the failureBlock
or am I on the wrong way?
Thank you, spinhaxo

successBlock is a function.
You need closures.
let successBlock: () -> () = {
// do something here...
}
let failureBlock: (NSError?) -> () = { error in
// do something with the error...
}
If you don't want to store them, you can just define them while calling the method like this:
loginToFacebookWithSuccess(
callingController,
successBlock: {
// do something here...
}, andFailure: { error in
// do something with the error...
}
)

If you decided to go with func, pass the function's name without the parentheses. Hence, pass successBlock instead of successBlock().
loginToFacebookWithSuccess(someViewController, successBlock: successBlock, andFailure: failureBlock)
That's because the type of the function successBlock is () -> () while its return type is only ().
Extra:
() has an alias which is Void: public typealias Void = ().
You can even omit Void nor ():
func successBlock() {
//code
//no return statement needed
}
func failureBlock(error: NSError?) {
//code
//no return statement needed
}

Seems you are confusing a closure with the result of calling the closure.
You just need to pass successBlock, which is a closure of type () -> (). (aka () -> Void)
But successBlock() represents the result of calling successBlock, which may be a void value (aka ()), not a closure.
Nearly the same for failureBlock. Do not pass the result of calling closures, pass closures themselves.

Related

How to use below closure variable in Swift

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)")
}
}
}

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() })

Dealing with Closures - Make code more generic

There are two functions as shown below. Most of the functionality is the same in both. Its idea is to get the output of the webservice from getResponse() [Helper Callback], parse and pass the info to wrapper call back through getResult().
static func getAllDealers(dealerSearchServiceDomain: ARSDealerSearchServiceDomain, wrapperCallback:(getResult: () throws -> Void) -> Void) throws
{
try ARSInputValidator.validateZipCode(dealerSearchServiceDomain.zip)
try ARSDealerConnection.getAllDealers(dealerSearchServiceDomain, helperCallback: { (getResponse) -> Void in
do
{
let result = try getResponse()
try ARSDealerParser.parseDealerSearchResponse(dealerSearchServiceDomain)
wrapperCallback(getResult: { return })
}
catch
{
wrapperCallback(getResult: { throw error })
}
})
}
static func getDealerDetails(dealerDetailsServiceDomain: ARSDealerDetailsServiceDomain, wrapperCallback:(getResult: () throws -> Void) -> Void) throws
{
try ARSDealerConnection.getDealerDetails(dealerDetailsServiceDomain, helperCallback: { (getResponse) -> Void in
do
{
let result = try getResponse()
try ARSDealerParser.parseDealerDetailsResponse(dealerDetailsServiceDomain)
wrapperCallback(getResult: { return })
}
catch
{
wrapperCallback(getResult: { throw error })
}
})
}
I am trying to add a separate function for the common functionality like,
static func parser(serviceCallDomain: ARSServiceCallDomain ,wrapperCallback:(getResult:() throws -> String) -> Void, helperCallback:(getResponse:() throws -> String) -> Void) throws
{
helperCallback { (getResponse) -> Void in
But there is a compilation error & i am not able to complete it. There are 15+ web service calls, so a common shown as i am trying will be very helpful.
Next step, i also need to pass the functions parseDealerSearchResponse() & parseDealerDetailsResponse() to the common function.
I am new to closures. Kindly help.
//EDIT -- ADDING SAMPLE
I have a sample for the problem in Git - Refer class Layer1.swift
https://github.com/vivinjeganathan/ErrorHandling/tree/Closures-Refactor
I think the best you can do to refactor the code is to define a function that handles some of the common functionality like parsing and validation and that ultimately calls the completion closure back to the controller, something like this:
static func handleResponse(parser: Parser, validator: Validator, getResult: () throws -> AnyObject, completion: (getParsedResult: () throws -> AnyObject) -> Void) {
do
{
let result = try getResult()
let parsedObject = try parser.parse(result)
try validator.validate(parsedObject)
completion(getParsedResult: { return parsedObject })
}
catch
{
completion(getParsedResult: { throw error })
}
}
notice that it receives the parser, validator, the closure that captures the result from the layer below and the completion closure that belongs to the final user (usually the View Controller), and then this function could be used like this:
static func getAllDealers(dealerSearchServiceDomain: AnyObject, wrapperCallback:(getResult: () throws -> AnyObject) -> Void) throws {
let validator = DealersValidator() // create real validator
let parser = DealersParser() // create real parser
try validator.validate(dealerSearchServiceDomain)
try ARSDealerConnection.getAllDealers(dealerSearchServiceDomain, helperCallback: { (getResponse) -> Void in
self.handleResponse(parser, validator: validator, getResult: getResponse, completion: wrapperCallback)
})
}
in this case handleResponse lives in the same class with getAllDealers but it can actually be a global function that every service can call.
I think that it might be possible to write a better implementation using generics but it wouldn't be much shorter than this, in the end you can't save yourself from creating the validators and parsers and call the next layer.

Resources