Establish Error Case in NSURLConnection, JSON Parsing in Swift - ios

I am attempting to pull information from a JSON on my website. In doing so, if there is an error with the connection, it should return that error and log it to the console. The problem I am running into is that if I turn on Airplane more or otherwise lose signal, the error: fatal error: unexpectedly found nil while unwrapping an Optional value crashes the application. Why is it returning this, when I have set conditions for the error to simply be logged? Thank you in advance!
I am unsure why the error is not being logged and preventing the application from crashing. Any pointers would be helpful.
JSONLoader.swift
import Foundation
var arrayOfMeals: [Meal] = [Meal]()
var weekDayArray = ["monday"]
func getJSON(urlToRequest: String) -> NSDictionary {
var url: NSURL = NSURL(string: urlToRequest)!
var jsonRequest: NSURLRequest = NSURLRequest(URL: url)
var jsonResponse: AutoreleasingUnsafeMutablePointer<NSURLResponse?> = nil
var error: NSError?
var dataValue: NSData = NSURLConnection.sendSynchronousRequest(jsonRequest, returningResponse: jsonResponse, error:&error)!
if error? == nil {
var jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(dataValue, options: NSJSONReadingOptions.MutableContainers , error: &error) as NSDictionary
NSURLRequestCachePolicy.ReloadIgnoringLocalAndRemoteCacheData
if error? == nil {
return jsonResult
}
else {
return NSDictionary(object: "Error: Something with parsing went wrong :(", forKey: "error")
}
}
else {
return NSDictionary(object: "Error: There was an error with your connection :(", forKey: "error")
}
}
func loadJSON(jsonDictionary: NSDictionary) {
for days in weekDayArray{
var resultsArray = jsonDictionary[days] as NSArray
for obj: AnyObject in resultsArray{
let breakfast = (obj.objectForKey("breakfast")! as String)
let lunch = (obj.objectForKey("lunch")! as String)
let dinner = obj.objectForKey("dinner")! as String
let dateString = obj.objectForKey("dateString")! as String
let dayOfWeek = obj.objectForKey("dayOfWeek")! as String
let newMeal = Meal(breakfast: breakfast, lunch: lunch, dinner: dinner, dayOfWeek: dayOfWeek, dateString: dateString)
if theDays(newMeal.dateString) >= 0 {
arrayOfMeals.append(newMeal)
}
}
}
}
I expect the function getJSON to establish an NSURLConnection to the site, parse the JSON date, and return an NSDictionary. An error case is established if there is an error in the connection, then it returns the dictionary with a value explaining the error. An additional error case occurs to parse the JSON file, and returns an NSDictionary with similar error.
I expect the function loadJSON to create instances of an object Meal, which I define as having properties of breakfast, lunch, dinner, a date, and a day of the week. The values of this instance are the results of the returned NSDictionary from function getJSON. If the day is the future, append it to my arrayOfMeals. Otherwise, ignore the instance.
MealViewController
override func viewDidLoad() {
super.viewDidLoad()
var req = getJSON("http://www.seandeaton.com/meals/Mealx")
loadJSON(req)
}
MealModel.swift - to create instances of the meals
class Meal {
let breakfast: String
let lunch: String
let dinner: String
let dayOfWeek: String
let dateString: String
init(breakfast: String, lunch: String, dinner: String, dayOfWeek: String, dateString: String) {
self.breakfast = breakfast
self.lunch = lunch
self.dinner = dinner
self.dayOfWeek = dayOfWeek
self.dateString = dateString
}
}
How can I prevent the app from crashing upon failure to establish a connection and log the error message to the console? Thanks again.

