Decoding the incoming json string in swift after api call - json

here is the string(my whole response is string) which i get after api call .
"{'result': {'ip':'49.36.183.40','id':'T1199','Date':'2022-7-24','Time':'20:58:36','Temp':38.94,'PM25':117.00,'lux':7.00,'VOC':586.00,'CO':0.97,'CO2':828.00,'O3':118.00,'RH':48.88,'Pres':989.00}}",,,
I tried this since I was receiving the string back :
#Published var singleData : [iotData] = []
func loadSingleData() {
if let url = URL(string: urlString) {
URLSession.shared.dataTask(with: url) { [weak self ] data , response, error in
DispatchQueue.main.async {
let decoder = JSONDecoder()
if let data = data {
if let users = try? decoder.decode([String : iotData].self, from: data) {
print(users)
} else {
print("failed to decode the data")
}
} else {
print("Failed to load the data")
}
}
}.resume()
} else {
print("wrong url")
}
}
but my output is failed to decode the data
also there are commas outside json string how do i take care of those .
i am beginner in ios development so i don't have any clue how to decode this json string and use it.how can i get past this
here is my model for this
struct iotData : Codable {
var result : Result
}
struct Result: Codable {
let ip, id, Date, Time: String
let Temp: Double
let PM25, lux, VOC: Int
let CO: Double
let CO2, O3: Int
let RH: Double
let Pres: Int
}

Use:
decoder.decode(iotData.self, from: data) { }
Instead of
decoder.decode(iotData, from: data) { }
Also make sure 'data' is actually 'Data' object

Related

Swift - JSON decoding

I have a JSON response from an api call. The problem is I get different JSON responses depending on whether the user has entered the correct credentials or not. My question is how do I read and decode these responses to a useable struct and what is the best way to go about decoding these different responses. one thing I noticed is both response have a common "isSuccess" that may be useful. I have little to no experience with swift or reading JSON so this is all a learning experience for me.
This is the response for successful login
{"result":{"login":{"isAuthorized":true,"isEmpty":false,"userName":{"isEmpty":false,"name":{"firstName":"Jason","lastName":"Test","displayName":"Test, Jason","isEmpty":false,"fullName":"Jason Test"},"canDelete":false,"id":5793,"canModify":false},"username":"test#testable.com"},"parameters":{"isEmpty":false,"keep_logged_in_indicator":false,"username":"test#testable.com"}},"isAuthorized":true,"version":{"major":"2021","minor":"004","fix":"04","display":"2021.004.04","isEmpty":false},"isSystemDown":false,"timestamp":"2021-07-28T02:47:33Z","isSuccess":true}
This is the response for failure
{"isAuthorized":true,"version":{"major":"2021","minor":"004","fix":"04","display":"2021.004.04","isEmpty":false},"isSystemDown":false,"errors":[{"password":"Unable to login as 'test#testable.com'"}],"timestamp":"2021-07-28T02:47:05Z","isSuccess":false}
This is the code I have written for my api calls
func request<T: Decodable>(endPoint: EndPoint, method: Method, parameters: [String: Any]? = nil, completion: #escaping(Result<T, Error>) -> Void) {
// Creates a urlRequest
guard let request = createRequest(endPoint: endPoint, method: method, parameters: parameters) else {
completion(.failure(AppError.invalidUrl))
return
}
let session = URLSession.shared
session.dataTask(with: request) { data, response, error in
var results: Result<Data, Error>?
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
completion(.failure(AppError.badStatusCode))
return
}
if let response = response {
// Gets the JSESSIONID
let cookieName = "JSESSIONID"
if let cookie = HTTPCookieStorage.shared.cookies?.first(where: { $0.name == cookieName }) {
debugPrint("\(cookieName): \(cookie.value)")
}
print(response)
}
if let data = data {
results = .success(data)
// Converts data to readable String
let responseString = String(data: data, encoding: .utf8) ?? "unable to convert to readable String"
print("Server Response: \(responseString.description)")
} else if let error = error {
results = .failure(error)
print("Server Error: \(error.localizedDescription)")
}
DispatchQueue.main.async {
self.handleResponse(result: results, completion: completion)
}
}.resume()
}
private func handleResponse<T: Decodable>(result: Result<Data, Error>?, completion: (Result<T, Error>) -> Void) {
guard let result = result else {
completion(.failure(AppError.unknownError))
return
}
switch result {
case .success(let data):
do {
let json = try JSONSerialization.jsonObject(with: data, options: [])
print("Server JsonObject response: \(json)")
} catch {
completion(.failure(AppError.errorDecoding))
}
let decoder = JSONDecoder()
// Decodes that json data
do {
} catch {
}
case .failure(let error):
completion(.failure(error))
}
}
Im mostly interesting in being able to display the json error that occurs when credentials are incorrect. The deadline for my project Is slowing approaching and any help or suggestions would be much appreciated.
You can use Swift's Result type to differentiate a successful result from a failed result.
The Result type is not decodable by default so you will need to write a custom decoder like this:
struct Response: Decodable {
let result: Swift.Result<Result, Errors>
enum CodingKeys: String, CodingKey {
case isSuccess
case errors
case result
}
struct Result: Codable {
let login: Login
struct Login: Codable {
let isAuthorized: Bool
}
}
struct Errors: Error {
let contents: [[String: String]]
}
init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
if try container.decode(Bool.self, forKey: .isSuccess) {
result = .success(try container.decode(Result.self, forKey: .result))
} else {
result = .failure(
Errors(contents: try container.decode([[String: String]].self, forKey: .errors))
)
}
}
}

