Swift2 - Using Alamofire 3 in nested JSON - ios

I have the following JSON:
{
"_embedded": {
"modifier_groups": [
{
"_embedded": {
"options": [
{
"id": "8kT9KTX7",
"name": "Perfect",
"open": false,
"pos_id": "8kT9KTX7",
"price_per_unit": 0
},
{
"id": "zRcEkcj8",
"name": "Overcooked",
"open": false,
"pos_id": "zRcEkcj8",
"price_per_unit": 0
}
]
},
"id": "eMiy4iR4",
"maximum": 1,
"minimum": 1,
"name": "Temperature",
"required": false
},
{
"_embedded": {
"options": [
{
"id": "E5cpac84",
"name": "Tomato",
"open": false,
"pos_id": "E5cpac84",
"price_per_unit": 0
},
{
"id": "GkiREiyL",
"name": "Cheese",
"open": false,
"pos_id": "GkiREiyL",
"price_per_unit": 100
}
]
},
"id": "kMT85Tay",
"maximum": null,
"minimum": 1,
"name": "Toppings",
"required": false
}
]
},
"count": 2,
"limit": 20
}
So there are modifier group names (e.g. "Temperature" and "Toppings"), and group options (e.g. "Perfect" and "Overcooked" for "Temperature" group).
What I am trying to do is build a [String] such as:
["Temperature - Perfect", "Temperature - Overcooked", "Toppings - Tomato", "Toppings - Cheese"]
What would be the quickest way to go about that?
Currently, I first extract the groups into a [String] using valueForKeyPath:
Alamofire.request(.GET, url, headers: headers, encoding: .JSON)
.responseJSON { response in
switch response.result {
case .Success(let JSON):
let jsonData = JSON as? NSDictionary
let groupNames = jsonData?.valueForKeyPath("_embedded.modifier_groups.name")
But how would I get from there to drilling deeper into the group options so that I append them into the [String]?
UPDATE
I tried this but it's not returning anything:
var mods = [String]()
let modGroups = jsonData?.valueForKeyPath("_embedded.modifier_groups")
if let modGroups = modGroups {
for modGroup in modGroups as! [AnyObject] {
let groupOptions = modGroups.valueForKeyPath("_embedded.options")
if let groupOptions = groupOptions {
for groupOption in groupOptions as! [AnyObject] {
mods.append("\(modGroup) - \(groupOption)")
}
}
}
}

Got it:
var mods = [String]()
let modGroups = jsonData?.valueForKeyPath("_embedded.modifier_groups") as? [NSDictionary]
if let modGroups = modGroups {
for modGroup in modGroups {
let groupOptions = modGroup.valueForKeyPath("_embedded.options") as? [NSDictionary]
if let groupOptions = groupOptions {
for groupOption in groupOptions {
mods.append("\(modGroup.valueForKey("name")!) - \(groupOption.valueForKey("name")!)")
}
}
}
}

Related

How to pass a value while parsing a nested JSON in Swift

I would like to pass the parent area Id to it's children areas while parsing the nested JSON structure as per the attached response, Here I would like to insert 'parentId' for each children which will link to it's immediate parent area,
{
"areas": [
{
"id": "271341877549072423",
"name": "Breeze Office Tower",
"children": [
{
"id": "271341877549072424",
"name": "100 flinders street",
"position": 0,
"children": []
},
{
"id": "271341877549130929",
"name": "100 flinders street",
"position": 1,
"children": []
},
{
"id": "271341877549072425",
"name": "100 Flinder Stree",
"position": 2,
"children": [
{
"id": "271341877549072426",
"name": "Büro",
"position": 0,
"children": [
{
"id": "271341877549072427",
"name": "Dachgeschoß",
"position": 0,
"children": []
}
]
}
]
},
{
"id": "271341877549130931",
"name": "100 Flinder Stree",
"position": 3,
"children": [
{
"id": "271341877549130933",
"name": "Büro",
"position": 0,
"children": [
{
"id": "271341877549130935",
"name": "Dachgeschoß",
"position": 0,
"children": []
}
]
}
]
}
]
}
]
}
My JSON Codable model struct looks like,
struct AreaModel: Decodable {
var areas: [NestedAreaModel]?
}
struct NestedAreaModel: Codable {
let areaId: String
let areaName: String
let children: [NestedAreaModel]
let hasChildren: Bool
var areaPosition: Int16?
var parentId: String?
var projectId: String?
enum CodingKeys: String, CodingKey {
case areaId = "id"
case areaName = "name"
case areaPosition = "position"
case children
}
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
self.areaId = try values.decode(String.self, forKey: .areaId)
self.children = try values.decode([NestedAreaModel].self, forKey: .children)
self.areaName = try values.decode(String.self, forKey: .areaName)
self.projectId = ORAUserDefaults.selectedProjectId()
self.areaPosition = try values.decodeIfPresent(Int16.self, forKey: .areaPosition)
if !self.children.isEmpty {
self.hasChildren = true
self.parentId = self.areaId
} else {
self.hasChildren = false
}
}
}
Here I am not able to set the parent Id, its pointing its own id always.
As I allready pointed out in the comments you would need to iterate over the decoded children and set their respective parentId to the current areaId.
One possible Solution would be:
init(from decoder: Decoder) throws {
let values = try decoder.container(keyedBy: CodingKeys.self)
self.areaId = try values.decode(String.self, forKey: .areaId)
// decode the children to a local var
var children = try values.decode([NestedAreaModel].self, forKey: .children)
self.areaName = try values.decode(String.self, forKey: .areaName)
self.projectId = ORAUserDefaults.selectedProjectId()
self.areaPosition = try values.decodeIfPresent(Int16.self, forKey: .areaPosition)
// if there are children loop over them and assign your id
if !children.isEmpty {
self.hasChildren = true
for (index, _ ) in children.enumerated(){
children[index].parentId = areaId
}
} else {
self.hasChildren = false
}
// assign to self
self.children = children
}
Tested with the given JSON:
let decoded = try JSONDecoder().decode(AreaModel.self, from: data)
print(decoded.areas?[0].areaId)
print(decoded.areas?[0].children[0].parentId)
Result:
Optional("271341877549072423")
Optional("271341877549072423")
Remarks:
Regarding your comment on complexity:
children[index].parentId = areaId runs exactly once per child no matter of the level deapth. So this function is (O)n.

How to parse json with Alamofire in Swift 5

I'm new in iOS programming language and I'm trying to fetch data from a WordPress JSON into a table view. I'm encountering the error:
value of type 'Any' has no subscripts
when I try to instantiate the object of the array.
Here is the JSON:
[
{
"id": 1352,
"date": "2019-10-16T09:30:39",
"date_gmt": "2019-10-16T09:30:39",
"guid": {
"rendered": "https://wepress.comm-it.it/ddjhgtr/"
},
"modified": "2019-10-16T13:23:41",
"modified_gmt": "2019-10-16T13:23:41",
"slug": "ddjhgtr",
"status": "publish",
"type": "post",
"link": "https://wepress.comm-it.it/ddjhgtr/",
"title": "ddjhgtr",
"content": "eryyreytyvggjggvhghhh",
"excerpt": "eryyreyty",
"author": 2,
"featured_media": {
"id": 1418,
"url": "https://wepress.comm-it.it/wp-content/uploads/2019/10/10-62.jpeg"
},
"comment_status": "open",
"ping_status": "open",
"sticky": false,
"template": "",
"format": "standard",
"meta": [],
"categories": [
{
"id": 1,
"name": "Uncategorized",
"description": ""
}
],
"tags": [],
"_links": {
"self": [
{
"href": "https://wepress.comm-it.it/wp-json/wp/v2/posts/1352"
}
],
"collection": [
{
"href": "https://wepress.comm-it.it/wp-json/wp/v2/posts"
}
],
"about": [
{
"href": "https://wepress.comm-it.it/wp-json/wp/v2/types/post"
}
],
"author": [
{
"embeddable": true,
"href": "https://wepress.comm-it.it/wp-json/wp/v2/users/2"
}
],
"replies": [
{
"embeddable": true,
"href": "https://wepress.comm-it.it/wp-json/wp/v2/comments?post=1352"
}
],
"version-history": [
{
"count": 3,
"href": "https://wepress.comm-it.it/wp-json/wp/v2/posts/1352/revisions"
}
],
"predecessor-version": [
{
"id": 1419,
"href": "https://wepress.comm-it.it/wp-json/wp/v2/posts/1352/revisions/1419"
}
],
"wp:featuredmedia": [
{
"embeddable": true,
"href": "https://wepress.comm-it.it/wp-json/wp/v2/media/1418"
}
],
"wp:attachment": [
{
"href": "https://wepress.comm-it.it/wp-json/wp/v2/media?parent=1352"
}
],
"wp:term": [
{
"taxonomy": "category",
"embeddable": true,
"href": "https://wepress.comm-it.it/wp-json/wp/v2/categories?post=1352"
},
{
"taxonomy": "post_tag",
"embeddable": true,
"href": "https://wepress.comm-it.it/wp-json/wp/v2/tags?post=1352"
},
{
"taxonomy": "difficulty-level-course",
"embeddable": true,
"href": "https://wepress.comm-it.it/wp-json/wp/v2/difficulty-level-course?post=1352"
},
{
"taxonomy": "category-course",
"embeddable": true,
"href": "https://wepress.comm-it.it/wp-json/wp/v2/category-course?post=1352"
},
{
"taxonomy": "location-course",
"embeddable": true,
"href": "https://wepress.comm-it.it/wp-json/wp/v2/location-course?post=1352"
},
{
"taxonomy": "duration-course",
"embeddable": true,
"href": "https://wepress.comm-it.it/wp-json/wp/v2/duration-course?post=1352"
}
],
"curies": [
{
"name": "wp",
"href": "https://api.w.org/{rel}",
"templated": true
}
]
}
},
....(many other News objects)
{
"id": 774,
"date": "2019-10-07T07:30:51",
"date_gmt": "2019-10-07T07:30:51",
"guid": {
"rendered": "https://wepress.comm-it.it/name-here/"
},
"modified": "2019-10-07T07:30:51",
"modified_gmt": "2019-10-07T07:30:51",
"slug": "name-here",
"status": "publish",
"type": "post",
"link": "https://wepress.comm-it.it/name-here/",
"title": "name here",
"content": "desc here",
"excerpt": "desc here",
"author": 2,
"featured_media": null,
"comment_status": "open",
"ping_status": "open",
"sticky": false,
"template": "",
"format": "standard",
"meta": [],
"categories": [
{
"id": 1,
"name": "Uncategorized",
"description": ""
}
],
"tags": [],
"_links": {
"self": [
{
"href": "https://wepress.comm-it.it/wp-json/wp/v2/posts/774"
}
],
"collection": [
{
"href": "https://wepress.comm-it.it/wp-json/wp/v2/posts"
}
],
"about": [
{
"href": "https://wepress.comm-it.it/wp-json/wp/v2/types/post"
}
],
"author": [
{
"embeddable": true,
"href": "https://wepress.comm-it.it/wp-json/wp/v2/users/2"
}
],
"replies": [
{
"embeddable": true,
"href": "https://wepress.comm-it.it/wp-json/wp/v2/comments?post=774"
}
],
"version-history": [
{
"count": 0,
"href": "https://wepress.comm-it.it/wp-json/wp/v2/posts/774/revisions"
}
],
"wp:attachment": [
{
"href": "https://wepress.comm-it.it/wp-json/wp/v2/media?parent=774"
}
],
"wp:term": [
{
"taxonomy": "category",
"embeddable": true,
"href": "https://wepress.comm-it.it/wp-json/wp/v2/categories?post=774"
},
{
"taxonomy": "post_tag",
"embeddable": true,
"href": "https://wepress.comm-it.it/wp-json/wp/v2/tags?post=774"
},
{
"taxonomy": "difficulty-level-course",
"embeddable": true,
"href": "https://wepress.comm-it.it/wp-json/wp/v2/difficulty-level-course?post=774"
},
{
"taxonomy": "category-course",
"embeddable": true,
"href": "https://wepress.comm-it.it/wp-json/wp/v2/category-course?post=774"
},
{
"taxonomy": "location-course",
"embeddable": true,
"href": "https://wepress.comm-it.it/wp-json/wp/v2/location-course?post=774"
},
{
"taxonomy": "duration-course",
"embeddable": true,
"href": "https://wepress.comm-it.it/wp-json/wp/v2/duration-course?post=774"
}
],
"curies": [
{
"name": "wp",
"href": "https://api.w.org/{rel}",
"templated": true
}
]
}
}
]
For the moment I'm interested to get just the image (featured_media), title and content of these "objects" and put them into the tableView. Indeed here are the structs that I created for them:
The news represents the struct contained into the JSON array
struct News {
public var id: Int
public var title: String
public var content: String
public var image: FeaturedMedia
}
struct FeaturedMedia {
public var id: Int
public var url: String
}
Here is the UITableViewCell class:
import UIKit
class NewsTableViewCell: UITableViewCell {
#IBOutlet weak var newsImage: UIImageView!
#IBOutlet weak var newsTitle: UILabel!
#IBOutlet weak var newsContent: UILabel!
override func awakeFromNib() {
super.awakeFromNib()
}
override func setSelected(_ selected: Bool, animated: Bool) {
super.setSelected(selected, animated: animated)
}
}
And here the UITableViewController class:
import UIKit
import Alamofire
import Alamofire_SwiftyJSON
import SwiftyJSON
class NewsTableViewController: UITableViewController {
var newsList: [News] = [News]()
func parseJsonNews() {
DispatchQueue.main.async {
Alamofire.request("link request", method: .get).responseJSON { (response) in
switch response.result {
case .success(let value):
let news = [value]
print(news) // here in console it prints correctly the json, starting with [<__NSArrayI 0x6000001a9e60 ....
for new in news {
let title = new["title"]
print(title)
}
print(newsss)
self.tableView.reloadData()
case.failure(let error):
print(error.localizedDescription)
}
})
}
}
override func viewDidLoad() {
super.viewDidLoad()
parseJsonNews()
}
// MARK: - Table view data source
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.newsList.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "newsCell", for: indexPath) as? NewsTableViewCell
// Configure the cell...
let imageUrl = URL.init(string: newsList[indexPath.row].featuredMedia.url)
cell?.newsTitle.text = self.newsList[indexPath.row].title
cell?.newsContent.text = self.newsList[indexPath.row].content
cell.newsImage.load(url: imageUrl!)
return cell!
}
}
extension UIImageView {
func load(url: URL) {
DispatchQueue.global().async { [weak self] in
if let data = try? Data(contentsOf: url) {
if let image = UIImage(data: data) {
DispatchQueue.main.async {
self?.image = image
}
}
}
}
}
}
Looking for a solution, I just found many ways to parse dictionary JSON, but in this case, it is an array so I modified the code as you read at parseJsonNews method but it doesn't work properly.
Would be grateful for any help.
You can decode with Codable like this:
let data = try? JSONDecoder().decode([DummyData].self, from: jsonData)
But first, all of your models must comform Codable protocol. For example:
struct DummyData: Codable {
let id: Int
let date, dateGmt: String
let modified, modifiedGmt, slug, status: String
let type: String
let link: String
let title, content, excerpt: String
let author: Int
let commentStatus, pingStatus: String
let sticky: Bool
let template, format: String
enum CodingKeys: String, CodingKey {
case id, date
case dateGmt = "date_gmt"
case modified
case modifiedGmt = "modified_gmt"
case slug, status, type, link, title, content, excerpt, author
case commentStatus = "comment_status"
case pingStatus = "ping_status"
case sticky, template, format
}
}
A couple of things:
The error value of type 'Any' has no subscripts, refers to your line let title = new["title"].
the response results from Alamofire in the .success enum returns a type of Any,
this is true to the whole array of data that you fetched.
The Any type in swift doesn't have any subscripts implementations (i.e: you cannot access vars inside it using the following syntax obj['MY_VAR_NAME'].
In order to access the title from your news object like so let title = new["title"], you have to first cast the objects to a dictionary, this could be done like this:
// ...
let news = [value]
print(news)
for new in news {
if let obj = new as? [String: Any] {
let title = obj["title"]
print(title)
}
}
// ...
In order to parse/use your custom structs, they must first comply to swift's Codable protocol.
/// Example show only News, but the same which be used for FeaturedMedia
struct News {
public var id: Int
public var title: String
public var content: String
public var image: FeaturedMedia?
init?(jsonString: String) {
guard let data = jsonString.data(using: .utf8) else {
return nil
}
guard let object = News(data: data) else {
return nil
}
self = object
}
init?(data: Data) {
guard let object = try? JSONDecoder().decode(News.self, from:
data) else {
return nil
}
self = object
}
// Optional, for custom key names (i.e: "image" instead of "featured_media"
private enum CodingKeys: String, CodingKey {
case id = "id"
case image = "featured_media"
// etc..
}
}
After you've done number 4, you can then init your objects like so:
// ...
let news = [value]
print(news)
for new in news {
if let obj = News(new) {
/// obj is now a News object
let title = obj.title
print(title)
}
}
// ...
if you are in pursuit for even more info (for example to init the whole array)
check out this
I hope I've cleared things up:)
func ComplainData() {
let semaphore = DispatchSemaphore(value: 0)
var request = URLRequest(url: URL(string: Constant.localBaseurl2 + "compID") !, timeoutInterval: Double.infinity)
request.httpMethod = "GET"
let task = URLSession.shared.dataTask(with: request) {
data,
response,
error in
if let response = response {
let nsHTTPResponse = response as!HTTPURLResponse
print(nsHTTPResponse)
}
if let error = error {
print("\(error)")
return
}
if let data = data {
DispatchQueue.main.async {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase //or any other Decoder\
do {
let jsonDecoder = JSONDecoder()
let memberRecord =
try jsonDecoder.decode(COMPLAINTSVC.GetComplaints.self, from: data)
print(memberRecord.message)
for detailData in memberRecord.message {
print(detailData)
}
} catch {
print(error.localizedDescription)
}
}
}
semaphore.signal()
}
task.resume()
semaphore.wait()
}

