Parse JSON File Starting with Curly Brace - json

I have a set of data in my database and I am unable to parse it through xcode. As the JSON file starts with a curly brace, I am constantly getting nil when i try to print the value upon parsing.
I am currently using XAMPP and I have done up the required PHP files and I have also tested that my database is working using INSOMNIA. However, I am unfamiliar on the parsing prior to data retrieval.
struct Coordinate: Codable {
let id: Int
let name: String
let latitude: Float
let longitude: Float
let address: String
class ViewController: UIViewController {
let url = URL(string: "http://127.0.0.1/callCenter/v2/getCoordinates.php")!
URLSession.shared.dataTask(with: url)
{ (data, _, _) in
if let data = data {
let coordinates = try? JSONDecoder().decode([Coordinate].self, from: data)
print(coordinates)
}
} .resume()
}
}
So upon running of program, I am printing NILs repeatedly. I highly its due to my first few lines of struct code. I have managed to print the size of the json file through this program as well.
Please do help me. Have been stuck for quite a few days.

Related

JSON to dict with class

I decide some JSON and try to typecast it to a dictionary of String: classy and it fails. I have found that often the reason I have trouble doing something is because of a misunderstanding of how Swift works, so here is what I want to happen. Feel free to tell me that I am doing it wrong and if I do it this way all will be wonderful.
I want my data to live between runs of the app so I have to save the data to storage between runs. I have an object, data and associated code, and I have places where changes I make to a copy should reflect back to the original so it is a class. I have a bunch of these objects and most of the time I pick the one I want based on an id that is an integer. An array is not good since it would be a sparse array cause come ids are not used. I came up with a dictionary with a key of the id and data of the structure. I turned the key from an Int to a String, by changing the Int id to a String, cause converting a dictionary to JSON is MUCH easier for a key that is a string. I save the JSON string. When the app starts again I read the string in and convert the JSON string to Any. Then I typecast the result to the desired dictionary. This is where it fails. The cast does not work. In my Googling the samples I found said this should work.
Here is my code:
class Y: Codable, Hashable {
var a: String = "c"
static func ==(lhs: Y, rhs: Y) -> Bool {
return lhs.a == rhs.a
}
func hash(into hasher: inout Hasher) {
hasher.combine(a)
}
}
struct ContentView: View {
var body: some View {
VStack {
Button ("Error") {
var y = Y()
var yDict = [String: Y]()
yDict["0"] = y
do {
let encodedData = try JSONEncoder().encode(yDict)
let jsonString = String(data: encodedData, encoding: .utf8)
let decoded = try JSONSerialization.jsonObject(with: encodedData, options: [])
if let yyDictDec = decoded as? [String:Y] {
print("yDict after decide")
print (yyDictDec)
}
} catch {
print(error.localizedDescription)
}
print("x")
}
}
}
}
In this code the if yyDictDec = is failing, I think, cause the prints after it never happen. I can cast it as [String, Any] but I really need it to be my class.
My problem is in the convert JSON back to the dictionary. I feel I am missing something fairly simple.
DonĀ“t use JSONSerialization use JsonDecoder and decode it to the the type it was before encoding. e.g.:
let decoded = try JSONDecoder().decode([String: Y].self, from: encodedData)

Using decoded JSON in SwiftUI

I have just started with Swift so please excuse if this is just a stupid question. I am working on my first App which should load a JSON from the web, parses and displays its content to a LazyVGrid. I have started with the apple tutorial which uses a local JSON and everything worked fine.
Now I have changed to the original nested JSON and URL and I can see the output on the console, but I simply do not know how to use it in my LazyVGrid now. I did a lot of research but all good tutorials end with the successful parsing. I ended up with:
func fetchData()
{
let url = URL(string: "https://myjsonurl")!
URLSession.shared.dataTask(with: url) {(json, response, error) in
guard let json = json else {
return
}
let welcome = try! JSONDecoder().decode(Welcome.self, from: json)
print(welcome)
print(welcome.data.data.results[0].title)
}.resume()
}
So as mentioned print() gives me the whole JSON or even specific values like the title. But how can I loop this to my LazyVGrid now?(Xcode is complaining that can not reach it or it is not identifiable because ID is only all the way down in the nested array structure...)
Do I have to create a new array first because the welcome thing is not available outside the fetchData() function? How should it look like to keep the whole JSON structure? I guess all of my experiments have been far too complicated.
I would highly appreciate if someone could give me a hint or an example.
Just in case you need the structure:
// MARK: - Welcome
struct Welcome: Codable {
var data: WelcomeData
}
// MARK: - WelcomeData
struct WelcomeData: Codable {
var success: Bool
var data: DataData
}
// MARK: - DataData
struct DataData: Codable {
var results: [Result]
}
// MARK: - Result
struct Result: Codable {
var id, title, alias, introtext: String
var fulltext, publishUp: String
enum CodingKeys: String, CodingKey {
case id, title, alias, introtext, fulltext
case publishUp = "publish_up"
}
}

Function for reading in JSON files