Fetching Data from API in Swift

So I am trying to fetch data from the Pokemon API, and I am getting stuck at the point where I am trying to decode the JSON into a struct. Here is my code:
{
"count":1118,
"next":"https://pokeapi.co/api/v2/pokemon/?offset=20&limit=20",
"previous":null,
"results":
[
{"name":"bulbasaur","url":"https://pokeapi.co/api/v2/pokemon/1/"},
{"name":"ivysaur","url":"https://pokeapi.co/api/v2/pokemon/2/"},
{"name":"venusaur","url":"https://pokeapi.co/api/v2/pokemon/3/"},
{"name":"charmander","url":"https://pokeapi.co/api/v2/pokemon/4/"},
{"name":"charmeleon","url":"https://pokeapi.co/api/v2/pokemon/5/"},
{"name":"charizard","url":"https://pokeapi.co/api/v2/pokemon/6/"},
{"name":"squirtle","url":"https://pokeapi.co/api/v2/pokemon/7/"},
{"name":"wartortle","url":"https://pokeapi.co/api/v2/pokemon/8/"},
{"name":"blastoise","url":"https://pokeapi.co/api/v2/pokemon/9/"},
{"name":"caterpie","url":"https://pokeapi.co/api/v2/pokemon/10/"},
{"name":"metapod","url":"https://pokeapi.co/api/v2/pokemon/11/"},
{"name":"butterfree","url":"https://pokeapi.co/api/v2/pokemon/12/"},
{"name":"weedle","url":"https://pokeapi.co/api/v2/pokemon/13/"},
{"name":"kakuna","url":"https://pokeapi.co/api/v2/pokemon/14/"},
{"name":"beedrill","url":"https://pokeapi.co/api/v2/pokemon/15/"},
{"name":"pidgey","url":"https://pokeapi.co/api/v2/pokemon/16/"},
{"name":"pidgeotto","url":"https://pokeapi.co/api/v2/pokemon/17/"},
{"name":"pidgeot","url":"https://pokeapi.co/api/v2/pokemon/18/"},
{"name":"rattata","url":"https://pokeapi.co/api/v2/pokemon/19/"},
{"name":"raticate","url":"https://pokeapi.co/api/v2/pokemon/20/"}
]
}
func fetchPokemon() {
let defaultSession = URLSession(configuration: .default)
if let url = URL(string: "https://pokeapi.co/api/v2/pokemon/") {
let request = URLRequest(url:url)
let dataTask = defaultSession.dataTask(with: request, completionHandler: { (data, response, error) -> Void in
guard error == nil else {
print ("error: ", error!)
return
}
guard data != nil else {
print("No data object")
return
}
guard let httpResponse = response as? HTTPURLResponse, (200...299).contains(httpResponse.statusCode) else {
print("response is: ", response!)
return
}
guard let mime = response?.mimeType, mime == "application/json" else {
print("Wrong MIME type!")
return
}
DispatchQueue.main.async {
guard let result = try? JSONDecoder().decode(PokemonList.self, from: data!) else {
print("Error Parsing JSON")
return
}
let pokemon = result.pokemon
self.Pokemon = pokemon
print(self.Pokemon)
}
})
dataTask.resume()
}
}
and here is the pokemon struct:
struct Pokemon {
// Various properties of a post that we either need or want to display
let name: String
let url: String
}
extension Pokemon: Decodable {
// properties within a Post returned from the Product Hunt API that we want to extract the info from.
enum PokemonKeys: String, CodingKey {
// first three match our variable names for our Post struct
case name = "name"
case url = "url"
}
init(from decoder: Decoder) throws {
let postsContainer = try decoder.container(keyedBy: PokemonKeys.self)
name = try postsContainer.decode(String.self, forKey: .name)
url = try postsContainer.decode(String.self, forKey: .url)
}
}
struct PokemonList: Decodable {
var pokemon: [Pokemon]
}
It keeps reaching the point when decoding which says "Error Parsing JSON". I'm assuming that there may be an error in how I setup the pokemon struct?
Any ideas?
you are getting a parse error because the data model is not the same. your struct should be:
struct PokemonList: Decodable {
var results: [Pokemon]
var count: Int
var next: String
}
you don't need the extension.

