iOS8/Swift strange null behaviour with response from REST service - ios

Throughout my project I can perfectly check Alamofire responses and data for
if fooData != nil {
//do stuff
}
somehow in this instance Swift seems to have problems checking what the actual incoming type is
what I get if I print it is
<null>
what is this supposed to be? An array of null? Casts to NSDictionary or NSArray both fail.
The Rest response is the same as always throughout the project where I will potentially receive a null value and it gets catched everywhere else.
€dit with more code:
my request:
Alamofire.request(.GET, "\(CurrentConfiguration.serverURL)/api/users/\(CurrentConfiguration.currentUser.id)/friends/\(targetUser)",encoding:.JSON)
.validate()
.responseJSON {(request, response, friendData, error) in
if friendData != nil {
println(friendData!)
//this is where the app obviously crashes as there is nothing inside of "friendData"
let resp = friendData as NSDictionary
//load friendData into the UI
}
}
the print statement gives me the above mentioned null representation, but obviously does not recognize as nil
the response from the node backend comes as
index.sendJsonResponse(res, 200, null);

Try this code in Playground:
let fooData:AnyObject = NSNull()
println(fooData)
It prints <null>.
fooData is not nil, but a instance of NSNull

Related

getting nil value while calling api (alamofire)

i am using alamofire to call api. but in this particular api i am getting nil value as a response. actually when i hit my api in my browser its working pretty well.
here is my code
func web()
{
request(.GET, "http://www.horecasupply.nl/AJAX?function=appStructure", parameters: nil, encoding: .JSON).responseJSON { (response : Response<AnyObject, NSError>) -> Void in
if response.result.value != nil {
print(response.result.value)
}
}
}
getting nil value and when i print respose its showing me
Printing description of response.result.Failure:
Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around character 1."
UserInfo={NSDebugDescription=Invalid value around character 1.}
EDIT 1:: tried responsestring instead of responsejson.
but getting different kind of response so how can i convert it?
\r\n\r\n\r\n\r\n</script>\r\n DD_belatedPNG.fix(\'img, .png_bg\'); //fix any or .png_bg background-images </script>\r\n\r\n\r\n</script>\r\n</script>-->\r\n\r\n\r\n\r\n\r\n\r\n \r\n\r\n")
Give this code a shot, this should work:
Alamofire.request(.GET, "http://www.horecasupply.nl/AJAX?function=appStructure").responseJSON { response in
if response.result.value != nil {
print(response.result.value)
}
}

unexpectedly found nil while unwrapping an Optional value - Using ALAMOFIRE

I am trying to use Alamofire for GETs in JSON.
When I use one URL - It works fine and when I use another I get an error unwrapping an optional value. I cannot seem to track where the error is coming from. I have resorted in putting the code in ViewDidLoad to track the error. I don't know if its the recipient and they want some sort of authorisation. but I know its not the println's cos when i // them - it still comes up as an error , heres the code :
request(.GET, "https://api.doingdata.net/sms/send?api_service_key='APIKey'&msg_senderid=Edify-FYI&msg_to=&msg_text={otp|Edify-FYI|3600|ETEN|4} &msg_clientref=abcdef123456&msg_dr=0&output=json")
.validate()
.responseJSON { (_, _, _, error) in
println(error)
}
but I use :
request(.GET, "http://httpbin.org/")
.validate()
.responseJSON { (_, _, _, error) in
println(error)
}
it works fine and returns nil for the error.
Any help would be great, as its driving me nuts.
The reason is simple: your URL has special characters. So, if you do
let url = NSURL(string:yourURLString) //returns nil
It will return nil. You would need to format your URL to be suitable for making requests. Here is one solution for you.
var urlString = "https://api.doingdata.net/sms/send?api_service_key='APIKey'&msg_senderid=Edify-FYI&msg_to=&msg_text={otp|Edify-FYI|3600|ETEN|4} &msg_clientref=abcdef123456&msg_dr=0&output=json"
urlString = urlString.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())!
request(.GET, urlString, parameters: nil, encoding: .JSON)

Error handling in Alamofire