Filter Array of Any Object Based on Object Property

I am getting a response in this format
[
"1234": {
"startDate": "1536278400",
"endDate": "1536796800",
"playerRank": 1,
"performance": 100,
"isProfit": false,
"members": 1
},
"adityaKumar": {
"startDate": "1536364800",
"endDate": "1540080000",
"playerRank": 1,
"performance": 100,
"isProfit": false,
"members": 6
},
"madhu60": {
"startDate": "1539388800",
"endDate": "1539475200",
"playerRank": 1,
"performance": 100,
"isProfit": false,
"members": 2
}
]
This response is of type [string:Any]. I have to sort the data using the playerRank property. I want the output to be of type [string:Any].
How is this possible in Swift?
A dictionary is unsorted
From swift documentation
A dictionary stores associations between keys of the same type and values of the same type in an collection with no defined ordering.
However, you can use the .sorted method on your dictionary which will give you an array of dictionaries (arrays are sortable).
let sorted = dictionary.sorted(by: { ($0.value["playerRank"]) < ($1.value["playerRank"]) })
From your original example sorted might look like this
[
(
"key": "madhu60",
"value": {
"startDate": "1539388800",
"endDate": "1539475200",
"playerRank": 1,
"performance": 100,
"isProfit": false,
"members": 2
}
)
....
]
Once you parse that JSON, you'll get a dictionary of type [String: [String:Any]], i.e.
let data = [
"1234": [
"startDate": "1536278400",
"endDate": "1536796800",
"playerRank": 1,
"performance": 100,
"isProfit": false,
"members": 1
],
"adityaKumar": [
"startDate": "1536364800",
"endDate": "1540080000",
"playerRank": 1,
"performance": 100,
"isProfit": false,
"members": 6
],
"madhu60": [
"startDate": "1539388800",
"endDate": "1539475200",
"playerRank": 1,
"performance": 100,
"isProfit": false,
"members": 2
]
]
You can sort this dictionary simply using sorted method, i.e.
let sortedData = data.sorted {
if let rank1 = $0.value["playerRank"] as? Int, let rank2 = $1.value["playerRank"] as? Int
{
return rank1 < rank2
}
return false
}
And you are good to go.
I show you a complete answer here.
First convert the response text to a qualified JSON Data.
Second build a customized structure to decode JSON Data to.
Third sort the structure
Last to print back to original response text (You may use a string to have the print result.)
First:
let json = """
[
"1234": {
"startDate": "1536278400",
"endDate": "1536796800",
"playerRank": 4,
"performance": 100,
"isProfit": false,
"members": 1
},
"adityaKumar": {
"startDate": "1536364800",
"endDate": "1540080000",
"playerRank": 2,
"performance": 100,
"isProfit": false,
"members": 6
},
"madhu60": {
"startDate": "1539388800",
"endDate": "1539475200",
"playerRank": 1,
"performance": 100,
"isProfit": false,
"members": 2
}
]
""".replacingOccurrences(of: "[", with: "{").replacingOccurrences(of: "]", with: "}").data(using: .utf8)
second:
struct Response {
struct ResponseData {
let name : String
let startDate : String
let endDate : String
let performance : Int
let playerRank : Int
let isProfit : Bool
let members : Int
}
var responseData: [ResponseData]
init (responseData: [ResponseData] = []){
self.responseData = responseData
}
}
extension Response: Decodable {
struct DataKey: CodingKey {
var stringValue: String
init?(stringValue: String) {
self.stringValue = stringValue
}
var intValue: Int? { return nil }
init?(intValue: Int) { return nil}
static let startDate = DataKey(stringValue: "startDate")!
static let endDate = DataKey(stringValue: "endDate")!
static let performance = DataKey(stringValue: "performance")!
static let playerRank = DataKey(stringValue: "playerRank")!
static let isProfit = DataKey(stringValue: "isProfit")!
static let members = DataKey(stringValue: "members")!
}
public init(from decoder: Decoder) throws {
var responseData = [ResponseData]()
let container = try decoder.container(keyedBy: DataKey.self)
for key in container.allKeys {
let dataContainer = try container.nestedContainer(keyedBy: DataKey.self, forKey: key)
let startDate = try dataContainer.decode(String.self, forKey: .startDate)
let endDate = try dataContainer.decode(String.self, forKey: .endDate)
let performance = try dataContainer.decode(Int.self, forKey: .performance)
let playerRank = try dataContainer.decode(Int.self, forKey: .playerRank)
let isProfit = try dataContainer.decode(Bool.self, forKey: .isProfit)
let members = try dataContainer.decode(Int.self, forKey: .members)
let each = ResponseData.init(name: key.stringValue, startDate: startDate, endDate: endDate, performance: performance, playerRank: playerRank, isProfit: isProfit, members: members)
responseData.append(each)
}
self.init(responseData: responseData)
}
}
let decoder = JSONDecoder()
var decodedStore = try decoder.decode(Response.self, from: json!)
third:
decodedStore.responseData.sort{$0.playerRank < $1.playerRank }
last:
print ("[")
for each in decodedStore.responseData {
print ("""
\(each.name): {
"startDate": \(each.startDate),
"endDate": \(each.endDate),
"playerRank": \(each.playerRank),
"performance": \(each.performance),
"isProfit": \(each.isProfit),
"members": \(each.members)
}
""")
}
print ("]\n")

