Unable to parse data from API Request Swift - ios

I am currently working on learning Swift and I tried to parse data from an open API working with Codable. I followed some tutorials and ended with non data fetched.
Codable:
import Foundation
struct currentWeather: Codable {
var temperature: Double
var time: String
var weathercode: Double
var winddirection: Double
var windspeed: Double
}
struct Weather: Codable {
var elevation: Double
var latitude: Double
var longitude: Double
var timezone: String
var timezone_abbreviation: String
var utc_offset_seconds: Int
var generationtime_ms: Double
var currentweather: currentWeather
}
RequestManager:
import Foundation
class RequestManager {
static let url = URL(string: "https://api.open-meteo.com/v1/forecast? latitude=42.70&longitude=23.32&current_weather=true")
static var temperature: Double = 0.0
class func getWeatherData() {
var request = URLRequest(url: url!)
request.httpMethod = "GET"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
let task = URLSession.shared.dataTask(with: request, completionHandler: {
(data, response, error) in
guard let weather = try? JSONDecoder().decode(Weather.self, from: data!) else {
print("Cannot parse data!")
return
}
RequestManager.temperature = weather.currentweather.temperature
})
task.resume()
}
}

First things first, url has blanks, you should update it like that.
static let url = URL(string: "https://api.open-meteo.com/v1/forecast?latitude=42.70&longitude=23.32&current_weather=true")
Further, you need to update currentweather property in the Weather struct. The name convention is different in the response. So you can use this struct instead. On the other hand, you may check CodingKeys for better property names.
struct Weather: Codable {
var elevation: Double
var latitude: Double
var longitude: Double
var timezone: String
var timezone_abbreviation: String
var utc_offset_seconds: Int
var generationtime_ms: Double
var current_weather: currentWeather
}

Related

How to add #Published property wrapper to class member in Swift [duplicate]

I am new to Swift and am having a spot of bother with a decodable class.
I am receiving an error on this class : Type 'VoucherCode' does not conform to protocol 'Decodable'
I have exactly the same syntax in another project without error which came from a tutorial.
Without the published line, the class works and is decoded from relevant json.
What am I missing please?
import Foundation
class VoucherCode: Decodable, Identifiable, ObservableObject {
#Published var logoData: Data?
var id:UUID?
var title: String?
var voucherCode: String?
var details: String?
var logo: String?
var url: String?
var termsAndConditions: String?
var highlight: String?
var whoFor:[String]?
func getLogoData() {
guard logo != nil else {
return
}
if let url = URL(string: logo!) {
let session = URLSession.shared
let dataTask = session.dataTask(with: url) { (data, response, error) in
if error == nil {
DispatchQueue.main.async {
self.logoData = data!
}
}
}
dataTask.resume()
}
}
}
A similar class (which works) from a CodeWithChris lesson. There is no error and it works.
add this to your class:
private enum CodingKeys: String, CodingKey {
case id, title, voucherCode, details, logo, url, termsAndConditions, highlight, whoFor
}
this will exclude logoData from the decodable and make VoucherCode Decodable.

How to display only 12 hours of hourly temp from OpenWeatherMap?

