How to parse a requests for different structures swift - ios

I have several URLs and, accordingly, there is a data structure for each of them.
URLS:
case "Get Day":
return "time/get_day.php"
case "Get Time":
return "time/get_time.php"
case "Get Current Time":
return "user/get_current_time.php"
STRUCTS:
struct Day: Codable {
var status: Int? = nil
var error_message: String? = nil
var result: [Result]? = nil
}
struct Time: Codable {
let status: Int?
let error_message: String?
let result: [Result]?
struct Result: Codable {
let id: String
let startTime: String
let endTime: String
}
}
struct CurrentTime: Codable {
let status: Int?
let error_message: String?
let current_time: Int?
}
struct Result: Codable {
let id: String
let name_en: String
let name_ru: String
let name_kk: String
}
At the moment I have a parseJson () function. In which I can manually change the type of structure for parsing one by one. But I cannot think of how to do this so that I would not change anything in the code manually.
func parseJson(data: Data) {
let decoder = JSONDecoder()
do {
let parsedData = try decoder.decode(Day.self, from: data)
print(parsedData)
} catch {
print("Error parsing Json:\(error)")
}
}
Please, if you have an example or ideas, share with me.

// Generic function to decode any decodable struct
func parseJson<T: Decodable>(data: Data) -> T? {
let decoder = JSONDecoder()
do {
let parsedData = try decoder.decode(T.self, from: data)
return parsedData
} catch {
return nil
}
}
// Usage
let someDay: Day? = parseJson(data: dayData)
let sometime: Time? = parseJson(data: timeData)

Related

How to get value from Optional(Optional(<__NSSingleObjectArrayI >(25)))

I am trying to get the value of "price" key which is "25"
I am getting this response Json From Backend
{
"errorCode": 0,
"message": "Request successfully served.",
"data": {
"games": {
"TWELVEBYTWENTYFOUR": {
"jackpot_amount": "KES 40,000.00",
"draw_date": "2021-05-21 10:59:45",
"extra": {
"jackpotAmount": 40000,
"unitCostJson": [
{
"currency": "KES",
"price": 25
}
]
},
}
},
"currentTime": {
"date": "2021-05-20 22:28:18.738038"
}
}
}
This is my code so far :
fetchData { (dict, error) in
let playerLoginInfo = dataDict["data"] as? NSDictionary
let playerGameInfo = playerLoginInfo?.value(forKey: "games") as? NSDictionary
if let TWELVEBYTWENTYFOUR = playerGameInfo?.value(forKey: "TWELVEBYTWENTYFOUR") as? NSDictionary {
let extra = TWELVEBYTWENTYFOUR.value(forKey: "extra") as? NSDictionary
let unitCostJson = extra?.value(forKey: "unitCostJson") as? NSArray
print("price")
print(unitCostJson?.value(forKey: "price") as? Any)
}
}
I get this is console :
Optional(Optional(<__NSSingleObjectArrayI 0x600001f091d0>(
25
)
))
I have seen this question How can I access values within Optional NSSingleObjectArrayI? but I couldn't figure out a solution
Edit:
I have now used Codeable to get data:
struct Resp: Codable {
let errorCode: Int
let message: String
let data: Dat
}
struct Dat: Codable {
let games: Games
let currentTime: CurrentTime
}
struct Games: Codable {
let game_code: String
let datetime: String
let estimated_jackpot: String
let guaranteed_jackpot: String
let jackpot_title: String
let jackpot_amount: String
let draw_date: String
let extra: Extra
let next_draw_date: String
let active: String
}
struct Extra: Codable {
let currentDrawNumber: Int
let currentDrawFreezeDate: String
let currentDrawStopTime: String
let jackpotAmount: Int
let unitCostJson: [UnitCostJson]
}
struct UnitCostJson: Codable {
let currency: String
let price: Int
}
struct CurrentTime: Codable {
let date: String
let timezone_type: Int
let timezone: String
}
I'm trying to get value from price now with this code
do{
let resp:Resp = try JSONDecoder().decode(Resp.self , from:data);
let data = resp.data
let games = data.games
let extra = games.extra
let unitCostJson = extra.unitCostJson
print(unitCostJson[0].price)
}
catch{
GlobalFunctions.shared.callOnMainThread {
self.showAlert(Message: "Something went wrong. Please retry.")
}
}
It is going into catch
How should I get the data inside on the unitCostJson now??
I butchered your struct and removed any irrelevant properties (compared to the json), if you want to add them back then you need to use an CodingKey enum
struct Resp: Codable {
let errorCode: Int
let message: String
let data: Dat
}
struct Dat: Codable {
let games: [String:Games]
let currentTime: CurrentTime
}
struct Games: Codable {
let extra: Extra
}
struct Extra: Codable {
let unitCostJson: [UnitCostJson]
}
struct UnitCostJson: Codable {
let currency: String
let price: Int
}
struct CurrentTime: Codable {
let date: String
}
Now you can access the unitCost like this
let unitCost = resp.data.games["TWELVEBYTWENTYFOUR"]?.extra.unitCostJson