I have the HTTP code in an AngularJS controller:
$http.post('/api/users/authenticate', {email: $scope.email, password: $scope.password})
.success(function (data, status, headers, config) {
authService.login($scope.email);
$state.go('home');
})
.error(function (data, status, headers, config) {
$scope.errorMessages = data;
$scope.password = "";
});
In the success case, the server will respond with a JSON representation of a user. In the error case the server will respond with a simple string such as User not found which can be accessed through the data parameter.
I'm having trouble figuring out how to do something similar in Alamofire. Here's what I have right now:
#IBAction func LoginPressed(sender: AnyObject) {
let params: Dictionary<String,AnyObject> = ["email": emailField.text, "password": passwordField.text]
Alamofire.request(.POST, "http://localhost:3000/api/users/authenticate", parameters: params)
.responseJSON {(request, response, data, error) in
if error == nil {
dispatch_async(dispatch_get_main_queue(), {
let welcome = self.storyboard?.instantiateViewControllerWithIdentifier("login") as UINavigationController;
self.presentViewController(welcome, animated: true, completion: nil);
})
}
else{
dispatch_async(dispatch_get_main_queue(), {
// I want to set the error label to the simple message which I know the server will return
self.errorLabel.text = "something went wrong"
});
}
}
}
I have no idea if I'm handling the non-error case correctly either and would appreciate input on that as well.
You are are on the right track, but you are going to run into some crucial issues with your current implementation. There are some low level Alamofire things that are going to trip you up that I want to help you out with. Here's an alternative version of your code sample that will be much more effective.
#IBAction func loginPressed(sender: AnyObject) {
let params: [String: AnyObject] = ["email": emailField.text, "password": passwordField.text]
let request = Alamofire.request(.POST, "http://localhost:3000/api/users/authenticate", parameters: params)
request.validate()
request.response { [weak self] request, response, data, error in
if let strongSelf = self {
let data = data as? NSData
if data == nil {
println("Why didn't I get any data back?")
strongSelf.errorLabel.text = "something went wrong"
return
} else if let error = error {
let resultText = NSString(data: data!, encoding: NSUTF8StringEncoding)
println(resultText)
strongSelf.errorLabel.text = "something went wrong"
return
}
var serializationError: NSError?
if let json: AnyObject = NSJSONSerialization.JSONObjectWithData(data!, options: .AllowFragments, error: &serializationError) {
println("JSON: \(json)")
let welcome = self.storyboard?.instantiateViewControllerWithIdentifier("login") as UINavigationController
self.presentViewController(welcome, animated: true, completion: nil)
} else {
println("Failed to serialize json: \(serializationError)")
}
}
}
}
Validation
First off, the validate function on the request will validate the following:
HTTPStatusCode - Has to be 200...299
Content-Type - This header in the response must match the Accept header in the original request
You can find more information about the validation in Alamofire in the README.
Weakify / Strongify
Make sure to weak self and strong self your closure to make sure you don't end up creating a retain cycle.
Dispatch to Main Queue
Your dispatch calls back to the main queue are not necessary. Alamofire guarantees that your completion handler in the response and responseJSON serializers is called on the main queue already. You can actually provide your own dispatch queue to run the serializers on if you wish, but neither your solution or mine are currently doing so making the dispatch calls to the main queue completely unnecessary.
Response Serializer
In your particular case, you don't actually want to use the responseJSON serializer. If you do, you won't end up getting any data back if you don't pass validation. The reason is that the response from the JSON serialization is what will be returned as the AnyObject. If serialization fails, the AnyObject will be nil and you won't be able to read out the data.
Instead, use the response serializer and try to parse the data manually with NSJSONSerialization. If that fails, then you can rely on the good ole NSString(data:encoding:) method to print out the data.
Hopefully this helps shed some light on some fairly complicated ways to get tripped up.
So Alamofire treats all requests successful. This really comes down to the API server http headers being returned.
You could use Alamofire.Request.validate()
It'll allow you to validate http headers, etc. Check out the example
https://github.com/Alamofire/Alamofire#validation
I am assuming the the error message will be in the data object.
to access the values from data you could do something like
I am not really sure about your api response looks but in this example
{
"message": "Could not authenticate"
}
let message: String? = data?.valueForKey("message") as String

handle JSON response with SwiftyJSON

