Swift Issue Listing out a Dynamic Dictionary from JSON - json

I am trying to build an application which receives a JSON Object from an API endpoint, which then I want to list out in the view. I've watched a lot of videos on this topic, but in each video they use very simplistic JSON Objects as examples and therefore the code they write doesn't really seem to transfer over, giving me errors no matter how I try to format it. The code is as follows
import SwiftUI
import Combine
import Foundation
public struct ActivityModel: Codable, Identifiable {
public let id: Int
public let name: String
public let activity_desc: String?
}
public struct ActivitiesModel2: Codable {
public let location: String
public let popular: [String:ActivityModel]
}
public struct ActivitiesModel: Codable {
public let activities: ActivitiesModel2
}
public class ActivityFetcher: ObservableObject {
var activities: ActivitiesModel?
init(){
guard let url = URL(string: "https://mywebsite.com/api/loadapi") else { return }
var urlRequest = URLRequest(url: url)
urlRequest.httpMethod = "POST"
URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
do {
if let d = data {
let decodedLists = try JSONDecoder().decode(ActivitiesModel.self, from: d)
DispatchQueue.main.async {
self.activities = decodedLists
}
} else {
print("No Data")
}
} catch {
print("Error")
}
}.resume()
}
}
struct ActivityGuestView: View {
let networkingServiceGeneral = NetworkingServiceGeneral()
#ObservedObject var viewRouter: ViewRouter
#ObservedObject var fetcher = ActivityFetcher()
var body: some View {
// This is where my issues start
List(fetcher.activities?.activities.popular) { result in
VStack {
Text(result.name)
Text(result.activity_desc)
.font(.system(size: 11))
.foregroundColor(Color.gray)
}
}
}
}
This code, as I put it, gives me 5 errors. They are the following;
- Initializer 'init(_:rowContent:)' requires that '(key: String, value: ActivityModel)' conform to Identifiable
- Initializer 'init(_:rowContent:)' requires that '[String : ActivityModel]' conform to 'RandomAccessCollection'
-Value of optional type '[String : ActivityModel]?' must be unwrapped to a value of type '[String : ActivityModel]'
- Coalesce using '??' to provide a default when the optional value contains 'nil'
- Force-unwrap using '!' to abort execution if the optional value contains 'nil'
Some of these errors have options to fix it, but when I press fix it adds code but doesn't actually fix the error so I figured to just include them anyways. I'm still fairly new to Swift, but I know what some of it is asking, particularly the conforming to Identifiable, but it says that struct ActivitiesModel does not conform to identifiable when I try to add the tag, and the JSON Object doesn't have an ID for that section, so I can't ask the ID to make it identifiable.
Any help would be greatly appreciated, this has kind of been a wall right now.
EDIT: Here's the JSON
"activities": {
"location": "Dallas",
"popular": {
"10": {
"id": 38,
"name": "Adventure Landing Dallas",
"activity_desc": "Aquatic complex chain with additional land attractions including mini-golf, laser tag & go-karts.",
},
"12": {
"id": 40,
"name": "Jumpstreet",
"activity_desc": "None provided.",
},
}
}
}

Related

How to decode this JSON data in Swift

