I am having an issue dynamically populating the date associated with each section.
The value of grouped is currently derived from grouping the raw JSON data by routeid
You can see that I am currently using a static value for date as a test, while $0.key represent the value of myID dynamically.
How can also add the dynamic value of myDate intead of "test"?
Variables:
var sections = [mySections]()
var structure = [myStructure]()
Currently this is true
sections.map(\.id) == [8,4]
At the end this must be true as well
sections.map(\.date) == [2021-01-20, 2021-01-18]
Decoding:
do {
let decoder = JSONDecoder()
let res = try decoder.decode([myStructure].self, from: data)
let grouped = Dictionary(grouping: res, by: { $0.myID })
_ = grouped.keys.sorted()
sections = grouped.map { mySections(date: "test", id: $0.key, items: $0.value) }
.sorted { $0.id > $1.id }
}
Struct:
struct mySections {
let date : String
let id : Int
var items : [myStructure]
}
struct myStructure: Decodable {
let name: String
let type: String
let myID: Int
let myDate: String
}
Example JSON:
[
{
"name": "Jeff",
"type": "large",
"myID": 8,
"myDate": "2021-01-20"
},
{
"name": "Jessica",
"type": "small",
"myID": 4,
"myDate": "2021-01-18"
},
{
"name": "Beth",
"type": "medium",
"myID": 4,
"myDate": "2021-01-18"
}
]
So, you want it grouped by both id number and date string? I would create a Hashable structure for those two values.
struct Section {
let date: String
let id: Int
var items: [Item]
}
extension Section {
struct Header: Hashable {
let date: String
let id: Int
}
}
struct Item: Decodable {
let name: String
let type: String
let myID: Int
let myDate: String
}
And
let decoder = JSONDecoder()
let res = try decoder.decode([Item].self, from: data)
let sections = Dictionary(grouping: res) { Section.Header(date: $0.myDate, id: $0.myID) }
.sorted { $0.key.id > $1.key.id }
.map { Section(date: $0.key.date, id: $0.key.id, items: $0.value) }
Related
How can I sort the following sections made dynamically from the datestamp field? For example the sections called 2021-11-3 needs to be displayed in the tableview above 2021-11-2
datestamp format: 2021-11-3
var sections = [mySections]()
var structure = [myStructure]()
private func fetchJSON() {
guard let url = URL(string: "test.com")
else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = "id=\1".data(using: .utf8)
URLSession.shared.dataTask(with: request) { data, _, error in
guard let data = data else { return }
do {
let decoder = JSONDecoder()
self.structure.sort { $0. datestamp > $1.datestamp }
let res = try decoder.decode([myStructure].self, from: data)
let grouped = Dictionary(grouping: res, by: { $0. datestamp })
let keys = grouped.keys.sorted()
self.sections = keys.map({mySections(date: $0, items: grouped[$0]!
)})
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
catch {
print(error)
}
}.resume()
}
I have tried doing the following under self.sections but it does not do anything:
self.sections.sorted { $0.date > $1.date }
Struct:
struct mySections {
let date : String
var items : [myStructure]
}
struct myStructure: Decodable {
let recordid: Int
let testname: Int
let datestamp: String
}
Example of Data:
[
{
"recordid": 1,
"testname": "Jen",
"datestamp": "2021-11-3"
},
{
"recordid": 1,
"testname": "Jake",
"datestamp": "2021-11-2"
}
]
Right now, you're sorting structure based on dateTimeStamp, which doesn't do anything, because structure doesn't have any data in it.
In my playground example, like your code, I used Dictionary(grouped:). Then, I map those results to mySections and sort by date.
The result of the print at the end is:
["2021-11-4", "2021-11-3", "2021-11-2"]
let data = """
[
{
"recordid": 1,
"testname": "Jake",
"datestamp": "2021-11-2"
},
{
"recordid": 2,
"testname": "Jen",
"datestamp": "2021-11-3"
},
{
"recordid": 3,
"testname": "Bob",
"datestamp": "2021-11-3"
},
{
"recordid": 4,
"testname": "Bill",
"datestamp": "2021-11-4"
}
]
""".data(using: .utf8)!
struct mySections {
let date : String
var items : [myStructure]
}
struct myStructure: Decodable {
let recordid: Int
let testname: String
let datestamp: String
}
var sections = [mySections]()
var structure = [myStructure]()
do {
let decoder = JSONDecoder()
let res = try decoder.decode([myStructure].self, from: data)
let grouped = Dictionary(grouping: res, by: { $0.datestamp })
sections = grouped.map { mySections(date: $0.key, items: $0.value) }
.sorted { $0.date > $1.date }
print(sections.map(\.date))
} catch {
print(error)
}
It's probably worth noting that right now your dates are stored in a uniform format that works well for string-based sorting. However, if your endpoint returned dates that weren't uniformly-formatted (like 2021-11-04 and 2021-11-3) you would want to convert to an actual Date first and sort based on that.
I imported json manually (json file). I get this error when I decode the json.
The data couldn’t be read because it isn’t in the correct format.
where am I doing wrong? Is my model wrong? Is my reference wrong?
JSON:
{
"allQuiz": [
{
"title":"Ağustos Test 1",
"test": [
{
"id": 1,
"question":"Şekle göre aşağıdakiler hangisi doğrudur",
"isQuestionImage": true,
"isSectionImage": false,
"imageName":"1.png",
"sections": {
"A":"2 numaralı aracın öncelikle geçmesi",
"B":"1 numaralı aracın hızını arttırarak kavşağa girmesi",
"C":"2 numaralı aracın 3 numaralı aracın geçmesini beklemesi",
"D":"3 numaralı aracın 2 numaralı aracı ikaz ederek durdurması"
},
"correct": "A"
}
]
}
]
}
Model:
struct QuizContainer: Codable {
var allQuiz: [Quiz]
}
struct Quiz: Codable {
var title: String
var test: [Test]
}
enum TestSectionType: String, Codable, Hashable {
case A = "A"
case B = "B"
case C = "C"
case D = "D"
}
struct Test: Codable {
var id: Int
var question: String
var isQuestionImage: Bool
var isSectionImage: Bool
var imageName: String
var sections: [TestSectionType.RawValue : String]
var correct: String
}
JSON Decode:
func getQuizQuestion() {
let databaseReference = Database.database().reference().child("allQuiz")
databaseReference.observeSingleEvent(of: .value) { snapshot in
do {
let foo = try FirebaseDecoder().decode(QuizContainer.self, from: snapshot.value ?? "")
print(foo)
} catch {
print(error.localizedDescription)
}
}
}
Could the getQuizQuestion function be faulty?
I solved my problem.
I don't need reference.
func getQuizQuestion() {
let databaseReference = Database.database().reference() // here
databaseReference.observeSingleEvent(of: .value) { snapshot in
do {
let foo = try FirebaseDecoder().decode(QuizContainer.self, from: snapshot.value ?? "")
print(foo)
} catch {
print(error.localizedDescription)
}
}
}
currently my project uses structure MVVM. I have a Pagination JSON like this:
{
"has_next": false,
"next_params": {
"limit": 10,
"offset": 10
},
"results": [
{ "id": 1, "name": "A Place" },
{ "id": 2, "name": "A Night" }
]
}
This is my ViewModel:
class LifeStoryViewModel: ObservableObject {
#Published var lifes: [Life] = []
var has_next: Bool = true
var next_params: [String:Any] = [:]
var fetching: Bool = false
func fetchLifeStories () {
let url = URL(string: STRINGURL)
URLSession.shared.dataTask(with: url!) { (data, res, err) in
DispatchQueue.main.async {
let vvv = try! JSONDecoder().decode(LifeStories.self, from: data!)
self.lifes = vvv.results
}
}.resume()
}
}
As you guys see, I have a model LifeStories:
struct Life: Identifiable, Codable {
var id: Int
var name: String
var description: String
var thumbnail: String
}
struct LifeStories: Codable {
var has_next: Bool
var results: [Life]
}
Can I remove LifeStories model and handle it inside the LifeStoryViewModel instead? How can I do this because I think LifeStories model is not necessary.
You can use a generic type for paginated responses. Here's an example from the app I'm working on:
struct PaginatedResponse<Element: Codable>: Codable {
var count: Int
var next: String?
var previous: String?
var results: [Element]
}
I'm new to Swift, I'm trying to have this JSON
{
"title": "newSurvey",
"pages": [
{
"questions": [
{
"title": "Untitled Question",
"type": "QUESTION_SLIDER",
"content": {
"min": "1",
"max": "10",
"step": "3",
"defaultValue": "5"
}
},
{
"title": "asdasddfdf",
"type": "QUESTION_TEXT",
"choices": ["choice1","choice2"]
}
]
}
]}
I'm suffering from converting subclass to JSON
I thought about divide my code to three objects then add them to the final
string from jsonEncoder
so that's what I did
public class Question : Encodable {
var title : String?
var description: String?
init(_ title: String , _ desc: String) {
self.title = title
self.description = desc
}}
struct Questions : Encodable{
var questions : [Question]
}
class Create : Encodable {
var title : String?
var pages : [Questions] = []
init(_ title:String , _ q : Questions) {
self.title = title
self.pages.append(q)
}
func postData () -> String{
let jsonEncoder = JSONEncoder()
do {
jsonEncoder.outputFormatting = .prettyPrinted
let jsonData = try jsonEncoder.encode([self])
let json = String(data: jsonData ,encoding: .utf8)
print( json!)
return json!
}
catch {
print("Error")
}
return ""
}
class Content :Encodable {
init(_ min : Int , _ max : Int){
self.min = min
self.max = max
}
var min : Int?
var max : Int?
var minLabel : String?
var maxLabel : String?
var defaultValue : Int?
var step : Int?
}
class SliderQuestion :Question {
let TYPE = "SHORT TEXT"
var content = Content(0,2)
init(_ title: String, _ desc: String,_ content : Content ) {
self.content = content
super.init(title, desc)
}
}
I'm sorry for the long code but I want to clarify my idea, is there any way to have the subclass converted to JSON?
I am new to iOS, and want to parse the JSON using Decodable but cant get through this, how should I work this out?
The view controller where I am trying to parse the data
class ViewController: UIViewController {
var servers = [Server]()
let apiUrl = "https://someurl/api/dashboard"
override func viewDidLoad() {
super.viewDidLoad()
guard let url = URL(string: self.apiUrl) else { return }
getDataFrom(url)
}
fileprivate func getDataFrom(_ url: URL) {
URLSession.shared.dataTask(with: url){ (data, response, error) in
guard let data = data else { return }
do {
let apiResponse = try JSONDecoder().decode(Server.self, from: data)
print(apiResponse)
} catch let jsonError {
print(jsonError)
}
}.resume()
}
}
The Server.swift file where I am confirming to the decodable protocol
struct Server: Decodable {
let current_page: Int
let data: [ServerData]
let first_page_url: String
}
struct ServerData: Decodable {
let hostname: String
let ipaddress: String
let customer: [Customer]
let latest_value: [LatestValue]
}
struct Customer: Decodable {
let name: String
let contact_person :String
let email: String
}
struct LatestValue: Decodable {
let systemuptime: String
let memtotal: Float
let memfree: Double
let loadaverage: Float
}
No value associated with key CodingKeys I get this error,
The response from the server
{
"servers": {
"current_page": 1,
"data": [
{
"hostname": "johndoes",
"ipaddress": "10.0.2.99",
"id": 7,
"latest_value_id": 1130238,
"customers": [
{
"name": "Jane Doe",
"contact_person": "John Doe",
"id": 2,
"email": "john.#example.com",
"pivot": {
"server_id": 7,
"customer_id": 2
}
}
],
"latest_value": {
"id": 1130238,
"server_id": 7,
"systemuptime": "80days:10hours:23minutes",
"memtotal": 3.7,
"memfree": 1.6400000000000001,
"loadaverage": 2.25,
"disktotal": {
"dev-mapper-centos-root_disktotal": "38",
"dev-mapper-proquote-xfs-lvm_disktotal": "200"
},
"diskused": "{\"dev-mapper-centos-root_diskused\":\"16\",\"dev-mapper-proquote-xfs-lvm_diskused\":\"188\"}",
"custom_field": "[]",
"additional_attributes": {
"fathom": {
"name": "fathom",
"status": 1
},
"trenddb": {
"name": "trenddb",
"status": 1
},
"trendwi": {
"name": "trendwi",
"status": 1
},
"appsrv": {
"name": "appsrv",
"status": 1
}
},
"created_at": "2019-06-15 02:25:02",
"updated_at": "2019-06-15 02:25:02"
}
}
]
},
"message": "Success"
}
You seem to have few different errors in your data structure.
First of all, you are trying to decode Server while your json has servers inside a dict {"servers": ... }, So use a parent root object for it.
Your latest_value inside ServerData is defined as array, while it should be LatestValue struct not [LatestValue].
There is no first_page_url element in your json, but your Server struct has the property, make it optional, so that JSONDecoder decodes it only if it is present.
Here is your refined data models.
struct Response: Decodable {
let servers: Server
}
struct Server: Decodable {
let current_page: Int
let data: [ServerData]
let first_page_url: String?
}
struct ServerData: Decodable {
let hostname: String
let ipaddress: String
let customers: [Customer]
let latest_value: LatestValue
}
struct Customer: Decodable {
let name: String
let contact_person :String
let email: String
}
struct LatestValue: Decodable {
let systemuptime: String
let memtotal: Float
let memfree: Double
let loadaverage: Float
}
And decode Response instead of decoding Server, like so,
do {
let apiResponse = try JSONDecoder().decode(Response.self, from: data)
let server = apiResponse.server // Here is your server struct.
print(server)
} catch let jsonError {
print(jsonError)
}