The key observation is that in the call to sendSynchronousRequest in which you're retrieving the NSData, you have defined NSData to not be optional, and you appended a ! which will force the unwrapping of the optional return value. Thus if the optional NSData was nil (which would happen if there were any network problems), this code would fail.
Instead, you should simply leave the NSData as an optional (i.e. a NSData?) and check whether it was nil or not before trying to unwrap it. Thus, I might suggest something like:
func retrieveJSON(urlToRequest: String) -> NSDictionary {
let url: NSURL = NSURL(string: urlToRequest)!
let jsonRequest: NSURLRequest = NSURLRequest(URL: url)
var jsonResponse: NSURLResponse?
var error: NSError?
let dataValue = NSURLConnection.sendSynchronousRequest(jsonRequest, returningResponse: &jsonResponse, error:&error)
if dataValue != nil {
if let jsonResult = NSJSONSerialization.JSONObjectWithData(dataValue!, options: .MutableContainers, error: &error) as? NSDictionary {
return jsonResult
} else {
return [
"error": "Error: Something with parsing went wrong :(",
"localizedDescription": error?.localizedDescription ?? ""
];
}
}
else {
return [
"error": "Error: There was an error with your connection :(",
"localizedDescription": error?.localizedDescription ?? ""
];
}
}
The above will fix crashes resulting from the forced unwrapping of the optional NSData, which would have been be nil if there were networking problems. Note, you don't show us the code that calls getJSON and then subsequently calls loadJSON, but I assume you're doing the necessary checking for error handling there.
Note, I also retired the awkward AutoreleasingUnsafeMutablePointer<NSURLResponse?> construct. I also added the localizedDescription of the error to the dictionary being returned.
Personally, I'd generally return the complete NSError object and the NSURLResponse objects, so that the caller can diagnose the precise nature of the error, rather than just text descriptions.
In a more radical edit to your code, I'd suggest you generally avoid synchronous requests. Never perform synchronous requests from the main thread.
For example, you might define the method to perform the request asynchronously, like so:
func retrieveJSON(urlToRequest: String, completionHandler:(responseObject: NSDictionary?, error: NSError?) -> ()) {
let url: NSURL = NSURL(string: urlToRequest)!
let jsonRequest: NSURLRequest = NSURLRequest(URL: url)
var jsonResponse: NSURLResponse?
NSURLConnection.sendAsynchronousRequest(jsonRequest, queue: NSOperationQueue.mainQueue()) {
response, data, error in
if data == nil {
completionHandler(responseObject: nil, error: error)
} else {
var parseError: NSError?
let jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: .MutableContainers, error: &parseError) as NSDictionary?
completionHandler(responseObject: jsonResult, error: error)
}
}
}
You'd then call it, using the "trailing closure syntax", like so:
retrieveJSON(urlString) {
responseObject, error in
if responseObject == nil {
// handle error here, e.g.
println(error)
return
}
// handle successful the `NSDictionary` object, `responseObject`, here
}
// but don't try to use the object here, because the above runs asynchronously

Related

Invalid conversion from throwing function type (_,_,_) throws to non-throwing function type (Data?, Response?, URLResponse?) Void