JSON Decoding Issues in Swift

I am trying to use the open weathermap.org api to return the "main" key in JSON. For some reason I keep getting caught up in my error around "failed to convert". I'm not exactly sure why I am failing to convert the JSON anyone have any ideas? Thank you.
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = .white
let url = "https://api.openweathermap.org/data/2.5/weather?zip=10514&appid=#####################"
getData(from: url)
}
struct Response: Codable {
let weather: MyResult
}
struct MyResult: Codable {
let main: String
}
Below is the function being called to change access the JSON.
private func getData(from url:String) {
URLSession.shared.dataTask(with: URL(string:url)!, completionHandler: {
data, response, error in
guard let data = data, error == nil else {
print("Something went wrong")
return
}
// have data
var result: Response?
do {
result = try JSONDecoder().decode(Response.self, from: data)
} catch {
print("failed to convert")
}
guard let json = result else {
return
}
print(json.weather.main)
}).resume()
}
EDIT: This is what the json pulls up. Am I creating the structs incorrectly? if so how should I do it properly? thank you
{"coord":{"lon":-73.73,"lat":41.2},"weather":[{"id":802,"main":"Clouds","description":"scattered clouds","icon":"03n"}],"base":"stations","main":{"temp":291.67,"feels_like":288.73,"temp_min":290.93,"temp_max":292.59,"pressure":1024,"humidity":72},"visibility":10000,"wind":{"speed":5.7,"deg":130},"clouds":{"all":40},"dt":1594956028,"sys":{"type":1,"id":4403,"country":"US","sunrise":1594892168,"sunset":1594945530},"timezone":-14400,"id":0,"name":"Mount Kisco","cod":200}
In the JSON response, the property weather is an array:
{..., weather: [{...}] }
but in your model it expects a MyResult type. Change it to expect an array of [MyResult]:
struct Response: Codable {
let weather: [MyResult]
}

