I am writing a class that returns JSON data. I create an array of the type NSArray. To this Array I add arrays of the type String.
I later want to append strings to the array of type String that is within the array of type NSArray.
However it does not allow this, I have no idea why because the append function does work when appending NSArrays to the main array.
This is the entire class:
import Foundation
class JSON {
struct Static {
let hostname = "http://192.168.2.115:8888/tcn/";
func getJSON(script:String, jsonMainElement:String, elements:[String]) -> [NSArray] {
var returnObject = [NSArray]()
let urlAsString = hostname + script;
let url = NSURL(string: urlAsString)!
let urlSession = NSURLSession.sharedSession()
let jsonQuery = urlSession.dataTaskWithURL(url, completionHandler: { data, response, error -> Void in
if (error != nil) {
println(error.localizedDescription);
}
var err: NSError?
var jsonResult = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &err) as NSDictionary
if (err != nil) {
println("JSON Error \(err!.localizedDescription)");
}
var i = 0;
let arrayFromJson: [[String:String]] = jsonResult[jsonMainElement] as [[String:String]]
dispatch_async(dispatch_get_main_queue(), {
for item in arrayFromJson {
returnObject.append([String]());
for jsonObject in elements {
returnObject[i].append(item[jsonObject]!);
// ^ THIS LINE GIVES ERROR
}
i++;
}
if (arrayFromJson.count > 0) {
//k
}
})
})
jsonQuery.resume()
return returnObject;
}
}
}
You may need to define the array as containing a mutable array as below.
var returnObject = [NSMutableArray]()
You could also use a Swift 'sub' array, and define it more like this.
var returnObject = Array<Array<String>>()
Don't forget to update your return on the method.
func getJSON() -> Array<Array<String>>
Related
JSON :
{
"11/08/22":[
{
"Bill Gates":"Microsoft",
"Steve Balmer":"Microsoft"
}],
"13/08/22":[
{
"Tim Cook":"Apple",
"Jony Ive":"Apple"
}]
}
Swift Code :
let jsonQuery = urlSession.dataTaskWithURL(url, completionHandler: { data, response, error -> Void in
do {
if let jsonDate = data, let jsonResult = try NSJSONSerialization.JSONObjectWithData(jsonDate, options: []) as? NSDictionary {
print(jsonResult)
//Get Result into Seperate Arrays
let keys = jsonResult.flatMap(){ $0.0 as? String }
let values = jsonResult.flatMap(){ $0.1 as? String }
}
} catch let error as NSError {
print(error)
}
})
jsonQuery.resume()
Requirements :
If i pass from program "11/08/22", I should be able to get all keys and values in the form of Array of String of only that array named "11/08/22" .
Better Explanation :
It should go into 11/08/22 and it should retrieve "Bill Gates","Steve Balmer" as Keys and "Microsoft" As a value in two separate arrays
For this example let's use an array to collect the people and a set to collect the companies:
var people: [String] = []
var companies: Set<String> = []
Subscript to the JSON dictionary with your "11/08/22" key and cast the result as an array of dictionaries.
Loop over this array and in the loop, add the keys to the people array and insert the values in the companies set.
let jsonQuery = urlSession.dataTaskWithURL(url, completionHandler: { data, response, error -> Void in
do {
if let jsonDate = data, let jsonResult = try NSJSONSerialization.JSONObjectWithData(jsonDate, options: []) as? NSDictionary {
if let dateContent = jsonResult["11/08/22"] as? [[String:String]] {
for group in dateContent {
people.appendContentsOf(group.keys)
group.values.forEach { companies.insert($0) }
}
}
}
} catch let error as NSError {
print(error)
}
})
jsonQuery.resume()
print(people)
print(companies)
Result:
["Steve Balmer", "Bill Gates"]
["Microsoft"]
let keys=jsonResult["11/08/22"]?.allKeys as? [String];
let values=jsonResult["11/08/22"]?.allValues as? [String];
It was as simple as this
I have a UIPickerView I want to fill with an array of values. The array of values is coming from a function inside of one of my classes (using json to grab the values and then put them into an array). The data is being grabbed successfully, and added to an array inside the function, but it's not returning for some reason.
Here's my class:
class Supplier {
var supplierId: Int
var supplierName: String
init(id: Int, name: String){
supplierId = id
supplierName = name
}
static func arrayOfSupplierNames() -> [String] {
let urlString = Constants.Urls.Suppliers.List;
let session = NSURLSession.sharedSession();
let url = NSURL(string: urlString)!;
var suppliers: Array<String> = []
session.dataTaskWithURL(url) { (data: NSData?, response:NSURLResponse?, error: NSError?) -> Void in
if let responseData = data {
do {
let json = try NSJSONSerialization.JSONObjectWithData(responseData, options: NSJSONReadingOptions.AllowFragments) as! Dictionary<String, AnyObject>;
if let suppliersDictionary = json["suppliers"] as? [Dictionary<String, AnyObject>] {
for aSupplier in suppliersDictionary {
if let id = aSupplier["id"] as? Int, let name = aSupplier["supplierName"] as? String {
let supplier = Supplier(id: id, name: name)
suppliers.append(supplier.supplierName)
}
}
}
}catch {
print("Could not serialize");
}
}
}.resume()
return suppliers
}
}
This seems to work because when I debug I can see the values being added to the array. I have another function in my ViewController that runs this function and adds it to a local array but the array returned from the function doesn't seem to get added to the array in the view controller:
func populateSuppliersArray() {
let sup:Array = Supplier.arrayOfSupplierNames()
for s in sup {
supplierArray.append(s) //supplierArray is at the top scope of view controller.
}
}
I even made the class function static so I wouldn't have to initialize the class just to use the function. I'm not sure this is the correct way. When I look at the sup variable while debugging it has zero values.
the json data is received inside an asynchronous block. your function returns as soon as you call resume on dataTaskWithURL. you should pass a completion block as an argument to your arrayOfSupplierNames and pass the array to that completion block instead. You could modify your function like this:
// take a completion block as an argument
func arrayOfSupplierNames(completion: (([String]) -> Void)) -> Void {
let urlString = Constants.Urls.Suppliers.List;
let session = NSURLSession.sharedSession();
let url = NSURL(string: urlString)!;
var suppliers: Array<String> = []
session.dataTaskWithURL(url) { (data: NSData?, response:NSURLResponse?, error: NSError?) -> Void in
if let responseData = data {
do {
let json = try NSJSONSerialization.JSONObjectWithData(responseData, options: NSJSONReadingOptions.AllowFragments) as! Dictionary<String, AnyObject>;
if let suppliersDictionary = json["suppliers"] as? [Dictionary<String, AnyObject>] {
for aSupplier in suppliersDictionary {
if let id = aSupplier["id"] as? Int, let name = aSupplier["supplierName"] as? String {
let supplier = Supplier(id: id, name: name)
suppliers.append(supplier.supplierName)
}
}
completion(suppliers) // pass the array to completion block
}
}catch {
print("Could not serialize");
}
}
}.resume()
}
You'll call it like this:
Supplier.arrayOfSupplierNames { (suppliers) -> Void in
// use suppliers as appropriate
}
please note that completion block is called asynchronously (some time in future).
The errors are:
Initializer for conditional binding must have Optional type, not 'NSData'
and
Call can throw, but it is not marked with 'try' and the error is not handled
class func loadMembersFromFile(path:String) -> [Member] //Function
{
var members:[Member] = []
var error:NSError? = nil
if let data = NSData(contentsOfFile: path, options:[]), //data
json = NSJSONSerialization.JSONObjectWithData(data, options: []) as? NSDictionary, //my array (json)
team = json["team"] as? [NSDictionary] { // display json
for memberDictionary in team { //cylce for
let member = Member(dictionary: memberDictionary)
members.append(member)
}
}
return members
}
First, you need to use the do catch syntax for methods that can throw exceptions. Secondly, the NSData initializer doesn't produce an Optional value so you can't put it in an if statement.
class func loadMembersFromFile(path:String) -> [Member] //Function
{
var members:[Member] = []
var error:NSError? = nil
do {
let data = try NSData(contentsOfFile: path, options:[])
if let json = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? NSDictionary,
let team = json["team"] as? [NSDictionary] {
for memberDictionary in team { //cylce for
let member = Member(dictionary: memberDictionary)
members.append(member)
}
}
} catch {
//handle exceptions
}
return members
}
See documentation.
Is there a way to convert a PFObject from Parse into JSON? I saved as JSON, but when I try to load I'm getting [AnyObject] back. Casting to JSON won't work:
class func loadPeople() -> [String : Person] {
var peopleDictionary: [String : Person] = [:]
let query = PFQuery(className: "userPeeps")
query.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if error == nil {
//this only returns the first entry, how do I get them all?
if let peopleFromParse = objects?.first?.objectForKey("userPeeps") as? JSON {
for name in peopleFromParse.keys {
if let personJSON = peopleFromParse[name] as? JSON,
let person = Person(json: personJSON) {
peopleDictionary[name] = person
}
}
}
below is my save function, which works and saves the JSON into Parse like I want:
class DataManager {
typealias JSON = [String: AnyObject]
class func savePeople(people: [String : Person]) {
var peopleDictionary = people
var peopleJSON: JSON = [:]
for name in peopleDictionary.keys {
peopleJSON[name] = peopleDictionary[name]!.toJSON()
}
let userPeeps = PFObject(className: "userPeeps")
userPeeps.setObject(peopleJSON, forKey: "userPeeps")
userPeeps.saveInBackgroundWithBlock { (succeeded, error) -> Void in
if succeeded {
println("Object Uploaded")
} else {
println("Error: \(error) \(error!.userInfo!)")
}
}
}
So the answer (as Paulw11 points out above) is that "objects" is sort of a wrapper for the real data, so it was necessary to iterate through the array and store each value as JSON:
var peopleDictionary: [String : Person] = [:]
//1 load the dictionary of JSON for key people from Parse
let query = PFQuery(className: "userPeeps")
query.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if error == nil {
if let unwrappedObjects = objects {
for object in unwrappedObjects {
if let peopleFromParse = object as? JSON {
for name in peopleFromParse.keys {
if let personJSON = peopleFromParse[name] as? JSON,
let person = Person(json: personJSON) {
peopleDictionary[name] = person
}
}
}
}
}
I am creating a weather app with Swift. So I have retrieved the JSON data and stored it in a dictionary:
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
///////getting URL:
let mainAddress = NSURL(string: "https://...") //for NY
//Now, getting the data syncronously by creating a session object::
let sharedSession = NSURLSession.sharedSession()
let downloadTask: NSURLSessionDownloadTask =
sharedSession.downloadTaskWithURL(mainAddress!, completionHandler: {
(location:NSURL!, response:NSURLResponse!, error: NSError!) -> Void in
//using the if statement to avoid crashing when the URL is wrong.
if error == nil {
//Now, creating a dataObject for the task:
let dataObject = NSData(contentsOfURL: location)
//getting a formated dictionary of the data from URL:
let weatherDictionary: NSDictionary = NSJSONSerialization.JSONObjectWithData(dataObject!, options: nil, error: nil) as NSDictionary //added '!' to NSdata for now
}
})
downloadTask.resume()
I have used a Struct, in a difirent file, in order to organize and initialize the dictionary's data:
import Foundation
import UIKit
import WatchKit
//created the struct just to better organize the data. In the future, if the API keys change, it would be easier to ajust the code, rather than if the data was directly read from the API onto the graph.
struct hourlyData {
///declaring only keys that have Integers as value.
var daylyPop0 : Int
var daylyPop1 : Int
var daylyPop2 : Int
var daylyPop3 : Int
var daylyPop4 : Int
var summaryNowDay : String
var summaryNowNight : String
var iconNow : String
var currentTime: String?
//Initializing the values here. With optional properties:
init(weatherDictionary:NSDictionary){
daylyPop0 = weatherDictionary["daily0_pop"] as Int
daylyPop1 = weatherDictionary["daily1_pop"] as Int
daylyPop2 = weatherDictionary["daily4_pop"] as Int
daylyPop3 = weatherDictionary["daily3_pop"] as Int
daylyPop4 = weatherDictionary["daily2_pop"] as Int
}
Now, I am implementing a chart for it. So I need to access the values from the dictionary in order to implement them on the chart. However, I've been unsuccessfull after many attemps.
The code lets me access the hourlyData struct, but not the weatherDictionary, since it was declared inside the session declaration.
Anyone knows an effective way to do it?
Any tips would be appreciated, thanks.
Try this for asynchronous request:
var request = NSURLRequest(URL: url)
var dict = NSDictionary()
var yourSavedData = hourlyData()
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue()) { (response, data, error) -> Void in
if data == nil
{
println("Error in connection")
return
}
var error = NSErrorPointer()
dict = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: error) as NSDictionary
if error != nil
{
println(error.debugDescription)
return
}
NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
if let yourDict = dict as? NSDictionary
{
yourSavedData = hourlyData(yourDict!)
}
}
})
Not tested, but should work.
You need to use if let to parse dictionary.
Alright, so after the answers you guys posted I've updated the code a little. This is how the viewDidLoad looks like:
override func viewDidLoad() {
super.viewDidLoad()
///////getting URL:
let url = NSURL(string: "http://..........") //for NY
var request = NSURLRequest(URL: url!)
var dict = NSDictionary()
var yourSavedData = hourlyData(weatherDictionary: NSDictionary())
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue()) { (response, data, error) -> Void in
if data == nil
{
println("Error in connection")
return
}
var error = NSErrorPointer()
dict = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: error) as! NSDictionary
if error != nil
{
println(error.debugDescription)
return
}
NSOperationQueue.mainQueue().addOperationWithBlock({ () -> Void in
if let yourDict = dict as? NSDictionary
{
yourSavedData = hourlyData(weatherDictionary: yourDict)
}
}
})
And this is how the other swift file with the Struct looks like:
struct hourlyData {
///declaring only keys that have Integers as value.
var daylyPop0 : Int
var daylyPop1 : Int
var daylyPop2 : Int
var daylyPop3 : Int
var daylyPop4 : Int
var summaryNowDay : String
var summaryNowNight : String
var iconNow : String
var currentTime: String?
//Initializing the values here. With optional properties:
init(weatherDictionary:NSDictionary){
daylyPop0 = weatherDictionary["hourly10_pop"] as! Int
daylyPop1 = weatherDictionary["hourly11_pop"] as! Int
daylyPop2 = weatherDictionary["hourly12_pop"] as! Int
daylyPop3 = weatherDictionary["hourly13_pop"] as! Int
daylyPop4 = weatherDictionary["hourly14_pop"] as! Int
summaryNowDay = weatherDictionary["today_day_fcttext_metric"] as! String
summaryNowNight = weatherDictionary["today_night_fcttext_metric"] as! String
iconNow = weatherDictionary["current_icon"] as! String
let currentTimeIntValue = weatherDictionary["forecast_time"] as! Int
currentTime = dateStringFromUnixTime(currentTimeIntValue)
}
//Converting unixTime to a desired style:::used ShortStyle in this case:
func dateStringFromUnixTime(unixTime: Int) -> String{
let timeInSeconds = NSTimeInterval(unixTime)
let weatherDate = NSDate(timeIntervalSince1970: timeInSeconds)
let dateFormatter = NSDateFormatter()
dateFormatter.timeStyle = .ShortStyle
return dateFormatter.stringFromDate(weatherDate)
}
}
Now the code looks fine, and it does not show any error, besides a warning under the 'if let', which says: Conditional cast from 'NSDictionary' to 'NSDictionary' always succeeds.
When I run the simulator, it crashes and displays: fatal error: unexpectedly found nil while unwrapping an Optional value. Highlighting, in green, the code line: daylyPop0 = weatherDictionary["hourly10_pop"] as! Int