How to parse array in Swift 3 - arrays

I have some JSON but it's in a matrix and I do not know how to get the values separately. For example, get the last name.
This is the JSON structure:
{
"login": true,
"token": "m5CnQ",
"usuario": [
{
"names": "Sergio Alejandro",
"las_name": "Rosado",
}
]
}
This is my code:
do {
print("recibimos respuesta")
let responseString = String(data: data, encoding: .utf8)
print("respuesta : \(responseString)")
if let json = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as? [String: String] {
DispatchQueue.main.async {//proceso principal
var nombre = json["nombre"]//constante
print("respuestanom : \(nombre)")
}
}
} catch let parseError {//manejamos el error
print("error al parsear: \(parseError)")
let responseString = String(data: data, encoding: .utf8)
print("respuesta : \(responseString)")
}
}
task.resume()

have you tried usuario[0].las_name

If you are on Swift 4, use the new Decodable protocol:
First define your data model:
struct MyDataModel: Decodable {
struct User: Decodable {
var names: String
var lastName: String
private enum CodingKeys: String, CodingKey {
case names, lastName = "las_name"
}
}
var login: Bool
var token: String
var user: [User]
private enum CodingKeys: String, CodingKey {
case login, token, user = "usuario"
}
}
Then the decoding is really simple:
do {
let model = try JSONDecoder().decode(MyDataModel.self, from: data)
print(model)
} catch let parseError {
print(parseError)
}

i resolved my problem this way:
JSON:
{
"login": "TRUE",
"token": "eyJ0eXAiOiJKV12la70",
"usuario": [
{
"id": "36",
"id_empresa": "1",
"nombres": "Sergio Alejandro",
"apellido_paterno": "Rosado",
"apellido_materno": "Dzul",
"zona_horaria": "America/Merida",
"id_perfil": "1",
"correo": "russelalexis123#gma.com",
"username": "empleadodemo",
"passwd": "*A4B6157319038724E3560894F7F932C8886EBFCF",
"activo": "1",
"fh_captura": "2017-02-20 21:02:55",
"domicilio": "",
"cp": "",
"telefono": "",
"usuario": "1",
"no_licencia": null,
"fecha_expedicion": null,
"fecha_vigencia": null
}
]
}
code
struct MyDataModel: Decodable {
struct User: Decodable {
var id: String?
var id_empresa: String?
var nombres: String?
var apellido_paterno: String?
var apellido_materno: String?
var zona_horaria: String?
var id_perfil: String?
var correo: String?
var username: String?
var activo: String?
var fh_captura: String?
var domicilio: String?
var cp: String?
var telefono: String?
var usuario: String?
private enum CodingKeys: String, CodingKey {
case id="id", id_empresa="id_empresa", nombres="nombres", apellido_paterno="apellido_paterno", apellido_materno="apellido_materno", zona_horaria="zona_horaria", id_perfil="id_perfil", correo="correo", username="username", activo="activo", fh_captura = "fh_captura", domicilio="domicilio", cp="cp", telefono="telefono", usuario="usuario"
}
}
var login: String
var token: String
var user: [User]
private enum CodingKeys: String, CodingKey {
case login, token, user = "usuario"
}
}
print("envar solicitud")
let url = URL(string: "http://webservice")!
var request = URLRequest(url: url)
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type");
request.httpMethod = "POST"
let postString = "usuario=jose&password=1234"
request.httpBody = postString.data(using: .utf8)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data else {//si existe un error se termina la funcion
print("solicitud fallida \(error)")//manejamos el error
return //rompemos el bloque de codigo
}
do {
let model = try JSONDecoder().decode(MyDataModel.self, from: data)
print(model.user[0].correo)
} catch let parseError {
print(parseError)
}
}
task.resume()

Related

Push the data from the request into the array