How to debug JSONDecoder when no errors show?

I'm building a Swift app and testing in an Xcode Playground. Calling the NYTimes Search API and trying to store its response in a struct. The code executes cleanly and no errors appear (I am using a do, try, catch), but I cannot print any properties from the resulting object (print(json.status)).
My hunch is that something is fishy with this line but I'm not sure what since no errors are printing from the catch statement
let task = URLSession.shared.dataTask(with: URL(string: url)!, completionHandler: { data, response, error in
Form the URL endpoint to make the API call:
func APICall() {
let APIKey = "MY_API_KEY_GOES_HERE_BUT_IT'S_A_SECRET"
let searchTerm = "A Super Bowl Sideshow: See the Ageless Man!"
// Remove the spaces and convert them to percents
guard let encodedSearchTerm = searchTerm.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlQueryAllowed)
else {
print("Error encoding search term in URL")
return
}
let url = "https://api.nytimes.com/svc/search/v2/articlesearch.json?q=" + encodedSearchTerm + "&api-key=" + APIKey
getData(from: url)
}
Data Task:
func getData(from url: String) {
//I believe something is wrong with the following line but I'm not sure what it is
let task = URLSession.shared.dataTask(with: URL(string: url)!, completionHandler: { data, response, error in
guard let data = data, error == nil else {
print("Error loading data")
return
}
var result: NYTSearchResponse?
do {
result = try JSONDecoder().decode(NYTSearchResponse.self, from: data)
} catch {
print(error)
}
guard let json = result else {
print("Error assigning result to json")
return
}
//Try to print these from the resulting object but these commands do not print
print(json.status)
print(json.response.docs[0].abstract)
})
task.resume()
}
My NYTSearchResponse struct which mirrors the NYT API JSON response. It's pretty complicated, but I pasted the json response into https://app.quicktype.io/ to build the struct.
// MARK: - Welcome
struct NYTSearchResponse: Codable {
let status, copyright: String
let response: Response
}
// MARK: - Response
struct Response: Codable {
let docs: [Doc]
let meta: Meta
}
// MARK: - Doc
struct Doc: Codable {
let abstract: String
let webURL: String
let snippet, leadParagraph, printSection, printPage: String
let source: String
let multimedia: [Multimedia]
let headline: Headline
let keywords: [Keyword]
let pubDate: Date
let documentType, newsDesk, sectionName, subsectionName: String
let byline: Byline
let typeOfMaterial, id: String
let wordCount: Int
let uri: String
enum CodingKeys: String, CodingKey {
case abstract
case webURL = "web_url"
case snippet
case leadParagraph = "lead_paragraph"
case printSection = "print_section"
case printPage = "print_page"
case source, multimedia, headline, keywords
case pubDate = "pub_date"
case documentType = "document_type"
case newsDesk = "news_desk"
case sectionName = "section_name"
case subsectionName = "subsection_name"
case byline
case typeOfMaterial = "type_of_material"
case id = "_id"
case wordCount = "word_count"
case uri
}
}
// MARK: - Byline
struct Byline: Codable {
let original: String
let person: [Person]
let organization: String?
}
// MARK: - Person
struct Person: Codable {
let firstname: String
let middlename: String?
let lastname: String
let qualifier, title: String?
let role, organization: String
let rank: Int
}
// MARK: - Headline
struct Headline: Codable {
let main: String
let kicker, contentKicker: String?
let printHeadline: String
let name, seo, sub: String?
enum CodingKeys: String, CodingKey {
case main, kicker
case contentKicker = "content_kicker"
case printHeadline = "print_headline"
case name, seo, sub
}
}
// MARK: - Keyword
struct Keyword: Codable {
let name, value: String
let rank: Int
let major: String
}
// MARK: - Multimedia
struct Multimedia: Codable {
let rank: Int
let subtype: String
let caption, credit: String?
let type, url: String
let height, width: Int
let legacy: Legacy
let subType, cropName: String
enum CodingKeys: String, CodingKey {
case rank, subtype, caption, credit, type, url, height, width, legacy, subType
case cropName = "crop_name"
}
}
// MARK: - Legacy
struct Legacy: Codable {
let xlarge: String?
let xlargewidth, xlargeheight: Int?
}
// MARK: - Meta
struct Meta: Codable {
let hits, offset, time: Int
}
I moved the code out of the playground and it works.

SWIFT "Expected to decode Dictionary<String, Any> but found an array instead.", underlyingError: nil))

