How to parse json with Alamofire in Swift 5 - ios

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()
}

Related

How to save multiple object in single UserDefaults

I've created NSObject class for "CurrentUser",
here, sign-up API will call & response data will insert into the "CurrentUser" model.
This is one type of global model, like in any screen user_logic_details will fetch, modify & save.
Example :
let loginuser : CurrentUser = CurrentUser.getLoginData()!
loginuser.email = "abc#gmail.com"
CurrentUser.saveLoginData(loginData: loginuser)
// working
let email = CurrentUser.getLoginData()?.email ?? "" // i'll get "abc#gmail.com"
in the above example if I write the below code then it shows nil data
let loginuser : CurrentUser = CurrentUser.getLoginData()!
loginuser.email = "abc#gmail.com"
CurrentUser.saveLoginData(loginData: loginuser)
// working
let email = CurrentUser.getLoginData()?.email ?? "" // i'll get "abc#gmail.com"
// not working
let roleName = CurrentUser.getLoginData()?.roles?.name ?? "" // showing get ""
I'm not able to find exact issues here,
Check below the code of how I use the model class, saving data into UserDefaults & retrieve data from the model.
class CurrentUser: NSObject, NSCoding, NSKeyedUnarchiverDelegate {
var email : String!
var roles : UserRole!
private var _isLoggedIn = false
required convenience init(coder aDecoder: NSCoder) {
self.init()
roles = aDecoder.decodeObject(forKey: "roles") as? UserRole
email = aDecoder.decodeObject(forKey: "email") as? String
}
func encode(with aCoder: NSCoder) {
aCoder.encode(email, forKey: "email")
aCoder.encode(roles, forKey: "roles")
}
class func getLoginData() -> CurrentUser? {
let userDefaults = UserDefaults.standard
if let UserData = userDefaults.object(forKey: "loginUser") {
guard let unarchivedFavorites = try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(UserData as! Data)
else {
return nil
}
return unarchivedFavorites as? CurrentUser
} else {
return CurrentUser()
}
}
class func saveLoginData(loginData: CurrentUser?) {
do {
let encodedData = try NSKeyedArchiver.archivedData(withRootObject: loginData as Any, requiringSecureCoding: false)
let userDefaults: UserDefaults = UserDefaults.standard
userDefaults.set(encodedData, forKey: "loginUser")
userDefaults.synchronize()
} catch {
print("Couldn't write file")
}
}
func unarchiver(_ unarchiver: NSKeyedUnarchiver, cannotDecodeObjectOfClassName name: String, originalClasses classNames: [String]) -> AnyClass? {
print("classNames", classNames)
return nil
}
}
class UserRole: NSObject, NSCoding {
var name : String!
required convenience init(coder aDecoder: NSCoder) {
self.init()
name = aDecoder.decodeObject(forKey: "name") as? String
}
func encode(with aCoder: NSCoder) {
aCoder.encode(name, forKey: "name")
}
}
The above model is for reference purposes.
I need to convert the below Response to the whole model class
{
"addressList": [
{
"addressLabel": "string",
"city": "string",
"country": "string",
"createdAt": "2022-03-16T12:10:41.148Z",
"homePhone": "string",
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"isDefault": true,
"isDeleted": true,
"state": "string",
"streetAddress": "string",
"updatedAt": "2022-03-16T12:10:41.148Z",
"version": 0,
"zip": "string"
}
],
"ageRange": "string",
"badges": [
{
"badge": {
"createdAt": "2022-03-16T12:10:41.148Z",
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"image": "string",
"isDeleted": true,
"label": "string",
"name": "string",
"status": "ACTIVE",
"updatedAt": "2022-03-16T12:10:41.148Z",
"users": [
null
],
"version": 0
},
"userBadgeId": {
"badgeId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"userId": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}
}
],
"classesTaken": 0,
"createdAt": "2022-03-16T12:10:41.148Z",
"deviceList": [
{
"createdAt": "2022-03-16T12:10:41.148Z",
"deviceId": "string",
"deviceName": "string",
"deviceType": "ANDROID",
"display": "string",
"fcmToken": "string",
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"isDeleted": true,
"osVersion": "string",
"updatedAt": "2022-03-16T12:10:41.148Z",
"version": 0
}
],
"displayName": "string",
"displayPic": "string",
"dob": "2022-03-16T12:10:41.148Z",
"email": "string",
"experience": 0,
"firebaseToken": "string",
"firebaseUserId": "string",
"followerCount0l": 0,
"fullName": "string",
"gender": "FEMALE",
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"isDeleted": true,
"lastLoginTime": "2022-03-16T12:10:41.148Z",
"location": "string",
"phoneNumber": "string",
"roles": [
{
"createdAt": "2022-03-16T12:10:41.148Z",
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"isDeleted": true,
"name": "ROLE_ADMIN",
"updatedAt": "2022-03-16T12:10:41.148Z",
"version": 0
}
],
"rubyBalance": 0,
"signupDateTime": "2022-03-16T12:10:41.148Z",
"signupSource": {
"buildNumber": "string",
"deviceId": "string",
"deviceType": "ANDROID",
"osVersion": "string"
},
"status": "ACTIVE",
"studentsTaught": 0,
"updatedAt": "2022-03-16T12:10:41.148Z",
"userInterest": [
"string"
],
"userName": "string",
"version": 0
}
If anyone has a better way to do it or a better solution, please answer it.
Your code reads like Objective-C from a few years ago. Something more modern and Swift-like might be:
#propertyWrapper
struct UserDefaultsCodableWrapper<T: Codable> {
let key: String
let defaultValue: T?
init(_ key: String, defaultValue: T?) {
self.key = key
self.defaultValue = defaultValue
}
var wrappedValue: T? {
get {
guard let data = UserDefaults.standard.data(forKey: key),
let decoded = try? JSONDecoder().decode(T.self, from: data)
else { return defaultValue }
return decoded
}
set {
let encoded = try? JSONEncoder().encode(newValue)
UserDefaults.standard.set(encoded, forKey: key)
}
}
}
struct User: Codable {
var email: String // TODO make a codable "Email" struct type that validates its input, no invalid strings allowed
var roles: [UserRole]
}
struct UserRole: Codable {
var name: String
}
class AppState {
#UserDefaultsCodableWrapper("CurrentUser", defaultValue: nil)
var currentUser: User?
}
class ViewController: UIViewController {
let state = AppState()
override func viewDidLoad() {
super.viewDidLoad()
print(String(describing: state.currentUser)) // nil
state.currentUser = User(email: "foo#bar.baz", roles: [.init(name: "Foo"), .init(name: "Bar")])
print(String(describing: state.currentUser)) // Optional(User...
Also worth pointing you to app.quicktype.io which can generate you codable models from your json.

Swift Nested jSON Decode

I have problem with nested json decoding. Im getting no error but response is empty { }. Down bellow is my sample json and struct.
{
"categories": [
{
"ID": 130,
"data": [
{
"en": [
{
"title": "test"
}
],
"fr": [
{
"title": "teste"
}
]
}
],
"lifts": [
{
"ID": 104,
"data": [
{
"en": [
{
"code": "test",
"title": "test"
}
],
"fr": [
{
"code": "test",
"title": "test"
}
]
}
]
},
{
"ID": 105,
"data": [
{
"en": [
{
"code": "test",
"title": "test"
}
],
"fr": [
{
"code": "test",
"title": "test"
}
]
}
]
}
]
}
And this is my struct
struct jsonResponse : Codable {
struct Categories : Codable {
let id : Int
let data : [LanguageData]
let lifts : [Lifts]
struct LanguageData : Codable {
let en, fr : [Data]
struct Data : Codable {
let code : String?
let title : String?
}
}
struct LiftsData : Codable {
let id : Int
let data : [LanguageData]
}
}
Then Im trying to decode JSON like this:
let lifts = try JSONDecoder().decode(jsonResponse.self, from: data)
But when I print lifts, i see only empty {}. Also no error message during decoding, so have no idea what can be wrong.

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.

Swift2 - Using Alamofire 3 in nested JSON

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")!)")
}
}
}
}

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