|
(i have the below code for decoding JSON data, but i am not getting th response. Instead, I get "Unknown error", which means the URL is correct, but i am not able to define where the problem is.)
import SwiftUI
struct Response: Decodable {
var users : [User]
}
struct User: Decodable {
let id: String
let isActive: Bool
let name: String
let age: Int
let company: String
let email: String
let address: String
let about: String
let registered: String
let tags: [String]
let friends : Friends
struct Friends: Decodable {
let id: String
let name: String
}
}
struct ContentView: View {
@State var users = [User]()
var body: some View {
List{
ForEach(users, id: \.id) { item in
VStack(alignment: .leading){
Text(item.name)
.font(.headline)
Text(item.company)
}
}
}
.onAppear(perform: loadData)
}
func loadData() {
guard let url = URL(string: "https://www.hackingwithswift.com/samples/friendface.json") else {
print("Invalid URL")
return
}
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) {data, response, error in
if let data = data {
if let decodedResponse = try? JSONDecoder().decode(Response.self, from: data) {
DispatchQueue.main.async {
self.users = decodedResponse.users
}
return
}
}
print("Fetch failed: \(error?.localizedDescription ?? "Unknown error")")
}.resume()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
|
|
hi,
URLSession is not one of my strong suits, but i have two ideas.
(1) the first debugging step is to catch any error thrown in the JSON decoding, perhaps something like this
if let data = data {
do {
let decodedResponse = try JSONDecoder().decode(Response.self, from: data)
DispatchQueue.main.async {
self.users = decodedResponse.users
}
} catch let jsonError as NSError {
print("JSON decode failed: \(jsonError.localizedDescription)")
}
return
}
(2) i see that you have defined let friends: Friends , but Friends is a struct containing a single id and name. did you mean to write let friends: [Friend] , where Friend is a struct containing a single id and name?
hope that helps,
DMG
|
|
As DMG say let friends: Friends should read let friends: [Friends] as you get an array back from the URL json which is causing the error.
|
|
Hello delawaremathguy & NigelGee ,
your reply is highly appreciated.
I revised the code to be let friends: [Friends] instead of let friends: Friends and still the same. However, also applied the below code as per delawaremathguy suggestion, now i get this message: JSON decode failed: The data couldn’t be read because it isn’t in the correct format.
So, what does that mean? (keep in mind i am still learning ..)
if let data = data {
do {
let decodedResponse = try JSONDecoder().decode(Response.self, from: data)
DispatchQueue.main.async {
self.users = decodedResponse.users
}
} catch let jsonError as NSError {
print("JSON decode failed: \(jsonError.localizedDescription)")
}
return
}
|
|
hi,
well, unfortunately, the error "The data couldn’t be read because it isn’t in the correct format" means that the data couldn’t be read because it isn’t in the correct format. (AAARRGGHH!)
(1) it would help to see what the data is that you are reading and whether it matches up against your definition.
(2) you could use a more informative catch handler. replace the generic catch handler
} catch let jsonError as NSError {
print("JSON decode failed: \(jsonError.localizedDescription)")
}
with this extended one that i have used in the past:
} 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)")
}
see if that tells you anything more.
hope that helps,
DMG
|
|
Great...
sure that told me more,
by applying your code, I got this error :
type mismatch for type Dictionary<String, Any> in JSON: Expected to decode Dictionary<String, Any> but found an array instead.
then i changed this part of code:
let decodedResponse = try JSONDecoder().decode(Response.self, from: data)
to:
let decodedResponse = try JSONDecoder().decode(Array<User>.self, from: data)
and it worked perfectly.
MANY MANY THANKS
|
|
Tweaks to your original code to make it work. See the comments for explanation of changes.
import SwiftUI
//don't need a Response struct
//struct Response: Decodable {
// var users : [User]
//}
struct User: Decodable {
let id: String
let isActive: Bool
let name: String
let age: Int
let company: String
let email: String
let address: String
let about: String
let registered: Date //should be a Date, not a String
let tags: [String]
let friends : [Friend] //array of Friend structs
struct Friend: Decodable { // a single Friend, not multiple Friends
let id: String
let name: String
}
}
struct ContentView: View {
@State var users = [User]()
var body: some View {
List{
ForEach(users, id: \.id) { item in
VStack(alignment: .leading){
Text(item.name)
.font(.headline)
Text(item.company)
}
}
}
.onAppear(perform: loadData)
}
func loadData() {
guard let url = URL(string: "https://www.hackingwithswift.com/samples/friendface.json") else {
print("Invalid URL")
return
}
let request = URLRequest(url: url)
URLSession.shared.dataTask(with: request) {data, response, error in
if let data = data {
//pull out the declaration of the decoder object
let decoder = JSONDecoder()
//set how we want to interpret dates in the JSON
decoder.dateDecodingStrategy = .iso8601
//decode directly to an array of User structs rather than a Response
if let decodedResponse = try?
decoder.decode([User].self, from: data) {
DispatchQueue.main.async {
//decodedResponse is now [User] rather than Response.User
self.users = decodedResponse
}
return
}
}
print("Fetch failed: \(error?.localizedDescription ?? "Unknown error")")
}.resume()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
|
|
How we can access friend properties?
|
|
I had the same exact problem and this was a very helpful thread for solving the problem and learning how to debug using error handling in the catch block. Thanks!
|