I am both new to stackoverflow as well as Swift programming (coming from statistics/R), so please bear with me in case I'm doing something super wrong. Ok, lets get to my problem: I want to read in multiple JSON files, e.g. metrics.json and accounts.json. After having read various posts on that topic, I have now written the following function and created one struct for each of the JSON files, replicating its structure:
func readJsonFile(fileName: String) -> [metric]? {
var result: [metric]?
if let path = Bundle.main.path(forResource: fileName, ofType: "json") {
do {
let jsonData = try Data(contentsOf: URL(fileURLWithPath: path))
result = try JSONDecoder().decode([metric].self, from: jsonData)
} catch {
fatalError("Unresolved error when loading JSON file \(fileName): \(error)")
}
}
return result
}
struct Metric: Codable {
let METRIC_ID: Int
let METRIC_NAME: String
}
struct Account: Codable {
let ACCOUNT_ID: Int
let ACCOUNT_NAME: String
}
Now what I was trying to achieve is somehow provide not just the filename to the function but also somehow the respective struct as a blueprint. Also, I would probably need to dynamically change the function output. It then should like somehow like that:
let metrics = readJsonFile("metrics", Metric)
let accounts = readJsonFile("accounts", Account)
I have a feeling that might be an easy thing if one knows his way around. Unfortunately, I do not. Can someone help me with any suggestions? Or should I in general take a different approach? Also, if there is anything else odd or wrong in the code, happy to receive any constructive feedback. Thanks guys.
I think what I would do is make the function generic over Codable, like this:
enum MyError:Error { case noSuchFile }
func readJsonFile<T:Codable>(fileName: String, type:T.Type) throws -> [T] {
if let path = Bundle.main.path(forResource: fileName, ofType: "json") {
let jsonData = try Data(contentsOf: URL(fileURLWithPath: path))
return try JSONDecoder().decode([T].self, from: jsonData)
}
throw MyError.noSuchFile
}
Now you can call it for either file and either struct:
let x = try? self.readJsonFile(fileName:"metric", type:Metric.self)
let y = try? self.readJsonFile(fileName:"account", type:Account.self)
I've revised your function to throw when there's an error, and I count the lack of the appropriate file as an error. The outcome of using try? in the call is that you get an Optional which is nil if things went badly and is an array of the correct struct type if things went well; if you don't like that, you can use do/catch instead of course, or even try! if you're dead sure this will always work (you should be, they are your files after all).

Creating Decode Path from JSON Data in Swift that Includes Numbers and Hyphens?

this is relatively new to me and I've searched high and low but have been unsuccessful in finding a similar scenario.
I have retrieved some JSON Data from an API URL and have successfully decoded and output various values from this data as strings by parsing the data to a separate sheet and using structs and constants with the 'Decodable' value set. The problem I have is that one of the containers in the Json data is a hyphenated date in this format dates['2020-11-04'] so swift will not let me create a struct with this name (also this looks like an array but there are no square brackets when viewing the unformatted JSON data in a web browser).
Here is the full path to the date I want to output as a string and the URL being used (copied from a web browser using JSON Viewer Pro):
dates['2020-11-04'].countries.Afghanistan.date
https://api.covid19tracking.narrativa.com/api/2020-11-04
Here is the sheet containing my Structs and constants to decode the data:
import Foundation
//I understand the below name will not work but i've included it to show my presumed process
struct CovidData: Decodable {
let dates: dates[2020-11-04]
}
//Once again the below struct name does not work but i've included it as an example of my presumed process.
struct dates[2020-11-04]: Decodable {
let countries: countries
}
struct countries: Decodable {
let Afghanistan: Afghanistan
}
struct Afghanistan: Decodable {
let date: String
}
Here is my management sheet with my API call and JSON Parse:
import Foundation
protocol CovidDataManagerDelegate {
func didUpdateCovidData(_ covidDataManager: CovidDataManager, covid: CovidModel)
}
struct CovidDataManager {
var delegate: CovidDataManagerDelegate?
let covidURL = "https://api.covid19tracking.narrativa.com/api/2020-11-04"
func getData() {
let urlString = covidURL
performRequest(with:urlString)
}
func performRequest(with 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 {
if let covid = parseJSON(safeData){
self.delegate?.didUpdateCovidData(self, covid: covid)
}
}
}
task.resume()
}
func parseJSON(_ covidData: Data) -> CovidModel? {
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode(CovidData.self, from: covidData)
let date = decodedData.dates['2020-11-04'].countries.Afghanistan.date
let covid = CovidModel(date: date)
print(date)
return covid
} catch {
print("Error with JSON Parse")
return nil
}
}
}
}
I have not included my UI update sheet as mentioned before the call and decode is working perfectly fine when decoding data with a JSON path made up entirely of strings it is only this container with additional symbols and numbers I am stumped with.
Hopefully I've supplied enough information and apologies if some of the terminology isn't accurate, this is still quite new to me.
Thanks!

parse json, populate collection view Swift 4.2

This is too simple but I am lost. I am still new to swift really.
I need to parse the downloaded json ( localized file in the Xcode project ) and populate the data to a CollectionView.
enum Response{
case success(Data)
case error(Error)
}
// struct follows the json
struct InformationFromJson: Decodable {
let id: Int
let name: String
}
class MYJSON {
public func downloadMYJSON(_ completion: #escaping (Response) -> ()) {
guard let bundle = Bundle(identifier: MYJSON.bundleId), let path = bundle.path(forResource: "data", ofType: "json"), let data = try? Data(contentsOf: URL(fileURLWithPath: path)) else {
completion(Response.error(NSError(domain: MYJSON.bundleId, code: MYJSON.bundleErrorCode, userInfo: [NSLocalizedDescriptionKey : MYJSON.bundleError])))
return
}
completion(Response.success(data))
}
}
So, without totally changing the function call, how do I parse the json? It's downloaded so far from the function, but I don't see how to even add a print statement to test, without getting errors because of the guard statement , the way it is.
I need to simple populate a cellForRowAt:
I never saw nested guard like this, so it got me. I am used to seeing the let statements separated so you can put print statements to at least see if things are getting downloaded or parsed.
You can decode your json by passing data, whatever you get from
let data = try? Data(contentsOf: URL(fileURLWithPath: path))
guard let decoded = try? JSONDecoder().decode(InformationFromJson.self, from: data) else {
return
}