Save JSON String in Object - json

i want to save a JSON-String into an existing Object in Swift:
This is my Object:
struct Benutzer : Decodable, Encodable{
let userRights: [String]
}
This is my String:
str = "{"user_rights":["terminal_create"]}"
That is my Code:
do { let data1 = str.data(using: String.Encoding.utf8, allowLossyConversion: false)
let User = try JSONDecoder().decode(Benutzer.self, from: data1 as Data)
print(User)
}catch{
print("Error serializing!")
}
With this code, "Error serializing!" shows up every time. Do you guys know whats up? Sorry, I am still a complete beginner. Sorry for not formatting the question I don't quite get it :( I get this String from another JSON request: I get this as an answer, the JSON data string that I want to decode is part of that answer:
Answer(api_version: 1, result: "login", success: true, token: "da39a3ee5e6b4b0d3255bfef95601890afd80709", data: "{\"user_rights\":[\"terminal_create\"]}")
This is a Answer-Object:
struct Answer: Decodable, Encodable{
let api_version: Int
let result: String
let success: Bool
let token: String
let data: String
}
Maybe you know another way how to extract that Data into a Benutzer Object.
I would be really thankful, thanks a lot guys!

your coding key not similar to what in your string userRights , user_rights
so do like that:
struct Benutzer: Codable {
let userRights: [String]
enum CodingKeys: String, CodingKey {
case userRights = "user_rights"
}
}

Related

I can't pull Json data while doing MVVM design project with swift

I am making a project in Swift with MVVM design. I want to get coin name, current price, Rank and Symbol from a Crypto site. I can't show the json data I get on the console. The model is in another folder because I did it with MVVM. How can I create a struct to get the data here? You can find screenshots of my project below. I would be glad if you help.
Below are the codes I wrote in my web service file
import Foundation
class WebService {
func downloadCurrencies(url: URL, completion: #escaping ([DataInfo]?) -> ()) {
URLSession.shared.dataTask(with: url) { (data, response, error) in
if let error = error {
print(error.localizedDescription)
completion(nil)
} else if let data = data {
let cryptoList = try? JSONDecoder().decode([DataInfo].self, from: data)
print(cryptoList)
if let cryptoList = cryptoList {
completion(cryptoList)
}
}
}
.resume()
}
}
Below are the codes I wrote in my model file
import Foundation
struct DataInfo : Decodable {
var name: String
var symbol: String
var cmc_rank: String
var usd: Double
}
Finally, here is the code I wrote to print the data in the viewController to my console. But unfortunately I can't pull the data.
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest?start=1&limit=10&convert=USD&CMC_PRO_API_KEY=5ac24b80-27a1-4d01-81bd-f19620533480")!
WebService().downloadCurrencies(url: url) { cryptos in
if let cryptos = cryptos {
print(cryptos)
}
}
}
I've seen your URL and tested it ON Postman and also i've got your code and tried to put it in a shape, the code is good, but it can't MAP JSON Data against your struct because this is the json Data from Postman
Postman Data
While Looking at your Struct, It Doesnt Match the format of JSON Data you're receiving,
Struct
Well, To Make your structure match the JSON String you need to create Nested String: Any Dictionary. But there's another issue with the logic, you need to decode data outside of the webservice call because it can contain errors which wont be mapped in the struct and can handle if you get other statusCode.
If you try to implement all these things manually, the code will become complex and hard to understand. I would rather recommend you to use Alamofire with SwiftyJSON and it can make your work a lot shorter and easier and understandable.
Sorry for the bad english.
Your api-key is not valid for me.
Your data must be inside of object or invalid keys and you are surely missing it thats why it is not parsing correctly.
My suggestion is to put your json response in this website
"https://app.quicktype.io/"
and replace your struct with new one, you will be good to go hopefully.
Your models does not have 1 to 1 correspondence with the response object. The root object is not a [DataInfo], but another structure that contains an array of DataInfos. Here are the correct models
struct Response: Codable {
let status: Status
let data: [CurrencyData]
}
struct Status: Codable {
let creditCount: Int
let elapsed: Int
let timestamp: String
let totalCount: Int
let errorCode: Int?
let errorMessage: String?
let notice: String?
enum CodingKeys: String, CodingKey {
case notice
case timestamp
case elapsed
case creditCount = "credit_count"
case errorCode = "error_code"
case errorMessage = "error_message"
case totalCount = "total_count"
}
}
enum Currency: String, Codable, Hashable {
case usd = "USD"
}
struct CurrencyData: Codable {
let circulatingSupply: Double?
let cmcRank: Int
let dateAdded: String?
let id: Int
let lastUpdated: String?
let maxSupply: Int?
let name: String
let numMarketPairs: Int
let platform: Platform?
let quote: [String: Price]
let selfReportedCirculatingSupply: String?
let selfReportedMarketCap: String?
let slug: String
let symbol: String
let tags: [String]?
let totalSupply: Double
func price(for currency: Currency) -> Double? {
return quote[currency.rawValue]?.price
}
enum CodingKeys: String, CodingKey {
case id
case name
case platform
case quote
case slug
case symbol
case tags
case circulatingSupply = "circulating_supply"
case cmcRank = "cmc_rank"
case dateAdded = "date_added"
case lastUpdated = "last_updated"
case maxSupply = "max_supply"
case selfReportedCirculatingSupply = "self_reported_circulating_supply"
case selfReportedMarketCap = "self_reported_market_cap"
case totalSupply = "total_supply"
case numMarketPairs = "num_market_pairs"
}
}
struct Price: Codable {
let fullyDilutedMarketCap: Double?
let lastUpdated: String?
let marketCap: Double?
let marketCapDominance: Double?
let percentChange1h: Double?
let percentChange24h: Double?
let percentChange30d: Double?
let percentChange60d: Double?
let percentChange7d: Double?
let percentChange90d: Double?
let price: Double?
let volume24h: Double?
let volumeChange24h: Double?
enum CodingKeys: String, CodingKey {
case price
case fullyDilutedMarketCap = "fully_diluted_market_cap"
case lastUpdated = "last_updated"
case marketCap = "market_cap"
case marketCapDominance = "market_cap_dominance"
case percentChange1h = "percent_change_1h"
case percentChange24h = "percent_change_24h"
case percentChange30d = "percent_change_30d"
case percentChange60d = "percent_change_60d"
case percentChange7d = "percent_change_7d"
case percentChange90d = "percent_change_90d"
case volume24h = "volume_24h"
case volumeChange24h = "volume_change_24h"
}
}
struct Platform: Codable {
let id: Int
let name: String
let symbol: String
let slug: String
let tokenAddress: String?
enum CodingKeys: String, CodingKey {
case id
case name
case symbol
case slug
case tokenAddress = "token_address"
}
}
and you can retrieve the cryptoList in your completion handler like this:
let cryptoList = (try? JSONDecoder().decode([Response].self, from: data))?.data
Also it's not safe to expose your personal data to the internet (API_KEY, etc.)

