How to detect when Alamofire request is null? - json

I am using Alamofire to do my requests of my API on my Swift application and it works well but I also want to detect when the JSON response is equals to null.
I have tried comparing the response to nil and NSNull but none of those worked for me. I also have tried using JSON.empty but it also does not seem to work. Further, I have created a default option on my switch application trying to catch the options that are not success or failure.
Actually I have only maintained the JSON.empty option but it never enters on else statement.
This is the code that I have right now:
Alamofire.request(encodedUrl!, method: .get, parameters: nil, encoding: JSONEncoding.default, headers: nil).responseJSON { response in
switch(response.result) {
case .success(_):
if let JSON = response.result.value as? [[String : AnyObject]]{
if JSON.isEmpty == false{
//Here the code if the request returns data
}else{
//Here I wanted to use the code if null is retrieved
}
}else{
//The JSON cannot be converted
}
break
case .failure(_):
//Failure
break
}
}
How can I handle null responses on Alamofire?
Thanks in advance!

According to you code, it'll hit the // The JSON cannot be converted since null can't be casted to [[String: AnyObject]].

Related

Could not cast value of type '__NSDictionaryI'

I m using this code to call my rest web service.
But if I try to decode the result of web service call I received error.
class func callPostServiceReturnJson(apiUrl urlString: String, parameters params : [String: AnyObject]?, parentViewController parentVC: UIViewController, successBlock success : #escaping ( _ responseData : AnyObject, _ message: String) -> Void, failureBlock failure: #escaping (_ error: Error) -> Void) {
if Utility.checkNetworkConnectivityWithDisplayAlert(isShowAlert: true) {
var strMainUrl:String! = urlString + "?"
for dicd in params! {
strMainUrl.append("\(dicd.key)=\(dicd.value)&")
}
print("Print Rest API : \(strMainUrl ?? "")")
let manager = Alamofire.SessionManager.default
manager.session.configuration.timeoutIntervalForRequest = 120
manager.request(urlString, method: .get, parameters: params)
.responseJSON {
response in
switch (response.result) {
case .success:
do{
let users = try JSONDecoder().decode(OrderStore.self, from: response.result.value! as! Data)
}catch{
print("errore durante la decodifica dei dati: \(error)")
}
if((response.result.value) != nil) {
success(response as AnyObject, "Successfull")
}
break
case .failure(let error):
print(error)
if error._code == NSURLErrorTimedOut {
//HANDLE TIMEOUT HERE
print(error.localizedDescription)
failure(error)
} else {
print("\n\nAuth request failed with error:\n \(error)")
failure(error)
}
break
}
}
} else {
parentVC.hideProgressBar();
Utility.showAlertMessage(withTitle: EMPTY_STRING, message: NETWORK_ERROR_MSG, delegate: nil, parentViewController: parentVC)
}
}
This is the error that I can print:
Could not cast value of type '__NSDictionaryI' (0x7fff86d70b80) to 'NSData' (0x7fff86d711e8).
2021-09-27 16:34:49.810245+0200 ArrivaArrivaStore[15017:380373] Could not cast value of type '__NSDictionaryI' (0x7fff86d70b80) to 'NSData' (0x7fff86d711e8).
Could not cast value of type '__NSDictionaryI' (0x7fff86d70b80) to 'NSData' (0x7fff86d711e8).
CoreSimulator 732.18.6 - Device: iPhone 8 (6F09ED5B-8607-4E47-8E2E-A89243B9BA90) - Runtime: iOS 14.4 (18D46) - DeviceType: iPhone 8
I generated OrderStore.swift class from https://app.quicktype.io/
//EDIT
.responseJSON returns deserialized JSON, in this case a Dictionary. It cannot be cast to Data what the error clearly confirms.
To get the raw data you have to specify .responseData
Replace
.responseJSON {
response in
switch (response.result) {
case .success:
do {
let users = try JSONDecoder().decode(OrderStore.self, from: response.result.value! as! Data)
with
.responseData {
response in
switch response.result {
case .success(let data):
do {
let users = try JSONDecoder().decode(OrderStore.self, from: data)
Consider that AF 5 supports even .responseDecodable to decode directly into the model
.responseDecodable {
(response : DataResponse<OrderStore,AFError>) in
switch response.result {
case .success(let users): print(users)
Side notes:
As mentioned in your previous question there is no AnyObject in the AF API. The parameters are [String:Any] and responseData is the decoded type. I recommend to make the function generic and use the convenient Result type.
Delete the break statements. This is Swift.
This is an addendum to Vadian's answer. I'm trying to illustrate the process that lead you into this error, with the hopes that you can notice it in the future, before it leads you astray
This is a pretty common "pattern" of error.
Picture it as though you're traversing a maze, starting from some initial data format, and trying to get to some destination data format. At each point along the way, there are several options to choose from, some which get you closer to your goal, and some which lead you further away.
You've chosen to enter the maze at the entryway called responseJSON, whose callback will give you a AFDownloadResponse<Any> (which is the inferred type of the variable you called response).
JSON structures always have an array or dictionary at the top level. Since Alamofire can't statically know which kind of JSON you'll be dealing with, it models this with an Any. At runtime, the type of the Value will be either NSDictionary (or one of its concrete subclasses, like __NSDictionaryI) or NSArray (or one of its concrete subclasses).
You then decide to get the result of that response. Its static type is Result<Any, Error>. You switch over this error, ensuring you're dealing with the success case and not the failure case. Inexplicably, you ignore the payload value associated with the success, but later force unwrap it out with result.response.value!.
result.response.value is an Any, but to placate the compiler your force-cast it to a Data. But we already know this will only ever be an NSArray or NSDictionary, so this will never work.
You could keep wandering around in this area of the maze, and stumble to the end goal via a long path. For example, you could force cast to NSDictionary, then re-serialize that dictionary structure back to a JSON string, which you can turn into Data, only for you to then pass it to JSONDecoder().decode, which will then decode that JSON back. Of course, this is all awfully round-about and wasteful. The issue was the that responseJSON maze entrance was not the right one for where you're trying to go!
You could have instead entered into the responseData maze entrance, which gets you right to your Data destination!
Though you might then realize that the Data was a red herring all along. You didn't actually want Data. You wanted to decode an OrderStore, and Data was how you thought you needed to get there. But it turns out that so many people were entering through the Data entrance with the intent to decode some JSON, that the Alamofire people carved out a new entrance just for you: responseDecodable. It takes you right to the OrderStore, and fiddles around with the JSON, Data or whatever, under the hood where you don't have to worry about it.

Send empty array with Alamofire API Rest

I want to send an empty array to a Rest Full API by using patch method. But i don't know why it is not working. In fact, i am also using postman for testing my requests, and this request works fine in postman ( I want wordTrads be empty ) :
And this is how i've implemented that API called in Swift by using Alamofire :
let parameters = [
"wordTrads" : [],
]
Alamofire.request("\(Auth.URL_API)/lists/205",method: .patch, parameters : parameters).responseJSON { (response) in
print("List patched")
}
But in swift it's not working like if Alamofire doesn't send empty arrays.
I am using Alamofire 4.6.0 and Swift 4.
Your screen snapshots suggest you're expecting to send JSON, but your Alamofire syntax is not doing that. You need to add encoding of JSONEncoding.default if you want to send JSON:
let parameters = [
"wordTrads" : []
]
Alamofire.request("\(Auth.URL_API)/lists/205", method: .patch, parameters: parameters, encoding: JSONEncoding.default)
.responseJSON { response in
switch response.result {
case .success(let value): print(value)
case .failure(let error): print(error)
}
}
And if you do that, the body of the request includes that empty array in JSON form:
{"wordTrads":[]}

Alamofire unexpectedly found nil while unwrapping an Optional, yet I can see the JSON

Using AlamoFire I can make an API call to my endpoint, it works and connects as expected. Using the print tools I can print the JSON response to the console and see the JSON string, but I am unable to get this string to move to the next function.
I keep getting the error:
"fatal error: unexpectedly found nil while unwrapping an Optional value (lldb)"
My code looks like this:
func getDataForUser(Username:String, UserToken:String) {
print("Getting data for user \(Username)")
Alamofire.request(.POST, baseURL+userdataURL, parameters: ["Username": Username, "UserToken": UserToken]).response { (req, res, data, error) -> Void in
let jSONResponse: NSDictionary = (try! NSJSONSerialization.JSONObjectWithData(data!,options: NSJSONReadingOptions.MutableContainers)) as! NSDictionary
print(jSONResponse)
if(jSONResponse["Success"] as! Bool == true) {
print("Success! API Request Worked")
self.delegate!.didReceiveAPIResults(jSONResponse)
}
}
}
The error is on line:
self.delegate!.didReceiveAPIResults(jSONResponse)
The console looks like this:
jSONResponse NSDictionary 2 key/value pairs 0x78e8d6a0
[0] (null) "Success" : "1"
[1] (null) "Response" : 2 key/value pairs
The debug screen for jSONResponse gives me data that I can drill into so I know its not nil.
Where is the nil coming from and how do I resolve?
In Swift, "1" is not equal to a Bool value of true.
It's crashing at jSONResponse["Success"] as! Bool == true. You could change this to jSONResponse["Success"] as! String == "1".
If you are able to modify the source of the API you're consuming, you're probably better of making Success a true/false JSON bool value, and leaving your Swift code as is.
My initial thoughts were that the JSON handler or API Controller were wrong.
What I had not passed was the delegate variable in the view controller setup.
EG:
api.delegate = self
Thank you to #paulvs and Eric D for pointing that out.

Swift- JSON Requests with more than one page

I am using Alamofire and SwiftyJSON to access Youtube's API and get a channels's playlist count. My problem is that Youtube returns only 5 items per page (or request) and I don't know how to access the rest!
func getPlaylists() {
Alamofire.request(.GET, playlistURL).validate().responseJSON { response in
switch response.result {
case .Success:
if let value = response.result.value {
let json = JSON(value)
print(json["items"].count) //Always returns 5
}
case .Failure(let error):
print(error)
}
}
}
Can anybody let me know how to access the next set of results?
You'll find the answer in the API documentation, possibly here YouTube Data API.
Note: in your request, you can set the max items per page with a query parameter: maxResults. This can be 5 to 50, default equals 5.
When you get a response, the server returns a JSON containing a JSON value "nextToken": <string>. Use this value in your next request, where you insert a query parameter pageToken=<string>.
You might modify your getPlaylists method so that it takes a parameter nextToken: String. Initially call this method with an empty string or better an optional initialized to nil.
In your completion handler, retrieve the "nextToken" value from the JSON response. If it exists, call your method with this parameter. Otherwise, you got all pages.
Your method getPlaylists should also have a completion handler. Call the completion handler when all pages have been retrieved.

HTTPTask response into Swifty for JSON serialization

I'm using HTTPTask to load data from openweathermap.org. Which is working fine. I'm having trouble converting the data to JSON. I'd like to use SwiftyJSON but, I can't quite figure out how to bridge the two.
HTTPTask has a JSON Serializer, which I got working, but I rather use Swifty, it's seems easier to work with.
Here's what I have so far. This loads the weather from openweathermap.org. I'm not sure how to pass the response into Swifty.
var request = HTTPTask()
request.requestSerializer = JSONRequestSerializer()
request.responseSerializer = JSONResponseSerializer()
request.GET(openWeatherURL, parameters: ["q":"San Francisco", "APPID":openWeatherAPIKey], success: {(response: HTTPResponse) in
if let dict = response.responseObject as? Dictionary<String, AnyObject> {
println("Response: \(response)")
println("Dictionary: \(dict)")
let description = dict["weather"]["description"]
println(description)
}
}, failure: {(error: NSError, repsonse: HTTPResponse?) in
println("error \(error)")
})
SwiftyJSON is quite happy to take a variety of objects, including Dictionary?, so just go for it!
let dict = JSON(response.responseObject)