when try a json file to decode, with the Jsondecoder().decode i get the errwor:
Thread 7: Fatal error: 'try!' expression unexpectedly raised an error: Swift.DecodingError.typeMismatch(Swift.Array, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array but found a dictionary instead.", underlyingError: nil))
what does the compiler want to tell me and where is the error in Code?
URLSession.shared.dataTask(with: url) { (dataLocation, _, _) in
if let dataLocation = dataLocation {
let JsonData = try! JSONDecoder().decode([LandmarkLocation].self, from: dataLocation)
print(JsonData.count)
}
}
.resume()
I also added the decodable protocol to the LandmarkLocation structure
Try not to decode the LandmarkLocation object as an array
if let dataLocation = dataLocation {
let JsonData = try JSONDecoder().decode(LandmarkLocation.self, from: dataLocation)
print(JsonData.count)
}
Related
I'm new to Swift 5.3 and having trouble retrieving my nested JSON data.
My JSON data result looks like this:
{
"sites":[
{
"site_no":"16103000",
"station_nm":"Hanalei River nr Hanalei, Kauai, HI",
"dec_lat_va":22.1796,
"dec_long_va":-159.466,
"huc_cd":"20070000",
"tz_cd":"HST",
"flow":92.8,
"flow_unit":"cfs",
"flow_dt":"2020-08-18 07:10:00",
"stage":1.47,
"stage_unit":"ft",
"stage_dt":"2020-08-18 07:10:00",
"class":0,
"percentile":31.9,
"percent_median":"86.73",
"percent_mean":"50.77",
"url":"https:\/\/waterdata.usgs.gov\/hi\/nwis\/uv?site_no=16103000"
}
]
}
My structs look like this:
struct APIResponse: Codable {
let sites: APIResponseSites
}
struct APIResponseSites: Codable {
let station_nm: String
let stage: Float
}
And my Decode SWIFT looks like this:
let task = URLSession.shared.dataTask(with: url, completionHandler: {
data, _, error in
guard let data = data, error == nil else {
return
}
var result: APIResponse?
do {
result = try JSONDecoder().decode(APIResponse.self, from: data)
}
catch {
print("Failed to decode with error: \(error)")
}
guard let final = result else {
return
}
print(final.sites.station_nm)
print(final.sites.stage)
})
And of course, I get an error that states:
Failed to decode with error:
typeMismatch(Swift.Dictionary<Swift.String, Any>,
Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue:
"sites", intValue: nil)], debugDescription: "Expected to decode
Dictionary<String, Any> but found an array instead.", underlyingError:
nil))
I know it has to do with 'sites' returning an array (a single one) but I don't know how to fix it. Any help would be greatly appreciated.
The error message it is pretty clear you need to parse an array of objects instead of a single object.
Just change your root declaration property from
let sites: APIResponseSites
to
let sites: [APIResponseSites]
**1.** First "sites" is an array so replace
let sites: APIResponseSites
with
let sites: [APIResponseSites]()
**2.** As sites is a array collection, please print value like given below:
print(final.sites.first?.station_nm ?? "")
print(final.sites.first?.stage ?? 0.0)
Final code is here:
struct APIResponse: Codable {
let sites: [APIResponseSites]()
}
struct APIResponseSites: Codable {
let station_nm: String
let stage: Float
}
let task = URLSession.shared.dataTask(with: url, completionHandler: {
data, _, error in
guard let data = data, error == nil else {
return
}
var result: APIResponse?
do {
result = try JSONDecoder().decode(APIResponse.self, from: data)
}
catch {
print("Failed to decode with error: \(error)")
}
guard let final = result else {
return
}
print(final.sites.first?.station_nm ?? "")
print(final.sites.first?.stage ?? 0.0)
})
I'm trying to learn to make an API call in swiftUI, I'm following the next tutorial https://www.youtube.com/watch?v=1en4JyW3XSI but the code is giving me an error that I can't find a solution for.
PostList.swift
import SwiftUI
struct PostList: View {
#State var posts: [Post] = []
var body: some View {
List(posts) { post in
Text(post.title)
}
.onAppear(){
Api().getPosts { (posts) in
self.posts = posts
}
}
}
}
struct PostList_Previews: PreviewProvider {
static var previews: some View {
PostList()
}
}
Data.swift
import SwiftUI
struct Post: Codable, Identifiable {
var id = UUID()
var title: String
var body: String
}
class Api{
func getPosts(completition: #escaping([Post]) -> ()){
guard let url = URL(string: "https://jsonplaceholder.typicode.com/posts") else { return }
URLSession.shared.dataTask(with: url) { (data, _, _) in
let posts = try! JSONDecoder().decode([Post].self, from: data!)
DispatchQueue.main.async {
completition(posts)
}
}
.resume()
}
}
The error that I'm getting is on here let posts = try! JSONDecoder().decode([Post].self, from: data!) and it's the next:
Thread 4: Fatal error: 'try!' expression unexpectedly raised an error:
Swift.DecodingError.typeMismatch(Swift.String,
Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index
0", intValue: 0), CodingKeys(stringValue: "id", intValue: nil)],
debugDescription: "Expected to decode String but found a number
instead.", underlyingError: nil))
I have noticed that the guy in the tutorial uses let id = UUID() but that gives me a problem as well and I'm being asked to change it to var id = UUID().
I'm sorry if it's a very simple or stupid question, I just can't see to find a way around it.
You can see the exact problem by adding try - catch block for the exact error.
Like this
URLSession.shared.dataTask(with: url) { (data, _, _) in
do {
let posts = try JSONDecoder().decode([Post].self, from: data!)
DispatchQueue.main.async {
completition(posts)
}
} catch {
print(error.localizedDescription)
}
}
So now the error is printed:
The data couldn’t be read because it isn’t in the correct format.
It means you are decoding the wrong type.
The problem is here
struct Post: Codable, Identifiable {
var id = UUID() //< Here
Here in json id have Int type and you are using UUID type.
So just change the data type UUID to Int. Like this
struct Post: Codable, Identifiable {
var id : Int //< Here
I am trying to convert JSON file to swift object, but I could not figure it out why it returns nill. as you use from code, I have two objects one Stakeholder and other MHSGroup I created a struct to hold data colled
StakeholderMHSGroup
struct StakeholderMHSGroup : Codable {
var stakeholders:[Stakeholder]?
var mhsGroups:[MhsGroup]?
}
main swift
let jsonStr = "{\"stakeholders\":[{\"id\":\"d95bb600-f63b-4ec7-bd2f-d14bdf1c145f\",\"firstName\":\"John\",\"lastName\":\"Doe\",\"emailAddress\":\"John.Doe#mail.com\",\"salutation\":\"Ms\"},{\"id\":\"d95bb600-f63b-4ec7-bd2f-d14bdf1c145d\",\"firstName\":\"John\",\"lastName\":\"Doe\",\"emailAddress\":\"John.Doe#mail.com\",\"salutation\":\"Ms\"},{\"id\":\"d95bb600-f63b-4ec7-bd2f-d14bdf1c545f\",\"firstName\":\"John\",\"lastName\":\"Doe\",\"emailAddress\":\"John.Doe#mail.com\",\"salutation\":\"Ms\"},{\"id\":\"d95bb600-f63b-4ec7-bd2f-d14baf1c145f\",\"firstName\":\"John\",\"lastName\":\"Doe\",\"emailAddress\":\"John.Doe#mail.com\",\"salutation\":\"Ms\"}],\"mhsGroups\":[{\"id\":\"495919eb-dcbc-48c5-99f5-48f6790b79e3\",\"name\":\"Group1\",\"membersCount\":5,\"createdDate\":\"2012-04-23T18:25:43.511Z\"},{\"id\":\"495919eb-dcbc-48c5-99f5-48f6290b79e3\",\"name\":\"Group1\",\"membersCount\":5,\"createdDate\":\"2012-04-23T18:25:43.511Z\"},{\"id\":\"495919eb-dcbc-48c5-99f5-48f6790b79e4\",\"name\":\"Group1\",\"membersCount\":5,\"createdDate\":\"2012-04-23T18:25:43.511Z\"},{\"id\":\"495919eb-dcbc-48c5-99f5-48f6790b79e2\",\"name\":\"Group1\",\"membersCount\":5,\"createdDate\":\"2012-04-23T18:25:43.511Z\"}]}"
"{"stakeholders":[{"id":"d95bb600-f63b-4ec7-bd2f-d14bdf1c145f","firstName":"John","lastName":"Doe","emailAddress":"John.Doe#mail.com","salutation":"Ms"},{"id":"d95bb600-f63b-4ec7-bd2f-d14bdf1c145d","firstName":"John","lastName":"Doe","emailAddress":"John.Doe#mail.com","salutation":"Ms"},{"id":"d95bb600-f63b-4ec7-bd2f-d14bdf1c545f","firstName":"John","lastName":"Doe","emailAddress":"John.Doe#mail.com","salutation":"Ms"},{"id":"d95bb600-f63b-4ec7-bd2f-d14baf1c145f","firstName":"John","lastName":"Doe","emailAddress":"John.Doe#mail.com","salutation":"Ms"}],"mhsGroups":[{"id":"495919eb-dcbc-48c5-99f5-48f6790b79e3","name":"Group1","membersCount":5,"createdDate":"2012-04-23T18:25:43.511Z"},{"id":"495919eb-dcbc-48c5-99f5-48f6290b79e3","name":"Group1","membersCount":5,"createdDate":"2012-04-23T18:25:43.511Z"},{"id":"495919eb-dcbc-48c5-99f5-48f6790b79e4","name":"Group1","membersCount":5,"createdDate":"2012-04-23T18:25:43.511Z"},{"id":"495919eb-dcbc-48c5-99f5-48f6790b79e2","name":"Group1","membersCount":5,"createdDate":"2012-04-23T18:25:43.511Z"}]}"
var data:Data?
data = jsonStr.data(using: .utf8)!
let userGroup = try! JSONDecoder().decode(StakeholderMHSGroup.self, from: data!)
print(userGroup)
update debug error
hread 1: Fatal error: 'try!' expression unexpectedly raised an error:
Swift.DecodingError.typeMismatch(Swift.Double,
Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue:
"mhsGroups", intValue: nil), _JSONKey(stringValue: "Index 0",
intValue: 0), CodingKeys(stringValue: "createdDate", intValue: nil)],
debugDescription: "Expected to decode Double but found a string/data
instead.", underlyingError: nil))
Judging from the error message, you seem to have declared createdDate as a Double, but in the JSON, the value associated with createdDate is a string.
You should declare createDate as a Date (you could always use createdDate.timeIntervalSince1970 if you want a Double), and set dateDecodingStrategy of the decoder to iso8601, because your dates seem to be in that format:
let decoder = JSONDecoer()
decoder.dateDecodingStrategy = .iso8601
// you shouldn't really use "try!" here...
let userGroup = try! decoder.decode(StakeholderMHSGroup.self, from: data!)
I receive the following error:
Error serialising json typeMismatch(Swift.Dictionary, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Dictionary but found an array instead.", underlyingError: nil))
Code:
//---------------
struct Currency: Decodable {
let symbol: String
let price: String
}
var myDict: [Currency] = []
//---------------
func testParse(){
let jsonUrlString = "https://api.binance.com/api/v3/ticker/price"
guard let url = URL(string: jsonUrlString) else
{ return }
URLSession.shared.dataTask(with: url) { (data,response,err) in
guard let data = data else
{
print("Error: No data to decode")
return
}
do
{
let exchanges = try
JSONDecoder().decode(Currency.self, from: data)
let X: [Currency] = [exchanges]
self.myDict = X
self.testFunc()
print("binance: "+self.myDict[0].symbol + ": "+self.myDict[0].price)
}
catch let jsonErr
{
print("Error serialising json",jsonErr)
}
}
.resume()
}
Is the issue with my struct layout? Or would it be how I'm parsing? I'd like to get a better understanding here for future reference. Therefore, if anyone could link a good Swift 4 guide it would be greatly appreciated. Alternatively, if you could give me a detailed answer that would be great (rather than spoon feeding the answer where I don't learn).
Please read the error message carefully and learn to understand it. It's very clear.
Expected to decode Dictionary but found an array instead
In other words: You want to decode a dictionary (Currency) but in truth it's an array ([Currency]).
In terms of Decodable a dictionary is the target struct or class.
And please don't name an object as ...dict which is actually an array.
var myArray = [Currency]()
...
let exchanges = try JSONDecoder().decode([Currency].self, from: data)
self.myArray = exchanges
How to check Optionals in Swift for nil values?
I'm trying to evaluate if parsing JSON was successful. This should print a error message when invalid json is passed, but fails with EXC_BAD_INSTRUCTION.
func parseJson(data: NSData) {
var error: NSError?
var json: NSDictionary = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &error) as NSDictionary
if let err = error {
println("error parsing json")
return
}
println("json parsed successfully")
}
Try running with valid json (works fine):
parseJson(NSData(data: "{}".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)))
Try with invalid json:
parseJson(NSData(data: "123".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)))
Did I miss the point on working with Optionals?
The crash is happening because JSONObjectWithData returns nil if there's an error. You then try to cast nil to NSDictionary. This works:
func parseJson(data: NSData) {
var error: NSError?
var json : AnyObject! = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &error)
if let err = error {
println("error parsing json")
return
}
println("json parsed successfully")
var jsonDict = json as NSDictionary
}
If you prefer, you can switch the type of var json to NSDictionary? so it directly handles nil. To do this you just need to add a '?' to the "as" keyword as so:
func parseJson(data: NSData) {
var error: NSError?
var json: NSDictionary? = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &error) as? NSDictionary
if let err = error {
println("error parsing json")
return
}
// unwrap the optional value
if let jsonDictionary = json {
println("json parsed successfully")
// use the dictionary
}
}
I restructured your code a bit:
func parseJson(data: NSData) {
var error: NSError?
var json: AnyObject? = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &error)
if let jsonDict: NSDictionary = json as? NSDictionary{
println("json parsed successfully")
//do something with the nsdictionary...
}
else{
println("error parsing json")
}
}
parseJson(NSData(data: "{}".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)))
parseJson(NSData(data: "123".dataUsingEncoding(NSUTF8StringEncoding, allowLossyConversion: true)))
The problem wasn't with your if code, but making json an NSDictionary when it isn't one when the method fails. Also, this code now checks if an NSDictionary was returned instead of checking for the error itself.
NSJSONSerialization.JSONObjectWithData returns an optional value that has been implicitly unwrapped, which means that the return value could be nil. In the invalid JSON case, a nil optional type was returned. So, your var json must also be an optional type since it could be nil. Further more, if var json is optional type then you must downcast it as optional by adding ? next to as. This is the final result
var json: NSDictionary? = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &error) as? NSDictionary
I was getting fatal error: unexpectedly found nil while unwrapping an Optional value until I made the NSDictionary type casting optional, e.g. as? NSDictionary instead of as Dictionary