I want to write the data that I received from the request into an array and then display it through a list
Here is my structure for the json file
struct DataRespons: Codable {
let data: [String]
let status: String
}
struct UserRespons: Codable {
let status: String
let data: UserData
}
struct UserData: Codable, Identifiable {
let id: String
let firstName: String
let lastName: String
let age: Int
let gender: String
let country: String
}
This is my class for JSON requests and decoding
import Foundation
#MainActor
class NetworkModel: ObservableObject {
#Published var listId: [String] = []
var statusList = ""
var statusUser = ""
var temp = ""
var user: [UserData] = [] // here I am not sure if this type Array
#Published var userData = UserRespons(status: "??", data: UserData(id: "???", firstName: "???", lastName: "??", age: 4, gender: "???", country: "???"))
this func for receive a letter with links to which I should make requests
func getList() {
guard let url = URL(string: "https://opn-interview-service.nn.r.appspot.com/list") else { fatalError("Missing URL") }
var request = URLRequest(url: url)
request.addValue("bearer \(token)", forHTTPHeaderField: "Authorization")
let dataTask = URLSession.shared.dataTask(with: request) { (data, response, error) in
if let error = error {
print("Requst error",error)
return
}
guard let response = response as? HTTPURLResponse else { return }
if response.statusCode == 200 {
guard let data = data else { return }
DispatchQueue.main.async { [self] in
do {
let decoded = try JSONDecoder().decode(DataRespons.self, from: data)
self.listId = decoded.data
self.statusList = decoded.status
for i in self.listId.indices {
print("This is id[\(i)] = \(listId[i])")
getUser(url: "\(listId[i])")
// #MARK: NEED HERE HELP user.append(.init(id: <#T##String#>, firstName: <#T##String#>, lastName: <#T##String#>, age: <#T##Int#>, gender: <#T##String#>, country: <#T##String#>))
}
} catch let error{
print("Error decode",error)
}
}
}
}
dataTask.resume()
}
I want to add data that will come from requests to an empty array so that it can then be displayed in the list
function for decoding data user
func getUser(url: String) {
guard let url = URL(string: "https://opn-interview-service.nn.r.appspot.com/get/\(url)") else { fatalError("Missing URL") }
var request = URLRequest(url: url)
request.addValue("bearer \(token)", forHTTPHeaderField: "Authorization")
let dataTask = URLSession.shared.dataTask(with: request) { (data, response, error) in
if let error = error {
print("Requst error",error)
return
}
guard let response = response as? HTTPURLResponse else { return }
if response.statusCode == 200 {
guard let data = data else { return }
DispatchQueue.main.async { [self] in
do {
let decoded = try JSONDecoder().decode(UserRespons.self, from: data)
self.userData = decoded
self.statusUser = decoded.status
print("UserData: name = \(userData.data.firstName) Lastname = \(userData.data.lastName) gender = \(userData.data.gender)")
} catch let error{
print("Error decode",error)
}
}
}
}
dataTask.resume()
}
I don't know how to throw all the data into the array
First of all iterating the indices is cumbersome, replace
for i in self.listId.indices {
print("This is id[\(i)] = \(listId[i])")
getUser(url: "\(listId[i])")
}
with
for id in self.listId {
print("This is \(id)")
getUser(url: id)
}
But this is not the right place to append the user data. Do it in getUser. But first declare the array as #Published (and in plural form)
#Published var users = [UserData]()
and delete the property userData because it's not needed.
#Published var userData = UserRespons(status: "??", data: UserData(id: "???", firstName: "???", lastName: "??", age: 4, gender: "???", country: "???"))
Now replace the decoding part in getUser with
let decoded = try JSONDecoder().decode(UserRespons.self, from: data)
self.statusUser = decoded.status
users.append(decoded.data)
print("UserData: name = \(decoded.data.firstName) Lastname = \(decoded.data.lastName) gender = \(decoded.data.gender)")
I recommend to move to async/await. It's much less code and it switches to the main thread automatically.

Get an [String] link from a json file

Here is my structure for the json file
struct DataRespons: Codable {
let data: [String]
let status: String
}
JSON file url
{
"status": "success",
"data": [
"f7c75c1f-10ab-4298-9dc9-e80b7bd07dfd",
"6f5f6eeb-191d-4ad9-b5ef-6f61fd5fcefc",
"8008800880088008",
"64a3f5d0-37c7-4c30-8d0f-3b67fb5c8fde"
]
}
This is my class for JSON requests and decoding
I use these functions to get an array of links
I hope I wrote correctly what I want to receive, and if there are any questions, please contact me
#MainActor
class NetworkModel: ObservableObject {
#Published var listId: [String] = []
var statusList = ""
var statusUser = ""
#Published var userData = UserRespons(status: "??", data: UserData(id: "???", firstName: "???", lastName: "??", age: 4, gender: "???", country: "???"))
func getList() {
guard let url = URL(string: "some URL") else { fatalError("Missing URL") }
var request = URLRequest(url: url)
request.addValue("bearer \(token)", forHTTPHeaderField: "Authorization")
let dataTask = URLSession.shared.dataTask(with: request) { (data, response, error) in
if let error = error {
print("Requst error",error)
return
}
guard let response = response as? HTTPURLResponse else { return }
if response.statusCode == 200 {
guard let data = data else { return }
DispatchQueue.main.async {
do {
let decoded = try JSONDecoder().decode(DataRespons.self, from: data)
self.listId = decoded.data
self.statusList = decoded.status
} catch let error{
print("Error decode",error)
}
}
}
}
dataTask.resume()
}
I can't through index [String] to get each element
use this code to access the data elements of an array:
// ....
self.listId = decoded.data
for i in listId.indices {
print("--> listId[\(i)] = \(listId[i]) ")
}
print("--> listId[0] = \(listId[0]) ")
print("--> listId[1] = \(listId[1]) ")
// ....
Read the very basics of Swift, specially regarding arrays here:
https://docs.swift.org/swift-book/LanguageGuide/CollectionTypes.html

Trouble with JSON Parsing in Swift

Having a slight trouble in saving my decoded JSON data into the struct, not sure what I'm doing wrong here.
What I'm trying to do is call the sneaker database and retrieve the results and display them in a view, i can see the JSON call is working with the print but now stuck on how to get this in a way I can display it into a view.
JSON
{
"count": 1,
"results": [
{
"id": "029ddc1d-a64b-469b-b9df-bd21c84a608e",
"brand": "Jordan",
"colorway": "Deep Ocean/Sail-Cement Grey-Fire Red",
"gender": "men",
"name": "SE Sashiko",
"releaseDate": "2020-12-05",
"retailPrice": 190,
"shoe": "Jordan 4 Retro",
"styleId": "CW0898-400",
"title": "Jordan 4 Retro SE Sashiko",
"year": 2020,
"media": {
"imageUrl": "https://images.stockx.com/images/Air-Jordan-4-Retro-SE-Deep-Ocean-Product.jpg?fit=fill&bg=FFFFFF&w=700&h=500&auto=format,compress&trim=color&q=90&dpr=2&updated_at=1609439370",
"smallImageUrl": "https://images.stockx.com/images/Air-Jordan-4-Retro-SE-Deep-Ocean-Product.jpg?fit=fill&bg=FFFFFF&w=300&h=214&auto=format,compress&trim=color&q=90&dpr=2&updated_at=1609439370",
"thumbUrl": "https://images.stockx.com/images/Air-Jordan-4-Retro-SE-Deep-Ocean-Product.jpg?fit=fill&bg=FFFFFF&w=140&h=100&auto=format,compress&trim=color&q=90&dpr=2&updated_at=1609439370"
}
}
]
}
Model:
struct APIResponse: Decodable {
enum CodingKeys: String, CodingKey {
case shoeResults = "results"
}
let shoeResults: [Shoe]
}
struct Shoe: Decodable {
public var id: String
public var brand: String
public var colorway: String
public var gender: String
public var name: String
public var releaseDate: String
public var retailPrice: Int
public var shoe: String
public var styleId: String
public var title: String
public var year: Int
public var media: mediaData
enum CodingKeys: String, CodingKey {
case id = "id"
case brand = "brand"
case colorway = "colorway"
case gender = "gender"
case name = "name"
case releaseDate = "releaseDate"
case retailPrice = "retailPrice"
case shoe = "shoe"
case styleId = "styleId"
case title = "title"
case year = "year"
case media = "media"
}
}
struct mediaData: Decodable {
var imageUrl: String
var smallImageUrl: String
var thumbUrl: String
}
Shoefetcher class:
public class ShoeFetcher: ObservableObject {
init(){
load()
}
func load() {
let url = URL(string: "https://api.thesneakerdatabase.com/v1/sneakers?limit=10&styleId=cw0898-400")!
URLSession.shared.dataTask(with: url) {(data,response,error) in
do {
if let d = data {
let decodedLists = try JSONDecoder().decode(APIResponse.self, from: d)
DispatchQueue.main.async {
print(decodedLists)
}
}else {
print("No Data")
}
}catch DecodingError.keyNotFound(let key, let context) {
Swift.print("could not find key \(key) in JSON: \(context.debugDescription)")
} catch DecodingError.valueNotFound(let type, let context) {
Swift.print("could not find type \(type) in JSON: \(context.debugDescription)")
} catch DecodingError.typeMismatch(let type, let context) {
Swift.print("type mismatch for type \(type) in JSON: \(context.debugDescription)")
} catch DecodingError.dataCorrupted(let context) {
Swift.print("data found to be corrupted in JSON: \(context.debugDescription)")
} catch let error as NSError {
NSLog("Error in read(from:ofType:) domain= \(error.domain), description= \(error.localizedDescription)")
}
}.resume()
}
}
output of decodedlists:
APIResponse(shoeResults: [Sneakers.Shoe(id: "029ddc1d-a64b-469b-b9df-bd21c84a608e", brand: "Jordan", colorway: "Deep Ocean/Sail-Cement Grey-Fire Red", gender: "men", name: "SE Sashiko", releaseDate: "2020-12-05", retailPrice: 190, shoe: "Jordan 4 Retro", styleId: "CW0898-400", title: "Jordan 4 Retro SE Sashiko", year: 2020, media: SneakerNews.mediaData(imageUrl: "https://images.stockx.com/images/Air-Jordan-4-Retro-SE-Deep-Ocean-Product.jpg?fit=fill&bg=FFFFFF&w=700&h=500&auto=format,compress&trim=color&q=90&dpr=2&updated_at=1609439370", smallImageUrl: "https://images.stockx.com/images/Air-Jordan-4-Retro-SE-Deep-Ocean-Product.jpg?fit=fill&bg=FFFFFF&w=300&h=214&auto=format,compress&trim=color&q=90&dpr=2&updated_at=1609439370", thumbUrl: "https://images.stockx.com/images/Air-Jordan-4-Retro-SE-Deep-Ocean-Product.jpg?fit=fill&bg=FFFFFF&w=140&h=100&auto=format,compress&trim=color&q=90&dpr=2&updated_at=1609439370"))])
Edit:
updated the shoefetcher class:
public class ShoeFetcher: ObservableObject {
#Published var shoeResults: [Shoe] = [] //added
init(){
load()
}
func load() {
let url = URL(string: "https://api.thesneakerdatabase.com/v1/sneakers?limit=10&styleId=cw0898-400")!
URLSession.shared.dataTask(with: url) {(data,response,error) in
do {
if let d = data {
let decodedLists = try JSONDecoder().decode(APIResponse.self, from: d)
DispatchQueue.main.async {
self.shoeResults = decodedLists.shoeResults //added
print(self.shoeResults)
}
}else {
print("No Data")
}
}catch DecodingError.keyNotFound(let key, let context) {
Swift.print("could not find key \(key) in JSON: \(context.debugDescription)")
} catch DecodingError.valueNotFound(let type, let context) {
Swift.print("could not find type \(type) in JSON: \(context.debugDescription)")
} catch DecodingError.typeMismatch(let type, let context) {
Swift.print("type mismatch for type \(type) in JSON: \(context.debugDescription)")
} catch DecodingError.dataCorrupted(let context) {
Swift.print("data found to be corrupted in JSON: \(context.debugDescription)")
} catch let error as NSError {
NSLog("Error in read(from:ofType:) domain= \(error.domain), description= \(error.localizedDescription)")
}
}.resume()
}
}
added a ContentView to just see if I can display something off the JSON file.
struct ContentView: View {
#ObservedObject var fetcher = ShoeFetcher()
#State var Shoes: Shoe
var body: some View {
VStack {
Text("Shoes")
Text(Shoes.brand)
}
}
}
errors I'm getting is Missing argument for a parameter 'Shoes' in call in my SneakersApp.swift file
import SwiftUI
#main
struct SneakersApp: App {
var body: some Scene {
WindowGroup {
ContentView() //error here
}
}
}
I have a feeling I need to initialise the JSON variables but cant seem to workout where/how I do that
You've done all of the hard work of getting the data from the API and decoding it into structs.
To display it in a view, you could do something like this:
struct ContentView : View {
#StateObject var fetcher = ShoeFetcher()
var body: some View {
VStack {
if let response = fetcher.apiResponse {
ForEach(response.shoeResults, id: \.id) { shoe in
Text(shoe.name)
Text(shoe.brand)
Text(shoe.title)
Text("\(shoe.year)")
Text("\(shoe.retailPrice)")
}
}
}
}
}
Your current API call is only returning one Shoe in the array, so the ForEach call isn't doing much, but it would certainly be important if you had more results.

Swift Vapor wrong decoding of array of objects

I'm trying to pass an array of objects inside another object
struct CreationData: Encodable {
enum CodingKeys: String, CodingKey {
case property1
case amount
case participants
case code
}
var property1: String?
var amount: Double?
var tipAmount: Double?
var participants: [Participant]
var currency: Currency
init() {
name = nil
property1 = nil
participants = []
tipAmount = nil
currency = .hryvna
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
if let name = name {
try container.encode(name, forKey: .name)
}
if let property1 = property1 {
try container.encode(property1, forKey: .property1)
}
if let tipAmount = tipAmount {
try container.encode(tipAmount, forKey: .tipAmount)
}
if !participants.isEmpty {
try container.encode(participants, forKey: .participants)
}
try container.encode(currency, forKey: .currencyCode)
}
}
where object in array is
struct Participant: Hashable, Encodable {
enum CodingKeys: String, CodingKey {
case amount
case id
}
var image: Data?
var displayName: String
var amount: Double? = 100
var id: Int?
var name: String?
var isSelected: Bool = false
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(amount, forKey: .amount)
try container.encode(id, forKey: .id)
}
}
so my JSON looks something like this
{
"property1" : 123,
"code" : "zen",
"name" : "No name",
"participants" : [
{
"amount" : 100,
"id" : 1
}
]
}
On the backend I use this handler
func createHandler(_ req: Request, splitCreate: Create) throws -> Future<SplitResponse> {
let logger = try req.make(Logger.self)
let user = try req.requireAuthenticated(User.self)
guard let id = user.id else {
throw Abort(.badRequest, reason: "Couldn't get an id for current user")
}
...
with this request content object
struct Create: Content {
struct Participant: Content {
var id: Int?
var amount: Double?
}
var amount: Double?
var tipAmount: Double?
var name: String?
var participants: [Participant]
var currencyCode: String
}
I create pivots by the participants array, and the problem is that instead of getting a single participant from an array like so
[App.Create.Participant(id: Optional(1), amount: Optional(100.0))]
on the backend from the specified JSON I get these participants
[App.Create.Participant(id: Optional(1), amount: nil), App.Create.Participant(id: Optional(1), amount: Optional(100.0))]
Seems like my array json is being decoded incorrectly, but I'm unable to find a way to fix it
For some reason adding Content-Type header to URLSessionConfiguration.additionalHeaders didn't change the encoding to JSON.
So I had to set Alamofire's request encoding to JSONEncoding.default explicitly.

Accessing embedded JSON using decodable in Swift 4

I am trying to access a particular an embedded array of dictionaries to create my swift objects. I am unsure about how to access that array in the JSON dictionary.
Here is the definition of my Swift object = StarWarsPeople
class StarWarsPeople: Decodable {
var name: String?
var height: String?
var weight: String?
var hair_color: String?
var skin_color: String?
var eye_color: String?
var birth_year: String?
var gender: String?
}
Here is my APIClient class:
class StarWarsPeopleAPIClient
{
class func getStarWarsPeopleInformation (page: Int, completion:#escaping ([StarWarsPeople])-> ()) throws {
let starWarsPeopleURL = "https://swapi.co/api/people/?page=\(page)"
let convertedStarWarsPeopleURL = URL(string: starWarsPeopleURL)
guard let unwrappedConvertedStarWarsPeopleURL = convertedStarWarsPeopleURL else { print("unwrappedConvertedStarWarsPeopleURL did not unwrap"); return}
let request = URLRequest(url: unwrappedConvertedStarWarsPeopleURL)
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let unwrappedData = data else { print("unwrappedData did not unwrap"); return}
do {
let starWarsPeopleDataArray = try JSONDecoder().decode([StarWarsPeople].self, from: unwrappedData)
completion(starWarsPeopleDataArray)
}
catch let error {
print("Error occured here: \(error.localizedDescription)")
}
}
task.resume()
}
}
Here is my Json, it is the results array that I would like to access, it is an array of dictionaries, which I need to iterate over to create my StarWarsPeople Object.
{
"count": 87,
"next": "url",
"previous": null,
"results": [
{
"name": "Luke Skywalker",
"height": "172",
"mass": "77",
"hair_color": "blond",
"skin_color": "fair",
"eye_color": "blue",
"birth_year": "19BBY",
"gender": "male",
"homeworld": "url",
"films": [
"url",
"url",
"url",
"url",
"url"
],
"species": [
"url"
],
"vehicles": [
"url",
"url"
Please read the JSON. You are ignoring the enclosing object
struct Root: Decodable {
let count: Int
let next: URL?
let previous: URL?
let results : [StarWarsPeople]
}
struct StarWarsPeople: Decodable {
private enum CodingKeys: String, CodingKey {
case name, height, mass
case hairColor = "hair_color", skinColor = "skin_color"
case eyeColor = "eye_color", birthYear = "birth_year", gender
}
let name: String
let height: String
let mass: String
let hairColor: String
let skinColor: String
let eyeColor: String
let birthYear: String
let gender: String
}
...
let root = try JSONDecoder().decode(Root.self, from: unwrappedData)
let starWarsPeopleDataArray = root.results
...
Notes:
A struct is sufficient.
Map the snake_cased keys to camelCased properties.
In almost all cases the properties can be declared as constants (let).
Don't declare all properties schematically as optional. Declare only those as optional which corresponding key can be missing or the value can be null.
Simply define a wrapper struct that holds the results property from the JSON response.
struct ApiResponse: Decodable {
results: [StarWarsPeople]
}
and later use
let apiResponse = try JSONDecoder().decode(ApiResponse.self, from: unwrappedData)
let starWarsPeopleDataArray = apiResponse.results
to parse it.
Results you are trying to fetch is actually present in results keys. Also we need to use properties same as parameter name( we can use CodingKeys enum too for overriding this).
So, first parse outer JSON , In new struct say StarWarsPeopleParent
class StarWarsPeopleParent: Decodable {
var count: Int?
var results: [StarWarsPeople]?
}
Update your StarWarsPeople struct's properties as:
class StarWarsPeople: Decodable {
var name: String?
var height: String?
var mass: String?
var hair_color: String?
var skin_color: String?
var eye_color: String?
var birth_year: String?
var gender: String?
}
Then parse it like:
let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let unwrappedData = data else { print("unwrappedData did not unwrap"); return}
do {
let starWarsPeopleDataArray = try JSONDecoder().decode(StarWarsPeopleParent.self, from: unwrappedData)
completion(starWarsPeopleDataArray)
}
catch let error {
print("Error occured here: \(error.localizedDescription)")
}
}
Hope this is fine for you to understand.

Resources