This is my first-time question.
I thought I did everything correctly. Have I built my model wrong? I'm not sure how to fix this error.
This is my code, after the correct model I want to pass the data to the table:
func createJSON() {
if let url = URL(string: "https://newsapi.org/v2/everything?q=apple&from=2020-11-15&to=2020-11-15&sortBy=popularity&apiKey=c5722efe6e65432fb5c116d3e1403dca") {
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) { (data, response, error) in
guard let data = data, error == nil else { return }
var result: NewsResult?
do {
result = try JSONDecoder().decode(NewsResult.self, from: data)
} catch {
print("error masage: \(error)")
}
guard let finalResult = result else { return }
print(finalResult.status)
print(finalResult.totalResults)
// print(finalResult.articles)
// let newNews = finalResult.artiscles
// self.newsApple.append(contentsOf: newNews)
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
task.resume()
}
}
// MARK: - Model Data Source
struct NewsResult:Codable {
let status: String
let totalResults: Int // totalResults
let articles: [articles]
}
struct articles:Codable {
let author: String // articles[0].author
let title: String
let description: String
let url:String
let urlToImage:String
let publishedAt: String
let content: String
}
You have written the model code that was not mapping correctly with JSON resultant. Try replacing your model structure with the following code:
struct NewsResult: Codable {
var status: String?
var totalResults: Int?
var articles: [Article]?
}
struct Article: Codable {
var source: Source?
var author: String?
var title: String?
var articleDescription: String?
var url: String?
var urlToImage: String?
var publishedAt: String?
var content: String?
}
struct Source: Codable {
var id : String?
var name: String?
}

Json parsing Swift not getting print