I am new at Swift programming and I am getting an error on a "let task = URLSession. I have researched and found some of those errors but none of the answers worked for me. Here is my code and line 44 throws this error
import UIKit
class loginViewController: UIViewController {
#IBOutlet weak var userLoginTextField: UITextField!
#IBOutlet weak var userPasswordTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func loginButtonTapped(_ sender: Any) {
let user_login = userLoginTextField.text;
let user_passwd = userPasswordTextField.text;
if((user_passwd?.isEmpty)! || (user_passwd?.isEmpty)!) { return; }
//Send user data to server
let myUrl = NSURL(string: "https://www.pcpusa.net/legacy/iOS/userLogin.php");
var request = URLRequest(url: myUrl! as URL);
request.httpMethod = "POST";
let postString = "login=\(String(describing: user_login))&password=\(String(describing: user_passwd))";
request.httpBody = postString.data(using: String.Encoding.utf8);
**let task = URLSession.shared.dataTask(with: request){**
data,response,error in
if error != nil {
print("error=\(String(describing: error))")
return
}
var err: NSError?
var json = try JSONSerialization.JSONObjectWithData(data, options: .mutableContainers, error: &err) as? NSDictionary
if let parseJSON = json {
var resultValue:String = parseJSON["status"] as String!;
print("result: \(resultValue)")
if(resultValue=="Success")
{
//Login is successful
NSUserDefaults.standardUserDefaults().setBool(true, value(forKey: "isUserLoggedIn"));
NSUserDefaults.standardUserDefaults().synchronize();
self.dismissViewControllerAnimated(true, completion:nil);
}
}
}
task.resume()
}
}
URLSession.dataTask expects a non-throwing completion handler and you are passing it a closure that throws. Because of this it fails to infer the method (in other words: it can’t find a method with that name that accepts a closure that throws).
To solve this you should catch and handle the errors that can happen when you decode the JSON.
The key to that error message is "throwing function type". In Swift, whether or not a function can throw an error is part of its method signature. URLSession is expecting a closure/function that will not throw an error.
Therefore, if you throw an error, you must catch it.
So, you basically have two options here:
Wrap the throwable try in a do-try-catch block.
Use optionals to ignore possible errors and safely unwrap the result.
Example of option 1:
var json: [String: Any]!
do {
json = try JSONSerialization.jsonObject(with: data!, options: .mutableContainers) as? [String: Any]
} catch let error { // `let error` is optional. It is the default.
// Handle error here
}
Example of option 2:
(Also, I suggest using the guard statement for situations like this. This is my preferred method most of the time, and is considered by many to be more "Swifty")
let task = URLSession.shared.dataTask(with: request) {
data,response,error in
guard error != nil else {
print("error=\(String(describing: error))")
return
}
guard
let data = data,
let jsonObject = try? JSONSerialization.jsonObject(with: data, options: .mutableContainers),
let json = jsonObject as? [String: Any]
else {
print("Could not parse JSON")
return
}
if let status = json["status"] as? String {
print(status)
}
}
EDIT: Adds surrounding context to option 2.
More on guard. It basically acts as an inside-out if-else statement.
You can use guard to test regular booleans. Or, like if, you can combine it with let to make a guard-let statement.
The difference between the two is:
You use if-let when you say, "If this exists, I'll do something with it. Then I'll continue."
You use guard-let when you say, "If I don't have this thing, then I can't continue".
Because of this, you must stop your function in its else statement, usually by returning or throwing an error (which you can't do here).
The problem is you try to decode (which can throw). Since you don't catch anywhere URLSession.dataTask complains because it doesn't expect that closure to throw. (If a method or closure can throw it will be marked with the throws keyword, this is an Apple provided method that does not have that keyword)
You just need to wrap in a do catch like so:
let task = URLSession.shared.dataTask(with: request){ (data,response,error) in
if error != nil {
print("error=\(String(describing: error))")
return
}
var err: NSError?
do {
var json = try JSONSerialization.JSONObjectWithData(data, options: .mutableContainers, error: &err) as? NSDictionary
if let parseJSON = json {
var resultValue:String = parseJSON["status"] as String!;
print("result: \(resultValue)")
...
} catch {
//print("Error: unable to serialize data: \(err)")
}
}

unwrapping an Optional value error - Checking Receipt Response For In App Purchase

On app start up im checking the receipt status of an auto renew in app purchase:
App delgate
let qualityOfServiceClass = QOS_CLASS_BACKGROUND
let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
dispatch_async(backgroundQueue, {
println("Check for receipt on background queue")
self.CheckReciptStatus()
}
func CheckReciptStatus(){
var response: NSURLResponse?
var error: NSError?
var receiptUrl = NSBundle.mainBundle().appStoreReceiptURL
println("reciptUrl: '\(receiptUrl)'")
//
// Nil error on this line
//
var receipt: NSData = NSData(contentsOfURL:receiptUrl!, options: nil, error: nil)!
//
var receiptdata: NSString = receipt.base64EncodedStringWithOptions(NSDataBase64EncodingOptions(rawValue: 0))
println(receiptdata)
var request = NSMutableURLRequest(URL: NSURL(string: "http://www.someurl.uk/verifyReceipt.php")!)
var session = NSURLSession.sharedSession()
request.HTTPMethod = "POST"
var err: NSError?
request.HTTPBody = receiptdata.dataUsingEncoding(NSASCIIStringEncoding)
var task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
var err: NSError?
var json = NSJSONSerialization.JSONObjectWithData(data, options: .MutableLeaves, error: &err) as? NSDictionary
if(err != nil)
{
println(err!.localizedDescription)
let jsonStr = NSString(data: data, encoding: NSUTF8StringEncoding)
println("Error could not parse JSON: '\(jsonStr)'")
}
else {
//
// ---Recipt data ---
//
if let parseJSON = json {
if parseJSON["status"] as? Int == 0 {
println("Sucessfully returned purchased receipt data")
}
else{
println("Receipt error")
NSUserDefaults.standardUserDefaults().setBool(false, forKey: "subscribed")
}
// Get latest expiry date
if let receiptInfo: NSArray = parseJSON["latest_receipt_info"] as? NSArray {
let lastReceipt = receiptInfo.lastObject as! NSDictionary
// Get last recipt
println("LAST RECIPT INFORMATION \n",lastReceipt)
// Format date
var formatter = NSDateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss VV"
formatter.locale = NSLocale(localeIdentifier: "en_US_POSIX")
// Get Expiry date as NSDate
let subscriptionExpirationDate: NSDate = formatter.dateFromString(lastReceipt["expires_date"] as! String) as NSDate!
println("\n - DATE SUBSCRIPTION EXPIRES = \(subscriptionExpirationDate)")
println(" - CURRENT DATE = \(NSDate())")
//
// --- Subscription Status ----
//
if subscriptionExpirationDate.timeIntervalSinceNow < 0.0 {
// Date has passed
println("\n -> SUBSCRIPTION EXPIRED \n ")
NSUserDefaults.standardUserDefaults().setBool(false, forKey: "subscribed")
}
else{
println("\n -> SUBSCRIBED \n")
NSUserDefaults.standardUserDefaults().setBool(true, forKey: "subscribed")
}
}
}
else {
let jsonStr = NSString(data: data, encoding: NSUTF8StringEncoding)
println("Receipt Error: \(jsonStr)")
}
}
})
task.resume()
}
}
Get receipt info
var receiptUrl = NSBundle.mainBundle().appStoreReceiptURL
Prints:
reciptUrl:
'Optional(file:///private/var/mobile/Containers/Data/Application/D0C4DAF0-952F-4C29-9D94-B68DB1FF036F/StoreKit/sandboxReceipt)'
var receipt: NSData = NSData(contentsOfURL:receiptUrl!, options: nil, error: nil)!
Produces error
unexpectedly found nil while unwrapping an Optional value
Even though i'm getting the storekit receipt url. So this leads to believe it's because of the force unwrapping in var receipt. Ok, so when I then try to change the line to
var receipt: NSData = NSData(contentsOfURL:receiptUrl?, options: nil, error: nil)?
Im just told that NSData is not unwrapped did I mean "!" or "??"
Postfix ! causes force unwrapping.
Postfix ? is used for optional chaining. This means, if the result of the expression before the ? is not nil, the next expression in the chain will be evaluated. The result of an optional-chain is always an Optional though - because any expression along the chain may result in nil.
Using the postfix ? operator on one expression is therefore useless, as it returns the expression itself.
?? is the nil-coalescing operator. This operator is syntactical sugar, replacing a common pattern with the ternary operator:
return myOptional ?? defaultResult
Is the same as:
return (myOptional != nil) ? myOptional! : defaultResult
Which is the same as:
if myOptional != nil {
return myOptional!
} else {
return defaultResult
}
If you wanted to use this operator you would therefore have to provide a default value, which is used if your Optional is in fact nil.
A check if the receipt exists using
var fileExists = NSFileManager.defaultManager().fileExistsAtPath(receiptUrl!.path!)
if fileExists {
println("Appstore Receipt already exists")
}
Sorts it

