I need help getting all the JSON data API provides - arrays

Hello can you help me get array of all the data including global_rank, brawlhalla_id, etc.? I am only able to get array of legend like legend_id, legend_key_name. This is the json
{
"name": "bmg | dan",
"brawlhalla_id": 2,
"rating": 1745,
"peak_rating": 1792,
"tier": "Platinum 2",
"wins": 207,
"games": 391,
"region": "US-E",
"global_rank": 5698,
"region_rank": 1644,
"legends": [
{
"legend_id": 4,
"legend_name_key": "cassidy",
"rating": 1736,
"peak_rating": 1792,
"tier": "Platinum 1",
"wins": 161,
"games": 300
},
{
"legend_id": 21,
"legend_name_key": "barraza",
"rating": 1640,
"peak_rating": 1640,
"tier": "Gold 5",
"wins": 41,
"games": 77
}
],
...
My code looks like this:
import SwiftUI
class BrawlhallaSevice {
let url = URL(string: "http://sion.red/brawlhalla/ranked.json")!
func getBrawlhalla(completion: #escaping (Result<[Brawlhalla], Error>) -> Void) {
URLSession.shared.dataTask(with: url) { data, response, error in
guard error == nil else {
completion(.failure(error!))
return
}
completion(.success(self.getBrawlhallaResponse(fromData: data!)))
}.resume()
}
private func getBrawlhallaResponse(fromData data: Data) -> [Brawlhalla] {
let brawlhallaData = try? JSONDecoder().decode(BrawlhallaResponse.self, from: data)
if let brawlhallaD = brawlhallaData {
print(brawlhallaD)
//return brawlhallaD.legends works and returns array of data about a legend
return brawlhallaD
}
return [Brawlhalla(legend_name_key: "Error", rating: 0, peak_rating: 0, tier: "Error")]
}
}
But it throws an error: "Cannot convert return expression of type 'BrawlhallaResponse' to return type '[Brawlhalla]'".
This is how my models look
import Foundation
struct BrawlhallaResponse: Codable {
let legends: [Brawlhalla]
let name: String
let rating: Int
let peak_rating: Int
let tier: String
}
import Foundation
struct Brawlhalla: Codable {
let name: String
let rating: Int
let peak_rating: Int
let tier: String
}
I have tried many changes to the code but nothing seem to work.

I think you got the right code, but it seems like you're trying to return brawhallaD in that if let statement. You're getting the error because your return type is [Brawlhalla], but you're returning a decodedResponse of type BrawlhallaResponse.
So what you can do is this:
class BrawlhallaSevice {
let url = URL(string: "http://sion.red/brawlhalla/ranked.json")!
func getBrawlhalla(completion: #escaping (Result<BrawlhallaResponse?, Error>) -> Void) {
URLSession.shared.dataTask(with: url) { data, response, error in
guard error == nil else {
completion(.failure(error!))
return
}
guard let safeData = data else { return }
completion(.success(self.getBrawlhallaResponse(fromData: safeData)))
}.resume()
}
private func getBrawlhallaResponse(fromData data: Data) -> BrawlhallaResponse?{
let brawlhallaData = try? JSONDecoder().decode(BrawlhallaResponse.self, from: data)
if let brawlhallaD = brawlhallaData {
return brawlhallaD // Will return BrawlhallaResponse.peak_rating and also legends
}
return nil
}
}

Related

Issue filtering array using criteria | Swift 5

How can the following JSON array be filtered to exclude values type = quiet to only initiate viewcontrollers where test.testid type everything else.
Currently doing the following:
do {
let nav = try JSONDecoder().decode([TestStruct].self, from: data)
self.viewControllers = nav.map { test -> UIViewController in
let selected = UIImage(named: "Tab4_Large")!
let normal = UIImage(named: "Tab4_Large")!
let controller = storyboard!.instantiateViewController(withIdentifier: String(test.testid))
controller.view.backgroundColor = UIColor.white
controller.floatingTabItem = FloatingTabItem(selectedImage: selected, normalImage: normal)
return controller
}
JSON Decode Structure:
struct TestStruct: Decodable {
let testname: String
let type: String
let testid: Int
}
Need to exclude type = "quiet", but include the rest in the nav.map
Example JSON:
[
{
"testname": "Teller",
"type": "load",
"testid": 2
},
{
"testname": "Manager",
"type": "load",
"testid": 8
},
{
"testname": "Tester",
"type": "quiet",
"testid": 8
}
]
For the result that you want to achieve. First need to filter the array with the values not having type = quiet.
let filteredArray = nav.filter { $0.type != "quiet" }
then you can use compactMap to get your desired result. I am not sure about the instantiateViewController part will work the way you want.
But I am adding a simple example which I tried in Playground:
var stringData = "[{\"testname\":\"Teller\",\"type\":\"load\",\"testid\":2},{\"testname\":\"Manager\",\"type\":\"load\",\"testid\":8},{\"testname\":\"Tester\",\"type\":\"quiet\",\"testid\":8}]"
let data = stringData.data(using: .utf8)!
struct TestStruct: Decodable {
let testname: String
let type: String
let testid: Int
}
struct ViewControl {
let id: Int
}
do {
let nav = try JSONDecoder().decode([TestStruct].self, from: data)
let filteredArray = nav.filter { $0.type != "quiet" }
print(filteredArray)
let controllers = filteredArray.compactMap({ test -> ViewControl in
let viewControl = ViewControl(id: test.testid)
return viewControl
})
print(controllers)
} catch let error as NSError {
print(error)
}

Value of type xxx has no member xxx Error in SwiftuI?

I've been struggling with this for ages and I can't figure out why I can't pass this at all.
I have an array that look like this:
{
"status": "OK",
"tickets": {
"open_tickets": [
{
"uuid": "XXXXXXX",
"package": {
"title": "XXXX",
"buyer": {
"name": "XXX",
"family_name": null,
"email": "XXXXX",
"buyer_profile": {
"telephone": "5435345"
}
}
}
}
],
"closed_tickets": []
}
}
I'm trying access the nested array(s). specifically, open_tickets.
So, I've tried this:
I've placed this in at the top of my view between the struct and body:
#ObservedObject var fetcherL = FetcherL()
And this is now I try to read that data from a remote URL:
public class FetcherL: ObservableObject {
#Published var ticket = [Open_Tickets]()
init(){
load()
}
func load() {
let url = URL(string: "HTTPS://xxxxxxx")
var request = URLRequest(url: url!)
URLSession.shared.dataTask(with: request) {(data,response,error) in
do {
if let d = data {
let res = try JSONDecoder().decode(RootO.self, from: d)
DispatchQueue.main.async {
//self.ticket = res.tickets.open_tickets
self.ticket = res.tickets.open_tickets
}
}else {
print("No Data")
}
} catch {
print (error)
}
}.resume()
}
}
struct RootO: Codable {
let tickets: [Tickets]
enum CodingKeys: String, CodingKey {
case tickets = "tickets"
}
}
struct Tickets: Codable {
let open_tickets: [Open_Tickets]
enum CodingKeys: String, CodingKey {
case open_tickets = "open_tickets"
}
}
struct Open_Tickets: Codable{
let ticket_number: String
enum CodingKeys: String, CodingKey {
case ticket_number = "ticket_number"
}
}
But this code Is giving this error and stops me from compiling my app:
Value of type '[Tickets]' has no member 'open_tickets'
Can someone please advice on this?
The reason you are getting the error is in your Root0:
struct RootO: Codable {
let tickets: [Tickets]
enum CodingKeys: String, CodingKey {
case tickets = "tickets"
}
}
You are expecting your Root0 to have a field called tickets expecting an array of Tickets, but the tickets attribute of the response is a dictionary of [String: [Tickets]
Here is a working solution:
let json = """
{
"status": "OK",
"tickets": {
"open_tickets": [
{
"uuid": "XXXXXXX",
"package": {
"title": "XXXX",
"buyer": {
"name": "XXX",
"family_name": null,
"email": "XXXXX",
"buyer_profile": {
"telephone": "5435345"
}
}
}
}
],
"closed_tickets": []
}
}
""".data(using: .utf8)!
struct RootO: Codable {
let tickets: [String: [Ticket]]
var openTickets: [Ticket] {
if let data = tickets["open_tickets"] {
return data
} else {
return []
}
}
var closedTickets: [Ticket] {
if let data = tickets["closed_tickets"] {
return data
} else {
return []
}
}
}
struct Ticket: Codable {
let ticketNumber: String
enum CodingKeys: String, CodingKey {
case ticketNumber = "uuid"
}
}
let decoder = JSONDecoder()
let decoded = try! decoder.decode(RootO.self, from: json)
print("Open tickets = \(decoded.openTickets.count)")
print("Closed tickets = \(decoded.closedTickets.count)")
Some advice:
Use singular nouns for your struct Ticket not Tickets.
You do not need to provide the coding keys if the attribute name is identical
Change RootO structure and try it, "tickets" is object not an array
struct RootO: Codable {
let tickets: Tickets
enum CodingKeys: String, CodingKey {
case tickets = "tickets"
}
}

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.

Issue displaying array data inside UIAlert View

Essentially I would like to parse the JSON received from a url and display the data inside of alert popup.
I am able to do this with a simple array below but don't know how this can be done with an JSON array from a URL.
Currently working:
let jsonObj:[String: Any] = ["error": [
"email": ["The email has already been taken."],
"phone": ["The phone has already been taken."]]
]
if let errorMsgs = jsonObj["error"] as? [String: [String]] {
let errMsg = errorMsgs.values.map { $0.reduce("", +) }.joined(separator: "\n")
print(errMsg)
self.presentAlert(withTitle: "Try again", message: errMsg)
}
JSON Array:
var structure = [Struct]()
private func fetchJSON() {
guard let url = URL(string: "url.com")
else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = "code=\(codeValue)".data(using: .utf8)
URLSession.shared.dataTask(with: request) { data, _, error in
guard let data = data else { return }
do {
self.structure = try JSONDecoder().decode([Struct].self,from:data)
}
catch {
print(error)
}
}.resume()
}
struct Struct: Decodable {
let id: Int
let reasons: String
}
Sample JSON Array:
[
{
"id": 1,
"reasons": "Test"
},
{
"id": 3,
"reasons": "Test"
}
]
How can I get the reasons string from the array to populate in the alert similar to above.
UPDATE:
To Print the records in the alert I have done the following but it only prints one record in the alert:
for item in self.structure {
print(item.reasons)
let errMsg = [item.reasons].compactMap { $0 }.joined(separator: "\n")
self.presentAlert(withTitle: "Try again", message: errMsg)
}
Here I have created example from your code.
First of all replace your Codable with:
// MARK: - Reason
struct Reason: Codable {
let id: Int
let reasons: String
}
typealias Reasons = [Reason]
Here is your server response in JSON String formate:
let json = """
[
{
"id": 1,
"reasons": "Test"
},
{
"id": 3,
"reasons": "Test"
}
]
"""
Then I have converted it to Data only for testing purpose:
let data = json.data(using: .utf8)!
And you can parse it with
do {
self.reasonsData = try JSONDecoder().decode(Reasons.self, from:data)
for item in self.reasonsData {
print(item.reasons) //This will print reasons.
}
}
catch {
print(error)
}
UPDATE:
If you want all reasons in one string then replace
for item in self.reasonsData {
print(item.reasons)
}
with
let errMsg = self.reasonsData.compactMap { $0.reasons }.joined(separator: "\n")

How to return a value of Dictionary that satisfies the given predicate?

I can't solve next problem. I get an array of Dictionaries from JSON with the same keys and different values.
For example:
{
"data": [{
"name": "Anna",
"features": {
"age": "18",
"hobby": "football"
}
}, {
"name": "Peter",
"data": {
"age": "16",
"hobby": "computer games"
}
}, {
"name": "Peter",
"data": {
"age": "25",
"hobby": "chess",
"job": "fireman"
}
}
],
"view": ["Peter", "Anna", "Peter"]
}
This is my structures for Decoding:
struct Object: Decodable {
let objectData: [DictionaryData]
let bjectView: [String]
}
struct DictionaryData: Decodable {
let name: String
let features: Features?
let data: DataClass?
}
struct DataContainer: Decodable {
let age, hobby: String
let job: String?
}
struct Features: Decodable {
let age, hobby: String
}
This is the point where I stuck. I use filter but it returns Bool while I need a value of dictionaries:
var items = [ModelItem]()
var singleObject: Object!
func jsonParsing(completionHandler: #escaping (([ModelItem], Error?) -> Void)) {
guard let url = URL(string: jsonUrlString) else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
guard let data = data else { DispatchQueue.main.async {completionHandler([], error)}
return
}
do {
self.singleObject = try JSONDecoder().decode(Object.self, from: data)
let sequenceArray = self.singleObject.objectView
for i in sequenceArray {
if i == "Peter" {
if let objectName = self.singleObject?.objectData.filter({ return $0.name == "Peter"}) ... {
let firstItem = ModelFirstItem(text: ...) //here should be a value
self.items.append(firstItem)
}
}
else if i == "Anna" {
...
}
}
}
DispatchQueue.main.async {completionHandler(self.items, nil)}
} catch {
print("Error serializing json:", error)
}
} .resume()
}
How can I get age, hobby and job values?
Thank you!
Codable Model of the JSON
struct Welcome: Codable {
let data: [Datum]
let view: [String]
}
struct Datum: Codable {
let name: String
let features: Feature?
let data: Data?
}
struct Data: Codable {
let age, hobby: String
let job: String?
}
struct Feature: Codable {let age, hobby: String}
Decode the JSON
let decoder = JSONDecoder()
let decodedObject = try! decoder.decode(Welcome.self, from: JSON)
let filterdPeter = decodedObject.data.filter {$0.name == "Peter"}.map({$0.data})
filterdPeter.forEach {print($0)}
if let json = object as? [String: Any],
let dataJSON = json["data"] as? [[String: Any]] { //in the data field you have an array of dictionaries, maybe this is what you're stuck on?
dataJSON.forEach { jsonElement in
if let name = jsonElement["name"] as? String, name == "Peter" {
print(jsonElement["data"])
}
}
}

Resources