how can I filter student with all details whose "active" value is "true"?

I want to filter all details(school, city, name, active) whose active value is true. I have stored value of key "details"
let details = jsonRes[RequestResponses.Keys.details.rawValue] as? Dictionary< String, Any>
{
"details": {
"code": 235,
"school": "sp school",
"students": [
{ "name": "1student", "Active": false },
{ "name": "2student", "Active": true },
{ "name": "3student", "Active": true },
{ "name": "4student", "Active": false },
{ "name": "5student", "Active": false}
]
}
}
Expected Result
[
"details": {
"code": 235,
"school": "sp school",
"students": [
{ "name": "2student", "Active": true },
{ "name": "3student", "Active": true }
]
}
]
You can use filter
if let details = jsonRes[RequestResponses.Keys.details.rawValue] as? Dictionary< String, Any> ,
let detailDic = details["details"] as? [String:Any],
let students = detailDic["students"] as? [[String:Any]] {
let activeStudents = students.filter { (item) -> Bool in
guard let active = item["Active"] as? Bool else {return false}
return active
}
print(activeStudents)
}
or you can use shourthand
if let details = jsonRes[RequestResponses.Keys.details.rawValue] as? Dictionary< String, Any> ,
let detailDic = details["details"] as? [String:Any],
let students = detailDic["students"] as? [[String:Any]] {
let activeStudents = (details["students"] as?
[[String:Any]])?.filter{ $0["Active"] as? Bool == true}
print(activeStudents)
}
You start realising the true elegance of Swift once you start turning this into regular objects using Codable. This will let you do things as in the Playground:
import Cocoa
let jsonData = """
{
"details": {
"code": 235,
"school": "sp school",
"students": [
{ "name": "1student", "Active": false },
{ "name": "2student", "Active": true },
{ "name": "3student", "Active": true },
{ "name": "4student", "Active": false },
{ "name": "5student", "Active": false}
]
}
}
""".data(using: .utf8)!
struct Student : Codable {
let name: String
let active: Bool
enum CodingKeys: String, CodingKey {
case name
case active = "Active"
}
}
struct School : Codable {
let code: Int
let school: String
let students: [Student]
}
struct Details: Codable {
let details: School
}
do {
let det = try JSONDecoder().decode(Details.self, from: jsonData)
print(det)
let activeStudents = det.details.students.filter({(student)->Bool in student.active})
print(activeStudents)
} catch {
print(error)
}
This is obviously much easier to understand and Xcode can also support you much better during the process. The effort spent on the parser is minimal and easily recovered by the sheer elegance and clarity of the final filtering line.