Cannot convert JSON to the correct format

I'd like to use JSON output from the public Web Service https://api.rbp-zp.cz/1.3/services/district/ but looks like there is a problem with encoding in this case.
AFNetworking use to do conversion in such cases (by specific respondSerializer) like this:
var op = AFHTTPRequestOperation (request: request)
op.responseSerializer = AFJSONResponseSerializer()
but it fails because of the invalid JSON structure.
I have tried to do conversion myself, but no outcome
let rObject: AnyObject? = NSJSONSerialization.JSONObjectWithData(decodedData, options:.AllowFragments, error: nil)
When I put manually this JSON from browser to validator http://jsonlint.com, then it says it is in incorrect format.
Strangely enough when I put the JSON message from my log into the validator then the structure is valid.
Any help would be appreciated.
func convertStringToDictionary(text: String) -> [String:String]? {
if let data = text.dataUsingEncoding(NSUTF8StringEncoding) {
var error: NSError?
let json = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.allZeros, error: &error) as? [String:String]
if error != nil {
println(error)
}
return json
}
return nil
}
let str = "{\"name\":\"James\"}"
let result = convertStringToDictionary(str) // ["name": "James"]
if let name = result?["name"] { // The `?` is here because our `convertStringToDictionary` function returns an Optional
println(name) // "James"
}
In your version, you didn't pass the proper parameters to NSJSONSerialization and forgot to cast the result. Also, it's better to check for the possible error. Last note: this works only if your value is a String. If it could be another type, it would be better to declare the dictionary conversion like this:
let json = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.allZeros, error: &error) as? [String:AnyObject]
and of course you would also need to change the return type of the function:
func convertStringToDictionary(text: String) -> [String:AnyObject]? { ... }
Ok, so workaround is trimming those funny characters in the end,..don't like it much but works
let range:NSRange = NSMakeRange(0, res.length - 3);
let refinedData:NSData = res.subdataWithRange(range)
let resObject = NSJSONSerialization.JSONObjectWithData(refinedData, options:NSJSONReadingOptions.allZeros, error: &error) as! NSArray
resObject is correctly filled as NSArray now

