I'm using Alamofire for fetching the data.URLRequestConvertible protocol is using for constructing the request.When calling the URLRequestConvertible enum in requested class function through Alamofire I'm getting an error like (Invalid conversion from throwing function of type '(AFDataResponse) throws -> Void' (aka '(DataResponse<Any, AFError>) throws -> ()') to non-throwing function type '(AFDataResponse) -> Void' (aka '(DataResponse<Any, AFError>) -> ()')).In requested function where i'm fetching the result how i can used the generic?
Q1: Getting an error when fetching the result
Q2: How i can used the generic in a function
URLRequestConvertible enum:
enum Router: URLRequestConvertible{
case getAllDishes
var bseUrl : URL{
return URL(string: "https://yummie.glitch.me/")!
}
var method: HTTPMethod{
switch self {
default:
return .get
}
}
var path:String{
switch self{
case .getAllDishes:
return "dish-categories"
}
}
func asURLRequest() throws -> URLRequest {
let url = bseUrl.appendingPathComponent(path)
var request = URLRequest(url: url)
request.method = method
return request
}
}
Calling requested func:
class NetworkLayer{
class func requested(_ request:Router,completion:#escaping(Result<Data,Error>) -> Void){
ProgressHUD.show() //if response comes that loader run
AF.request(request).responseJSON{ (response) in
switch response.result{
case .success(let data):
do{
let getDishesData = data as? [String:Any]
let resp = try JSONSerialization.data(withJSONObject: getDishesData?["data"], options: .prettyPrinted)
completion(.success(response))
}
case .failure(let error):
completion(.failure(error))
}
}
}
You need to add Do-Catch Statement
catch – If the throwing method fails and raises an error, the execution will fall into this catch block.
class NetworkLayer{
class func requested(_ request:Router,completion:#escaping(Result<Data,Error>) -> Void){
ProgressHUD.show() //if response comes that loader run
AF.request(request).responseJSON{ (response) in
switch response.result{
case .success(let data):
do{
let getDishesData = data as? [String:Any]
let resp = try JSONSerialization.data(withJSONObject: getDishesData?["data"], options: .prettyPrinted)
completion(.success(response))
}catch{
print(error)
completion(.failure(error))
}
case .failure(let error):
completion(.failure(error))
}
}
}
}
One more suggestion for you here no need to do JSONSerialization because responseJSON gives you direct response(That Alamofire will do JSONSerialization).
Final code
class NetworkLayer{
class func requested(_ request:Router,completion:#escaping(Result<Data,Error>) -> Void){
ProgressHUD.show() //if response comes that loader run
AF.request(request).responseJSON{ (response) in
switch response.result{
case .success(let response):
do{
print(response)
completion(.success(response))
}catch{
print(error)
completion(.failure(error))
}
case .failure(let error):
completion(.failure(error))
}
}
}
}
Related
I am using REST api for showing some details in my application. For this i am planning to have a data model class to pass the datas back to the controller.
import UIKit
import AlamofireObjectMapper
import Alamofire
class ContactUsModelClass {
func getContactUsApiCall(URL: URL, callback: #escaping ((Dictionary<AnyHashable,Any>) -> ())) {
Alamofire.request(URL, method: .get, parameters: nil, encoding: JSONEncoding.default, headers: nil)
.responseObject { (response: DataResponse<GetContactusResponse>) in
print(response.result.value!)
switch response.result {
case .success:
// optional is NOT NULL, neither NIL nor NSNull
guard let end = response.result.value else {
return
}
//end = nullToNil(end.loginAuthenticationInfo?.accessToken)
callback(response.result.value)
break
case .failure:
if let error = response.result.error as? URLError {
print("URLError occurred: \(error)")
} else {
print("Unknown error: \(String(describing: response.result.error))")
}
break
}
}
}
}
In My view controller i am trying to access it through:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
//getContactUsApiCall(URL:contactusURL!as URL)
getContactUsApiCall(URL:contactusURL!as URL) { dictionary in
print(dictionary)
}
But i am getting the following errors:
Try using this function instead
class func getContactUsApiCall(URL: URL, completionHandler: #escaping ((Dictionary<AnyHashable,Any>)->Void)) {
Alamofire.request(URL, method: .get, parameters: nil, encoding: JSONEncoding.default, headers: nil)
.responseJSON{ response in
print(response.result.value!)
switch response.result {
case .success(let value):
// optional is NOT NULL, neither NIL nor NSNull
guard let end = response.result.value else {
return
}
//end = nullToNil(end.loginAuthenticationInfo?.accessToken)
completionHandler(value as! Dictionary<AnyHashable, Any>)//I would be careful here to make sure you data is returned in this format
break
case .failure:
if let error = response.result.error as? URLError {
print("URLError occurred: \(error)")
} else {
print("Unknown error: \(String(describing: response.result.error))")
}
break
}
}
}
and then this is your view did load
ContactUsModelClass.getContactUsApiCall(URL:contactusURL!as URL, completionHandler: { (dictionary) -> Void in
print(dictionary)
})
Hope that helps. If you are using the data in a view did load, you can put the call in the same VC.
The errors are actually self-explanatory. Use of unresolved id means that the method you are calling is not in the current scope. Since getContactUsApiCall is actually declared inside the class ContactUsModelClass, you'll need to call it from an object of that class. Alternatively, you could modify the method to be a class method and call it as ContactUsModelClass.getContactUsApiCall(...).
As for the second error, response.result.value is of the type GetContactusResponse?. Hence, your callback's signature should also use that type. Like so:
class func getContactUsApiCall(URL: URL, callback: #escaping ((GetContactusResponse) -> ())) {
Alamofire.request(URL, method: .get, parameters: nil, encoding: JSONEncoding.default, headers: nil)
.responseObject { (response: DataResponse<GetContactusResponse>) in
switch response.result {
case .success:
// optional is NOT NULL, neither NIL nor NSNull
guard let end = response.result.value else {
return
}
callback(end)
break
case .failure:
if let error = response.result.error as? URLError {
print("URLError occurred: \(error)")
} else {
print("Unknown error: \(String(describing: response.result.error))")
}
break
}
}
}
I getting the result as an array of strings like this
["India","America","Australia","China","Russia"]
And I'm using Alamofire to get the response using code. There's no error, but I got the result as null. Please help in parsing this.
sessionManager?.request(strURL, method: method, parameters: params, encoding: encoding , headers: headers).responseJSON { (response) in
switch response.result {
case .success:
let resJson = JSON(response.result.value!)
success(resJson)
break
case .failure(let error):
failure(error as NSError)
break
}
}
Try this:
if let responseData = response.result.value{
let responsevalue = responseData as? [String]
}
For anyone looking for another derived answer, just put this chunk of code after Alamofire.request(...):
.responseJSON(completionHandler: { (response) in
switch response.result{
case .success(let value):
// Here is your array of String
let arrayOfStrings = value as? [String]
case .failure(let error):
// Some code when error happens...
print(error.localizedDescription)
}
})
This solution using SwiftyJSON:
.responseJSON(completionHandler: { (response) in
switch response.result{
case .failure(let error):
print(error.localizedDescription)
case .success(let res):
let json = JSON(res)
let res = json["result"]
var models = [String]()
if let models1 = company["models"].array {
for model in models1 {
guard let mod = model.string else { return }
models.append(mod)
}
}
}
})
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?
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)
}
}
I'm trying to reuse Alamofire's Result type for own API callbacks.
Here is a shortened version of result type I'm using:
public enum Result<Value> {
case Success(Value)
case Failure(NSData?, ErrorType)
}
So for my API calls I'm using it in completion blocks:
func getUserContent(userId: String, completion: (result: Result<User>) -> Void) {
Alamofire.request(UserRouter.GetUser(userId))
.validate()
.responseJSON { (request, response, result) -> Void in
switch result {
case .Failure(_, let error):
completion(result: .Failure(nil, error))
case .Success(let value):
if let responseDict = value as? [String: AnyObject] {
do {
// synchronous call which parses response and
// creates User struct instance or throws exception
let user = try self.processUserResponse(responseDict)
completion(result: .Success(user))
} catch(let error) {
completion(result: .Failure(nil, error))
}
} else {
completion(result: .Failure(nil, MyAPIError.WrongResponseFormat))
}
}
}
}
I think its perfectly fits here but there is one issue. I have some calls with completion blocks which supposed to return either .Success with no value or .Failure.
E.g. deleteUser method should look something like:
func deleteUser(userId: String, completion: (result: Result<nil>) -> Void) {
// ... do some stuff here
}
so when I call it later I can do:
deleteUser(userId) { (result) -> Void in
switch result {
case .Success:
print("success")
case .Failure(nil, let error):
print("Error: \(error)")
}
}
But I can't create "empty" .Success. Result<nil> of course gives me a compile error. But I don't have any type to pass to some of .Success cases. Does anyone has a better solution that defining another Result Type with no type on .Success?
#ogreswamp is right! You can omit the type requirement with Void. Type Void is simply an empty tuple, in effect a tuple with zero elements, which can be written as (). Here is an example:
enum Result<T, E: ErrorType> {
case Success(T)
case Failure(E)
init(value: T) {
self = .Success(value)
}
init(error: E) {
self = .Failure(error)
}
}
Use this like
enum AuthenticationError: ErrorType {
case MissingEmail
case InvalidPassword
}
func signUp(email email: String, password: String) -> Result<Void, AuthenticationError>
You can return the result like
// Success
return Result(value: ())
// Failure
return Result(error: .InvalidPassword)
And finally, check the result
switch result {
case .Success(_):
print("Request SignUp was sent")
case .Failure(let error):
switch error {
case .InvalidEmail:
print("Invalid email")
default:
break
}
}
You will need to define your own Result type. Also note that Alamofire 3.0 uses a much different Result type that may better suit your needs.