In my project I can't access the "Floor" to be used in for each loop. I need to count floors of a bulding in the loop.
// MARK: - UcmData
struct UcmData: Codable {
let buildings: [Building]
}
// MARK: - Building
struct Building: Codable {
let id: Int
let title, subtitle, info, image: String
let floor: [Floor]
}
// MARK: - Floor
struct Floor: Codable {
let number: Int
let title, subtitle, image: String
}
import SwiftUI
import Foundation
struct NamJHerduView: View {
let ucmData = Bundle.main.decode(UcmData.self, from: "ucm_data.json")
var body: some View {
ScrollView {
VStack {
ForEach(0 ..< ucmData.) //here i need to count floors and use maybe where clause to filter just floors from a special building
Add Identifiable conformance
struct Building: Codable, Identifiable {
Now you can loop through buildings
ForEach(ucmData.buildings) { building in
Text("Number of floors in \(building.title): \(building.floors.count)")
}
If you also need to access the index in the loop, see this answer: How do you use .enumerated() with ForEach in SwiftUI?
I think you may want to organize your data so that it is nested like this:
// MARK: - UcmData
struct UcmData: Codable {
let buildings: [Building]
// MARK: - Building
struct Building: Codable {
let id: Int
let title, subtitle, info, image: String
let floor: [Floor]
// MARK: - Floor
struct Floor: Codable {
let number: Int
let title, subtitle, image: String
}
}
}
Then each building will have a floor array.
Related
Hello I'm trying to decode this API, https://gorest.co.in/public-api/posts. What I'm doing wrong with structs? Thank you.
struct Root: Decodable {
let first: [Code]
let second: [Meta]
let third: [Post] }
struct Code: Decodable {
let code: Int
}
struct Meta: Decodable {
let meta: [Pagination]
}
struct Pagination: Decodable {
let total: Int
let pages: Int
let page: Int
let limit: Int
}
struct Post: Decodable {
let id: Int
let user_id: Int
let title: String
let body: String
}
Quicktype.io is your friend. Post the JSON to it and get the struct back automatically.
// This file was generated from JSON Schema using quicktype, do not modify it directly.
// To parse the JSON, add this file to your project and do:
//
// let root = try? newJSONDecoder().decode(Root.self, from: jsonData)
import Foundation
// MARK: - Root
struct Root: Codable {
let code: Int
let meta: Meta
let data: [Datum]
}
// MARK: - Datum
struct Datum: Codable {
let id, userID: Int
let title, body: String
enum CodingKeys: String, CodingKey {
case id
case userID = "user_id"
case title, body
}
}
// MARK: - Meta
struct Meta: Codable {
let pagination: Pagination
}
// MARK: - Pagination
struct Pagination: Codable {
let total, pages, page, limit: Int
}
So I have a JSON File with following structure:
public struct main: Decodable {
let features: [Features]
}
struct Features: Decodable{
let something: String
let properties: [Properties]
}
struct Properties: Decodable{
let title: String
}
With my JSON Decoder Function I have no problems to get Information from Features.something, but I'm struggling to find out, how could I get to the title of Properties.
getJSON(urlString: urlString) { (results: main?) in
if let result = results {
for result in results.features {
print(result.something)
}
}
}
so I use I am trying to parse through this data:
{"status":"success","data":{"city":"Sunnyvale","state":"California","country":"USA","location":{"type":"Point","coordinates":[-122.03635,37.36883]},"current":{"weather":{"ts":"2020-07-23T00:00:00.000Z","tp":25,"pr":1009,"hu":44,"ws":6.2,"wd":330,"ic":"02d"},"pollution":{"ts":"2020-07-23T00:00:00.000Z","aqius":7,"mainus":"p2","aqicn":2,"maincn":"p2"}}}}
I am trying to get a hold of the aqius result, as well as the tp...
Here is my code right now, I have created these structs:
struct Response: Codable{
let data: MyResult
let status: String
}
struct MyResult: Codable {
let city: String
}
As you can see, I have gotten city, and I can confirm it works because when I get the request and print(json.data.city) it prints "Sunnyvale".
But how would I get the other values? I have been stuck on how to obtain values within the location , current and pollution data structures, how would I do this?
Thanks
There are tools that automatically generate Codable models from json string like: https://app.quicktype.io)
So your base struct models looks like below;
// MARK: - Response
struct Response: Codable {
let status: String
let data: DataClass
}
// MARK: - DataClass
struct DataClass: Codable {
let city, state, country: String
let location: Location
let current: Current
}
// MARK: - Current
struct Current: Codable {
let weather: Weather
let pollution: Pollution
}
// MARK: - Pollution
struct Pollution: Codable {
let ts: String
let aqius: Int
let mainus: String
let aqicn: Int
let maincn: String
}
// MARK: - Weather
struct Weather: Codable {
let ts: String
let tp, pr, hu: Int
let ws: Double
let wd: Int
let ic: String
}
// MARK: - Location
struct Location: Codable {
let type: String
let coordinates: [Double]
}
Decode;
let jsonData = jsonString.data(using: .utf8)!
let model = try? JSONDecoder().decode(Response.self, from: jsonData)
I have several JSON feeds, for this example let's these two (A & B). Both have different structures but both have an array of structures identifying the elements I'd like to parse, the bike stations.
I'd like to avoid creating a different class for each JSON feed I have to parse and if possible parse the underlying array of structures with the same Decodable struct. The definition of my model is as follows,
struct Places: Decodable {
var name: String
let lat: Double
let lng: Double
let id: String
let bikes: Int
enum CodingKeys: String, CodingKey {
case name
case lat = "latitude"
case lng = "longitude"
case id
case bikes = "free_bikes"
}
}
This model would only serve for one JSON feed and for each I'd have to create different CodingKeys. This is also a problem because the intermediate elements differ from feed to feed.
What I currently have is different parsers used on each feed. My app uses the array of Places to add pins on a map so the defined struct has to be the same for every feed that I parse. This is not a scalable solution for me and I'd like to ask if the following is correct,
Can I have only one parser that fit all my needs?
Can I have only one parser with different root elements and the same Places struct in the end?
Can I build a parser that only accesses the intermediate elements defined in the Places structure and "forget" about the top level differences between the feeds
I asked a similar question here only for the inner elements. While that is still true I now have problems parsing all the documents only to get the array of Places.
One thing right off the top, I would suggest rename your Places struct to Place (singular). I would also suggest using the full names for latitude and longitude.
On to the specific questions:
I think 1 & 2 would be possible, but you might end up with much less maintainable code.
Yes. Doable with the caveat above.
Yes. Challenging, because in the NextBike & CitiBike examples you provided, the IDs are of different types (String id in CitiBike and Int uid), so would need to be mapped.
No, I don't think so, because you'll still need to do the type mapping mentioned above in 2.
For maintainability, I would suggest creating Decodable structs/classes for each of the types of feeds with (possibly) embedded structs/classes to support hierarchy and then provide a map to [Place]. You can use a protocol to enforce compliance.
protocol PlacesProviding {
var places: [Place] { get }
}
struct Place: Decodable {
var name: String
let latitude: Double
let longitude: Double
let id: String
let bikes: Int
Using your NextBike & CitiBike examples, you might want to be able to do something like:
let decoder = JSONDecoder()
let nyc = decoder.decode(NYCitiBike.self, from: citiBikeNYC)
let nb = decoder.decode(NextBike.self, from: nextBike)
var places = [Place]()
places.append(contentsOf: nb.places)
places.append(contentsOf: nyc.places)
print(places.count)
Here are example Decodable PlaceProviding objects that support this. The structure of these make very clear the expected structure of the associated feed and make it easy to surface additional properties from a feed. This improves maintainability, especially in the longer term.
struct NYCitiBike: Decodable, PlacesProviding {
struct Network: Decodable {
let stations: [Station]
}
struct Station: Decodable {
var name: String
let latitude: Double
let longitude: Double
let id: String
let bikes: Int
enum CodingKeys: String, CodingKey {
case name
case latitude
case longitude
case id
case bikes = "free_bikes"
}
}
let network: Network
var stations: [Station] { return network.stations }
var places: [Place] {
stations.map { Place(name: $0.name, latitude: $0.latitude, longitude: $0.longitude, id: $0.id, bikes: $0.bikes) }
}
struct NextBike: Decodable, PlacesProviding {
struct Country: Decodable {
let name: String
let cities: [City]
}
struct City: Decodable {
let name: String
let places: [Station]
}
struct Station: Decodable {
let name: String
let lat: Double
let lng: Double
let uid: Int
let bikes: Int
}
let countries: [Country]
var stations: [Station] {
return countries
.flatMap { $0.cities }
.flatMap { $0.places }
}
var places: [Place] {
stations.map { Place(name: $0.name, latitude: $0.lat, longitude: $0.lng, id: String($0.uid), bikes: $0.bikes) }
}
Good afternoon! I have a task, I need to do navigation (When I tap a cell, I navigate to another controller that contains the album list , then When I tap an album, I navigate to another controller that contains the list of images.
I do not know what the error is? Thank you ! My Json is http://appscorporation.ga/api-user/test
I created struct.
struct ProfileElement: Decodable {
let user: User
let postImage: String
let postLikes: Int
let postTags: String
}
struct User: Decodable {
let name, surname: String
let profilePic: String
let albums: [Album]
}
struct Album : Decodable {
let id: Int
let title: String
var images: [Image]
}
struct Image: Decodable {
let id: Int
let url: String
}
I decoded by this code
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let result = try decoder.decode([ProfileElement].self, from: data)
I was advised to receive image data with
for item in result {
for album in item.user.albums {
for image in album.images {
print(image)
}
}
}
But unfortunately, I did not understand how I use data acquisition, then transfer and use them to other controllers.
Help me please.
My project in GitHub https://github.com/VladimirRebricov/TestProject