I am trying to get the title from json data but I am getting an error. Here is my model object
struct SpaceNewsModel: Identifiable, Codable {
var id = UUID()
let title: String
let url: String
let source: String
}
And when I get to this point in my networking, I get an error.
let task = session.dataTask(with: request as URLRequest, completionHandler: { data, response, error in
if error != nil {
print(error!)
} else {
let decoder = JSONDecoder()
if let safeData = data {
do {
let astroNews = try decoder.decode([SpaceNewsModel].self, from: safeData)
print(astroNews[0].title)
} catch {
print("DEBUG: Error getting news articles \(error.localizedDescription)")
}
}
let httpResponse = response as? HTTPURLResponse
// print(httpResponse)
}
})
task.resume()
}
My main problem is with the line print(astroNews[0].title I am not formatting it right and I don't know how to access this data whether in an array or otherwise.
Here is the returned JSON data after making the request on the website postman.
[
{
"title": "Wales' new £2bn space strategy hopes",
"url": "https://www.bbc.co.uk/news/uk-wales-60433763",
"source": "bbc"
},
{
"title": "Could Port Talbot become a centre of space tech? Video, 00:02:02Could Port Talbot become a centre of space tech?",
"url": "https://www.bbc.co.uk/news/uk-wales-60471170",
"source": "bbc"
}
]
Normally there would be a title for each that I can access but here it is just the data. So to summarize, how would I get the first or second title in this JSON data? Because astroNews[0].title does not work.
EDIT: I want to clarify that if I were to decode an empty struct, I would not get any errors.
struct EMPTYSTRUCT: Codable {
}
But as soon as I add any variable like let title: String I get an error. I believe something is wrong in the formatting of the JSON because usually the JSON would look something this
{
"coord": {
"lon": -95.3633,
"lat": 29.7633
}
}
where I could name the struct "coord" however no such names exist in the returned JSON I'm working with. Only the variable names. The confusion is how would I construct my SpaceNewModel file to work with the returned JSON with no apparent object name.
With reference to this answer.
Just change your structure to
struct SpaceNewsModel: Identifiable, Codable {
let id = UUID()
let title: String
let url: String
let source: String
}
This is not the solution but a workaround; if you want to solve the issue, refer to this answer

Swift code can not find the symbol of the data

I am new to swift . I am trying to convert json into model by using swift . I am using generic functions to complete the functions . Here is the structure of the json .
Here is the model I created based on jason .
import Foundation
// MARK: - Welcome
struct Welcome: Codable {
let photos: [Photo]
}
// MARK: - Photo
struct Photo: Codable {
let id, sol: Int
let camera: Camera
let imgSrc: String
let earthDate: String
let rover: Rover
enum CodingKeys: String, CodingKey {
case id, sol, camera
case imgSrc = "img_src"
case earthDate = "earth_date"
case rover
}
}
// MARK: - Camera
struct Camera: Codable {
let id: Int
let name: String
let roverID: Int
let fullName: String
enum CodingKeys: String, CodingKey {
case id, name
case roverID = "rover_id"
case fullName = "full_name"
}
}
// MARK: - Rover
struct Rover: Codable {
let id: Int
let name, landingDate, launchDate, status: String
enum CodingKeys: String, CodingKey {
case id, name
case landingDate = "landing_date"
case launchDate = "launch_date"
case status
}
}
Here is the code in generic function.
func getModel<Model: Codable>(_ type: Model.Type, from url: String, completion: #escaping (Result<Model, NetworkError>) -> ()) {
guard let url = URL(string: url) else {
completion(.failure(.badURL))
return
}
URLSession.shared.dataTask(with: url) { data, response, error in
if let error = error {
completion(.failure(.other(error)))
return
}
if let data = data {
do {
let response = try JSONDecoder().decode(type, from: data)
completion(.success(response))
} catch let error {
completion(.failure(.other(error)))
}
}
}
.resume()
}
I am trying to call this function form controller but it is showing the error Value of type 'Post' has no member 'data'
Here is the code to call the function.
class ViewModel {
private let networkManager = NetworkManager()
private var rovers = [Post]()
func getStories (){
networkManager
.getModel(Post.self, from: NetworkURLs.baseURL) {[weak self]result in
switch result{
case .success(let response):
self?.rovers = response.data.camera.map{$0.data} **// error on this line**
case .failure( let error):
print( error.localizedDescription)
}
}
}
Your response is of type Post which has no property data. You'll need to extract your photos array from the response, and then map across that array and retrieve the rovers property from it.
I think what you meant to write was
self?.rovers = response.photos.camera.map{$0.rover}
However even that won't work as your data structures don't match your JSON. From what can be seen, rover is a property on photo not on camera.
You will need to validate the JSON -> Model mapping
EDIT after JSON linked in comment below:
Using the JSON from the API, it confirms that camera and rover sit at the same level in the JSON:
{
"photos": [
{
"id": 102693,
"sol": 1000,
"camera": {
"id": 20,
"name": "FHAZ",
"rover_id": 5,
"full_name": "Front Hazard Avoidance Camera"
},
"img_src": "http://mars.jpl.nasa.gov/msl-raw-images/proj/msl/redops/ods/surface/sol/01000/opgs/edr/fcam/FLB_486265257EDR_F0481570FHAZ00323M_.JPG",
"earth_date": "2015-05-30",
"rover": {
"id": 5,
"name": "Curiosity",
"landing_date": "2012-08-06",
"launch_date": "2011-11-26",
"status": "active"
}
},
....
So you will need to change your data model:
struct Photo : Codable{
let id : Int
let sol : Int
let camera : Camera
let imgSrc: String
let earthDate: String
let rover: Rover
}
and then to decode it
self?.rovers = response.photos.map{$0.rover}
nb. in Swift all struct types should be capitalised by convention.
Your struct of type Post does not have a member called "data", indeed.
You seem to be assuming, that your response object is of type Photo - but the error message is telling you, that it is of type Post, which only holds an array of Photo objects.
Try something like:
response.photos[0] to get the first Photo object out of the array - if there is one.
Then, assuming you got one response.photos[0].data gives you a Camera object already - you seem to be calling via the type, instead of the member name.
So in case you want to go one step further and access a Rover object, you need to do: response.photos[0].data.data
I see, that you want to extract several Rovers, supposedly one from each Post, but this will clash with your initial rovers variable being assigned a type of an array of Posts - this means you have to change it to [Rover]. I'm not sure if the map-function is actually suitable for what you want to do here.
Using a loop, iterating through the Posts and appending Rover objects to the Rover array would be the "manual" way to do it.
Hope this helps.
Edit: because you have edited your model mid-question, I can't see where "Post" has gone now. My reply might only fit the way the original question was posted.

How do I parse the nested JSON data from Brawl Stars api in Swift?

The API website I used. I made an account -> Documentation -> Brawlers if you want to try it out
Here is part of the JSON I want to parse. I want to print the name inside the "starPowers" array as well as the "gadgets" array.
{
"items":[
{
"id":16000014,
"name":"BO",
"starPowers":[
{
"id":23000090,
"name":"CIRCLING EAGLE"
},
{
"id":23000148,
"name":"SNARE A BEAR"
}
],
"gadgets":[
{
"id":23000263,
"name":"SUPER TOTEM"
},
{
"id":23000289,
"name":"TRIPWIRE"
}
]
}
]
}
I tried this first way of parsing the JSON which worked but I couldn't find a way to print the "name" inside the "starPower" or "gadgets" array.
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let apiToken = "abcdefg"
if let url = URL(string: "https://api.brawlstars.com/v1/brawlers") {
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.addValue("Bearer \(apiToken)", forHTTPHeaderField: "authorization")
request.addValue("application/json", forHTTPHeaderField: "Accept")
URLSession.shared.dataTask(with: request) { (data, response, error) in
if error != nil {
print(error!)
} else {
guard let data = data else {return}
do {
let jsonResult = try JSONSerialization.jsonObject(with: data, options: .mutableContainers) as AnyObject
print(jsonResult)
if let items = jsonResult["items"] as? NSArray {
for item in items as [AnyObject] {
if let name = item["name"] {
print(name!)
}
if let gadgets = item["gadgets"] {
print(gadgets!)
}
if let starPowers = item["starPowers"]{
print(starPowers!)
}
}
}
} catch {
print("JSON processing failed: \(error.localizedDescription)")
}
}
}.resume()
} else {
print("Something went wrong")
}
}
}
So I added another file with this data:
struct StarPowers: Codable {
let starPowerName: String
}
struct Gadgets: Codable {
let gadgetName: String
}
struct Items: Codable {
let name: String
let starPowers: [StarPowers]
let gadgets: [Gadgets]
}
And replaced the code inside the do statement with this but it returned "JSON processing failed: The data couldn’t be read because it is missing." (Catch statement)
let items = try JSONDecoder().decode(Items.self, from: data)
print(items.name.first ?? "")
I'm still fairly new to Swift in general as well as StackOverflow, so any help or feedback will be greatly appreciated. Thanks!
When using the Codable APIs you want to model all the data you see. So this JSON:
{
"items":[
{
"id":16000014,
"name":"BO",
"starPowers":[
{
"id":23000090,
"name":"CIRCLING EAGLE"
},
{
"id":23000148,
"name":"SNARE A BEAR"
}
],
"gadgets":[
{
"id":23000263,
"name":"SUPER TOTEM"
},
{
"id":23000289,
"name":"TRIPWIRE"
}
]
}
]
}
should result in these structs:
//This represents one star power
struct StarPower: Codable {
let id: Int
let name: String
}
//This represents one gadget
struct Gadget: Codable {
let id: Int
let name: String
}
//This represents one "item" (I think they're brawlers, but I didn't make an account so I can't confirm what the API calls them
struct Item: Codable {
let id: Int
let name: String
let starPowers: [StarPower]
let gadgets: [Gadget]
}
However, the excerpt that you provided is actually of type [String:[Item]] (a dictionary (aka a JSON object) with one string key, with the value being an array of items. You could make a Codable struct to handle that, or you can just do this:
let decoded = try! JSONDecoder().decode([String:[Item]].self, from: data)
let items = decoded["items"]!
//items is an array of type [Item]
// Using your example, it would only have one element.
let element = items.first!
for starPower in element.starPowers {
print(starPower.name)
}
for gadget in element.gadgets {
print(gadget.name)
}
(keep in mind that I'm force-unwrapping and force-trying because I'm assuming that fetching the data worked fine and that it is correct. You should probably check these assumptions and use constructs such as if-let and do-catch)
Name the properties of your codable structs exactly as the keys in the corresponding JSON:
struct StarPower: Codable {
let name: String
}
struct Gadget: Codable {
let name: String
}
struct Item: Codable {
let name: String
let starPowers: [StarPower]
let gadgets: [Gadget]
}
The decoder couldn't find the data, because "gadgetName" and "starPowerName" are not part of the JSON you provided.
Another tip: use singular while naming structs, so one element of gadgets is a gadget. the decoder doesn't care how you name your structs, it only cares about the properties

Swift - Different output based on JSON response contents

This question will probably come off as very basic, and something that probably can be found through searching, but despite my efforts of searching StackOverflow and just google I can't find any up-to-date thread or post regarding how to handle the different responses of a REST API, and, as I've found out, having an up-to-date thread is important to save trouble down the road when errors occur. So, to jump into it, I have an API endpoint on my server for logging in. It responds, as one would assume, with either two cases given login credentials;
If the login information succeeds, it returns this JSON Object:
{
"user": {
"id": 1,
"type": "user",
"name": "username",
"api_token": "accesstokenhere"
},
"access_token": "accesstokenhere"
}
If it doesn't succeed, it gives this response
{
"message": "Invalid credentials"
}
Now I have the login screen for my app, upon pressing "log in", submit the information to the server and get this response back, which is not of issue and very well documented. I have the following code so far:
import SwiftUI
import Combine
import Foundation
public struct UserModel: Decodable {
let id: Int
let username: String
let age: Int
enum CodingKeys: String, CodingKey {
case id = "id"
case username = "name"
case age = "age"
}
}
public struct UserResponse: Decodable {
public let user: UserModel
public let accessToken: String
enum CodingKeys: String, CodingKey {
case user = "user"
case accessToken = "access_token"
}
}
public class UserFetcher: ObservableObject {
public let objectWillChange = PassthroughSubject<UserFetcher,Never>()
#Published var hasFinished: Bool = false {
didSet {
objectWillChange.send(self)
}
}
var user: UserResponse?
#Published var incorrectLogin: Bool = false {
didSet {
objectWillChange.send(self)
}
}
init(){
guard let url = URL(string: "https://mywebsite.com/api/login") else { return }
var urlRequest = URLRequest(url: url)
urlRequest.httpMethod = "POST"
URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
do {
if let d = data {
let decodedRes = try JSONDecoder().decode(UserResponse.self, from: d)
DispatchQueue.main.async {
self.user = decodedRes
self.hasFinished = true
print("Dispatching")
}
} else {
print("No Data")
}
} catch {
print("Error")
}
}.resume()
}
}
I have taken this section in its entirety except for minor tweaks to fit the different object from another file I have for a similar task, albeit that it has no alternate responses and so I didn't have to handle any other types of data responses.
I'm still fairly new to swift, so I have basic understanding of do-try-catch syntax, but I don't how I would catch different response models or where to place them in my code to prevent any errors from happening.
Ideally, I would like it to toggle the incorrectLogin variable, which can be observed and trigger a popup saying incorrect login information, as all login screens do when you input incorrect credentials. If it doesn't, it should just toggle the hasFinished variable and leave incorrectLogin as false, and then I would use the user model to do all of the behind the scenes stuff.
Again, I'm still fairly new to swift, I'm sure there's probably security issues here or something else I'm overlooking, and please, let me know if that's the case.
A suitable solution is an enum with associated values.
Add a struct for the error case
public struct ErrorResponse: Decodable {
let message : String
}
and the enum
enum Response : Decodable {
case success(UserResponse)
case failure(ErrorResponse)
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
do {
let userData = try container.decode(UserResponse.self)
self = .success(userData)
} catch DecodingError.typeMismatch {
let errorData = try container.decode(ErrorResponse.self)
self = .failure(errorData)
}
}
}
After decoding the data switch on the result and handle the cases
do {
if let d = data {
let result = try JSONDecoder().decode(Response.self, from: d)
switch result {
case .success(let userData):
DispatchQueue.main.async {
self.user = userData
self.hasFinished = true
print("Dispatching")
}
case .success(let errorData):
print(errorData.message)
// handle the error
}
} else {
print("No Data")
}
} catch {
print(error) // never print a meaningless literal string in a Decoding catch block
}

