Ive got an answer from an XML API that comes back to me as a String. I need it to be an int so that i can add it to another value in laters on. Ive tried to unwrap it and read it as an int but it didnt work. Ive also tried trimming blank spaces and then unwrap it but that didnt work either.
If i set the leading let value: Int it will give me an error saying that the value is not in the correct format.
What i have so far is this:
import UIKit
import Foundation
struct HydroData: Decodable {
let value: String
let textTranslationId: String?
let titleTranslationId: String?
let style: String?
}
struct HydroResult: Decodable {
let HydroData: [HydroData]
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
calcIndex()
let url = URL(string: "https://driftsdata.statnett.no/restapi/ProductionConsumption/GetLatestDetailedOverview")!
let task = URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data else {
print("No data")
return
}
do {
let result = try JSONDecoder().decode(HydroResult.self, from: data)
if let seDesc = result.HydroData.filter({ $0.titleTranslationId == "ProductionConsumption.HydroSEDesc" }).first {
let hydroValue = seDesc.value
print(seDesc.value)
} else {
print("Error: no value")
}
} catch {
print(error.localizedDescription)
}
}
task.resume() }
func calcIndex(){
let newHydro = hydroValue + 1000
print(newHydro)
}
}
Optional
(你猜我猜不猜你猜)
2
struct HydroData {
let value: Int
let textTranslationId: String?
let titleTranslationId: String?
let style: String?
enum CodingKeys: String, CodingKey {
case value
case textTranslationId
case titleTranslationId
case style
}
}
extension HydroData: Decodable {
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
value = Int(try container.decode(String.self, forKey: .value)) ?? 0
textTranslationId = try container.decode(String.self, forKey: .textTranslationId)
titleTranslationId = try container.decode(String.self, forKey: .titleTranslationId)
style = try container.decode(String.self, forKey: .style)
}
}
let json = [
"value": "12",
"textTranslationId": "textTranslationId",
"titleTranslationId": "titleTranslationId",
"style": "style"
] as [String : Any]
let decoder = JSONDecoder()
let model = try decoder.decode(HydroData.self, from: JSONSerialization.data(withJSONObject: json, options: .prettyPrinted))
print(model.value)
print(model.textTranslationId)
print(model.titleTranslationId)
print(model.style)
thank you - worked great!
sveinhal
(Svein Halvor Halvorsen)
4
If you expect to use this pattern often, you can create a small property wrapper to avoid having to repeatedly implement the boilerplate in init(from decoder: Decoder)
@propertyWrapper
struct StringDecodable<Wrapped: LosslessStringConvertible>: Decodable {
let wrappedValue: Wrapped
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let string = try container.decode(String.self)
if let value = Wrapped(string) {
wrappedValue = value
} else {
throw DecodingError.dataCorruptedError(
in: container,
debugDescription: "Value \(string) is not convertible to \(Wrapped.self)"
)
}
}
}
It allows you to define your models like so:
struct HydroData: Decodable {
@StringDecodable var value: Int
let textTranslationId: String?
let titleTranslationId: String?
let style: String?
}
2 Likes