Receive response as NSDictionary instead of NSData - ios

I'm trying to get the response from the server as NSDictionary instead of NSData, so first I'm using AFNetworking library and the server request these settings to be HTTP not JSON serializer as the following :
self.responseSerializer = AFHTTPResponseSerializer() as AFHTTPResponseSerializer
self.requestSerializer = AFHTTPRequestSerializer() as AFHTTPRequestSerializer
self.requestSerializer.HTTPMethodsEncodingParametersInURI = NSSet(array: ["GET"]) as! Set<String>
self.responseSerializer.acceptableContentTypes = NSSet(objects: "application/json","text/json","text/javascript","text/html") as? Set<String>
Next is when i get the response from the server it comes to be NSData and its because I'm using AFHTTPRequestSerializer() and here is my code :
self.POST(addUserURL, parameters: dictionary, progress: nil, success: { (task, responseObject) -> Void in
print(responseObject)
}) { (task, error) -> Void in
}
Also inside AFNetworking block its not allowed to handle try and catch for NSJSONSerializtion which can be a possible solution but it doesn't work.

Use NSJSONSerialization for that as shown in below code :
do {
let jsonObject = try NSJSONSerialization.JSONObjectWithData(responseObject, options: .AllowFragments) as! NSDictionary
// use jsonObject here
} catch {
print("json error: \(error)")
}

As response that you get from the server doesn't have top level object that is either Array or Dictionary you have to specify custom reading options in that should be used by AFNetworking.
In order to do that you have to set responseSerializer property on an instance of AFURLSessionManager class. You can do it as follows:
let sessionManager = OLFURLSessionManager.init() //you probably want to keep session manager as a singleton
sessionManager.responseSerializer = AFJSONResponseSerializer.serializerWithReadingOptions(.AllowFragments)
//you should use that instance of session manager to create you data tasks
After that you should be able to correctly parse response from the server as follows:

Related

Indicated 'Anyobject' is not a subtype of 'NSProxy' when using AFNetworking

I'm using AFNetworking to retrieve weather info form openweathermap API.
let manager = AFHTTPSessionManager()
manager.requestSerializer = AFJSONRequestSerializer()
let url = "http://api.openweathermap.org/data/2.5/weather"
let params = ["lat": latitude,"lon": longitude,"cnt": 0]
manager.get(url, parameters: params,
success: {(operation: URLSessionDataTask,responseObject: AnyObject!) in print("JSON" + responseObject.description!) },
failure: {(operation: URLSessionDataTask?,error: Error) in print(error.localizedDescription)}
)
highlighting at responseObject.description indicated that 'Anyobject' is not a subtype of 'NSProxy'
If remove .description the error will disappear.
platform:xcode 8.3.2 swift:3
'Anyobject' is not a subtype of 'NSProxy'
First on of all the get method you are using is a deprecated one (I assume you have newest AFNetworking version). Please use the new one this way:
let manager = AFHTTPSessionManager()
manager.requestSerializer = AFJSONRequestSerializer()
let url = "http://api.openweathermap.org/data/2.5/weather"
let params = ["lat": 5.0,"lon": 5.0,"cnt": 0]
manager.get(url, parameters: params, progress: nil, success: { (operation, responseObject) in
if let responseObject = responseObject {
print(responseObject)
} else {
print("There is no response object") //assume parsing error for JSON
}
}) { (operation, error) in
print(error.localizedDescription)
}
As the last tip: if you are using Swift, better use Alamofire:
https://github.com/Alamofire/Alamofire
It supports lots of nice features coming from Swift and much nicer error handling. For example Alamofire treats parsing error as real errors and calls failure block, not success block like ANetworking.
Also allows you to easily integrate some JSON parsing libs like SwiftJSON
https://github.com/SwiftyJSON/SwiftyJSON

Custom Made Web Service returning NIL in iOS Swift 2.0