I’m having a brain block right now and I can’t figure out how to display only 12 hours of hourly temperature rather than the 48 hours with the OpenWeatherMap API OneCall. I've tried messing around with the for-in loop but not having any luck.
Here is my ViewModel
class WeatherModel: ObservableObject {
//Declare published property wrapper, that when the the property value changes, we want to notifty anyone who is observing it
#Published var weatherData: Weather?
#AppStorage("cityName") var cityName = ""
init(){
getWeatherData(cityName)
}
//Init method gets run when a new instance of WeatherModel is created
//MARK: - OpenWeatherMap API methods
func getWeatherData(_ cityName: String){
CLGeocoder().geocodeAddressString(cityName){(placemarks, error ) in
if let error = error {
print(error)
}
if let lat = placemarks?.first?.location?.coordinate.latitude,
let lon = placemarks?.first?.location?.coordinate.longitude {
//first is the first element of the collection
let weatherUrlString = "https://api.openweathermap.org/data/2.5/onecall?lat=\(lat)&lon=\(lon)&exclude=minutely,daily,alerts&units=imperial&appid=\(Constants.apiKey)"
let weatherUrl = URL(string: weatherUrlString)
guard weatherUrl != nil else {
return
}
let request = URLRequest(url: weatherUrl!)
//Create a URL session
let session = URLSession.shared
let dataTask = session.dataTask(with: request) { data, response, error in
guard error == nil else {
return
}
do{
let decoder = JSONDecoder()
var result = try decoder.decode(Weather.self, from: data!)
//parsing the weather data into the constant, result
//Add UUId's to the hourly weather objects. Use the variable Result since that is parsing the weather
for i in 0..<result.hourly.count {
result.hourly[i].id = UUID()
}
DispatchQueue.main.async {
self.weatherData = result
}
}catch {
print(error)
}
}
dataTask.resume()
}
}
}//func getWeatherData
}
My Model
struct Weather: Decodable {
var current: Current
var hourly: [Current]
//Hourly is an arrary of weather responses (i.e. Current). It parses the data because the arrary is similar to Current properties
}
struct Current: Decodable, Identifiable {
var id: UUID?
var dt: Double
var temp: Double
var feels_like: Double
var weather: [WeatherInfo]
}
struct WeatherInfo: Decodable {
var description: String
}
Right now this is just a rough view and will update the look of it but for now I’m putting it as a list. I only want 12 hours of the hourly temperature rather than the 48 hours
View
List(model.weatherData?.hourly ?? [] ) {
hour in
Text("\(Constants.dtConversion(hour.dt)), \(Constants.tempToString(hour.temp))")

Cannot assign value, SwiftUI fetch API

I wanted to grab data from exchangeratesapi.io, but I have been struggling with modeling my data.
it says "Cannot assign value of type 'rates' to type rates.Type"
I have no idea what I did nor have any visualization , if there's any reference please do comment below.
Here's my class
class MoneyView:ObservableObject {
#Published var currency = rates.self//[rates]()
init() {
fetchData()
}
func fetchData() {
guard let url = URL(string: "http://api.exchangeratesapi.io/v1/latest?access_key=24a5ab7688a7044f60bfeb491eb37550") else {
return
}
let session = URLSession(configuration: .default)
let task = session.dataTask(with: url) {(data, response, error) in
if error == nil {
let decoder = JSONDecoder()
if let safeData = data {
do{
let result = try decoder.decode(rates.self, from: safeData)
DispatchQueue.main.async {
self.currency = result // here's the error
}
} catch {
print(error)
}
}
}
}
task.resume()
}
}
Here's the rates type :
struct rates: Decodable{
// USD CAD IDR GBP CHF SGD INR MYR JPY KRW
var USD:Int
var CAD:Int
var IDR:Int
var GBP:Int
var CHF:Int
var SGD:Int
var INR:Int
var MYR:Int
var JPY:Int
var KWR:Int
}
in case you guys wonder how the API looks like
{
"success":true,
"timestamp":1620597364,
"base":"EUR",
"date":"2021-05-09",
"rates":{
"AED":4.469059,
"AFN":93.55172,
"ALL":122.991702,
"AMD":629.683505,
"ANG":2.167635,
"AOA":795.883245,
}
}
Change
#Published var currency:Rate?
struct Root: Decodable{
var rates:Rate
}
struct Rate: Decodable{
var USD:Int
var CAD:Int
var IDR:Int
var GBP:Int
var CHF:Int
var SGD:Int
var INR:Int
var MYR:Int
var JPY:Int
var KWR:Int
}
let result = try decoder.decode(Root.self, from: safeData)
currency = result.rates
Change class start letter to capital Rates

Having trouble accessing the API via data.nba.net

I have the Url saved in the info.plist as such:
BASE_URL <-> String <-> $(BASE_URL)
and in my project's Build Settings, I added a user-defined setting as such:
BASE_URL http://data.nba.net
After setting this up, when I try to get the website into the url variable, the variable returns "". As I debug the issue, I don't see the website stored under that variable.
I am new to Swift and still learning so any comments on the way I have setup my structs will be appreciated as well.
import UIKit
struct sports_content: Decodable {
let sports_meta_expanded: sports_meta
let teams_expanded: teams
}
struct sports_meta: Decodable {
let date_time: String?
let season_meta_list: season_meta
}
struct season_meta: Decodable {
let calendar_date: Date
let season_year: Int?
let stats_season_year: Int?
let stats_season_id: Int?
let stats_season_stage: Int?
let roster_season_year: Int?
let schedule_season_year: Int?
let standings_season_year: Int?
let season_id: Int?
let display_year: String
let display_season: String
let season_stage: Int?
}
struct next: Decodable {
let url: String
}
struct teams: Decodable {
let season_year: year
let team_data: [team]
}
struct year: Decodable {
let season_year: Int?
}
struct team: Decodable {
let is_nba_team: Bool
let team_name: String
let team_nickname: String
let team_code: String
let team_abbrev: String
let city: String
let state: String
let team_short_name: String
let team_id: Int?
let conference: String
let division_id: String
}
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let url = Bundle.main.infoDictionary?["BASE_URL"] as? String ?? ""
guard let convertedURL = URL(string: url) else {
return
}
URLSession.shared.dataTask(with: convertedURL) { (data, response, error) in
guard let data = data else {
return
}
do{
let dataSet = try JSONDecoder().decode(sports_content.self, from: data)
print(dataSet)
} catch {
print("JSONSerialization error:", error)
}
}.resume()
}
}
A build setting is used at build / compile time and not necessarily at run time.
To get your URL into the infoDictionary, you need to add it to the Info.plist file. Double click on your Info.plist to get the view open in your Xcode, then click "Add Value" under the Editor menu, then you can add BASE_URL as the key and your URL as the value.
Try using $(BASE_URL) as the value in your Info.plist and see if your build setting gets added in at build time. :)

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