I am new to Swift. I want to fetch some json data from the server using the url. I tried many other solutions but they didn't work. I want to print the duration key (text and value) from the array and then print it in console.
The Json data is attached below
{
"status": "OK",
"rows": [
{
"elements": [
{
"duration": {
"text": "3 hours 49 mins",
"value": 13725
},
"distance": {
"text": "225 mi",
"value": 361715
},
"status": "OK"
}
]
}
],
"origin_addresses": [
"Washington, DC, USA"
],
"destination_addresses": [
"New York, NY, USA"
]
}
Attached Code
func getdatajson1(){
if let url = URL(string: "http://www.json-generator.com/api/json/get/bQywstyfkO?indent=2") {
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
do {
let res = try JSONDecoder().decode(Root.self, from: data)
print(res.rows)
} catch let error {
print(error)
}
}
}.resume()
}
}
struct Root: Codable {
let rows: [Root2]
}
struct Root2: Codable {
let elements: [Root3]
}
struct Root3: Codable {
let elements: [node]
}
struct node: Codable {
let duration : [valuesarray]
}
struct valuesarray: Codable {
let text : String
let value : Int
}
The duration is an Object and not an Array, also change your names and you can use this:
struct Root: Decodable {
let rows: [Rows]
}
struct Rows: Decodable {
let elements: [Elements]
}
struct Elements: Decodable {
let duration, distance: LocationValues
}
struct LocationValues: Decodable {
let text: String
let value: Int
}
func getdatajson1(){
if let url = URL(string: "http://www.json-generator.com/api/json/get/bQywstyfkO?indent=2") {
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
do {
let res = try JSONDecoder().decode(Root.self, from: data)
if let row = res.rows.first, let elements = row.elements.first {
print(elements.duration.text) //This is how you can get the text value
print(elements.distance.text) //This will print the distance
}
} catch let error {
print(error)
}
}
}.resume()
}
}
Replace your codable struct with the below
class Result: Codable {
var status:String?
var rows:[Row]?
var origin_addresses:[String]?
var destination_addresses:[String]?
}
class Row: Codable {
var elements:[Element]?
}
class Element: Codable {
var status:String?
var duration:Duration?
var distance:Distance?
}
class Duration: Codable {
var text:String?
var value:Int?
}
class Distance: Codable {
var text:String?
var value:Int?
}
You should update your node model like below
struct node: Codable {
let duration : valuesarray
let distance : valuesarray
let status : String
}
And you can access your duration data from API response like below
if let rows = res.rows, rows.count > 0 {
//Access the element objects from rows
let arrElements = rows[0].elements, arrElements.count > 0 {
if let durationData = arrElements[0].duration { //get your duration object
print(durationData.text)
print(durationData.value)
}
}
}
try this:
and a hint: go to https://app.quicktype.io/ -> here you can paste your json and you will get your datastructs for free! ;)
func getdatajson1(){
if let url = URL(string: "http://www.json-generator.com/api/json/get/bQywstyfkO?indent=2") {
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
do {
let res = try JSONDecoder().decode(Welcome.self, from: data)
print(res.rows)
} catch let error {
print(error)
}
}
}.resume()
}
}
getdatajson1()
struct Welcome: Codable {
let status: String
let rows: [Row]
let originAddresses, destinationAddresses: [String]
enum CodingKeys: String, CodingKey {
case status, rows
case originAddresses = "origin_addresses"
case destinationAddresses = "destination_addresses"
}
}
// MARK: - Row
struct Row: Codable {
let elements: [Element]
}
// MARK: - Element
struct Element: Codable {
let duration, distance: Distance
let status: String
}
// MARK: - Distance
struct Distance: Codable {
let text: String
let value: Int
}

Error while trying to phrase JSON with Swift

I'm trying to receive data from a JSON link in Swift Playground on Mac, I've struct all the data, but I'm having issue trying to decode all of the data, receiving the error: "Referencing instance method 'decode(_:from:)' on 'Array' requires that 'Bicimia' conform to 'Decodable'"
I've already tries to add the Codable/Decodable option, and tried to change the URLSession respectively, but nothing has changed.
struct Bicimia {
let network: Network
}
struct Network {
let company: [String]
let href, id: String
let location: Location
let name: String
let stations: [Station]
}
struct Location {
let city, country: String
let latitude, longitude: Double
}
struct Station {
let emptySlots: Int
let extra: Extra
let freeBikes: Int
let id: String
let latitude, longitude: Double
let name, timestamp: String
}
struct Extra {
let extraDescription: String
let status: Status
}
enum Status {
case online
}
let url = "https://api.citybik.es/v2/networks/bicimia"
let urlOBJ = URL(string: url)
URLSession.shared.dataTask(with: urlOBJ!) {(data, response, error) in
do {
let res = try JSONDecoder().decode([Bicimia].self, from: data!)
print(res)
}
catch {
print(error)
}
}.resume()
To be Decodable all properties should be Decodable down the chain:
struct Bicimia: Decodable {
let network: Network // should be decodable
}
struct Network: Decodable {
let company: [String]
let href, id: String
let location: Location // should be decodable
let name: String
let stations: [Station] // should be decodable
}
struct Location: Decodable {
let city, country: String
let latitude, longitude: Double
}
struct Station: Decodable {
let emptySlots: Int
let extra: Extra // should be decodable
let freeBikes: Int
let id: String
let latitude, longitude: Double
let name, timestamp: String
}
struct Extra: Decodable {
let extraDescription: String
let status: Status // should be decodable
}
enum Status: String, Decodable {
case online
}
Note that enums can not be Decodable alone, because they should know what is the raw value, or you should manually decode them in decode function.

Resources