I'm stuck when a I need to load a json file to swiftUI when this json comes from an URL with entry variables that must be filled by the user before getting the data.
I have the following code:
// This is the structure to get the data from the API.
struct loka_result: Codable {
var date: String
var time: String
var unix_time: Int
var seqNumber : Int
var lat: Double
var lng: Double
var device: String
var accuracy: Double
var info: String
var temperature : Double?
var battery_voltage : Float?
}
// Now conform to Identifiable
extension loka_result: Identifiable {
public var id: Int { return unix_time }
}
// Model Data to get the data
final class ModelData: ObservableObject {
#Published var allthelokas : [loka_result] = loadAllData(deviceId:selected_loka, timeDelta: selected_delta)
}
// Function to get and parse the data
func loadAllData<T: Decodable>(deviceId:String,timeDelta:Int) -> T
{
let url = URL(string: "https://xxxx.php?device=\(deviceId)&hours=\(timeDelta)"),
data = try? Data(contentsOf: url!)
return
try! JSONDecoder().decode(T.self, from: data!)
}
Then in TempChartView I would like to get a list
import SwiftUI
struct TempChartNew: View {
#EnvironmentObject var modelData: ModelData
var body: some View {
VStack {
List(modelData.allthelokas) { j in
// ForEach(modelData.allthelokas) { jj in
HStack {
Text(j.device)
Text(j.date)
Text(j.time)
Text(String(j.seqNumber))
}
}
}
}
}
struct TempChartNew_Previews: PreviewProvider {
static var previews: some View {
TempChartNew().environmentObject(ModelData())
}
}
all this its working because just to test it I have added two global variables to fill in the data for the function.
var selected_loka: String = "3JJJ30"
var selected_delta: Int = 24
But the ideia would be for the view to have two entry points.
And its here where I'm stuck ! I don't know how to pass the variables to the class.
Does anyone can please point me in the correct direction ? - Thank you
Related
A beginner here.
I'm having some trouble trying to parse this json url in SwiftUI
Here is what I've tried:
import SwiftUI
struct PriceData2: Decodable {
var id: Int
var last: Double
var lowestAsk: Double
var highestBid: Double
var percentChange: Double
var baseVolume: Double
var quoteVolume: Double
var isFrozen: Int
var high24hr: Double
var low24hr: Double
var change: Double
var prevClose: Double
var prevOpen: Double
}
struct ContentView: View {
#State var priceData2: PriceData2?
var body: some View {
if #available(iOS 15.0, *) {
VStack(){
Text("\(priceData2?.id ?? 0)")
}.onAppear (perform: loadData2)
} else {
// Fallback on earlier versions
}
}
public func loadData2() {
guard let url = URL(string: "https://api.bitkub.com/api/market/ticker?sym=THB_BTC") else {
return
}
URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data else {return}
if let decodedData = try? JSONDecoder().decode(PriceData2.self, from: data){
DispatchQueue.main.async {
self.priceData2 = decodedData
}
}
}.resume()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
I think it has something to do with the THB_BTC property of the dictionary that I haven't dealt with but I'm not totally sure of.
Any help with this is much appreciated.
Thanks
What is missing when decoding is a top level type to hold the price data and there are two ways to solve this, either by using a top level struct or by using a dictionary.
Since the top level key, THB_BTC, seems to be the "symbol" argument from the query and thus might change a top level dictionary is the best alternative here
do {
let result = try JSONDecoder().decode([String: PriceData].self, from: data)
if let priceData = result.values.first { //or = result["THB_BTC"]
print(priceData)
}
} catch {
print(error)
}
Just for completeness, here is the variant with the top level struct
struct Result: Decodable {
enum CodingKeys: String, CodingKey {
case data = "THB_BTC"
}
let data: PriceData
}
let result = try JSONDecoder().decode(Result.self, from: data)
print(result.data)
I'm stuck on this.
I have a json that I'm parsing and that Json has an entry value and I want to do a swift list based in user input that will trigger from the external json different responses.
I have the following code so far
import SwiftUI
import Combine
import Foundation
var lokas : String = "ABCY"
struct ContentView: View {
#State var name: String = ""
#ObservedObject var fetcher = Fetcher()
var body: some View {
VStack {
TextField("Enter Loka id", text: $name)
List(fetcher.allLokas) { vb in
VStack (alignment: .leading) {
Text(movie.device)
Text(String(vb.seqNumber))
.font(.system(size: 11))
.foregroundColor(Color.gray)
}
}
}
}
}
public class Fetcher: ObservableObject {
#Published var allLokas = [AllLoka_Data]()
init(){
load(devices:lokas)
}
func load(devices:String) {
let url = URL(string: "https://xxx.php?device=\(devices)&hours=6")!
URLSession.shared.dataTask(with: url) {(data,response,error) in
do {
if let d = data {
let decodedLists = try JSONDecoder().decode([AllLoka_Data].self, from: d)
DispatchQueue.main.async {
self.allLokas = decodedLists
}
}else {
print("No Data")
}
} catch {
print ("Error")
}
}.resume()
}
}
struct AllLoka_Data: Codable {
var date: String
var time: String
var unix_time: Int
var seqNumber : Int
}
// Now conform to Identifiable
extension AllLoka_Data: Identifiable {
var id: Int { return unix_time }
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
But I just added a Global variable to test it, but how do I pass the variable name to make the function work ?
Thank you
I am playing with the Instagram API and I encounter the following issue: I managed to decode and access the JSON data but when I try to access a certain value inside a SwiftUI View I encounter the following error: Thread 1: Fatal error: Index out of range. I am totally aware that this is triggered by the fact that the API load is asynchronous but I can not find a fix for this.
I would greatly appreciate your help
Below you can find the API response
Here you can find my model and JSON Decoder
struct InstaAPI: Codable {
var name: String
var period: String
var description: String
var values: [ValueResponse]
}
struct ValueResponse: Codable {
let value: String
}
struct Entry: Codable {
let data: [InstaAPI]
}
class getData: ObservableObject {
#Published var response = [Entry]()
init() {
downloadJSON(from: URL(string: "https://graph.facebook.com/v6.0/17841402116620153/insights?metric=impressions&period=day&access_token=accounttoken")!)
}
func downloadJSON(from url: URL) {
URLSession.shared.dataTask(with: url) { data, response, error in
if let data = data {
let jsonDecoder = JSONDecoder()
do {
let parsedJson = try jsonDecoder.decode(Entry.self, from: data)
DispatchQueue.main.async {
self.response.append(parsedJson)
}
for data in parsedJson.data {
print(data.values[0].value)
}
} catch {
print(error)
}
}
}.resume()
}
}
maybe the old fashioned if would do the trick:
struct ContentView: View {
#ObservedObject var response = getData()
#State var responseNdx = 0
#State var dataNdx = 0
var body: some View {
VStack {
if responseNdx < self.response.response.count {
if dataNdx < self.response.response[responseNdx].data.count {
Text(self.response.response[responseNdx].data[dataNdx].name)
}
}
}
}
}
So over the last couple hours, I've been trying to create a horizontal card stack that will show the most recent headlines for a project I am working on. Where I am at now, is holding up my entire flow, because my list cannot find my id variable in my results struct. Below I've attached the code, and any help would be appreciated.
HeadlinesUI.swift - Screenshot
import SwiftUI
struct Response: Codable {
var results: [Result]
}
struct Result: Codable {
var id: Int
var title: String
var header_image: String
var summary: String
}
struct HeadlineUI: View {
#State var results = [Result]()
var body: some View {
ScrollView(.horizontal) {
HStack(spacing: 20) {
List(results, id: \.id) { items in
}
.onAppear(perform: loadData)
}
}
}
func loadData() {
}
}
struct HeadlineUI_Previews: PreviewProvider {
static var previews: some View {
HeadlineUI()
}
}
As mentioned in the comments Result is a type in Swift, so you have to use a different name.
I went for:
struct MyResult: Codable {
var id: Int
var title: String
var header_image: String
var summary: String
}
since it has got an id you can make it Identifiable:
extension MyResult: Identifiable {}
as for the body:
var body: some View {
ScrollView(.horizontal) {
HStack(spacing: 20) {
ForEach(results) { result in
Text(result.title)
}
.onAppear(perform: { self.loadData() })
}
}
}
You rather need a ForEach then a List and the builder cannot be empty. Also onAppear takes a closure as its argument. Once you make all the changes the View will behave as expected.
I have problem with showing JSON Data in SwiftUI, I get the data from Genius API I currently search for song and can confirm that I get the data extracted correctly; example I can print out the title of the result:
This is how I fetch the data
class NetworkManager: ObservableObject {
var objectWillChange = PassthroughSubject<NetworkManager, Never>()
var fetchedSongsResults = [hits]() {
willSet {
objectWillChange.send(self)
}
}
init() {
fetchSongs()
}
func fetchSongs() {
guard let url = URL(string: "https://api.genius.com/search?q=Sia") else { return }
var urlRequest = URLRequest(url: url)
urlRequest.setValue("Bearer TOKEN", forHTTPHeaderField: "Authorization")
URLSession.shared.dataTask(with: urlRequest) {data, response, error in
guard let data = data else { return }
//print(String(decoding: data, as: UTF8.self))
let songs = try! JSONDecoder().decode(feed.self, from: data)
DispatchQueue.main.async {
self.fetchedSongsResults = songs.response.hits
}
}.resume()
}
}
So when I get the data I save to the variable fetchedSongsResults and this seems correctly but for what ever reason when I try to print the count for example it says that i empty and also I can't loop through the fetchedSongsResults using a list or ForEach this is how, (which I believe s because I have not made the model identifiable) I tried to print the count of fetchedSongsResults,
This initialized outside the body (just so you know)
#State var networkManager = NetworkManager()
This is inside the body
Text("\(networkManager.fetchedSongsResults.count)")
If your are wondering how my structure looks like when I decode the JSON Data then here it is
struct feed: Codable {
var meta: meta
var response: response
}
struct meta: Codable {
var status: Int
}
struct response: Codable {
var hits: [hits]
}
struct hits: Codable {
var index: String
var type: String
var result: song
}
struct song: Codable, Identifiable {
var id: Int
var header_image_thumbnail_url: String
var url: String
var title: String
var lyrics_state: String
var primary_artist: artist
}
struct artist: Codable {
var name: String
}
Try: #ObservedObject var networkManager = NetworkManager().