I have a UITableViewlike this:
and a tableView data fetch from JSON with Alamofire and SwiftyJson:
JSON Data:
{
"timeline": {
"Milan": {
"place": [
{
"name": "Place 1",
"kind": "historic",
},
{
"name": "Place 2",
"kind": "historic",
},
{
"name": "Place 3",
"kind": "historic",
},
{
"name": "Place 4",
"kind": "historic",
}
]
},
"Paris": {
"place": [
{
"name": "Place 1",
"kind": "historic",
},
{
"name": "Place 2",
"kind": "historic",
},
{
"name": "Place 3",
"kind": "historic",
}
]
}
}
}
my question is how I separate city place in one array that inherit from class called Place and put data in tableView like above image.
struct Place {
var name = ""
var type = ""
}
I wrote some code in Alamofire .success but this is not enough:
if let jsonData = response.result.value {
let json = JSON(jsonData)
if let items = json["timeline"].dictionary {
for (key,subJson):(String, JSON) in items {
self.cityName.append(key)
...
}
}
}
https://gist.github.com/himanshu-benzatine/75fa44dc0e3f2752973a79984b182d70
Check this link and if any problem then tell me. i give you basic idea of how to create tableview with header and selection.
Happy coding
Related
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.
I am trying to read from a local JSON file and populate my tableView with cells to be retrieved from the Decoder. Since my table view was still empty, I added a breakpoint on the JSONDecoder.decode line to see what is going on. I get this error, even though I made sure that my naming convention is the same in both my structs and JSON file. Is there something I am missing here.
Since my naming convention was consistent across the files, at first I did not try add CodingKeys enum inside my structs. After a few failed runs I added that in but it feels kind of obsolete.
Where I run the decoder:
let fileName = "settings"
...
if let url = Bundle.main.url(forResource: fileName, withExtension: "json") {
do {
let data = try Data(contentsOf: url)
let list = try JSONDecoder().decode(SettingsPayload.self, from: data)
completion(list.sections)
} catch {
completion(nil)
}
}
fileprivate struct SettingsPayload: Decodable {
let sections: [Section]
}
My structs that will be used to store the data when retrieved:
struct Item: Decodable {
let title: String
let type: String
let url: String
private enum CodingKeys: String, CodingKey {
case title = "title"
case type = "type"
case url = "url"
}
}
struct Section: Decodable {
let title: String
let items: [Item]
private enum CodingKeys: String, CodingKey {
case title = "title"
case items = "items"
}
}
and my .json file:
{
"sections": [{
"section": {
"title": "Main Settings",
"items": [{
"item": {
"title": "Notifications",
"type": "notification",
"url": ""
},
"item": {
"title": "Log Out",
"type": "",
"url": ""
}
}]
},
"section": {
"title": "Feedback",
"items": [{
"item": {
"title": "Contact Us",
"type": "email",
"url": ""
},
"item": {
"title": "Rate on App Store",
"type": "webView",
"url": "https://www.apple.com/uk/ios/app-store/"
}
}]
},
"section": {
"title": "About",
"items": [{
"item": {
"title": "Terms of Service",
"type": "webView",
"url": ""
},
"item": {
"title": "Privacy Policy",
"type": "webView",
"url": "https://www.apple.com/uk/ios/app-store/"
},
"item": {
"title": "Version Info",
"type": "webView",
"url": ""
}
}]
}
}]
}
This is the error message I get:
- debugDescription : "No value associated with key CodingKeys(stringValue: \"title\", intValue: nil) (\"title\")."
I think the problem is that the decoder expects an Array of the item Section, but your json has an Array of dictionaries with a key "section" and an item Section in there.
Maybe try to modify your json like this:
{
"sections": [{
"title": "Main Settings",
"items": [ {
"title": "Notifications",
"type": "notification",
"url": ""
}, {
"title": "Log Out",
"type": "",
"url": ""
}]}, {
"title": "Feedback",
"items": [{
"title": "Contact Us",
"type": "email",
"url": ""
},{
"title": "Rate on App Store",
"type": "webView",
"url": "https://www.apple.com/uk/ios/app-store/"
}]}, {
"title": "About",
"items": [{
"title": "Terms of Service",
"type": "webView",
"url": ""
}, {
"title": "Privacy Policy",
"type": "webView",
"url": "https://www.apple.com/uk/ios/app-store/"
}, {
"title": "Version Info",
"type": "webView",
"url": ""
}
]}]
}
Update
JSONDecoder doesn't look for the name of your decodable struct in the JSON, it only looks for the name of the properties.
To parse a JSON, as I found also on the web, I usually used this kind of code:
guard let results = receivedUserJSON["results"] as? [String: Any] else {
print("Error interpreting results")
return
}
This time I have a problem, because it seems to end in the else of this guard let. The JSON has the following structure:
{
"results": [{
"gender": "female",
"name": {
"title": "mrs",
"first": "silene",
"last": "almeida"
},
"location": {
"street": "2594 rua maranhão ",
"city": "pouso alegre",
"state": "distrito federal",
"postcode": 20447,
"coordinates": {
"latitude": "-70.0198",
"longitude": "123.6577"
},
"timezone": {
"offset": "+4:30",
"description": "Kabul"
}
},
"email": "silene.almeida#example.com",
"login": {
"uuid": "d06a46b3-1c00-42be-b8fc-d271bf901f7d",
"username": "silversnake251",
"password": "ventura",
"salt": "UcckU6RG",
"md5": "7c8c4129587c61da01ca7cf4f88353c5",
"sha1": "6cbf7ec377ff4ebad5a392ec487343bf613858ef",
"sha256": "8dedf3649fb833a1936b8885627b86c6cf02062eb74f727b2cbd674a30f73e75"
},
"dob": {
"date": "1969-07-13T00:58:26Z",
"age": 49
},
"registered": {
"date": "2003-09-28T09:44:56Z",
"age": 15
},
"phone": "(95) 0094-8716",
"cell": "(20) 1014-3529",
"id": {
"name": "",
"value": null
},
"picture": {
"large": "https://randomuser.me/api/portraits/women/66.jpg",
"medium": "https://randomuser.me/api/portraits/med/women/66.jpg",
"thumbnail": "https://randomuser.me/api/portraits/thumb/women/66.jpg"
},
"nat": "BR"
}],
"info": {
"seed": "dd971cddf636d2d7",
"results": 1,
"page": 1,
"version": "1.2"
}
}
What should I do to properly parse this JSON? I would prefer not to go for the Codable solution because I don't need all of these values.
PS: I know the json is correct because I tried and printed it with:
if let JSONString = String(data: responseData, encoding: String.Encoding.utf8) {
print(JSONString)
}
results is an array
guard let results = receivedUserJSON["results"] as? [[String:Any]] else {
print("Error interpreting results")
return
}
I see no value for it to be an array as it contains 1 element so you may think to alter this json
current strucsture
{
"results": [{}],
"info": {
"seed": "dd971cddf636d2d7",
"results": 1,
"page": 1,
"version": "1.2"
}
}
you may alter it to
{
"results": {},
"info": {
"seed": "dd971cddf636d2d7",
"results": 1,
"page": 1,
"version": "1.2"
}
}
I get JSON data as follows:
{
"upcoming": [
{
"id": "17",
"date": "2018/04/23 13:25",
"title": "Team A"
},
{
"id": "20",
"date": "2018/04/23 13:25",
"title": "Team B"
},
{
"id": "10",
"date": "2019/06/16 21:45",
"title": "Team c"
}
]
}
I need to show a table view with sections according to the date key. How can I populate a table view with sections and rows accordingly?
Swift comes with the amazing Codable protocol built in, you should read up on it. This will easily allow you to understand what is going on in this Playground:
import Cocoa
let jsonData = """
{
"upcoming": [
{
"id": "17",
"date": "2018/04/23 13:25",
"title": "Team A"
},
{
"id": "20",
"date": "2018/04/23 13:25",
"title": "Team B"
},
{
"id": "10",
"date": "2019/06/16 21:45",
"title": "Team c"
}
]
}
""".data(using: .utf8)!
struct Match : Codable {
let id: String
let date: String
let title: String
}
struct Matches : Codable {
let upcoming: [Match]
}
do {
let matches = try JSONDecoder().decode(Matches.self, from:jsonData)
print(matches.upcoming.count)
} catch {
print(error)
}
Now your matches.upcoming are your model array and using it as a TableView datasource is really straightforward.
I have a JSON file I call from the internet
{
"Editions": [
{
"Version": "November",
"Articles": [
{
"title": "hello",
"subheading": "Article 1",
"content": "stuff",
"author": "John Smith",
"authorDescription": "Author",
"image": "pic1.jpg"
},
{
"title": "article2",
"subheading": "Article 2",
"content": "stuff2",
"author": "first name last name",
"authorDescription": "Author",
"image": ""
},
{
"title": "article3",
"subheading": "Article 3",
"content": "stuff3",
"author": "Callum West",
"authorDescription": "Author",
"image": ""
},
{
"title": "Article 4",
"subheading": "Article 4",
"content": "stuff 4",
"author": "tom C",
"authorDescription": "Author",
"image": ""
}
]
},
{
"Version": "October",
"Articles": [
{
"title": "article 1",
"subheading": "Article1",
"content": "stuff1.1",
"author": "Tom",
"authorDescription": "Author",
"image": ""
},
{
"title": "article2",
"subheading": "Article 2",
"content": "stuff2.1",
"author": "Sam",
"authorDescription": "Author",
"image": ""
},
{
"title": "article3",
"subheading": "Article 3",
"content": "stuff3.1",
"author": "TomC",
"authorDescription": "Author and Editor",
"image": ""
},
{
"title": "article 4",
"subheading": "Article 4",
"content": "stuff4.1",
"author": "brad name",
"authorDescription": "Author",
"image": ""
},
{
"title": "article5",
"subheading": "Article 5",
"content": "stuff5.1",
"author": "chris evuan",
"authorDescription": "Author",
"image": ""
},
{
"title": "article6",
"subheading": "Article 6",
"content": "stuff6.1",
"author": "Jo",
"authorDescription": "Author",
"image": ""
},
{
"title": "article7",
"subheading": "Article7",
"content": "stuff7.1",
"author": "Tom Hall",
"authorDescription": "Author",
"image": ""
}
]
}
]
}
On my first view controller I pull out the editions version with the following code
func loaddata(){
Alamofire.request(.GET, "my JSON url")
.responseJSON { response in
//get json from response data
//print (response.data)
let json = JSON(data: response.data!)
//print(json)
//for loop over json and write all article titles articles array
for (key, subJson) in json["Editions"] {
let version = subJson["Version"].string
let stuff = Edition(Version: version!)
// print(stuff)
self.editions.append(stuff!)
}
// let x = self.editions[0].Version
// print ("\(x)")
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
}
}
}
I then use a segue to pass the version clicked into my second view controller
Once done this on my second view controller I can print out the string of the version, in the view did load
let worked = pleasework as String!
//print("\(worked)")
I then want to use this string to traverse the Json and pull out the correlating content
So I call the function and pass it through
loaddata("\(worked)")
Here is the load data function
func loaddata(verionTitle: String){
Alamofire.request(.GET, "my JSON url")
.responseJSON { response in
//get json from response data
let json = JSON(data: response.data!)
// print(json)
//for loop over json and write all article titles articles array
// print("\(verionTitle)")
for (key, subJson) in json["Editions"][0]["Articles"]{
// print(subJson)
//let versionmuted = version as String!
//print("\(version)")
//if verionTitle =
//if version == verionTitle{
//print("The month is \(version)")
let author = subJson["title"].string
//print("\(author)")
let subheading = subJson["subheading"].string
let content = subJson["content"].string
let Author = subJson["author"].string
//let image = subJson["image"].string
let stuff = Article(name: author!, subheading: subheading!, content: content!, author: Author!)
self.articles.append(stuff!)
}
//end iff
//if let content = subJson["content"].string {
// self.Content.append(content)
//}
//end for
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
}
}
}
I am struggling with looping over the JSON and only pulling out the articles relevant to the version, I am using the swiftyjson library
The current function obviously prints out all the articles in Editions[0]
However can i loop through all of Editions and use the string to only print Articles under that version
For example
for (key, subJson) in json["Editions"][0]["Version"] = veriontitle??{
//do stuff
}
Hope I explained myself well, hoping you can help
func loaddata(verionTitle: String){
Alamofire.request(.GET, "your json")
.responseJSON { response in
//get json from response data
let json = JSON(data: response.data!)
//print(json)
//start looping function pass through identifer from month, i've set October to 1 and so forth
func looping(Number: Int){
for (key, subJson) in json["Editions"][Number]["Articles"]{
let author = subJson["title"].string
//print("\(author)")
let subheading = subJson["subheading"].string
let content = subJson["content"].string
let Author = subJson["author"].string
let Image = subJson["image"].string
let stuff = Article(name: author!, subheading: subheading!, content: content!, author: Author!, image: Image!)
self.articles.append(stuff!)
//end loop
}
//end looping
}
//Get Version Titles, here is where I assign months to a number releavnt to where they are in the json
switch verionTitle{
case "November":
looping(0)
case "October":
looping(1)
case "December":
looping(2)
default:
print("no month")
}
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
}
}
}