Reputation: 63
I'm new in swift and I've been probably for more than an hour around this.
I make a request to a webservice, and now i want to act according to the response code (200 Ok) or other but I can't understand the syntax for returning a value and throwing the exception.
typealias ThrowableCallBack = () throws -> Bool
func authenticate()
{
let services = ServiceManager()
do {
try services.login(username: emailField.text!, password: passwordField.text!, authenticated: { auth in
self.loadingDialog.dismiss(animated: false, completion: {
if (auth) // '() throws -> Bool' is not convertible to 'Bool'
{
self.performSegue(withIdentifier: "LoginSegue", sender: self)
}
})
} )
}
catch RequestError.invalidRequest {
showLoginFailedAlert()
}
catch {
showLoginFailedAlert()
}
}
Then on services
func login(username : String, password : String, authenticated: @escaping (_ inner: ThrowableCallBack) -> Void )
{
let parameters = [
"_username" : username,
"_password" : password
]
let request = makePostCall(request: "login", parameters: parameters, completion: {
response in
let statusCode = String(describing: response["statusCode"]!)
if (statusCode != "200")
{
authenticated( { throw RequestError.invalidRequest })
}
else
{
self.jwt = String(describing: response["jwt"]!)
authenticated( { return true })
}
} )
}
How should I fix the auth '() throws -> Bool' is not convertible to 'Bool' to be able to both catch the error or succeed ? Is my alias correct?
Thank you in advance
Upvotes: 1
Views: 1160
Reputation: 47886
In your login
method, the type of the parameter authenticated
is @escaping (_ inner: ThrowableCallBack) -> Void
.
So, the closure passed to authenticated
takes one parameter of type ThrowableCallBack
, which means the parameter auth
in your closure is of type ThrowableCallBack
, not Bool
.
You need to use auth
as if try auth()
as ThrowableCallBack
takes one parameter of type ()
and throws
.
func authenticate() {
let services = ServiceManager()
services.login(username: emailField.text!, password: passwordField.text!, authenticated: { auth in
self.loadingDialog.dismiss(animated: false, completion: {
do {
if try auth() {
self.performSegue(withIdentifier: "LoginSegue", sender: self)
}
} catch RequestError.invalidRequest {
self.showLoginFailedAlert()
} catch {
self.showLoginFailedAlert()
}
})
} )
}
To make this work, you may need to modify the type of authenticated
as follows:
func login(username : String, password : String,
authenticated: @escaping (_ inner: @escaping ThrowableCallBack) -> Void ) {
//...
}
Anyway, closure type taking a parameter of another closure type, is very confusing and you should better re-consider your design.
When you get runtime errors saying some main thread things, you may need to use DispatchQueue.main
as found in many async examples:
func authenticate() {
let services = ServiceManager()
services.login(username: emailField.text!, password: passwordField.text!, authenticated: { auth in
DispatchQueue.main.async {
self.loadingDialog.dismiss(animated: false, completion: {
do {
if try auth() {
self.performSegue(withIdentifier: "LoginSegue", sender: self)
}
} catch RequestError.invalidRequest {
self.showLoginFailedAlert()
} catch {
self.showLoginFailedAlert()
}
})
}
} )
}
Upvotes: 1
Reputation: 2873
You can have a look at this thread
We make use of a Completion
enum to accomplish this
public enum Completion<E:Error> {
case success
case failure(E)
}
Implement it in your function as follows:
func login(username : String, password : String, authenticated: @escaping (Completion<ServiceError> )
{
...
if (statusCode != "200") {
authenticated(.failure(ServiceError.accessDenied))
} else {
self.jwt = String(describing: response["jwt"]!)
authenticated(.success)
}
...
}
Test it as follows:
try services.login(username: emailField.text!, password: passwordField.text!, authenticated: { auth in
switch auth {
case .success: //handle success
case .failure(let error): print(error)
}
}
Note ServiceError
is an enum that implements Error and holds all the different kinds of service errors that we potentially get.
We use a similar enum in the case where we want to return a result.
public enum Result<Value, E:Error> {
case success(Value)
case failure(E)
}
Upvotes: 0