I am using this Alamofire request and returning a json
let todoEndpoint: String = "https://api.abc.com/api/v3/products?pid=uid8225&format=json&&offset=0&limit=10"
Alamofire.request(todoEndpoint)
.responseJSON { response in
guard let json = response.result.value as? [String: Any] else {
print("didn't get todo object as JSON from API")
print("Error: \(response.result.error)")
return
}
print(json)
}
Now i have do loop where i want to use this json value but i am getting:
error : Use of unresolved identifier json ?
do {
for (index,subJson):(String, JSON) in json {
print("Index :\(index) Title: \(subJson)" )
} catch
{
print("there was an error")
}
As mentioned :
https://github.com/Alamofire/Alamofire#response-string-handler
" the result of a request is only available inside the scope of a response closure. Any execution contingent on the response or data received from the server must be done within a response closure."
How can i use this json value outside scope of response closure ?
Can you please suggest
Is there any completion handler i need to write and how can it be done ?
Thanks
Better use the SwiftyJson for easy json parsing with Almofire like bellow :
import Alamofire
import SwiftyJSON
static func getRequest(urlString: URL?, Parameter:NSDictionary?, completion: #escaping (_ serverResponse: AnyObject?,_ error:NSError?)->()){
Alamofire.request(urlString!, parameters:nil, headers: nil).responseJSON { response in
if(response.result.value != nil){
let serverResponse = JSON(response.result.value!)
print("Array value is \(serverResponse.arrayValue)")
completion(serverResponse as AnyObject?, nil)
}
else{
completion(nil, response.result.error as NSError?)
}
}
}
You can write this function in a separate class like NetworkLayer and then call it from any class for the WebService call as given bellow :
let url = NSURL(string: yourWebServiceUrlString)
NetworkLayer.getRequest(urlString: url as URL?, Parameter: nil) { (serverResponse, error) in
if (error == nil){
print("Server response: \(serverResponse)")
}
}
Note: You can also pass the parameter dictionary in it.
Related
similar to this but this time i need to retrieve the JSOn response of the server.
here is my existing code:
return Observable.create{ observer in
let _ = self.provider
.request(.getMerchantDetails(qrId: qrId))
.filterSuccessfulStatusCodes()
.mapJSON()
.subscribe(onNext: { response in
observer.onNext(RQRMerchant(json: JSON(response)))
}, onError: { error in
observer.onError(error)
})
return Disposables.create()
my question is: I can get the error response code 404 by error.localizedDescription But I also want to get the JSON response of the 404 HTTP request.
I've been faced with the same problem, and for me the easiest and cleanest solution was to extend MoyaError to include a property for the decoded error object. In my case I'm using Decodable objects, so you could write something like this for a decodable BackendError representing the error you may get from your server:
extension MoyaError {
public var backendError: BackendError? {
return response.flatMap {
try? $0.map(BackendError.self)
}
}
}
If you instead prefer to directly deal with JSON you can invoke the mapJSONmethod instead of mapping to a Decodable.
Then you just have to do the following to get the error information for non successful status codes:
onError: { error in
let backendError = (error as? MoyaError).backendError
}
Since the response of your server is also contained in a JSON, that means that your onNext emissions can be successful JSON responses or invalid JSON responses.
Check the validity of the response using do operator
You can check for the validity of the response by doing the following:
return Observable.create{ observer in
let _ = self.provider
.request(.getMerchantDetails(qrId: qrId))
.filterSuccessfulStatusCodes()
.mapJSON()
.do(onNext: { response in
let isValidResponse : Bool = false // check if response is valid
if !isValidResponse {
throw CustomError.reason
}
})
.subscribe(onNext: { response in
observer.onNext(RQRMerchant(json: JSON(response)))
}, onError: { error in
observer.onError(error)
})
return Disposables.create()
Use the do operator
Check if the onNext emission is indeed a valid emission
Throw an error if it is invalid, signifying that the observable operation has failed.
Response validation
To keep your response validation code in the right place, you can define a class function within your response class definition that verifies if it is valid or not:
class ResponseOfTypeA {
public class func isValid(response: ResponseOfTypeA) throws {
if errorConditionIsTrue {
throw CustomError.reason
}
}
}
So that you can do the following:
// Your observable sequence
.mapJSON()
.do(onNext: ResponseOfTypeA.isValid)
.subscribe(onNext: { response in
// the rest of your code
})
This is my code for Jason parsing in Swift:
static func POST(url: String, parameters: NSDictionary, completionBlock: #escaping CompletionBlock){
let todoEndpoint: String = Webservices.Base_Url.appending(url)
guard let url = NSURL(string: todoEndpoint) else {
print("Error: cannot create URL")
return
}
var request = URLRequest(url: url as URL)
//var request = URLRequest(url: NSURL(string: todosEndpoint)! as URL)
let session = URLSession.shared
request.httpMethod = "POST"
var err: NSError?
let jsonData = try? JSONSerialization.data(withJSONObject: parameters)
request.httpBody = jsonData
request.addValue("application/x-www-form-urlencoded;charset=UTF-8 ", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let task = session.dataTask(with: request, completionHandler: {data, response, error -> Void in
guard error == nil else {
print("error calling POST on /todos/1")
print(error)
return
}
// make sure we got data
guard let dataTemp = data else {
print("Error: did not receive data")
return
}
// parse the result as JSON, since that's what the API provides
do {
guard let todo = try JSONSerialization.jsonObject(with: dataTemp, options: []) as? [String: AnyObject] else {
print("error trying to convert data to JSON")
return
}
// now we have the todo, let's just print it to prove we can access it
print("The todo is: " , todo)
// the todo object is a dictionary
// so we just access the title using the "title" key
// so check for a title and print it if we have one
} catch {
print("error trying to convert data to JSON")
return
}
})
task.resume()
}
I got while jason parsing:
error expression produced error: error: Execution was interrupted,
reason: EXC_BAD_ACCESS (code=1, address=0x0). The process has been
returned to the state before expression evaluation.
What's wrong?
I hope I manage to ask this properly:
I am using Alamofire and SwiftyJSON for managing the JSON files I get from the server.
I have issues with understanding the type of response.result.value , how to cast it to an object I can construct it with SwiftyJSON's JSON(data: data) constructor.
This is my code for the request using Alamofire:
func performRequest() {
// parameters["retry_count"] = retryNum
if let _ = host, let path = path {
let request = Alamofire.request(HOST + path, method: method, parameters: parameters, headers: headers)
request.responseJSON { response in
print("-----")
print(response.response?.statusCode)
print("-----")
// check if responseJSON already has an error
// e.g., no network connection
if let json = response.result.value {
print("--------")
print(json)
print("--------")
}
guard response.result.error == nil else {
print(response.result.error?.localizedDescription ?? "Response Error")
self.completionHandler?(response.result.isSuccess, nil)
self.retryRequest()
return
}
// make sure we got JSON and it's a dictionary
guard let json = response.result.value as? [String: AnyObject] else {
print("didn't get dictionary object as JSON from API")
self.completionHandler?(response.result.isSuccess, nil)
self.retryRequest()
return
}
// make sure status code is 200
guard response.response?.statusCode == 200 else {
// handle status code
self.completionHandler?(response.result.isSuccess, nil)
return
}
self.completionHandler?(response.result.isSuccess, json)
RequestsQueue.sharedInstance.sema.signal()
}
}
This results with this print:
{
numOfShiftsInDay = 3;
shifts = (
{
endTime = "14:00";
startTime = "07:30";
},
{
endTime = "20:00";
startTime = "13:30";
},
{
endTime = "02:00";
startTime = "19:30";
}
);
}
this data type is a [String: AnyObject].
I want to use it to construct a SwiftyJSON JSON object since it is easier for me to parse the data using SwiftyJSON methods..
This is the code I try for parsing it and then using it but obviously it doesn't work:
let json = JSON(data: data)
I get this compilation error:
Cannot convert value of type '[String : AnyObject]?' to expected argument type 'Data'
So how should I go about this?
You need to use JSON(data) instead of JSON(data: data) because this init(data:) wants Data as argument.
Changed line
let json = JSON(data: data)
To
let json = JSON(data)
I have APIManager singleton class and have a function to get data from server like this:
func scanOrder(order: String, completion:#escaping Handler){
let url = K.API_URL + "/api/containers/picking/" + order
Alamofire.request(url, method: .get, parameters: nil, encoding: URLEncoding.default, headers: getHeader()).responseJSON { (response) in
DispatchQueue.main.async {
completion(response)
}
}
}
and I call this function in other class like this:
apiMan.scanOrder(order: tfCode.text!) { (response) in
...
}
while waiting for server to response, my UI is blocked. I tried to wrap alamofire request call within DispatchQueue.global().async but it still blocks the UI.
Please help!
I never used Alamofire.request with DispatchQueue.main.async like you do. The reason is that Alamofire in combination with completion blocks already operates async and shouldn't block the UI, which is settled in the Main Thread.
Have you tried something like:
class NetworkManager {
func scanOrder(order: String, completion:#escaping (Any?) -> Void){
let url = "https://example.com/api/containers/picking/" + order
Alamofire.request(url, method: .get, parameters: nil, encoding: URLEncoding.default, headers: AppConfiguration.sharedInstance.defaultHeader())
.responseJSON { response in
guard response.result.isSuccess else {
Log.info("Error while fetching: \(response.result.error)")
completion(nil)
return
}
guard let responseJSON = response.result.value as? [String: AnyObject] else {
Log.info("Invalid information received from service")
completion(nil)
return
}
completion(responseJSON)
}
}
}
Call:
class CallingClass {
func scanOrder(order:String){
let manager = NetworkManager()
var result: Any?
manager.scanOrder(order: "example") { response in
result = response
}
print(result as Any)
}
}
I have the next function that make an http request with completionhandler and receives a json response:
func makeRequest3(request: URLRequest, completion: #escaping (JSON!)->Void){
let task = URLSession.shared.dataTask(with: request){ data, response, error in
//Code
print(data as NSData)
let json = JSON(data: data)
completion(json)
}
task.resume()
}
I need to call this function , and find the "id_token" field for save it in a variable. Im beginner and im trying this code, but i have the error "Type '()' has no subscript members"
var response2 = makeRequest3(request: request) {response in //<-`response` is inferred as `String`, with the code above.
return(response)
}
var idtoken = response2["id_token"]
How i can do it? Im using swift3.
Your makeRequest3 itself does not return value, but the response is passed to the completion handler, do all things needed for the response in the completion handler:
makeRequest3(request: request) {response in //<-`response` is inferred as `JSON`, with your `makeRequest3`.
var idtoken = response["id_token"]
//Use `idtoken` inside this closure
//...
}