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)
}
}
Related
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))
}
}
}
}
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 need to wait for response.response?.allHeaderFields data before executing function. I've searched the net and didn't quite get how to add "completion handler" to alamofire request. Or if there are any other ways to make the function wait.
#IBAction func comfirmation(sender: UIButton) {
if CodeTextField.text != "" {
print("in comfirmation function")
let comfirmationRequestData = [
"phone" : "\(appDelegate.savedNumber)",
"code" : "\(CodeTextField.text!)"
]
Alamofire.request(.POST,
"http://192.168.214.241:4000/login",
parameters: comfirmationRequestData,
encoding: .JSON).responseJSON {
response in
switch response.result {
case .Success:
let jsonDecrypted = JSON(response.result.value!)
print(jsonDecrypted)
let headerFile = response.response?.allHeaderFields as? [String:String]
print(headerFile)
case .Failure(let error):
print(error)
}
}
print("in comfirmation function. success")
appDelegate.defaults.setValue(appDelegate.savedNumber, forKey: "phoneNumber")
} else {
print("in comfirmation function. failed")
}
}
Use Alamofire like this
func postRequest( urlSuffix : String, params:[String : AnyObject]?, filterParams : [String]?, success: (response: AnyObject!) -> Void, failure: (error: NSError?) -> Void)
{
Alamofire.request(.POST, webServicesURLPrefix + urlSuffix, parameters: params, encoding: .JSON, headers: self.headers)
request?.responseJSON { response in
switch response.result
{
case .Success:
success(response: response.result.value)
case .Failure(let error):
failure(error: error)
}
}
}
Call the method from anywhere as
self.postRequest("do-registration.php", params: params, filterParams: nil, success: { (response) -> Void in
self.afterResponse(response)
}) { (error) -> Void in
failure(error: error)
}
OR you can write a saperate method which you will have to call after the completion.
func afterResponse(responseData : AnyObject)
{
print("Done")
print(responseData)
}
You can cause the operation to be synchronous, but in order to do that you are going to have to use a semaphore for that you set up prior to the Alamofire request, and that you then release within the completion handler. You will wait on the semaphore right after you initiate Alamo fire.
There is a library Alamofire-Synchronous which works using semaphore.
Sample:
//json
let response = Alamofire.request(.GET, "https://httpbin.org/get", parameters: ["foo": "bar"]).responseJSON()
if let json = response.result.value {
print(json)
}
In my application when I try to obtain data Alamofire, but present connection problems, randomly presented the error = Optional (Error Domain = NSURLErrorDomain Code = -1005 "The network connection was lost." so responseObject is nil. What is the best way to handle the data so that when it is nil, it’s make the call again?
Attached my call to the server:
makeCall(dirurl, param: parame, completionHandler: { responseObject, error in
print("responseObject = \(responseObject); error = \(error)")
//here should control for the call is generated until it is different from nil
self.datosRequest = responseObject!
self.datosContenido = self.datosRequest["datos"] as! NSDictionary
self.extractData()
self.tableGastosComunes.reloadData()
return
})
func makeCall(dirurl: String, param : [String:NSObject], completionHandler: (NSDictionary?, NSError?) -> ()) {
Alamofire.request(.POST, dirurl, parameters: param, encoding:.URL).responseJSON { response in
switch response.result {
case .Success(let value):
completionHandler(value as? NSDictionary, nil)
case .Failure(let error):
completionHandler(nil, error)
}
}
}
I use Alamofire to send multiple requests at the same time. But the request A need request B's response named token. I will give a example :
var token = "111111111111"
let URLString1 = "http://httpbin.org/get?token=\(token)"
Alamofire.request(.GET, URLString1, parameters: nil, encoding:.JSON).responseJSON { response in
switch response.result {
case .Success(let JSON):
print("http1: \(JSON)")
token = "22222222222"
case .Failure(let error):
print("Request failed with error: \(error)")
}
}
//I want to wait for request1's response and get the new token value.
let URLString2 = "http://httpbin.org/get?token=\(token)"
Alamofire.request(.GET, URLString2, parameters: nil, encoding:.JSON).responseJSON { response in
switch response.result {
case .Success(let JSON):
print("http2: \(JSON)")
print("token: \(token)")
case .Failure(let error):
print("Request failed with error: \(error)")
}
}
I don't want to put request2 in request1's success.
So now my solution is: I give a variable named tokenChange.I have a array to store all the request that need the token. When the token is changed , I send them one by one from the array. But this solution is not elegant.
So is there a good pattern to control this ?
I find this method dispatch_semaphore_signal.It can solve my problem. So any other way ?
var token = "111111"
let group = dispatch_group_create();
dispatch_group_async(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) { () -> Void in
let semaphore = dispatch_semaphore_create(0)
//token is 111111
let URLString = "http://httpbin.org/get?token=\(token)"
Alamofire.request(.GET, URLString, parameters: nil, encoding:.JSON).responseJSON { response in
switch response.result {
case .Success(let JSON):
print("http1: \(JSON)--class:")
token = "222222"
print("\n")
dispatch_semaphore_signal(semaphore)
case .Failure(let error):
print("Request failed with error: \(error)")
}
}
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)
//token is 222222
let URLString2 = "http://httpbin.org/get?token=\(token)"
Alamofire.request(.GET, URLString2, parameters: nil, encoding:.JSON).responseJSON { response in
switch response.result {
case .Success(let JSON):
print("http2: \(JSON)--class:")
print("\n")
print ("token is:\(token)")
case .Failure(let error):
print("Request failed with error: \(error)")
}
}
//token is 2222222
let URLString3 = "http://httpbin.org/get?token=\(token)"
Alamofire.request(.GET, URLString3, parameters: nil, encoding:.JSON).responseJSON { response in
switch response.result {
case .Success(let JSON):
// token = (JSON["args"]!!["value"]) as! String
print("http3: \(JSON)--class:")
print("\n")
print ("token is:\(token)")
case .Failure(let error):
print("Request failed with error: \(error)")
}
}
}
dispatch_group_notify(group,dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) { () -> Void in
NSLog("finish");
}
you need to execute the second url string inside the first request because the response will came in background thread so your code will be like this:
var token = "111111111111"
let URLString1 = "http://httpbin.org/get?token=\(token)"
Alamofire.request(.GET, URLString1, parameters: nil, encoding:.JSON).responseJSON { response in
switch response.result {
case .Success(let JSON):
print("http1: \(JSON)")
token = "22222222222"
case .Failure(let error):
print("Request failed with error: \(error)")
}
let URLString2 = "http://httpbin.org/get?token=\(token)"
Alamofire.request(.GET, URLString2, parameters: nil, encoding:.JSON).responseJSON { response in
switch response.result {
case .Success(let JSON):
print("http2: \(JSON)")
print("token: \(token)")
case .Failure(let error):
print("Request failed with error: \(error)")
}
}
}