Swift - Expected expression in list of expressions

I'm new to Swift been reading but have no clue what this means. On the line of code below, I have "Expected expression in list of expressions" after parameters[String]. AS well at the same point it is looking for "Expected ',' separator. I believe these are related.
AppDelegate.submitLacunaRequest(module: "empire", method: "login", parameters[String]:["myuserid", "mypassword", "mykey"]) {
responseObject, error in
// some network error or programming error
if error != nil {
println("error = \(error)")
println("responseObject = \(responseObject)")
return
}
// network request ok, now see if login was successful
if let responseDictionary = responseObject as? NSDictionary {
if let errorDictionary = responseDictionary["error"] as? NSDictionary {
println("error logging in (bad userid/password?): \(errorDictionary)")
} else if let resultDictionary = responseDictionary["result"] as? NSDictionary {
println("successfully logged in, refer to resultDictionary for details: \(resultDictionary)")
} else {
println("we should never get here")
println("responseObject = \(responseObject)")
}
}
}
Here is the related code from AppDelegate
public func submitLacunaRequest (#module: String, method: String, parameters: AnyObject, completion: (responseObject: AnyObject!, error: NSError!) -> (Void)) -> NSURLSessionTask? {
let session = NSURLSession.sharedSession()
let url = NSURL(string: "https://us1.lacunaexpanse.com")?.URLByAppendingPathComponent(module)
let request = NSMutableURLRequest(URL: url!)
request.HTTPMethod = "POST"
request.setValue("application/json-rpc", forHTTPHeaderField: "Content-Type")
let requestDictionary = [
"jsonrpc" : "2.0",
"id" : 1,
"method" : "login",
"params" : ["myuserid", "mypassword", "mykey"]
]
var error: NSError?
let requestBody = NSJSONSerialization.dataWithJSONObject(requestDictionary, options: nil, error: &error)
if requestBody == nil {
completion(responseObject: nil, error: error)
return nil
}
request.HTTPBody = requestBody
let task = session.dataTaskWithRequest(request) {
data, response, error in
// handle fundamental network errors (e.g. no connectivity)
if error != nil {
completion(responseObject: data, error: error)
return
}
// parse the JSON response
var parseError: NSError?
let responseObject = NSJSONSerialization.JSONObjectWithData(data, options: nil, error: &parseError) as? NSDictionary
if responseObject == nil {
// because it's not JSON, let's convert it to a string when we report completion (likely HTML or text)
let responseString = NSString(data: data, encoding: NSUTF8StringEncoding) as String
completion(responseObject: responseString, error: parseError)
return
}
completion(responseObject: responseObject, error: nil)
}
task.resume()
return task
}
You are using external parameter name for a parameter when calling the function, but the external parameter is not defined in your function declaration. Simply use it this way.
submitLacunaRequest(module: "empire", "login", ["myuserid", "mypassword", "mykey"]) {
You're calling the function incorrectly. You don't need the [String] in the parameters param...
AppDelegate.submitLacunaRequest(module: "empire", method: "login", parameters: ["myuserid", "mypassword", "mykey"]) {
...
}
I called my function parameter protocol.
If I was to try using this property as usual, I would notice it to be written in pink being a keyword and I would rename it.
Instead, I used this property in a string like this and I didn't get any clues from the compiler:
func configure(_ protocol: Protocol, host:String, port:String) {
urlString = "\(protocol)://\(host):\(port)"
}
I spend good 5 minutes confused out of my mind by this error, but then I figured it out. The problem was in the name of the parameter.
I didn't want to rename the parameter, so I ended up writing it like this:
urlString = "\(`protocol`)://\(host):\(port)"

Create variables from JSON array

I'm trying hard to learn IOS development.
I have followed this guide and successfully managed to create a working quiz game. The last couple of days I have been trying to connect the game to an external database. Finally after many hours I'm able to read from MYSQL using JSON parsing.
Right now Im struggling with a way to convert the json array into a normal array.
My current hardcoded questions look like this:
let questionOne = questionTemplate("the first question?", answerOne: "a answer", answerTwo: "a second answer", answerThree: "a third aswer", answerFour: "tast possible answer", correctAnswer: 2)
Then they are added to an array
spormslaArray = [questionOne, questionTwo, questionThree, questionFour, questionFive, questionSix,questionSeven]
Then im doing some more loading of answers and questions before i add them to the GUI based on an array counter from the first to the last question.
func questionTemplate(question:String, answerOne:String, answerTwo:String, answerThree:String, answerFour:String, correctAnswer:Int) -> NSArray {
//Set the question
var quizQuestion = question
//set the answers and the right answer
var firstAnswer = answerOne
var secondAnswer = answerTwo
var thirdAnswer = answerThree
var fourthAnswer = answerFour
var rightAnswer = correctAnswer
var gjennverendeSporsmal = 1
//Add all the questions and answers to an array
let questionAnswerArray = [question, firstAnswer, secondAnswer, thirdAnswer, fourthAnswer, rightAnswer]
return questionAnswerArray
}
I now want to add questions from my database into spormslaArray.I got questions loaded into xcode using this code:
func lasteJson(){
let urlPath = "http://universellutvikling.no/utvikling/json.php"
let url: NSURL = NSURL(string: urlPath)!
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithURL(url, completionHandler: {data, response, error -> Void in
if error != nil {
// If there is an error in the web request, print it to the console
println(error.localizedDescription)
}
var err: NSError?
var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as NSDictionary
if err != nil {
// If there is an error parsing JSON, print it to the console
println("JSON Error \(err!.localizedDescription)")
}
let json = JSON(jsonResult)
let count: Int? = json["data"].array?.count
// println("found \(count!) challenges")
//Im just setting a hardcoded number, it will be based on the array when I have figured that out
var tall = 7
let ct = count
for index in 0...tall-1 {
println(json["data"][index] )
//DEtte printer ut induviduelt
/*
if let questionId = json["data"][index]["id"].string {
println(questionId)
}
if let spm1 = json["data"][index]["answerOne"].string {
println(spm1)
}
if let spm2 = json["data"][index]["answerTwo"].string {
println(spm2)
}
if let spm3 = json["data"][index]["answerThree"].string {
println(spm3)
}
if let spm4 = json["data"][index]["answerFour"].string {
println(spm4)
}
if let correctAnswer = json["data"][index]["correctAnswer"].string {
println(correctAnswer)
}
*/
}
//}
})
task.resume()
This is mostly based on this code.
If Im ignoring the fact that Im getting some breakpoints when im running the app, and that nordic characters in my database makes the ios simulator crash; This is the parsing result in the command line:
{
"correctAnswer" : "1",
"id" : "0",
"answerThree" : "aa3",
"answerFour" : "aa4",
"questionTemplate" : "sporsmal",
"answerOne" : "as1",
"answerTwo" : "aa2"
}
//////Finally here is the problem///////
I have tried for hours to make a variable from the json array, into the guestion array.
I want to do something like this:
let questionOne = json["data"][index]["answerOne"].string
and then add them to an array
let questionArray[questionOne, QuestionTwo.. etc]
I have tried for hours without any progress, so my last hope is you guys! :-)
Use this...
To post JSON or to receive JSON (Leave dictionary nil to GET)
///Use completion handler to handle recieved data
func sendJSON(params:Dictionary<String, String>?, toAdressOnServer:String, customCompletionHandler:((parsedData:AnyObject?, statusCode: Int) -> Void)?){
var request = NSMutableURLRequest(URL: NSURL(string: SERVER_NAME + toAdressOnServer)!)
var session = NSURLSession.sharedSession()
var err: NSError?
if (params == nil){
request.HTTPMethod = "GET"
}else{
request.HTTPMethod = "POST"
request.HTTPBody = NSJSONSerialization.dataWithJSONObject(params!, options: nil, error: &err)
}
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
var task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
println("Response: \(response)")
var strData = NSString(data: data, encoding: NSUTF8StringEncoding)
println("Body: \(strData)")
var err: NSError?
var json: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.AllowFragments , error: &err)
// Did the JSONObjectWithData constructor return an error? If so, log the error to the console
if(err != nil) {
println(err!.localizedDescription)
let jsonStr = NSString(data: data, encoding: NSUTF8StringEncoding)
println("Error could not parse JSON: '\(jsonStr)'")
customCompletionHandler?(parsedData: json, statusCode: -1)
}
else {
// The JSONObjectWithData constructor didn't return an error. But, we should still
// check and make sure that json has a value using optional binding.
if let parseJSON: AnyObject = json {
// Okay, the parsedJSON is here, let's get the value for 'success' out of it
// Use keyword "success" in JSON from server to register successful transmission
let success = parseJSON["success"] as? Int
if (success == nil){
customCompletionHandler?(parsedData: json, statusCode: -2)
}else{
customCompletionHandler?(parsedData: json, statusCode: success!)
}
}
else {
// The json object was nil, something went worng. Maybe the server isn't running?
let jsonStr = NSString(data: data, encoding: NSUTF8StringEncoding)
println("Error could not parse JSON: \(jsonStr)")
customCompletionHandler?(parsedData: json, statusCode: -1)
}
}
})
task.resume()
}
And To decode the JSON in your case the array, but it can have any form.
self.sendJSON(nil, toAdressOnServer: "ADRESS", customCompletionHandler: { (parsedData, statusCode) -> Void in
//check for valid data
if (parsedData != nil){
//Loop through results
for (var x = 0; x < parsedData!.count; x++){
///primary key of the item from the internet
let pk:Int = (parsedData![x] as NSDictionary).objectForKey("pk") as Int
let month = ((parsedData![x] as NSDictionary).objectForKey("fields") as NSDictionary).objectForKey("month")! as String
let quote = ((parsedData![x] as NSDictionary).objectForKey("fields") as NSDictionary).objectForKey("quote")! as String
let quotee = ((parsedData![x] as NSDictionary).objectForKey("fields") as NSDictionary).objectForKey("quotee")! as String
})
This is an example, use parsed data as "json" and use it with the appropriate structure. In this case the JSON was An array of some dictionary with a fields dictionary that has another dictionary with more fields. So you can have any JSON structure.
I Hope this helps!
It seems that you almost have the answer there. I think what you are missing is questionArray.append(... in your loop to build your array. You could also make things easier for yourself if you modified your JSON so that the questions were in an array rather than discrete keys and change your questionTemplate to take an array rather than discrete answers.
Working with what you have however -
func lasteJson(){
let urlPath = "http://universellutvikling.no/utvikling/json.php"
let url: NSURL = NSURL(string: urlPath)!
let session = NSURLSession.sharedSession()
questionsArray=[Question]()
let task = session.dataTaskWithURL(url, completionHandler: { (data, response, error) -> Void in
if error != nil {
// If there is an error in the web request, print it to the console
println(error.localizedDescription)
}
else {
var err: NSError?
var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as NSDictionary
if err != nil {
// If there is an error parsing JSON, print it to the console
println("JSON Error \(err!.localizedDescription)")
}
else {
let questions=jsonResult["data"] as? [[String:String]]
if (questions != nil) {
for question in questions! {
let answer1=question["answerOne"]!
let answer2=question["answerTwo"]!
let answer3=question["answerThree"]!
let answer4=question["answerFour"]!
let id=question["id"]!
let questionTemplate=question["questionTemplate"]!
let correctAnswer=question["correctAnswer"]!
let newQuestion=Question(questionTemplate, answerOne: answer1, answerTwo:answer2, answerThree: answer3, answerFour: answer4, correctAnswer: correctAnswer)
questionsArray.append(newQuestion)
}
}
}
}
})
task.resume()
}
You don't show your questionTemplate, but I am not sure why/how it returns an array. My code above assumes that there is a class Question and fills in a property questionsArray

Resources