Use Swifty JSON to parse array

This is my json to parse (example):
[
{
"id": 1,
"name": "Team name",
"shower": {
"id": 1,
"status": 1,
"startLocation": {
"id": 1,
"name": "abc 16"
}
}
},
{
"id": 2,
"name": "Team name",
"shower": {
"id": 2,
"status": 1,
"startLocation": {
"id": 1,
"name": "efg 16"
}
}
}
]
paste it this json viewer to view it easily.
as you can see, it is an array (of teams).
I need to get each team and do something with it.
After many attempts, I tried using SwiftyJSON, because I thought it will be easier. But, it did not worked for me.
This is what I tried:
let array = JSON(response)
// print each subJSON in array
for team in array.arrayValue {
print(team)
}
But the loop does not work. It does not go in to the loop at all.
Maybe it does not understand that my json is an array.
I can see the array object in the debugger. It looks like this:
How can I get these sub-JSONs?
Thanks.
I think you should use
let array = JSON(parseJSON: response)
instead of
let array = JSON(response)
SwiftyJSON contains methods to parse JSON string into a JSON object, check documentation
/**
Parses the JSON string into a JSON object
- parameter json: the JSON string
- returns: the created JSON object
*/
public init(parseJSON jsonString: String) {
if let data = jsonString.data(using: .utf8) {
self.init(data)
} else {
self.init(NSNull())
}
}
/**
Creates a JSON from JSON string
- parameter string: Normal json string like '{"a":"b"}'
- returns: The created JSON
*/
#available(*, deprecated: 3.2, message: "Use instead `init(parseJSON: )`")
public static func parse(json: String) -> JSON {
return json.data(using: String.Encoding.utf8)
.flatMap{ JSON(data: $0) } ?? JSON(NSNull())
}
or alternatively you can convert son string into son object like
Swift 3:
let dataFromString = response.data(using: .utf8)
let jsonArray = JSON(data: dataFromString!)
In the following example, I save team names in an array. I've tested it.
var names = [String]()
if let array = json.array {
for i in 0..<array.count {
let name = array[i]["name"]
names.append(name.stringValue)
}
}
print(names) // ["Team name", "Team name"]
Here is the answer for Swift 5. In My case data response is something like below :
[
{
"Name": "Some Type",
"Data": [
{
"ParentId": 111,
"Code": "Personal",
"SortOrder": 1,
"Name": "Personal",
"Id": 323
},
{
"ParentId": null,
"Code": "Work",
"SortOrder": 2,
"Name": "Work",
"Id": 324
}
],
"KeyType": "Integer"
},
{
"Name": "Phone Type",
"Data": [
{
"ParentId": null,
"Code": "Phone",
"SortOrder": 1,
"Name": "Phone",
"Id": 785
},
{
"ParentId": null,
"Code": "Cell",
"SortOrder": 2,
"Name": "Cell",
"Id": 786
},
{
"ParentId": null,
"Code": "Fax",
"SortOrder": 3,
"Name": "Fax",
"Id": 787
},
{
"ParentId": null,
"Code": "Home",
"SortOrder": 4,
"Name": "Home",
"Id": 788
},
{
"ParentId": null,
"Code": "Office",
"SortOrder": 5,
"Name": "Office",
"Id": 789
}
],
"KeyType": "Integer"
}
]
I was handled it with following code.
struct responseObjectClass:BaseModel {
var responsearray: [arrayData]? = nil
init(json: JSON) {
responsearray = json.arrayValue.map { arrayData(json: $0) }
}
}
struct arrayData:BaseModel {
let Name: String?
var DataValue: [DataLookup]? = nil
let KeyType: String?
init(json: JSON) {
Name = json["Name"].stringValue
DataValue = json["Data"].arrayValue.map { DataLookup(json: $0) }
KeyType = json["KeyType"].stringValue
}
}
struct DataLookup:BaseModel {
let ParentId: Any?
let Code: String?
let SortOrder: Int?
let Name: String?
let Id: Int?
init(json: JSON) {
ParentId = json["ParentId"]
Code = json["Code"].stringValue
SortOrder = json["SortOrder"].intValue
Name = json["Name"].stringValue
Id = json["Id"].intValue
}
}
BaseModel is Optional it's just used for init Json.
protocol BaseModel {
init(json: JSON)
}
Without SwiftyJSON
Below is the valid JSON
data.json File
[{
"id": 1,
"name": "Team name",
"shower": {
"id": 1,
"status": 1,
"startLocation": {
"id": 1,
"name": "abc 16"
}
}
}, {
"id": 2,
"name": "Team name",
"shower": {
"id": 2,
"status": 1,
"startLocation": {
"id": 1,
"name": "efg 16"
}
}
}]
Below is the code to read your json.
if let path = Bundle.main.path(forResource: "data", ofType: "json") {
let url = URL(fileURLWithPath: path)
do {
let data = try Data(contentsOf: url)
if let jsonArray = try JSONSerialization.jsonObject(with: data, options: .allowFragments) as? NSArray {
for (_, item) in jsonArray.enumerated() {
let itemDict = item as! NSDictionary
let id = itemDict["id"] as! Int
let name = itemDict["name"] as! String
let shower = itemDict["shower"] as! NSDictionary
let showerId = shower["id"] as! Int
let showerStatus = shower["status"] as! Int
let startLocation = shower["startLocation"] as! NSDictionary
let startLocationId = startLocation["id"] as! Int
let startLocationName = startLocation["name"] as! String
}
}
} catch {
print("Error: \(error.localizedDescription)")
}
}
this is what worked for me:
// Convert JSON to Array
func JSONToArray(_ json: String) -> Array<Any>? {
if let data = json.data(using: String.Encoding.utf8) {
do {
return try JSONSerialization.jsonObject(with: data, options: []) as? Array
} catch let error as NSError {
print(error)
}
}
return nil
}
After using this function i could loop trough the sub JSONs.
Thanks.

Resources