I am trying to display data from an api. When I try to do it on the app it works but once I move the code to the widget it doesn't work the onAppear or Button. I need help on how to refresh the json when the decoder loads it. I think my decoder needs works I am not sure what the issue is.
struct panchangWidgetEntryView : View {
#State private var panchangData: PanchangData?
var entry: Provider.Entry
var body: some View {
HStack {
Text(panchangData?.tithi ?? "")
.font(.subheadline)
.padding()
Text(panchangData?.paksha ?? "")
.font(.subheadline)
.padding()
}
}
func loadData(){
guard let url = URL(string: "API URL") else {
return
}
URLSession.shared.dataTask(with: url){ data, response, error in
guard let data = data else { return }
if let decodedData = try? JSONDecoder().decode(PanchangData.self, from: data){
DispatchQueue.main.async {
self.panchangData = decodedData
}
}
}.resume()
}
}
struct PanchangData: Decodable{
var slon: Double
var mlon: Double
var tithinum: Int
var tithi: String
var paksha: String
var tithiTill: String
var nakshatra: String
var nakshatraTill: String
var yoga: String
var yogTill: String
var karana: String
var karanTill: String
var rashi: String
var requestsremaining: Int
var requeststotal: Int
var plan: String
var status: String
var reqdate: String
var reqtime: String
}
Related
I'm trying to get a response from this link : https://zsr.octane.gg/players/5f5ae840c6cbf591c568a477 but it won't work and I can't figure why.
There is my ContentView.swift :
struct ContentView: View {
#State private var players = [Player]()
var body: some View {
List(players, id: \._id) { item in
VStack(alignment: .leading) {
Text(item.name)
.font(.headline)
Text(item.country)
}
}.task {
await loadData()
}
}
func loadData() async {
guard let url = URL(string: "https://zsr.octane.gg/players/5f5ae840c6cbf591c568a477") else {
print("URL invalide")
return
}
do {
let (data, _) = try await URLSession.shared.data(from: url)
if let decodedResponse = try? JSONDecoder().decode(Response.self, from: data) {
players = decodedResponse.players
}
} catch {
print("Invalid data")
}
}
}
My Response struct :
struct Response: Codable {
var players: [Player]
}
Player struct :
struct Player: Codable {
var _id: String
var slug: String
var tag: String
var name: String
var country: String
var team: Team
var accounts: [Account]
var revelant: Bool
}
Team struct :
struct Team: Codable {
var _id: String
var slug: String
var name: String
var region: String
var image: String
var relevant: Bool
}
Account struct :
struct Account: Codable {
var platform: String
var id: String
}
Edit: the error that I have from the do catch is :
Invalid data with error: keyNotFound(CodingKeys(stringValue: "revelant", intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: "No value associated with key CodingKeys(stringValue: "revelant", intValue: nil) ("revelant").", underlyingError: nil))
I followed a tutorial which works well but when I replace the link and use my own structs nothing happens when I launch the app.
Thanks for your help.
Your response is a dictionary not an array to to log error use try instead of try? and log the error inside the catch part , this snippet is after the edit and it shows currently info of 1 player
One player
func loadData() async {
guard let url = URL(string: "https://zsr.octane.gg/players/5f5ae840c6cbf591c568a477") else {
print("URL invalide")
return
}
do {
let (data, _) = try await URLSession.shared.data(from: url)
let decodedResponse = try JSONDecoder().decode(Player.self, from: data)
players = [decodedResponse]
} catch {
print("Invalid data with error: ",error)
}
}
All players
May be you mean to drop 5f5ae840c6cbf591c568a477 from guard let url = URL(string: "https://zsr.octane.gg/players/5f5ae840c6cbf591c568a477") else { to get all players , check here complete working demo
import SwiftUI
struct ContentView: View {
#State private var players = [Player]()
var body: some View {
List(players, id: \._id) { item in
VStack(alignment: .leading) {
Text(item.name ?? "")
.font(.headline)
Text(item.country ?? "")
}
}.task {
await loadData()
}
}
func loadData() async {
guard let url = URL(string: "https://zsr.octane.gg/players") else {
print("URL invalide")
return
}
do {
let (data, _) = try await URLSession.shared.data(from: url)
let decodedResponse = try JSONDecoder().decode(Response.self, from: data)
players = decodedResponse.players
} catch {
print("Invalid data",error)
}
}
}
struct Response: Codable {
var players: [Player]
}
struct Player: Codable {
var _id: String
var slug: String
var tag: String
var name: String?
var country: String?
var accounts: [Account]?
}
struct Team: Codable {
var _id: String
var slug: String
var name: String
var region: String
var image: String
var relevant: Bool
}
struct Account: Codable {
var platform: String
var id: String
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Note: a variable that may exist in a model or not should be marked as optional
Upon login and validation, the user is sent to the main application page. I have the following code set.
import SwiftUI
typealias MyDefendant = [Defendant]
struct ContentView: View {
var email: String
#State var myDefendant: MyDefendant = []
func getUserData(completion:#escaping (MyDefendant)->()) {
var urlRequest = URLRequest(url: URL(string: "https://milanobailbonds.com/getDefendant.php")!)
urlRequest.httpMethod = "post"
let authData = [
"defEmail" : email
] as [String : Any]
do {
let authBody = try JSONSerialization.data(withJSONObject: authData, options: .prettyPrinted)
urlRequest.httpBody = authBody
urlRequest.addValue("application/json", forHTTPHeaderField: "content-type")
} catch let error {
debugPrint(error.localizedDescription)
}
URLSession.shared.dataTask(with: urlRequest) { (data, response, error) in
if let error = error {
print(error.localizedDescription)
return
}
guard let data = data else {
return
}
do {
let responseString = String(data: data, encoding: .utf8)!
print(responseString)
var returnValue: MyDefendant?
let decoder = JSONDecoder()
returnValue = try decoder.decode([Defendant].self, from: data)
completion(returnValue!)
}
catch { fatalError("Couldn't Parse")
}
}.resume()
return
}
var body: some View {
NavigationView {
VStack {
Text(email)
Text("I Need Bail")
.font(.largeTitle)
.fontWeight(.semibold)
Button {
print("Test")
} label: {
Label("I Need Bail", systemImage: "iphone.homebutton.radiowaves.left.and.right")
.labelStyle(IconOnlyLabelStyle())
.font(.system(size: 142.0))
}
} .foregroundColor(.green)
.shadow(color: .black, radius: 2, x: 2, y: 2)
.navigationBarTitle("Home")
.navigationBarTitleDisplayMode(.inline)
.navigationBarItems(leading:
Button {
print("Test")
} label: {
Label("I Need Bail", systemImage: "line.3.horizontal")
.labelStyle(IconOnlyLabelStyle())
})
}.onAppear() {
getUserData() { myDefendant in
self.myDefendant = myDefendant
}
}
}
}
In my data models I have created a struct for Defendant as such:
struct Defendant: Codable, Hashable, Identifiable {
var id: Int
var defImage: String
var defName: String
var defAddress: String
var defCity: String
var defState: String
var defZip: String
var defPhone: String
var defEmail: String
var defUserName: String
var defPW: String
var defDOB: String
var defPriorFTA: Int
var defFTAExplained: String
var defAssignedAgency: Int
} // Defendant Model
The PHP is working fine and returning valid JSON with all of the required items for the struct.
"\"[\\n {\\n \\\"Id\\\": 5,\\n \\\"defImage\\\": \\\"\\\",\\n \\\"defName\\\": \\\"Some Dude\\\",\\n \\\"defAddress\\\": \\\"123 Main St\\\",\\n \\\"defCity\\\": \\\"Some City\\\",\\n \\\"defState\\\": \\\"FL\\\",\\n \\\"defZip\\\": \\\"12345\\\",\\n \\\"defPhone\\\": \\\"888-888-8888\\\",\\n \\\"defEmail\\\": \\\"someone#someone.com\\\",\\n \\\"defUserName\\\": \\\"\\\",\\n \\\"defPW\\\": \\\"91492cffa4032765f6b025ec6b2c873e49fe5e58\\\",\\n \\\"defDOB\\\": \\\"01\\\\\\/01\\\\\\/1955\\\",\\n \\\"defPriorFTA\\\": 0,\\n \\\"defFTAExplained\\\": \\\"\\\",\\n \\\"defAssignedAgency\\\": 0\\n }\\n]\""
Unfortunately, I keep getting an error "Unable to Parse".
I'm new to Swift, and coding in general.
Any thoughts or ideas are greatly appreciated.
Thank you
Im not sure but you can try to change id into Id in your struct. Please remember name of your struct property must exactly the same with key in Json response.
I'm using a decode function to get data from a json file in the project. There is no error when compelling but the xcode can't resume the canvas.
I'm using a decode function to get data from a json file in the project. There is no error when compelling but the xcode can't resume the canvas.
my data modle:
struct Movie1:Codable {
var MOVIE_ID:String
var NAME:String
var ALIAS:String?
var ACTORS:String?
var COVER:String?
var DIRECTORS:String?
var DOUBAN_SCORE:Double?
var DOUBAN_VOTES:Int?
var GENRES:String?
var IMDB_ID:String?
var LANGUAGES:String?
var MINS:Double?
var OFFICIAL_SITE:String?
var REGIONS:String?
var RELEASE_DATE:String?
var SLUG:String?
var STORYLINE:String?
var TAGS:String?
var YEAR:String?
var ACTOR_IDS:String?
var DIRECTOR_IDS:String?
}
my json data:
{"MOVIE_ID":"27021694",
"NAME":"耶尔玛",
"ALIAS":"",
"ACTORS":"比莉·派佩/布伦丹·考威尔/约翰·麦克米兰",
"COVER":"",
"DIRECTORS":"西蒙·斯通",
"DOUBAN_SCORE":"8.2",
"DOUBAN_VOTES":"187",
"GENRES":"剧情/戏曲",
"IMDB_ID":"tt6847880",
"LANGUAGES":"英语",
"MINS":"0",
"OFFICIAL_SITE":"http://ntlive.nationaltheatre.org.uk/productions/ntlout22-yerma",
"REGIONS":"英国",
"RELEASE_DATE":"2017/8/31",
"SLUG":"72FZ3FZ3b",
"STORYLINE":"A young woman is driven to the unthinkable by her desperate desire to have a child in Simon Stone’s radical production of Lorca’s achingly powerful masterpiece.",
"TAGS":"NTLive/女性/舞台剧/戏剧/英国/英国国家剧院现场/BilliePiper/NTL",
"YEAR":"2017",
"ACTOR_IDS":"比莉·派佩:1049548|Maureen Beattie:|布伦丹·考威尔:1022987|约翰·麦克米兰:1319678|Charlotte Randle:|Thalissa Teixeira:",
"DIRECTOR_IDS":"西蒙·斯通:1336274"}
my bundle function
import Foundation
extension Bundle{
func decode<T:Codable>(filename:String) -> T {
guard let url = Bundle.main.url(forResource: filename, withExtension: nil),
let data = try? Data(contentsOf: url),
let getResponse = try? JSONDecoder().decode(T.self, from: data)
else { fatalError("no data")}
return getResponse
}
}
my view calling the function
struct homePage: View {
let datas:[Movie1]=Bundle.main.decode(filename: "movieData1.json")
var body: some View {
HStack {
Text("hello")
Text(datas[1].ACTORS ?? "none")
}
}
}
my structure and json file image
Your Movie1 structure is wrong. All your var in Movie1 should be String?.
No Double, no Int
Here is the code that works for me:
struct Movie1:Codable {
var MOVIE_ID:String
var NAME:String
var ALIAS:String?
var ACTORS:String?
var COVER:String?
var DIRECTORS:String?
var DOUBAN_SCORE:String? // <-- here
var DOUBAN_VOTES:String? // <-- here
var GENRES:String?
var IMDB_ID:String?
var LANGUAGES:String?
var MINS:String? // <-- here
var OFFICIAL_SITE:String?
var REGIONS:String?
var RELEASE_DATE:String?
var SLUG:String?
var STORYLINE:String?
var TAGS:String?
var YEAR:String?
var ACTOR_IDS:String?
var DIRECTOR_IDS:String?
}
struct ContentView: View {
#State var movieData: [Movie1] = []
var body: some View {
HStack {
Text("hello")
if !movieData.isEmpty {
Text(movieData[0].ACTORS ?? "none")
}
}
.onAppear {
if let datas: [Movie1] = Bundle.main.decode(filename: "movieData1.json") {
movieData = datas
}
}
}
}
// do what #Paulw11 is suggesting, use do/try/catch with [T]?
extension Bundle{
func decode<T:Codable>(filename:String) -> [T]? {
guard let url = Bundle.main.url(forResource: filename, withExtension: nil),
let data = try? Data(contentsOf: url),
let getResponse = try? JSONDecoder().decode([T].self, from: data)
else {
return nil
}
return getResponse
}
}
data in your "movieData1.json" file (note the start "[" and end "]"):
[
{
"MOVIE_ID":"27021694",
"NAME":"耶尔玛",
"ALIAS":"",
"ACTORS":"比莉·派佩/布伦丹·考威尔/约翰·麦克米兰",
"COVER":"",
"DIRECTORS":"西蒙·斯通",
"DOUBAN_SCORE":"8.2",
"DOUBAN_VOTES":"187",
"GENRES":"剧情/戏曲",
"IMDB_ID":"tt6847880",
"LANGUAGES":"英语",
"MINS":"0",
"OFFICIAL_SITE":"http://ntlive.nationaltheatre.org.uk/productions/ntlout22-yerma",
"REGIONS":"英国",
"RELEASE_DATE":"2017/8/31",
"SLUG":"72FZ3FZ3b",
"STORYLINE":"A young woman is driven to the unthinkable by her desperate desire to have a child in Simon Stone’s radical production of Lorca’s achingly powerful masterpiece.",
"TAGS":"NTLive/女性/舞台剧/戏剧/英国/英国国家剧院现场/BilliePiper/NTL",
"YEAR":"2017",
"ACTOR_IDS":"比莉·派佩:1049548|Maureen Beattie:|布伦丹·考威尔:1022987|约翰·麦克米兰:1319678|Charlotte Randle:|Thalissa Teixeira:",
"DIRECTOR_IDS":"西蒙·斯通:1336274"
}
]
EDIT1: adding a new element.
Adding a new var is no problem at all. In your Movie1 struct:
struct Movie1:Codable {
var isFavored:Bool // <---
var MOVIE_ID:String
....
}
and at the same time you need to modify your data to include this new element, like this:
[
{
"isFavored": true,
"MOVIE_ID":"27021694",
...
}
]
If you do not want your data to have a isFavored element, then give it an initial value
in the Movie1 struct, and use CodingKeys to omit this isFavored That will remove the JSON coding and decoding of isFavored:
struct Movie1:Codable {
var MOVIE_ID:String
var NAME:String
var ALIAS:String?
var ACTORS:String?
var COVER:String?
var DIRECTORS:String?
var DOUBAN_SCORE:String?
var DOUBAN_VOTES:String?
var GENRES:String?
var IMDB_ID:String?
var LANGUAGES:String?
var MINS:String?
var OFFICIAL_SITE:String?
var REGIONS:String?
var RELEASE_DATE:String?
var SLUG:String?
var STORYLINE:String?
var TAGS:String?
var YEAR:String?
var ACTOR_IDS:String?
var DIRECTOR_IDS:String?
var isFavored: Bool = false // <--- here
// note the missing isFavored
enum CodingKeys: String, CodingKey {
case MOVIE_ID, NAME,ALIAS,ACTORS,COVER,DIRECTORS,DOUBAN_SCORE
case DOUBAN_VOTES,GENRES,IMDB_ID,LANGUAGES,MINS,OFFICIAL_SITE
case REGIONS,RELEASE_DATE,SLUG,STORYLINE,TAGS,YEAR,ACTOR_IDS,DIRECTOR_IDS
}
}
I'm trying to create a list view with some data I am pulling from an API. I'm struggling to understand how to take the data from the API response and putting it into the state for my app to use. Below is the content view in my application that is pulling the data.
import SwiftUI
import Alamofire
struct ContentView: View {
#State var results = [Bottle]()
var body: some View {
List(results, id: \.id) { item in
VStack(alignment: .leading) {
Text(item.name)
}
}.onAppear(perform: loadData)
}
func loadData() {
let request = AF.request("https://bevy-staging.herokuapp.com")
request.responseJSON { (data) in
print(data)
}
}
}
I've tried adding this to the result block
AF.request("https://bevy-staging.herokuapp.com/").responseJSON { response in
guard let data = response.data else { return }
if let response = try? JSONDecoder().decode([Bottle].self, from: data) {
DispatchQueue.main.async {
self.results = response
}
return
}
}
However nothing populates in my view and I get the following error.
nw_protocol_get_quic_image_block_invoke dlopen libquic failed
Why am I receiving this error and how can I get my data to display in the list vew?
Here is the model I am working with.
struct Bottle: Decodable {
var id: String
var name: String
var price: String
var sku: String
var size: String
var origination: String
var varietal: String
var brand_bottle: String
}
You need to add "?" to model data that can have null data, for all model rows which can obtain "null" need to use "?" or JSONDecoder wouldn't decode data to your model. Inside the model your rows "origination, varietal, brand_bottle" have "String" data type but from the server, you obtain "null", thus JSONDecoder can't recognize data.
You can check responses use services like "http://jsonviewer.stack.hu/" or any other.
Need to modify model data like below:
struct Bottle: Decodable {
var id: String
var name: String
var price: String
var sku: String
var size: String
var origination: String?
var varietal: String?
var brand_bottle: String?
}
I did recreate your project and all work well code below:
import SwiftUI
import Alamofire
struct ContentView: View {
#State var results = [Bottle]()
var body: some View {
List(results, id: \.id) { item in
VStack(alignment: .leading) {
Text(item.name)
}
}.onAppear(perform: loadData)
}
func loadData() {
AF.request("https://bevy-staging.herokuapp.com/").responseJSON { response in
guard let data = response.data else { return }
if let response = try? JSONDecoder().decode([Bottle].self, from: data) {
DispatchQueue.main.async {
self.results = response
}
return
}
}
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct Bottle: Decodable {
var id: String
var name: String
var price: String
var sku: String
var size: String
var origination: String?
var varietal: String?
var brand_bottle: String?
}
I have to pass the value of movie.id which is received from a View which is called ReviewView.
I need to pass the movie.id value received in this view to ReviewFetcher and then make a network request using that movie.id. As of now I have hard coded the movie id in ReviewFetcher but I require this to be received from ReviewView and then make a request and then update the list in ReviewView.
Below is the Code:-
ReviewFetcher.swift
import Foundation
import Alamofire
import SwiftUI
class ReviewObserver: ObservableObject {
#Published var review = ReviewArray(id: 1, page: 9, results: [])
// #State var movieID:Int
init() {
// self.movieID = movieID
getReviews(movieID : 181812)
}
func getReviews(movieID:Int) {
//self.review.results.removeAll()
let reviewURL = "https://api.themoviedb.org/3/movie/"+String(movieID)+"/reviews?api_key=a18f578d774935ef9f0453d7d5fa11ae&language=en-US&page=1"
Alamofire.request(reviewURL)
.responseJSON { response in
if let json = response.result.value {
if (json as? [String : AnyObject]) != nil {
if let dictionaryArray = json as? Dictionary<String, AnyObject?> {
let json = dictionaryArray
if let id = json["id"] as? Int,
let page = json["page"] as? Int,
let results = json["results"] as? Array<Dictionary<String, AnyObject?>> {
for i in 0..<results.count {
if let author = results[i]["author"] as? String,
let content = results[i]["content"] as? String,
let url = results[i]["url"] as? String {
let newReview = ReviewModel(author: author,
content: content,
url: url)
self.review.results.append(newReview)
}
}
}
}
}
}
}
}
}
ReviewView.swift
import SwiftUI
struct ReviewsView: View {
#State var movie: MovieModel
#Binding var reviews:[ReviewModel]
#ObservedObject var fetcher = ReviewObserver()
var body: some View {
VStack(alignment:.leading) {
Text("Review")
.font(.largeTitle)
.bold()
.foregroundColor(Color.steam_rust)
.padding(.leading)
Divider()
// Text(String(fetcher.movieID))
List(fetcher.review.results) { item in
VStack(alignment:.leading) {
Text("Written by : "+item.author)
.font(.body)
.bold()
.padding(.bottom)
Text(item.content)
.font(.body)
.lineLimit(.max)
}
}
}
}
}
MovieModel.swift
import Foundation
import SwiftUI
import Combine
struct MovieArray: Codable {
var page: Int = 0
var total_results: Int = 0
var total_pages: Int = 0
var results: [MovieModel] = []
}
struct MovieModel: Codable, Identifiable {
var id : Int
var original_title: String
var title: String
var original_language:String
var overview: String
var poster_path: String?
var backdrop_path: String?
var popularity: Double
var vote_average: Double
var vote_count: Int
var video: Bool
var adult: Bool
var release_date: String?
}
Remove the init() of your ReviewObserver class. and then call getReviews method in .onAppear modifier of your VStack. The idea of what you need:
class ReviewObserver: ObservableObject {
#Published var review = ReviewArray(id: 1, page: 9, results: [])
func getReviews(movieID:Int) {
//you block,, anything you wanna do with movieID.
//Assume you are going to change 'review' variable
}
}
struct ReviewsView: View {
#State var movie:MovieModel
#Binding var reviews:[ReviewModel]
#ObservedObject var fetcher = ReviewObserver()
var body: some View {
VStack(alignment:.leading){
Text("Review")
Divider()
Text(String(fetcher.movieID))
List(fetcher.review.results)
{
item in
VStack(alignment:.leading){
Text("Written by : "+item.author)
}
}.onAppear {
self.fetcher.getReviews(movieID: movie.id)
}
}
}