I am trying to learn how to make a web service on my apache server, and then service the resulting JSON to an iOS application.
My web service certainly returns JSON information when I input the url, which is MYIP/?code=1 (the code could be anything, this is only for testing). Here is my PHP web service:
<?php
/**
* Created by PhpStorm.
* User: Clayton
* Date: 2/12/2016
* Time: 6:18 PM
*/
//process client request
header("Content-Type:application/json");
if(!empty($_GET['code'])){
$code = $_GET['code'];
$xml = simplexml_load_file("C:\Users\Clayton\Desktop\DataFiles\warehouse.city") or die("Error: Cannot create object");
$sum1 = $xml->to;
deliver_request(200,"Sum delivered", $sum1);
}
else{
// throw invalid request
deliver_request(404,"Sum not delivered", NULL);
}
function deliver_request($status, $status_message, $data){
header("HTTP/1.1 $status, $status_message");
$response['status'] = $status;
$response['status_message'] = $status_message;
$response['data'] = $data;
$json_response=json_encode($response);
echo $json_response;
}
?>
Now the issue that I am having, is when I go onto the iOS end, the data response is returning NIL, and I'm not sure why. Here is the swift code
let url = NSURL(string: "35.11.183.52/?code=1")
let session = NSURLSession.sharedSession()
session.dataTaskWithURL(url!, completionHandler:
{(data: NSData?, response:NSURLResponse?, error: NSError?) -> Void in
// Read the Json
do {
if let textString = NSString(data:data!, encoding: NSUTF8StringEncoding){
//Parse the Json to get the text
let jsonDictionary = try NSJSONSerialization.JSONObjectWithData(
data!, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary
let origin = jsonDictionary["status"] as! String
//update the label text
self.performSelectorOnMainThread("updateIPLabel:",
withObject: origin, waitUntilDone: false)
}
} catch {
print("something went wrong")
}
}).resume()
Any advice on why I'm getting NIL returned would be greatly appreciated. Again, I can see a response when I manually enter the URL into a browser on iOS, but can pull anything from the app code for some reason.

How can I process multiple links of JSON data?

The code works perfectly. The problem is that, after trying for a while, I cannot figure out how to make my program process a second link of different JSON data.
Here is my viewDidLoad where everything goes on:
override func viewDidLoad() {
super.viewDidLoad()
var err: NSError?
let urlPath: String = "https://na.api.pvp.net/api/lol/na/v1.4/summoner/by-name/" + searchFieldDataPassed + "?api_key=(removed my private api key for obvious reasons"
var url: NSURL = NSURL(string: urlPath)!
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url) { data, response, error in
// cast response as NSHTTPURLResponse and switch on statusCode if you like
if let httpResponse = response as? NSHTTPURLResponse { switch httpResponse.statusCode { case 200..<300: println("OK") default: println("Not OK") } }
// parse JSON using NSJSONSerialization if you've got data
if let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as? NSDictionary,
let include = jsonResult.objectForKey(self.searchFieldDataPassed) as? NSDictionary {
if let summLevel = include[ "summonerLevel" ] as? NSNumber {
dispatch_async(dispatch_get_main_queue()) {
self.summonerLevel.text = "\(summLevel.integerValue)"
println("summoner level: \(summLevel.integerValue)")
}
}
if let profIconId = include[ "profileIconId" ] as? NSNumber {
dispatch_async(dispatch_get_main_queue()) {
self.profileIconId.text = "\(profIconId.integerValue)"
println("profile icon id: \(profIconId.integerValue)")
}
}
if let idNum = include [ "id" ] as? NSNumber {
dispatch_async(dispatch_get_main_queue()) {
self.idNumber = idNum
println("id number: \(self.idNumber)")
}
}
}
// spawn off another network call here if you like
}
task.resume()
}
That is from my secondViewController where all the processing goes on for JSON and then is displayed.
Here is the JSON data that I'm processing (for the first JSON parsing):
{"soon2challenger":{"id":43993167,"name":"soon2challenger","profileIconId":844,"summonerLevel":30,"revisionDate":1435549418000}}
All of that works fine, now, I want to process this JSON data which actually takes the id from the first parsed JSON data and uses it in the link to process more data, which I would like to output, part of it, to the screen.
Second JSON data:
{"summonerId":43993167,"playerStatSummaries":[{"playerStatSummaryType":"AramUnranked5x5","wins":25,"modifyDate":1423007927000,"aggregatedStats":{"totalChampionKills":676,"totalTurretsKilled":20,"totalAssists":991}},{"playerStatSummaryType":"CAP5x5","wins":15,"modifyDate":1429065922000,"aggregatedStats":{"totalChampionKills":312,"totalMinionKills":4885,"totalTurretsKilled":31,"totalNeutralMinionsKilled":511,"totalAssists":216}},{"playerStatSummaryType":"CoopVsAI","wins":28,"modifyDate":1421882181000,"aggregatedStats":{"totalChampionKills":266,"totalMinionKills":2802,"totalTurretsKilled":50,"totalNeutralMinionsKilled":385,"totalAssists":164,"maxChampionsKilled":0,"averageNodeCapture":0,"averageNodeNeutralize":0,"averageTeamObjective":0,"averageTotalPlayerScore":49,"averageCombatPlayerScore":0,"averageObjectivePlayerScore":49,"averageNodeCaptureAssist":0,"averageNodeNeutralizeAssist":0,"maxNodeCapture":0,"maxNodeNeutralize":0,"maxTeamObjective":0,"maxTotalPlayerScore":49,"maxCombatPlayerScore":0,"maxObjectivePlayerScore":49,"maxNodeCaptureAssist":0,"maxNodeNeutralizeAssist":0,"totalNodeNeutralize":0,"totalNodeCapture":0,"averageChampionsKilled":0,"averageNumDeaths":0,"averageAssists":0,"maxAssists":0}},{"playerStatSummaryType":"CoopVsAI3x3","wins":15,"modifyDate":1421882181000,"aggregatedStats":{"totalChampionKills":140,"totalMinionKills":1114,"totalTurretsKilled":9,"totalNeutralMinionsKilled":449,"totalAssists":91}},{"playerStatSummaryType":"OdinUnranked","wins":1,"modifyDate":1421882181000,"aggregatedStats":{"totalChampionKills":31,"totalAssists":45,"maxChampionsKilled":10,"averageNodeCapture":4,"averageNodeNeutralize":4,"averageTeamObjective":0,"averageTotalPlayerScore":843,"averageCombatPlayerScore":268,"averageObjectivePlayerScore":575,"averageNodeCaptureAssist":3,"averageNodeNeutralizeAssist":1,"maxNodeCapture":6,"maxNodeNeutralize":7,"maxTeamObjective":2,"maxTotalPlayerScore":1468,"maxCombatPlayerScore":529,"maxObjectivePlayerScore":939,"maxNodeCaptureAssist":5,"maxNodeNeutralizeAssist":2,"totalNodeNeutralize":22,"totalNodeCapture":25,"averageChampionsKilled":5,"averageNumDeaths":5,"averageAssists":8,"maxAssists":19}},{"playerStatSummaryType":"RankedSolo5x5","wins":116,"losses":120,"modifyDate":1433630047000,"aggregatedStats":{"totalChampionKills":1699,"totalMinionKills":33431,"totalTurretsKilled":219,"totalNeutralMinionsKilled":6501,"totalAssists":1969}},{"playerStatSummaryType":"RankedTeam3x3","wins":0,"losses":0,"modifyDate":1377726216000,"aggregatedStats":{}},{"playerStatSummaryType":"RankedTeam5x5","wins":3,"losses":0,"modifyDate":1383784473000,"aggregatedStats":{"totalChampionKills":28,"totalMinionKills":636,"totalTurretsKilled":6,"totalNeutralMinionsKilled":101,"totalAssists":41}},{"playerStatSummaryType":"Unranked3x3","wins":9,"modifyDate":1421882181000,"aggregatedStats":{"totalChampionKills":90,"totalMinionKills":1427,"totalTurretsKilled":11,"totalNeutralMinionsKilled":428,"totalAssists":105}},{"playerStatSummaryType":"URF","wins":4,"modifyDate":1435024847000,"aggregatedStats":{"totalChampionKills":68,"totalMinionKills":642,"totalTurretsKilled":14,"totalNeutralMinionsKilled":182,"totalAssists":55}},{"playerStatSummaryType":"Unranked","wins":566,"modifyDate":1435549418000,"aggregatedStats":{"totalChampionKills":8419,"totalMinionKills":128213,"totalTurretsKilled":960,"totalNeutralMinionsKilled":26117,"totalAssists":7812}}]}
Heres the link of the second JSON data I want to parse (just adding it, could be useful, but not sure):
https://na.api.pvp.net/api/lol/na/v1.3/stats/by-summoner/43993167/summary?season=SEASON2015&api_key=(took-out-my-private-api-key-for-obvious-reasons)
The link doesn't work because I have to keep my api key private to myself, but the JSON data that it displays is right above the link, which is the what it would result if you were to use the link with the api key.
Just to restate, I would like to process the second part (above of this) of JSON data, but I do not understand how to process multiple links of JSON. I have the first JSON data parsed, but am unable to parse the second JSON data.
I believe Apple is deprecating NSURLConnection. Take a look at NSURLSession. Using it, you can pass in a completion block that takes three arguments: NSData?, NSURLResponse?, and NSError?. The data object contains the JSON you can pass into the JSON serializer. After that, if you need to make another network call, just call it from inside the completion block with another NSURLSession data task. Alamofire is a great framework, but sometimes you don't need everything it provides, and it adds complexity into your app that if something goes wrong or doesn't behave the way you intend/understand, you may not fully understand why. If you want to keep it simple and under your control, use NSURLSession.
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url) { data, response, error in
// cast response as NSHTTPURLResponse and switch on statusCode if you like
// parse JSON using NSJSONSerialization if you've got data
// spawn off another network call here if you like
}
task.resume() // or in Swift 2, task?.resume()
First, i would totally prefer using some common frameworks for http requests - expecially if youre new in swift. For example here with alamofire.
https://github.com/Alamofire/Alamofire
There is also a version with integrated SwiftyJSON, so you are able to parse JSON Responses very easily.
https://github.com/SwiftyJSON/Alamofire-SwiftyJSON
So if you want to make a request, use this:
Alamofire.request(.GET, "http://httpbin.org/get")
.responseJSON { (_, _, json, _) in
var json = JSON(json)
// get the id out (depends on your structure of JSON):
let id = json["id"].int
}
Now you are able to perform a second Request (with the same Code) - Read the Documentation, how to make different Requests (like with POST) and add Parameters.
If you want to use Segues, so you want to load more data from the ID in another ViewController, you can use Segues to push the data to a second ViewController, and Load the new Content from JSON when the new ViewController is initialised.
Check out this how to send data through segues:
Sending data with Segue with Swift

