Reputation: 2869
I'm trying to do this seemingly trivial thing:
static func list() -> Promise<[Activity]> {
let endpoint = "\(self.baseUrl)/v1/activities"
return Promise { fulfill, reject in
self.fetchHeaders { (headers) in
return Alamofire.request(
endpoint,
method: .get,
parameters: nil,
encoding: JSONEncoding.default,
headers: headers
).validate().responseJSON().then() { response in
guard let json = response as? JSON else {
reject(ActivityError.parse("Malformed JSON"))
}
guard let jsonActivities = json["activities"] as? [JSON] else {
reject(ActivityError.parse("Missing field"))
}
var activities: [Activity] = []
for jsonActivity in jsonActivities {
guard let activity = Activity(json: jsonActivity) else {
reject(ActivityError.parse("Unable to parse an Activity object"))
}
activities.append(activity)
}
fulfill(activities)
}.catch { error in
reject(ActivityError.network("HTTP response failure"))
}
}
}
}
However, the compiler (rightfully) complains that:
'guard' body may not fall through, consider using 'return' or 'break' to exit the scope
I understand I need to return a Promise here. I just can't figure out what exactly to put below the reject() and fulfill() calls.
Upvotes: 2
Views: 3763
Reputation: 437917
There's nothing wrong with reject
or fulfill
calls. The issue is that after you reject
in your guard
statement, you also have to return
to exit the closure:
guard let json = response as? JSON else {
reject(ActivityError.parse("Malformed JSON"))
return
}
guard let jsonActivities = json["activities"] as? [JSON] else {
reject(ActivityError.parse("Missing field"))
return
}
The key point is that you do not want to conflate the promise that is returned by this method (which is later satisfied by fulfill
or reject
), with the fact that within this closure, you have to immediately exit the closure with a return
in the guard
clause.
I cannot reproduce this issue with your code (because you haven't provided a MCVE and there are references here that I cannot resolve). But here is a simplified rendition of your code illustrating the use of guard
:
So, if not using PromiseKit/Alamofire, you can do:
func list() -> Promise<[String: Any]> {
return Promise { fulfill, reject in
Alamofire.request(url)
.validate()
.responseJSON { response in
switch response.result {
case .success(let json):
guard let dictionary = json as? [String: Any] else {
reject(ActivityError.malformed("not a dictionary"))
return
}
fulfill(dictionary)
case .failure(let error):
reject(error)
}
}
}
}
As you can see, you are returning a Promise
, but inside the Alamofire closure, you simply are exiting your guard
statement.
If you're using PromiseKit/Alamofire and call then
, you presumably want to create a promise that it can return, such as:
func list() -> Promise<String> {
return Alamofire.request(endPoint)
.validate()
.responseJSON()
.then { value in
return Promise { fulfill, reject in
guard let dictionary = value as? [String: Any], let name = dictionary["name"] as? String else {
reject(ActivityError.malformed("not dictionary"))
return
}
fulfill(name)
}
}
}
Or if that's too hairy, you can pull out that parsing of the value
:
func list() -> Promise<String> {
return Alamofire.request(endPoint)
.validate()
.responseJSON()
.then { value in
self.parse(value)
}
}
func parse(_ value: Any) -> Promise<String> {
return Promise { fulfill, reject in
guard let dictionary = value as? [String: Any], let name = dictionary["name"] as? String else {
reject(ActivityError.malformed("not dictionary"))
return
}
fulfill(name)
}
}
But, either way, even when using PromiseKit/Alamofire, you still just return
within the guard
clause.
Upvotes: 7