Parsing JSON With Codable in Swift

I am attempting to parse JSON with codable in Swift. I have successfully done this before, but I have a more complicated JSON object with some arrays and I am having trouble.
Here is my JSON:
{
"data": [ {
"type":"player",
"id":"account.7e5b92e6612440349afcc06b7c390114",
"attributes": {
"createdAt":"2018-04-06T04:59:40Z",
"name":"bob",
"patchVersion":"",
"shardId":"pc-na",
"stats":null,
"titleId":"bluehole-pubg",
"updatedAt":"2018-04-06T04:59:40Z"
},
"relationships": {
"assets": {
"data":[]
},
"matches": {
"data": [
{"type":"match","id":"3e2a197a-1453-4569-b35b-99e337dfabc5"},
{"type":"match","id":"15f41d2f-9da2-4b95-95ca-b85e297e14b7"},
{"type":"match","id":"a42c496c-ad92-4d3e-af1f-8eaa2e200c2b"}
{"type":"match","id":"b6e33df5-4754-49da-9a0f-144842bfc306"},
{"type":"match","id":"5b357cd1-35fe-4859-a2d7-48f263120bbd"},
{"type":"match","id":"99fc5f81-c24c-4c82-ae03-cd21c94469c0"},
{"type":"match","id":"1851c88e-6fed-48e8-be84-769f20f5ee6f"},
{"type":"match","id":"e16db7ea-520f-4db0-b45d-649264ac019c"},
{"type":"match","id":"6e61a7e7-dcf5-4df5-aa88-89eca8d12507"},
{"type":"match","id":"dcbf8863-9f7c-4fc9-b87d-93fe86babbc6"},
{"type":"match","id":"0ba20fbb-1eaf-4186-bad5-5e8382558564"},
{"type":"match","id":"8b104f3b-66d5-4d0a-9992-fe053ab4a6ca"},
{"type":"match","id":"79822ea7-f204-47f8-ae6a-7efaac7e9c90"},
{"type":"match","id":"1389913c-a742-434a-80c5-1373e115e3b6"}
]
}
},
"links": {
"schema":"",
"self":"https://api.playbattlegrounds.com/shards/pc-na/players/account.7e5b92e6612440349afcc06b7c390114"
}
}],
"links": {
"self":"https://api.playbattlegrounds.com/shards/pc-na/players?filter[playerNames]=dchilds64"
},
"meta":{}
}
Here are the models I am using:
public struct PlayerResponse: Codable {
let data: [Player]
}
For Player:
public struct Player: Codable {
let type: String
let id: String
let attributes: Attributes
let relationships: Relationships
}
For Attributes:
public struct Attributes: Codable {
let name: String
let patchVersion: String
let shardId: String
let titleId: String
let updatedAt: String
}
For Relationships:
public struct Relationships: Codable {
let matches: Matches
}
For Matches:
public struct Matches: Codable {
let data: [Match]
}
For Match:
public struct Match: Codable {
let type: String
let id: String
}
Decoding as:
let players = try decoder.decode([Player].self, from: jsonData)
I have this function which runs my network request:
func getPlayerData(for name: String, completion: ((Result<[Player]>) -> Void)?) {
var urlComponents = URLComponents()
urlComponents.scheme = "https"
urlComponents.host = "api.playbattlegrounds.com"
urlComponents.path = "/shards/\(regionShard.rawValue)/players"
let playerNameItem = URLQueryItem(name: "filter[playerNames]", value: "\(name)")
urlComponents.queryItems = [playerNameItem]
guard let url = urlComponents.url else { fatalError("Could not create URL from components") }
print(url)
var request = URLRequest(url: url)
request.httpMethod = "GET"
request.setValue("bearer \(apiKey)", forHTTPHeaderField: "Authorization")
request.setValue("application/vnd.api+json", forHTTPHeaderField: "Accept")
let config = URLSessionConfiguration.default
let session = URLSession(configuration: config)
let task = session.dataTask(with: request) { (responseData, response, responseError) in
DispatchQueue.main.async {
if let error = responseError {
completion?(.failure(error))
} else if let jsonData = responseData {
let decoder = JSONDecoder()
do {
let players = try decoder.decode([Player].self, from: jsonData)
completion?(.success(players))
} catch {
completion?(.failure(error))
}
} else {
let error = NSError(domain: "", code: 0, userInfo: [NSLocalizedDescriptionKey : "Data was not retrieved from request"]) as Error
completion?(.failure(error))
}
}
}
task.resume()
}
The problem I am facing is that I get this error when I try to run the network request:
I think there is an issue with my codable structs, but I'm not sure. Could someone point me in the right direction to look for my error?
I suggest you build this up from the ground, since the errors of JSONDecoder (as with any compiler) get worse the more involved your structures are. Let's see how far we get:
Your Match struct is pretty sound:
public struct Match: Codable {
let type: String
let id: String
}
let decoder = JSONDecoder()
let mData = """
{"type":"match","id":"3e2a197a-1453-4569-b35b-99e337dfabc5"}
""".data(using:.utf8)!
let match = try! decoder.decode(Match.self, from:mData)
print(match)
No unexpected problems here. Shortening Matches a bit you already get your first error, rather an unexpected one
public struct Matches: Codable {
let data: [Match]
}
let mtchsData = """
{
"data": [
{"type":"match","id":"3e2a197a-1453-4569-b35b-99e337dfabc5"},
{"type":"match","id":"15f41d2f-9da2-4b95-95ca-b85e297e14b7"},
{"type":"match","id":"a42c496c-ad92-4d3e-af1f-8eaa2e200c2b"}
{"type":"match","id":"b6e33df5-4754-49da-9a0f-144842bfc306"},
{"type":"match","id":"5b357cd1-35fe-4859-a2d7-48f263120bbd"}
]
}
""".data(using:.utf8)!
do {
let mtches = try decoder.decode(Matches.self, from:mtchsData)
print(mtches)
} catch {
print(error)
}
will print the following error:
"dataCorrupted(Swift.DecodingError.Context(codingPath: [],
debugDescription: "The given data was not valid JSON.",
underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840
"Badly formed array around character 233."
UserInfo={NSDebugDescription=Badly
formed array around character 233.})))\n"
This is a trivial error, you are missing a comma on line 3 of the data Array. Adding that all will go well, but if it comes like this from your service you will have to fix it first.
I guess you get the idea and know your way about building up the structure successively. On the top level you will notice that your top level structure goes beyond an array of Player, it is actually a Dictionary with "data" as its sole key as you modelled correctly in PlayerResponse as #AnkitJayaswal pointed out already. That makes two errors already, those are the ones I managed to spot easily, but as I suggested before you should continue the build up of tests, that way you will know that the "lower" levels parse correctly and can concentrate on the problem at hand.
All of the above works easily in a Playground and there is no need to actually call the WebService in the process. Of course you will have to import Cocoa, but you already knew that. Anyway it always helps to reduce the level of complexity by splitting up your problem into smaller parts.
As I can see your whole player response is in key data. And your parsing player info with Player codable struct directly rather than data key which is used in PlayerResponse codable struct.
To resolve this update your code as:
let players = try decoder.decode(PlayerResponse.self, from: jsonData)
Hope this will solve your problem.