NSJSONSerialization not updating (possibly getting cached)

Hi I'm downloading JSON in my app to the device with NSJSONSerialization and it seems that when I update the JSON file and re-run the download JSON function it doesn't download the new JSON. It's just downloading the non updated JSON file. I've tried running the app on different devices and it works the first time but once I update the JSON again it doesn't download the new JSON. I'm thinking it may be getting cached but I'm not sure.
Here's my code:
func downloadJSON(){
let p = NSURLRequest(URL: NSURL(string: "https://themartini.co/pythonStuff/quotey.json")!)
let sharedSession = NSURLSession.sharedSession()
let dwn = sharedSession.dataTaskWithRequest(p, completionHandler: { (data : NSData!,re : NSURLResponse!, error: NSError!) -> Void in
if error == nil {
let js : AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: nil) as NSArray // downloads JSON and puuts it into an array
println(js)
dispatch_async(dispatch_get_main_queue()){
self.quotes = js as NSArray
NSNotificationCenter.defaultCenter().postNotificationName("dataTaskFinished", object: nil)
}
}
})
dwn.resume()
}
Any ideas? Thanks.
You should pass the correct cache policy to your NSURLRequest. There is an alternative constructor that includes this parameter.
let p = NSURLRequest(URL: url,
cachePolicy: ReloadIgnoringLocalCacheData,
timeoutInterval: 15.0)
You can add a cachebuster to your NSURLRequest. (change the url to https://themartini.co/pythonStuff/quotey.json?23421231231
where the string of numbers at the end is random. This will break the cache. Also, instead of using NSURLSession, look into NSURLConnection.SendAsynchroniousRequest

AFNetworking 2 + SwiftyJSON

SwiftyJSON https://github.com/SwiftyJSON/SwiftyJSON looks really promising. However, the instructions only talk about integration with Alamofire (AFNetworking for Swift). However, due to the nature of the current project, it's already using AFNetworking2 and cannot switch to Alamofire. However, I'm having trouble integrating AFNetworking with SwiftyJSON.
I have the following block of code, how do I convert responseObject, which can be downcasted to NSDictionary, to a JSON object as defined in SwiftyJSON? The code below doesn't work.
let task:NSURLSessionDataTask = AFHTTPSessionManager.GET(
url,
parameters: params,
success: { (task: NSURLSessionDataTask!, responseObject: AnyObject!) in
dispatch_async(dispatch_get_main_queue(), {
// How do I convert responseObject, which can be downcasted to NSDictionary to a JSON object?
let data = JSON(data: responseObject)
completion(task: task, response: data as JSON!, error: nil)
})
},
failure: { (task: NSURLSessionDataTask!, error: NSError!) in
dispatch_async(dispatch_get_main_queue(), {
completion(task: task, response: nil, error: error)
})
}
)
First of all, you don't need to dispatch to the main queue. AFNetworking already calls these blocks on the main thread.
The initializer you're using is expecting data, not a JSON object.
You're currently using this initializer, which expects an NSData:
let json = JSON(data: dataFromNetworking)
Instead, you should be using this initializer:
let json = JSON(jsonObject)
SwiftyJSON will work fine if you pass responseObject here.
One other note: this code doesn't belong in the completion blocks. In AFNetworking's design, you should be creating a response serializer that handles the server's response. I recommend looking at the source for AFJSONResponseSerializer and porting that over to use SwiftyJSON.

Resources