Swift Nested try inside a closure - ios

I have a requirement in the below way
enum CustomError1: Error {
case errorA
}
enum CustomError2: Error {
case errorA
}
public func func1(completion: #escaping () -> Void) throws {
//some code
if #somecondition {
throw CustomError1.errorA
}
completion()
}
public func func2(completion: #escaping () -> Void) throws {
//some code
if #somecondition {
throw CustomError2.errorA
}
completion()
}
func result() {
do {
try func1() {
try self.func2 (){
}
}
} catch {
}
}
the result function gives an error as below
Invalid conversion from throwing function of type '() throws -> ()' to non-throwing function type '() -> Void'
That is because the func1 and func2 are giving different type of Error.
Due to this I need to write another do catch inside the first closure as below
func result() {
do {
try func1() {
do {
try self.func2 (){
}
} catch {
}
}
} catch {
}
}
Is there a way to simplify this kind of nested try catches

The problem is that the argument to func1 is typed as escaping () -> Void. That means you cannot throw inside the function you pass as that argument. You would need to have typed this as escaping () throws -> Void.

enum CustomError1: Error {
case errorA
}
enum CustomError2: Error {
case errorA
}
public func func1(completion: #escaping () throws -> Void) throws {
//some code
if true {
throw CustomError1.errorA
}
try completion()
}
public func func2(completion: #escaping () throws -> Void) throws {
//some code
if true {
throw CustomError2.errorA
}
try completion()
}
func result() {
do {
try func1(completion: {
try func2 (completion: {
})
})
} catch {
}
}
I wouldn't suggest using throw with completions. The better way is using better completion implementation. Something like this:
public func func1(completion: #escaping (Error?) throws -> Void) throws {
//some code
if true {
completion(CustomError1.errorA)
}
completion(nil)
}

Related

How to fix "'async' call in an autoclosure that does not support concurrency"?

I have these two functions (which compile without errors):
func hasLocalChanges() -> Bool {
return false
}
func hasRemoteChanges() async -> Bool {
return await Task{ true }.value
}
Now, let's say I want to introduce a third function which will look like this:
func hasChanges() async -> Bool {
return self.hasLocalChanges() || await self.hasRemoteChanges()
}
Then this function will give me a compiler error saying:
'async' call in an autoclosure that does not support concurrency
But why is that?
I can resolve the error by swapping the operands …
func hasChanges() async -> Bool {
return await self.hasRemoteChanges() || self.hasLocalChanges()
}
…, which, again, will make the code compile without errors.
But I really want to make use of lazy evaluation and have the asynchronous function be executed last. So the only way to achieve that would be to say …
func hasChanges() async -> Bool {
if !self.hasLocalChanges() {
return await self.hasRemoteChanges()
} else {
return true
}
}
…, which seems a little cumbersome.
Can anyone explain to me why I am getting that error in the first place?
It's because || is actually a function that wants the right hand side to look like rhs: #autoclosure () throws -> Bool (vs. e.g rhs: #autoclosure () async throws -> Bool)
See the source here: https://github.com/apple/swift/blob/e6cbf5483237aa593bdbafb6c7db7ebcb2d0e26a/stdlib/public/core/Bool.swift#L320
When you move the await first, it resolves into a boolean and then self.hasLocalChanges() is a non async () throws -> Bool
Examples that compile below
func hasChanges() async -> Bool {
return or( a: await hasRemoteChanges(), b: hasLocalChanges())
}
// This is pretty much what `||` looks like today
func or(a: Bool, b: #autoclosure () throws -> Bool) -> Bool {
return a ? true : try b()
}
func hasChanges() async -> Bool {
return await or( a: hasLocalChanges(), b: await hasRemoteChanges())
}
// This is what an `async` friendly `||` could look like.
// Note that the caller would need to await the result.
func or(a: Bool, b: #autoclosure () async throws -> Bool) async -> Bool {
return a ? true : try! await b()
}
I’ve also just encountered this and was looking to do exactly what you were: avoid calling the async func if my other value is true. Going off of what aciniglio said, I believe you could avoid the or func and keep this inline by having something like:
func hasChanges() async -> Bool {
return self.hasLocalChanges() ? true : await self.hasRemoteChanges()
}
This can also work with an if of course, which is what I was trying to do, like:
func doSomething() {
Task { #MainActor in
if self.hasLocalChanges() ? true : await self.hasRemoteChanges() {
print("Something")
}
}
}

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

iOS Swift function with type () -> ()

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.

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.

Sending a function to another function as a parameter

I want to send a func as a parameter to another func but I know how to do it only in case the func being sent has an input and an output parameters:
aFunc (sentFunc: Int -> String) {
...
}
But I want to achieve something like the following code where the function being sent does not have parameters:
func firstFunc(<i want to declare paramenter for a function here that itself does not have any parameters>) {
if (servicedCities != nil) {
<execute sent fuction>
} else {
objectMapper().getServicedCitiesifAvailable()
<execute sent fuction>
}
}
the func calling the above func should look like
func secondFunc() {
func myNestedFunction() {
....
}
....
firstFunc(myNestedFunction())
....
}
func firstFunc(passedFunc:()->()) {
passedFunc()
}
func secondFunc() {
func myNestedFunction() {
print("hi")
}
firstFunc(myNestedFunction)
}
secondFunc()
will ouput
hi
in the context of the firstFunc call.
Note that you have to omit the () in the last line after the myNestedFunction because you dont want to call the method but pass the method itself.
func firstFunc(sentFunc: () -> ()) {
sentFunc()
}
or
func firstFunc(sentFunc: Void -> Void) {
sentFunc()
}
I think you are looking for Swift closures.
Check this Swift closures and functions
func someFunc(paramFunc: () -> ()) {
paramFunc()
}
...
self.someFunc { () -> () in
//Do some code here;
}

Resources