I tried a long time to handle a JSON response with SwiftyJSON, but I don't know how to parse the response. Here's my code:
var jsonString:String = ""
Alamofire
.request(.GET, url + "/HMServer/rest/administration/version")
.responseJSON {
(request, response, data, error) -> Void in
let json = JSON(object: data!)
//here I want to do something with parsing
}
The requests I do with Alamofire and get back a JSON response. The response look like:
[message: [SERVER_VERSION: 0.1, INTERFACE_VERSION: 0.1], type: success]
I want to save all elements in strings and give them back. How can I parse the JSON response saved in the let let json? I tried to use Alamofire-SwiftJSON but the code does not work. All examples I found are too old because the SwiftyJSON code was refactored a few days ago.
THX!
I have fixed Alamofire-SwiftJSON's issue,
but you can do it by yourself in responseJSON's closure like:
Alamofire.request(.GET, url + "/HMServer/rest/administration/version")
.responseJSON { (request, response, data, error) -> Void in
if error != nil {
self.swiftyJSON = SwiftyJSON.JSON.Null(error)
} else if object != nil {
self.swiftyJSON = SwiftyJSON.JSON(object: object!)
} else {
self.swiftyJSON = SwiftyJSON.JSON.Null(nil)
}
}
Above code is not like Alamofire-SwiftJSON in the global queue, the initialization (AnyObject to SwiftyJSON) is running in the main queue.

SLRequestHandler error using Swift

I am trying to mimic the app as in this youtube tutorial, but using Swift and I am facing a problem constructing the closure as shown in the code snippet below.
func twitterTimeline() {
let account = ACAccountStore()
let accountType = account.accountTypeWithAccountTypeIdentifier(ACAccountTypeIdentifierTwitter)
// take action
account.requestAccessToAccountsWithType(accountType, options: nil,
completion: { (granted, error) in
if (granted) {
// invoke twitter API
let arrayOfAccount: NSArray = account.accountsWithAccountType(accountType)
if (arrayOfAccount.count > 0) {
let twitterAccount = arrayOfAccount.lastObject as ACAccount
let requestAPI = NSURL.URLWithString("http://api.twitter.com/1.1/atuses/user_timeline.json")
var parameters = Dictionary<String, String>()
parameters["100"] = "count"
parameters["1"] = "include_entities"
let posts = SLRequest(forServiceType: SLServiceTypeTwitter, requestMethod: SLRequestMethod.GET, URL: requestAPI, parameters: parameters)
posts.account = twitterAccount
// This is the Error Prone Area
let handler: SLRequestHandler = { (response, urlResponse, error) in
self.array = NSJSONSerialization.JSONObjectWithData(data: response, options: NSJSONReadingOptions.MutableLeaves, error: &error) as NSArray
}
posts.performRequestWithHandler(handler)
}
} else {
// do something
}
}
)
}
The error I get is
Cannot convert expression's type '($T1, $T2, $T3) -> $T0' to type '()'
I have tried checking and explicitly casting the types with no much help. I believe the error is somewhere else. Could anyone help me with what exactly is the trouble? I am sorry, if this turns out to be a näive question.
Thanks in advance,
Nikhil
This looks like an interesting case of error propagation in the compiler — I'd suggest filing a bug report with Apple.
The error message you're getting says that you can't assign a closure (which takes three parameters and returns one value) to something that takes no parameters. What's actually going wrong is that the handler closure you're defining takes its error input parameter and tries to pass it to JSONObjectWithData(_:options:error:). That's problematic from a language perspective because the error you're getting in is an immutable (optional) reference to one error, and the parameter you're passing it to expects a mutable pointer for it to (potentially) write another error into.
It's also incorrect API usage. The error you receive as a parameter in the closure is a report of an error that happened in whatever procedure calls your closure. You should log this error, present it to the user, or examine it so your app can gracefully fail. The error parameter you pass to JSONObjectWithData is a place for you to receive reports of additional errors that occur when decoding JSON from your data — you should be handling this error, too. These are two separate places to receive errors, so you shouldn't be passing one to the other.
If you fix that, you'll find a more helpful compiler message saying that the data: label on the first parameter to that function should be omitted. Also, you can use type inference for the options: parameter. So, your handler definition should look something more like this:
let handler: SLRequestHandler = { (response, urlResponse, error) in
// check for error and do something about it if need be, then...
var err: NSError?
if let jsonArray = NSJSONSerialization.JSONObjectWithData(response, options: NSJSONReadingOptions.MutableLeaves, error: &err) as? NSArray {
self.array = jsonArray
} else {
// do something about err
}
}
(You can also probably use a Swift typed array instead of an NSArray if you know what to expect from your JSON. But that's another subject for another question. Actually, several questions.)

Resources