How to convert JSON into [WeatherModel] in Swift/SwiftyJSON?

I was trying to make a weather app. And having a problem using SwiftyJSON.
I need to assign [WeatherModel] to my JSON data.
Basically, I need to set json variable to weatherData. The code is below.
Here is my controller:
var weatherData = [WeatherModel]()
func getJSONData(completed: #escaping () -> ()) {
if let filepath = Bundle.main.path(forResource: "weather", ofType: "json") {
do{
let data = try Data(contentsOf: URL(fileURLWithPath: filepath), options: .alwaysMapped)
let json = JSON(data: data)
// And here I need to set json to weatherData
} catch let error{
print(error.localizedDescription)
}
DispatchQueue.main.async {
completed()
}
} else {
print("file not found")
}
}
Here is my WeatherModel struct:
struct WeatherModel {
let cod: String
let message: Double
let cnt: Int
let list: [List]
let city: City
}
Note: I really need this to be made using only SwiftJSON. Any help will be appreciated :]
Well, we don't know what your JSON looks like.
To provide a sample, if this is what your JSON looked like:
{
"data": [
{
"cod": "some string here",
"message": 2.0,
"cnt": 1
...
}
]
}
... you would decode it as follows:
for (_, dict) in json["data"] {
guard let cod = dict["cod"].string else { continue }
guard let message = dict["message"].double else { continue }
guard let cnt = dict["cnt"].int else { continue }
// ...
let weather = WeatherModel(cod: cod, message: message, cnt: cnt, ...)
weatherData.append(weather)
}
You would have to modify this to work with your json format and the exact requirements.
Try this, i am not sure about your json structure correctly.
struct WeatherModel:Codable {
let cod: String
let message: Double
let cnt: Int
let list: [List]
let city: City
enum CodingKeys: String, CodingKey
{
case title = "name"
case url = "message"
case cnt
case list
case city
}
}
struct City:Codable {
let name
}
struct List:Codable {
//define your list data as in json
}
after this decode your json data.
if let wheatherData = try? JSONDecoder().decode(WeatherModel.self, from: data) {
// here Is your json model weatherData
}

Swift 3 - How can i extract elements from a json?

Hello i want to extract certain elements from a json .
CODE: .
var datas:[Usuario]?
struct Usuario : Codable {
let correo: String?
let contrasena: String?
}
let urlString = "http://localhost:8080/swiftdb/logear.php"
guard let url = URL(string: urlString) else { return }
URLSession.shared.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error!.localizedDescription)
}
guard let data = data else { return }
//Implement JSON decoding and parsing
do {
//Decode retrived data with JSONDecoder and assing type of Article object
let articlesData = try JSONDecoder().decode([Usuario].self, from: data)
//Get back to the main queue
DispatchQueue.main.async {
}
} catch let jsonError {
print(jsonError)
}
}.resume()
What i want to do, is compare what it is in the json with a textfield ,so it can go to a different view.
Json Contains:
[{"correo":"unknown11#hotmail.com","contrasena":"12345"},{"correo":"lalo123#hotmail.com","contrasena":"12121"},{"correo":"kokunz#hotmail.com","contrasena":"11111"},{"correo":"a","contrasena":"a"}]
Textfield Contains: {Something which the user writes in in. txtEmail.text Ex.}
Before the answer, you can check the Apple Reference about JSON and parsing . https://developer.apple.com/swift/blog/?id=37
if let dictionary = netoxJsonVariable as? [String: Any] {
for (key, value) in dictionary {
// access all key / value pairs in dictionary
// I am not sure that you can reach key with string but I think you are searching for this.
//if(value == "searchedMailAddress"){//then}
//if(key == "correoLikeKey"){//then}
}
}
I used a for statement so
let {newVar} = self.{yourJson}
for item in {newVar}! {
let {anotherVar} = item.{field}
let {anotherVar2} = item.{field2}