Fetching city name from json with swift

I need help with proper parsing of OpenWeatherAPI. I need to fetch city name.
I always got this line of code:
021-09-23 08:16:13.526604+0200 Clima[1943:50811] Writing analzed variants.
Optional("")
This is my JSON:
JSON example
This is my struct for api:
struct WeatherData: Decodable{
let message: String?
let list: [List]?
}
struct List: Decodable{
let name: String?
}
And my function for fetching json:
func parseJSON(weatherData: Data) {
let decoder = JSONDecoder()
do{
let decodedData = try decoder.decode(WeatherData.self, from: weatherData)
print(decodedData.list![0].name!)
}catch{
print(error)
}
}
I need to fetch a name. I understand that I need to make in my main struct WeatherData let for another struct and then use it. E.g. decodedData.list.name to fetch some data.
One more question: should I always use ? (optional) when fetching data from JSON ?

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"
}
}

parsing json with dynamic keys

I have been working on this for a few hours and have not been able to find an answer. I have a JSON with dynamic keys that I am trying to parse into a struct. I thought I could keep it simple but I'm getting serialization errors. Please help - thanks
{"rates":{
"btc":{"name":"Bitcoin","unit":"BTC","value":1.0,"type":"crypto"},
"eth":{"name":"Ether","unit":"ETH","value":35.69,"type":"crypto"},
}}
my stuct
struct CryptoCoins: Decodable {
let rates: [String: [Coin]]
}
struct Coin: Decodable {
let name: String
let unit: String
let value: Double
let type: String
}
my decoder:
guard let container = try? JSONDecoder().decode(CryptoCoins.self, from: json) else {
completion(.failure(.serializationError)) // <- failing here
return
}
You're decoding the property rates into the wrong type - it's not a dictionary of String keys and an array of Coin values - it's just a single Coin value.
struct CryptoCoins: Decodable {
let rates: [String: Coin] // <- here
}
On a related note, don't hide the error with try?. Capture it and log it, if necessary:
do {
let cryptoCoins = try JSONDecoder().decode(CryptoCoins.self, from: json)
// ..
} catch {
print(error)
}
Then you would have gotten a typeMismatch error for btc key: "Expected to decode Array<Any> but found a dictionary instead.", which would have at least given you a hint of where to look.

How can i append JSON with multiple struct into array?

In order to parse the JSON, I needed to use 3 structs.
struct AppleApi: Decodable {
let feed: Feed
}
struct Feed: Decodable {
let results: [Result]
}
struct Result: Decodable {
let artistName: String
let artWorkUrl: String
enum CodingKeys : String, CodingKey {
case artistName = "artistName"
case artWorkUrl = "artworkUrl100"
}
}
But when I try to populate the array with that parsed data I got that message:
Cannot convert value of type '[Result]' to expected argument type
'AppleApi'
This is my error message:
do {
let appData = try JSONDecoder().decode(AppleApi.self, from: jsonData)
print(appData.feed.results.count)
var dataApp = appData.feed.results
print(appData)
DispatchQueue.main.async {
self.feedReseult.append(dataApp)
self.myCollectionView.reloadData()
}
} catch let err {
print("Error",err)
}
And this is my array:
var feedReseult = [AppleApi]()
I probably need to reach to 3. struct to reach the array inside JSON in order to have same type of argument type. How can I do that?
Your declaration of feedReseult should be this,
var feedReseult = [Result]()
and append the dataApp as below,
DispatchQueue.main.async {
self.feedReseult.append(contentsOf: dataApp)
self.myCollectionView.reloadData()
}
Also i feel a typo, feedResult instead of feedReseult
It looks like your struct from Json is not put together correct, you should just need one struct per one JSON load. Could you give JSON sample please?
If you want to Decode Feed which is part of AppleAPI then you should create an object that is of type AppleApi.Feed and put results into that.
Hope this helps a little