Get all items in JSON and put them in an array - json

I am using Alamofire to get some JSON data and rather than having everything hardcoded, I was wondering how to simply include everything in the array.
Currently, this is the JSON file:
{"One":"Item1","Two":"Item2","Three":"Item3","Four":"Item4","Five":"Item5"}
My swift code:
var pickerData: [String] = [String]() //Array of content.
override func viewDidLoad() {
super.viewDidLoad()
Alamofire.request(.GET, "https://example.com/file.json").responseJSON{ (response) -> Void in
if let JSON = response.result.value{
let One = JSON["One"] as! String
let Two = JSON["Two"] as! String
let Three = JSON["Three"] as! String
let Four = JSON["Four"] as! String
let Five = JSON["Five"] as! String
self.mypickerview.delegate = self
self.mypickerview.dataSource = self
self.pickerData = [One, Two, Three, Four, Five]
...
How can I not make them hardcoded so that I can have my file like this and it would still work and include everything in the JSON, even if I add more in the future:
{"Item1","Item2","Item3","Item4","Item5","Item6"}

Aaron is right, your JSON should use an array to hold the item values.
But if you're stuck with this format, you can get the values in your pickerData like this:
if let JSON = response.result.value as? [String:AnyObject] {
let result = JSON.values.flatMap({ String($0) })
self.pickerData.appendContentsOf(result)
self.pickerData.sortInPlace()
}
pickerData is now:
["Item1", "Item2", "Item3", "Item4", "Item5"]
and items will be sorted in the picker array even if you add new ones later.

Related

SIGABRT error in Swift when JSON deserializing

I am attempting to make a Swift application with two main focuses. One is to display all the data from an URL in a ScrollView and the other is a button to get a random name of a game.
The button works and I get a random game but when I try to load the application with the UIScrollView, I get a SIGABRT on line 33.
Any help is appreciated
EDIT: I have since fixed the SIGABRT but I can't seem to display any information into the UIScrollView. Anyone see any glaring issues in the code now?
#IBOutlet weak var infoView: UIView!
#IBOutlet weak var label: UILabel!
#IBOutlet weak var labelScroll: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
parseGame()
}
func parseGame() {
let url: URL = URL(string: "https://www.giantbomb.com/api/games/?api_key=5094689370c2cf4ae42a2a268af0595badb1fea8&format=json&field_list=name")!
print(url)
let responseData: Data? = try? Data(contentsOf: url)
if let responseData = responseData {
let json: Any? = try? JSONSerialization.jsonObject(with: responseData, options: [])
print(json ?? "Couldn't get JSON")
if let json = json {
let dictionary: [String: Any]? = json as? [String: Any]
if let dictionary = dictionary {
guard let result = dictionary["results"] as? [String:Any]? else { return }
if let result = result {
let name = result["name"] as? String?
if let name = name {
for i in 1...100 {
labelScroll.text = "Name: \(name)"
}
}
}
}
}
}
}
Oh no! It's the json parsing pyramid of death!
A Neat Little Alternative
If you know the structure of the json you will be receiving, you can create some structs to model your data. Also Swift has a cool protocol called "Codable" which does a lot of heavy lifting in parsing json and converting it to an object you create in code.
Let's Model the Data!
We'll create 2 structs that conform to the Codable Protocol. The first will hold the entire response data
struct ApiGameReponse:Codable {
var error:String
var limit:Int
var offset:Int
var pages:Int
var totalResults:Int
var status:Int
var version:String
var games:[Game]
private enum CodingKeys:String, CodingKey {
//The enum's rawValue needs to match the json's fieldName exactly.
//That's why some of these have a different string assigned to them.
case error
case limit
case offset
case pages = "number_of_page_results"
case totalResults = "number_of_total_results"
case status = "status_code"
case version
case games = "results"
}
}
and the second struct to model our game object. (Which is a single string, yes I know very exciting...) Since this particular json data represents a game with only 1 name property we don't really need a whole struct, but if the json was different and say had a "gamePrice" and "genre" properties, a struct would be nicer to look at.
struct Game:Codable {
var name:String
}
Keepin it pretty
Idk about you, but I don't like to look at ugly code. To keep it clean, we're going to split the function you made into two pieces. It's always better to have a bunch of smaller readable/reusable functions that each complete a single task rather than 1 superSizeMe number 3 with a large coke.
Getting the Data
Piece 1: Get the data from the URL
func getJSONData(_ urlString: String) -> Data? {
guard
let url:URL = URL(string: urlString),
let jsonData:Data = try? Data(contentsOf: url)
else { return nil }
return jsonData
}
Piece 2: Decode the data into something we can use
func getGameNames() -> [String] {
let apiEndpoint = "https://www.giantbomb.com/api/games/?api_key=5094689370c2cf4ae42a2a268af0595badb1fea8&format=json&field_list=name"
guard
let data = getJSONData(apiEndpoint),
let response = try? JSONDecoder().decode(ApiGameReponse.self, from: data)
else { return [] }
//Neat little function "map" allows us to create an array names from the array of game objects.
let nameArray = response.games.map { $0.name }
return nameArray
}
Implementation
And finally we can get the names and use them as needed.
override func viewDidLoad() {
//I recommend putting this somewhere else
//Perhaps create a method called "setup()" and call it in this class's inializer.
let names = getGameNames()
print(names) //Implement the array of names as needed
}

Get JSON Element in Swift 3

Please excuse me if this is a simple question, but I am stuck. I have tried to read everything I can to work it out myself.
I am trying to extract a URL from JSON data, I get the JSON data fine and I can print it to the console, however I can't work out how to access the URL for the audio file.
This is the code I use to get the JSON:
let session = URLSession.shared
_ = session.dataTask(with: request, completionHandler: { data, response, error in
if let response = response,
let data = data,
let jsonData = try? JSONSerialization.jsonObject(with: data, options: .mutableContainers) {
if let dictionary = jsonData as? [String: Any] {
if let prounce = dictionary["pronunciations"] as? [String: Any]{
if let audioPath = prounce["audioFile"] as? String {
print(audioPath)
}
}
}
print(response)
print(jsonData)
} else {
print(error)
print(NSString.init(data: data!, encoding: String.Encoding.utf8.rawValue))
}
}).resume()
The output I get is:
metadata = {
provider = "Oxford University Press";
};
results = (
{
id = maladroit;
language = en;
lexicalEntries = (
{
entries = (
{
etymologies = (
"late 17th century: French"
);
grammaticalFeatures = (
{
text = Positive;
type = Degree;
}
);
senses = (
{
definitions = (
"inefficient or inept; clumsy:"
);
examples = (
{
text = "both men are unhappy about the maladroit way the matter has been handled";
}
);
id = "m_en_gb0494140.001";
}
);
}
);
language = en;
lexicalCategory = Adjective;
pronunciations = (
{
audioFile = "http://audio.oxforddictionaries.com/en/mp3/maladroit_gb_1.mp3";
dialects = (
"British English"
);
phoneticNotation = IPA;
phoneticSpelling = "\U02ccmal\U0259\U02c8dr\U0254\U026at";
}
);
text = maladroit;
}
);
type = headword;
word = maladroit;
}
);
}
I want to get the URL called audioFile in the pronunciations. Any help is much appreciated.
If my guess is right, your output shown above lacks opening brace { at the top of the output.
(I'm also assuming the output is taken from your print(jsonData).)
Your jsonData is a Dictionary containing two values:
A dictionary value for "metadata"
An array value for "results"
So, you cannot retrieve a value for "pronunciations" directly from jsonData (or dictionary).
You may need to:
Retrieve the value for "results" from jsonData, it's an Array
Choose one element from the "results", it's a Dictionary
Retrieve the value for "lexicalEntries" from the result, it's an Array
Choose one element from the "lexicalEntries", it's a Dictionary
Retrieve the value for "pronunciations" from the lexicalEntry, it's an Array
Choose one element from the "pronunciations", it's a Dictionary
Here, you can access the values in each pronunciation Dictionary. In code, you need to do something like this:
if
let dictionary = jsonData as? [String: Any],
let results = dictionary["results"] as? [[String: Any]],
//You need to choose one from "results"
!results.isEmpty, case let result = results[0],
let lexicalEntries = result["lexicalEntries"] as? [[String: Any]],
//You need to choose one from "lexicalEntries"
!lexicalEntries.isEmpty, case let lexicalEntry = lexicalEntries[0],
let pronunciations = lexicalEntry["pronunciations"] as? [[String: Any]],
//You need to choose one from "lexicalEntries"
!pronunciations.isEmpty, case let pronunciation = pronunciations[0]
{
//Here you can use `pronunciation` as a Dictionary containing "audioFile" and some others...
if let audioPath = pronunciation["audioFile"] as? String {
print(audioPath)
}
}
(You can use let result = results.first instead of !results.isEmpty, case let result = results[0], if you always use the first element for arrays. Other two lines starting from !...isEmpty, case let... as well.)
You need to dig into the target element from the outermost element step by step.

Create new Realm List and add items

I make a API request with Alamofire , I then get a response in JSON format, I then parse the JSON into a NSDictionary to get to the data I want.
The data I get is four Arrays of different items.
I want to the create a new List in Realm to save these items in.
Here are my Realm Object Classes :
class ListOfDefinitions: Object {
let listOfItems = List<Item>()
}
and
class Item: Object {
dynamic var AverageCost = Int()
dynamic var Barcode = ""
dynamic var Description = ""
dynamic var InternalUnique = Int()
dynamic var LastCost = Int()
dynamic var LimitToMainRegionUnique = Int()
dynamic var Notes = ""
dynamic var StockCategoryUnique = Int()
dynamic var StockCode = ""
dynamic var StockGroupUnique = Int()
dynamic var UnitDescriptor = ""
}
Here is my code on how I handle the JSON response and where I want to save the data in my code.
var newItemInStockList : ListOfDefinitions! // declared in the class
let newItemInStock = Item()
.responseJSON { response in
switch response.result {
case .Success(let JSON):
// print("Success with JSON: \(JSON)")
let response = JSON as! NSDictionary
let responseParams = response.objectForKey("ResponseParameters") as! NSDictionary
//print(responseParams)
//let stockItemGroupList = responseParams.objectForKey("StockItemGroupList")
let stockItemList = responseParams.objectForKey("StockItemList") as! NSDictionary
//print(stockItemList)
let listofDefinitions = stockItemList.objectForKey("ListofDefinitions") as! NSArray
print(listofDefinitions.count)
for defJson in listofDefinitions {
print(defJson["Description"])
someString = defJson["Description"] as! String
print(someString)
// Because there are 4 arrays of items this for loop will be red 4 times, each time it is red I want o create a new list and add the items to the list
// This comment area is where I tried to create a new list and then .append the items in it, but it doesn't work.
// let newOne = ListOfDefinitions()
//
//
// try! realm.write{
//
// realm.add(newOne)
// }
// self.newItemInStock.AverageCost = defJson["AverageCost"] as! Int
// self.newItemInStock.Barcode = defJson["Barcode"] as! String
// self.newItemInStock.Description = defJson["Description"] as! String
// self.newItemInStock.InternalUnique = defJson["InternalUnique"] as! Int
// self.newItemInStock.LastCost = defJson["LastCost"] as! Int
// self.newItemInStock.LimitToMainRegionUnique = defJson["LimitToMainRegionUnique"] as! Int
// self.newItemInStock.Notes = defJson["Notes"] as! String
// self.newItemInStock.StockCategoryUnique = defJson["StockCategoryUnique"] as! Int
// self.newItemInStock.StockCode = defJson["StockCode"] as! String
// self.newItemInStock.StockGroupUnique = defJson["StockGroupUnique"] as! Int
// self.newItemInStock.UnitDescriptor = defJson["UnitDescriptor"] as! String
//
// try! realm.write{
//
// self.newItemInStockList.listOfItems.append(self.newItemInStock)
// }
}
case .Failure(let error):
print("Request failed with error: \(error)")
}
And here is what I get when I print the 4 Arrays
Looking at your sample code, I think the main issue happening here is that you're re-using the same self.newItemInStock instance for each object you're adding to the list.
It would be best to create a new Item object in the loop as you're going along and append that to the List object.
I recommend using a combination of AlamofireObjectMapper to handle all your JSON mapping (both ways) https://github.com/tristanhimmelman/AlamofireObjectMapper
and the ListTransform found in ObjectMapper+Realm https://github.com/Jakenberg/ObjectMapper-Realm
They're both available to be installed through cocoapods. Your code will look much cleaner and easier to maintain

Swift Convert type

I need to parse JSON file. Here is my getJSON func
public var shops: NSArray = []
public func getShop() {
let url = NSURL (string: "http://urltojson.com")
let data = NSData (contentsOfURL: url!)
shops = try! NSJSONSerialization.JSONObjectWithData(data!,options: .AllowFragments) as! NSArray
}
and here is how I get data from JSON
override func viewDidLoad() {
let test = shops ["Titile"] as? String
print(test)
}
The problem is when I run my code it shows me an error "Cannot convert value of type 'String' to expected argument type Int"
so, If I change ["Titile"] in let test = shops ["Titile"] for any Int, e.g. [8] let test = shops [8] as? String it works.
What did I do wrong?
looks like your variable "shops" is an array which takes an index as an integer instead of a dictionary which takes a key as a String.
//here shops is an array of strings
var shops:[String] = ["dog","cat","pig","cow"]
//shops[2] is equal to "pig"
//here shops is a dictionary of string keys and string values
var shops:[String:String] = ["animal1":"dog","animal2":"cat","animal3":"pig","animal4":"cow"]
//shops["animal2"] is equal to "cat"

JSON with Swift 2, Array Structure

I'm Having trouble with JSON and Swift 2.
I'm getting this Array from the server
[{"KidName":"Jacob","KidId":1,"GardenID":0},
{"KidName":"Sarah","KidId":2,"GardenID":0},
{"KidName":"Odel","KidId":3,"GardenID":0}]
I'm familiar with JSON and I know it's not the recommended way to get a JSON, since it's supposed to be something like
{"someArray":[{"KidName":"Jacob","KidId":1,"gardenID":0}, .....
So my first question is it possible to run over the first JSON I've post and get the KidName number without editing the JSON and Add to it a JSON OBJECT to hold the array ?
my second question is really with Swift 2, how can I get the KidName (after I've edited the JSON to have an holder for the array)?
this is my code... (please read the Notes I've added)
BTW, I'm familiar with SwiftyJSON as well...
// Method I've build to get the JSON from Server, the Data is the JSON
sendGetRequest { (response, data ) -> Void in
// need to convert data to String So I can add it an holder
if let str = NSString(data: data, encoding: NSUTF8StringEncoding) as? String {
/**
after editing the str, i'm Having a valid JSON, let's call it fixedJSON
*/
let fixedJSON = "{\"kidsArray\":\(dropLast)}"
// Now I'm converting it to data back again
let jsonTodata = fixedJSON.dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: false)!
// After Having the data, I need to convert it to JSON Format
do{
let dataToJson = try NSJSONSerialization.JSONObjectWithData(jsonTodata, options: []) as! [String:AnyObject]
//Here I'm getting the KidID
if let kidID = jsonSe["kidsArray"]![0]["KidId"]!!.integerValue {
print("kidID in first index is: \(kidID)\n")
}
//NOW trying to get the KidName which not working
if let kidname = jsonSe["kidsArray"]![0]["KidName"]!!.stringValue {
print("KidName is \(kidname)\n")
}
}
So as you can see, I'm not able to get the KidName.
Any Help Would be Appreciate.
You can use the following function to get the 'someArray' array and then use this getStringFromJSON function to get the 'KidName' value.
func getArrayFromJSON(data: NSDictionary, key: String) -> NSArray {
if let info = data[key] as? NSArray {
return info
}
else {
return []
}
}
let someArray = self.getArrayFromJSON(YourJSONArray as! NSDictionary, key: "someArray")
func getStringFromJSON(data: NSDictionary, key: String) -> String {
if let info = data[key] as? String {
return info
}
return ""
}
let KidName = self.getStringFromJSON(someArray as! NSDictionary, key: "KidName")
Hope this might be useful to you.