Im new at this and having some trouble getting my head around how this all works.
I have this struct:
struct EventDetail:Decodable {
let EventName: String
let EventInformation: String
let EventStartDate: Date
let EventEndDate: Date
}
And this func to download the json:
func downloadJSON(completed: #escaping () -> ()) {
let url = URL(string: "http://someurl.php")
URLSession.shared.dataTask(with: url!) { (data, response, error) in
if error == nil {
do {
self.events = try JSONDecoder().decode([EventDetail].self, from: data!)
DispatchQueue.main.async {
completed()
}
}catch {
print(error)
}
}
}.resume()
}
}
This is the JSON error I get:
JSON Error
typeMismatch(Swift.Double, Swift.DecodingError.Context(codingPath: [Foundation.(_JSONKey in _12768CA107A31EF2DCE034FD75B541C9)(stringValue: "Index 0", intValue: Optional(0)), Cosplay_Life.EventDetail.(CodingKeys in _52013DB7ECF3BE1EBFBF83BE6BA8F9E9).EventStartDate], debugDescription: "Expected to decode Double but found a string/data instead.", underlyingError: nil))
This all seems to be working if the EventStartDate and EventEndDate is a String, but I do wish to have it as a Date because I will sort data later using the date field. The value that gets downloaded is in the format "yyyy-MM-dd" eks: "2017-02-25" What am I doing wrong?
A date is a Double in that it is the number of ms since the epoch.
Try this:
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
// Use format appropriate to your JSON String. This is for ISO-8601
// You MUST account for the milliseconds even if you don't want them
// or it won't parse properly
dateFormatter.dateFormat = "yy-MM-dd'T'HH:mm:ss.SSS"
eventDetail.EventStartDate = dateFormatter.date(from: jsonEventStartDateField)!
eventDetail.EventEndDate = dateFormatter.date(from: jsonEventEndDateField)!
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 am trying to call an API and I am trying to map the response to a model class. But each time, I am getting a an error that invalid data.
Api call is as follows.
let endpoint = "http://apilayer.net/api/live?access_key=baab1114aeac6b4be74138cc3e6abe79¤cies=EUR,GBP,CAD,PLN,INR&source=USD&format=1"
guard let url = URL(string: endpoint) else {
completed(.failure(.invalidEndPoint))
return
}
let task = URLSession.shared.dataTask(with: url) { data, response, error in
if let _ = error {
completed(.failure(.unableToComplete))
return
}
guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
completed(.failure(.invalidResponse))
return
}
guard let data = data else {
completed(.failure(.invalidData))
return
}
do {
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let convertedValues = try decoder.decode([Converted].self, from: data)
completed(.success(convertedValues))
} catch {
completed(.failure(.invalidData))
}
}
task.resume()
}
Sample Response is as follows.
{
"success":true,
"terms":"https:\/\/currencylayer.com\/terms",
"privacy":"https:\/\/currencylayer.com\/privacy",
"timestamp":1610986207,
"source":"USD",
"quotes":{
"USDEUR":0.828185,
"USDGBP":0.736595,
"USDCAD":1.276345,
"USDPLN":3.75205
}
}
Model class
struct Converted: Codable {
let success: Bool
let terms, privacy: String?
let timestamp: Int?
let source: String?
let quotes: [String: Double]?
}
Can someone help me to understand where I am getting wrong?. Thanks in advance.
Error messages can sometimes help. If you catch the error and print it, it reads:
typeMismatch(Swift.Array, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array but found a dictionary instead.", underlyingError: nil))
So an array of Converted was expected when in fact a single object was returned. The solution is to update the expectation to match the actual response:
let convertedValues = try decoder.decode(Converted.self, from: data)
I'm new to Swift language and I'm trying to build a simple JSON parsing App in Xcode.
Here's an error I get: typeMismatch(Swift.Double, Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "Date", intValue: nil)], debugDescription: "Expected to decode Double but found a string/data instead.", underlyingError: nil))
Here's a link a want to parse: https://www.cbr-xml-daily.ru/daily_json.js
What model do I have for it: (I used https://app.quicktype.io for creating it)
struct Welcome: Codable {
var date, previousDate: Date
var previousURL: String
var timestamp: Date
var valute: [String: Valute]
enum CodingKeys: String, CodingKey {
case date = "Date"
case previousDate = "PreviousDate"
case previousURL = "PreviousURL"
case timestamp = "Timestamp"
case valute = "Valute"
}
}
struct Valute: Codable {
var id, numCode, charCode: String
var nominal: Int
var name: String
var value, previous: Double
enum CodingKeys: String, CodingKey {
case id = "ID"
case numCode = "NumCode"
case charCode = "CharCode"
case nominal = "Nominal"
case name = "Name"
case value = "Value"
case previous = "Previous"
}
}
And here's a code for parsing and decoding:
func request(urlString: String, completion: #escaping (Welcome?, Error?) -> Void) {
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
if let error = error {
print("Some error")
completion(nil, error)
return
} else {
guard let data = data else { return }
do {
let welcome = try JSONDecoder().decode(Welcome.self, from: data)
completion(welcome, nil)
} catch let jsonError {
print("Failed to decode JSON: \(jsonError)")
completion(nil, jsonError)
}
}
} .resume()
}
Will be really grateful for any help/advice.
This is because you're not decoding Date properly.
Try setting dateDecodingStrategy for your JSONDecoder:
do {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .iso8601
let welcome = try decoder.decode(Welcome.self, from: data)
completion(welcome, nil)
} catch let jsonError {
print("Failed to decode JSON: \(jsonError)")
completion(nil, jsonError)
}
I am a complete newby to Swift and have a JSON file that im looking to decode, bit its just an array of objects (strings directing to other JSON Paths) with no object definition:
Example of list.json:
[
"filepath1.json",
"filepath2.json",
"filepath3.json",
"filepath4.json",
"filepath5.json",
"filepath6.json",
"filepath7.json",
"filepath8.json",
"filepath9.json"
]
when trying to decode i am getting an error:
typeMismatch(Swift.Dictionary, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Dictionary but found an array instead.", underlyingError: nil))
For the purpose of the example i have removed the web url (supplierURL) where the file is stored
example decoding that I'm using:
let supplierURL = "list.json"
func getSuppliers() {
let urlString = supplierURL
performRequest(urlString: urlString)
}
func performRequest(urlString: String) {
if let url = URL(string: urlString) {
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error!)
return
}
if let safeData = data {
self.parseJSON(supplierList: safeData)
}
}
task.resume()
}
}
func parseJSON(supplierList: Data){
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode([SupplierList].self, from: supplierList)
print(decodedData)
} catch {
print(error)
}
}
I have also tried:
let decodedData = try decoder.decode(Array<SupplierList>.self, from: supplierList)
and
let decodedData = try decoder.decode(SupplierList[0].self, from: supplierList)
with no luck.
any assistance is appreciated 😊
Try:
let list = try decoder.decode([String].self, from: supplierList)
You didn't show SupplierList, but you should be able to construct it from that array.
I would like to assign a value from JSON to a variable, the issue is Swift thinks I am passing an entire array and not just the JSON value of code to the variable.
I have the following structure and JSON decode function:
private func JSONFunction() {
guard let url = URL(string: "https://example.com/example/example"),
let nameValue = stringValue.addingPercentEncoding(withAllowedCharacters: .urlQueryValueAllowed)
else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = "name=\(nameValue)".data(using: .utf8)
URLSession.shared.dataTask(with: request) { data, _, error in
guard let data = data else { return }
do {
let myData = try JSONDecoder().decode(codeStruct.self, from:data)
DispatchQueue.main.async {
codeNum = myData.code
print(codeNum)
}
}
catch {
print(error)
}
}.resume()
}
The following is the structure for decoding the JSON:
struct codeStruct: Codable {
let code: String
let line: String
let person: String
let maker: String
}
Error received:
typeMismatch(Swift.Dictionary,
Swift.DecodingError.Context(codingPath: [], debugDescription:
"Expected to decode Dictionary but found an array
instead.", underlyingError: nil))
Without looking at the json, if I were to guess, I would say that your incoming JSON is actually an array of codeStruct objects, for which you should change your line to
let myData = try JSONDecoder().decode([codeStruct].self, from:data)