What does it mean in Swift: 'case .success(let dict):' - ios

In Swift PromiseKit library there's Alamofire example using a bit strange syntax:
func login(completionHandler: (NSDictionary?, ErrorProtocol?) -> Void {
Alamofire.request(.GET, url, parameters: ["foo": "bar"])
.validate()
.responseJSON { response in
switch response.result {
case .success(let dict):
completionHandler(dict, nil)
case .failure(let error):
completionHandler(nil, error)
}
}
}
response is an Alamofire enum describing two cases with associated values:
public enum Result<Value> {
case success(Value)
case failure(Error)
(...)
What I don't get is what does let mean in each case: line and where does the dict (or error) come from? Is this syntactic sugar for something more verbose but less confusing?

In Swift, enums can have associated values (docs). This means, that you can associate an object with cases. The part (let dict) simply means - take the associated value, and put in in a let constant named dict.

Related

"'isSuccess' is inaccessible due to 'internal' protection level", AlamoFire not working like before

Im using alamoFire on swift but i came across this problem: "isSuccess' is inaccessible due to 'internal' protection level".
I have tried this and i have also tried this,
here is my code:
AF.request(jsonURL, method: .get, parameters: parameters).responseJSON { (response) in
if response.result.isSuccess { //problem is here
print("Got the info")
print(response)
let flowerJSON : JSON = JSON(response.result.value!)
let list = flowerJSON["..."]["..."]["..."].stringValue
print(list)
}
}
result is now of the built-in Result enum type, which means you can do pattern matching on it. Your code can be rewritten as:
AF.request("", method: .get, parameters: [:]).responseJSON { (response) in
if case .success(let value) = response.result {
print("Got the info")
print(response)
let flowerJSON : JSON = JSON(value)
...
}
}
Use a switch statement if you want the error case as well:
switch response.result {
case .success(let value):
// ...
case .failure(let error):
// ...
}

throwing an error in a callback with Swift 3

I'm currently using Alamofire for executing an API call:
Alamofire.request(baseUrl + path,method:rMethod,parameters:parameters,encoding: JSONEncoding.default,headers:headers)
.responseJSON { response in
switch response.result {
case .success(let data):
let json = JSON(data)
onCompletion(json, nil)
case .failure(let error):
print("Request failed with error: \(error)")
onCompletion(nil,error)
}
}
Alamofire is notoriously based on a "Result" enum approach for managing the response (check this article):
public enum Result<T, Error: ErrorType> {
case Success(T)
case Failure(Error)
}
Now, I would like to migrate to a try/catch approach for managing the error scenario, something like this:
Alamofire.request(baseUrl + path,method:rMethod,parameters:parameters,encoding: JSONEncoding.default,headers:headers)
.responseJSON { response in
switch response.result {
case .success(let data):
let json = JSON(data)
onCompletion(json)
case .failure(let error):
throw error
}
}
Obviously this approach doesn't work because Alamofire's responseJSON function doesn't throw:
/// Adds a handler to be called once the request has finished.
///
/// - parameter options: The JSON serialization reading options. Defaults to `.allowFragments`.
/// - parameter completionHandler: A closure to be executed once the request has finished.
///
/// - returns: The request.
#discardableResult
public func responseJSON(
queue: DispatchQueue? = nil,
options: JSONSerialization.ReadingOptions = .allowFragments,
completionHandler: #escaping (DataResponse<Any>) -> Void)
-> Self
{
return response(
queue: queue,
responseSerializer: DataRequest.jsonResponseSerializer(options: options),
completionHandler: completionHandler
)
}
}
Question: is there anyway to throw an exception from inside this closure? I have tried forwarding the error to another function which then throw the exception but this is what I get displayed:
Does this mean that if inside a 3rd party library callback I can never throw an error?

Alamofire: Is there a way to get response data in case of Failure?

In Alamofire calling .validate() does automatic validation and passes status code 200...299 as success.
In case of failed API request server sends status code 400 and some internal error message and code in JSON to identify which kind of error is this. I couldn't find a way to get this JSON data in case status code is 400 under case .Failure in following example:
Alamofire.request(.GET, "https://httpbin.org/get", parameters: ["foo": "bar"])
.validate()
.responseJSON { response in
switch response.result {
case .Success:
print("Validation Successful")
case .Failure(let error):
print(error)
}
}
error doesn't contain response data. Is there anyway to get it?
If there is json data returned from the server on error, you should be able to get it from the response object, like:
print(response.result) // result of response serialization
if let JSON = response.result.value {
print("JSON: \(JSON)")
}
You can also subclass the ErrorType object returned by Alamofire:
public enum BackendError: ErrorType {
case Network(error: NSError)
case DataSerialization(reason: String)
case JSONSerialization(error: NSError)
case ObjectSerialization(reason: String)
case XMLSerialization(error: NSError)
}
This will give you more information about the error if you dont want to construct a custom error object to return. More on this from the Alamofire docs: https://github.com/Alamofire/Alamofire#handling-errors

Completion handler for Alamofire network fetch

I am attempting to create a function which will return a list of custom objects, created from parsing JSON. I am using AlamoFire to download the content. I have written this function which, on success, creates an array of locations to be returned. However, the returns are always nil. My code is below:
func fetchLocations() -> [Location]? {
var locations : [Location]?
Alamofire.request(.GET, myURL)
.responseJSON { response in
switch response.result {
case .Success(let data):
locations = createMapLocations(data)
case .Failure(let error):
print("Request failed with error: \(error)")
}
}
return locations
}
I am pretty positive the issue is that the functioning is returning before the network request is complete. I am new to Swift, and unsure how to handle this. Any help would be appreciated!
You can read more about closures/ completion handlers https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/Closures.html or google.
func fetchLocations(completionHandler: (locations: [Location]?, error: NSError) -> ()) -> () {
var locations : [Location]?
Alamofire.request(.GET, myURL)
.responseJSON { response in
switch response.result {
case .Success(let data):
locations = createMapLocations(data)
completionHandler(locations, error: nil)
case .Failure(let error):
print("Request failed with error: \(error)")
completionHandler(locations: nil, error: error)
}
}
}
Usage
fetchLocations(){
data in
if(data.locations != nil){
//do something witht he data
}else{
//Handle error here
print(data.error)
}
}

How to get the result value of Alamofire.request().responseJSON in swift 2?

I have a question about the new version of Alamofire for Swift 2
Alamofire.request(.POST, urlString, parameters: parameters as? [String : AnyObject])
.responseJSON { (request, response, result) -> Void in
let dico = result as? NSDictionary
for (index, value) in dico! {
print("index : \(index) value : \(value)")
}
}
In this section I would like to cast the result in to a NSDictionary. But When I compile and put a breakpoint, the debugger says that dico is nil. If I use debugDescription to print result, it is not nil and contains what I expected
How can I cast the Result variable?
The accepted answer works great but with the introduction of Alamofire 3.0.0 there are some breaking changes that affects this implementation.
The migration guide has further explanations but i will highlight the ones related to the actual solution.
Response
All response serializers (with the exception of response) return a generic Response struct.
Response type
The Result type has been redesigned to be a double generic type that does not store the NSData? in the .Failure case.
Also take in count that Alamofire treats any completed request to be successful, regardless of the content of the response. So you need to chain a .validate() before .responseJSON() to hit the .Failure case.
Read more about it here.
Updated code:
let url = "http://api.myawesomeapp.com"
Alamofire.request(.GET, url).validate().responseJSON { response in
switch response.result {
case .Success(let data):
let json = JSON(data)
let name = json["name"].stringValue
print(name)
case .Failure(let error):
print("Request failed with error: \(error)")
}
}
For reference:
Xcode 7.3 (Swift 2.2)
Alamofire 3.3.1
SwiftyJSON 2.3.3
If you don't mind using SwiftyJSON library, here's a working example in Xcode 7 Beta 5 + Alamofire 2.0.0-beta.1 + SwiftyJSON (xcode7 branch)
Alamofire.request(.GET, url, parameters: params, encoding: ParameterEncoding.URL).responseJSON { (_, _, result) in
switch result {
case .Success(let data):
let json = JSON(data)
let name = json["name"].string
case .Failure(_, let error):
print("Request failed with error: \(error)")
}
}
Edit:
Updated SwiftyJSON git page
You can now achieve most of the required behaviour out of the box without the need for SwiftyJSON for example. My OAuthTokenResponse is a simple struct that is Codable. The Alamofire library 5.2.2 lets you respond using the 'responseDecodable'
If you have say a struct that looks like this:
struct OAuthTokenResponse : Codable
{
var access_token:String?
var token_type:String?
var expires_in:Int?
var scope:String?
}
And then in your network call (using Alamofire)
let request = AF.request(identityUrl, method: .post, parameters: parameters, encoding: URLEncoding.httpBody)
request
.validate()
.responseDecodable { (response:AFDataResponse<OAuthTokenResponse>) in
switch response.result {
case .success(let data):
do {
let jwt = try decode(jwt: data.access_token!) // example
self.connected = true
print(jwt)
} catch {
print(error.localizedDescription)
self.connected = false
}
case .failure(let error):
self.connected = false
print(error.localizedDescription)
}
}
In the above code, the success case automatically deserialises your JSON using the decodable protocol into your struct. Any errors will result in the error case being hit instead.

Resources