This API function of mine called fetchAuthorArticles can not fetch JSON data at the first tap. If I close the sheet and tap it once more it does fetch JSON and fills SelectedAuthorView screen accordingly. What might be the reason?
class AuthorService: ObservableObject {
#Published var authorArticles = AuthorList(author: Author(author_id: 0, articles: 0, newspaper_image: "", author_name: "", author_image: "", newspaper: ""), articles: [])
func fetchAuthorArticles(authorID: Int){
isLoadingPage = true
if let selectedAuthorUrl = URL(string:"http://author/article/list/\(authorID)/") {
let session = URLSession(configuration: .default)
var request = URLRequest(url: selectedAuthorUrl)
request.httpMethod = "GET"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
let task = session.dataTask(with: request as URLRequest) { (data, response, error) in
print(data)
print(response)
print(error)
if error == nil {
let decoder = JSONDecoder()
if let safeData = data {
do {
let results = try decoder.decode(AuthorArticleList.self, from: safeData)
DispatchQueue.main.async {
self.authorArticles = results
self.isLoadingPage = false
}
} catch {
print(error)
}
}
}
}
task.resume()
}
}
}
AuthorArticleList Model
struct AuthorArticleList:Decodable {
var author: Author
var articles: [Articles]
}
struct Author: Decodable, Identifiable {
var id: Int {
return author_id
}
var author_id: Int
var articles: Int
var newspaper_image: String
var author_name: String
var author_image: String
var newspaper: String
}
struct Articles: Decodable, Identifiable {
var id: Int {
return article_id
}
var article_id: Int
var title: String
var url: String
var abstract: String
var article_date: String
}
SelectedAuthorView
struct SelectedAuthorView: View {
#Binding var selectedAuthor: AuthorPost
#ObservedObject var service = AuthorService()
var cellNumber: Int = 0
let columns: [GridItem] = Array(repeating: GridItem(.flexible(), spacing: 0), count: 2)
var body: some View {
ScrollView(.vertical, showsIndicators: true) {
LazyVGrid(columns: columns, spacing: 0) {
ForEach(Array(service.authorArticles.articles.enumerated()), id: \.1.id) {i, post in
AuthorArticleListItem(articlePost: post, cellNumber: i)
}
.background(Color(.white))
if service.isLoadingPage {
ProgressView()
.padding()
.offset(x: UIScreen.main.bounds.width / 4)
}
}
.onAppear {
self.service.fetchAuthorArticles(authorID: selectedAuthor.author_id)
}
Related
I know this type of question seems to be answered a lot but I really can't seem to make this work. I'm trying to decode some JSON data into my data structs. I think the problem is there. I may have my data model wrong, but can't quite work it out. The data is not an array, there is an array within it. Its trying to decode a dictionary into array but when I try to initialise my results variable as something other than array it won't build. I'll submit my code and the JSON data in the hopes someone can shed light!
The error I'm getting is:
JSON decode failed: Swift.DecodingError.typeMismatch(Swift.Array, Swift.DecodingError.Context(codingPath: [], debugDescription: "Expected to decode Array but found a dictionary instead.", underlyingError: nil))
thank you so much
import SwiftUI
struct DataFormatted: Codable, Hashable {
var deliveryPoints: [DeliveryPoints]
var deliveryPointCount: Int
var postalCounty: String
var traditionalCounty: String
var town: String
var postCode: String
}
struct DeliveryPoints: Codable, Hashable {
var organisationName: String
var departmentName: String
var line1: String
var line2: String
var udprn: String
var dps: String
}
struct ContentView: View {
// I reckon the error is here:
#State private var results = [DataFormatted]()
var body: some View {
VStack{
List{
ForEach(results, id: \.self) { result in
Text(result.postalCounty)
}
}
}
.task {
await loadData()
}
}
func loadData() async {
guard let url = URL(string: "https://pcls1.craftyclicks.co.uk/json/rapidaddress?key=APIKEY&postcode=aa11aa&response=data_formatted") else {
print("Invalid URL")
return
}
var request = URLRequest(url: url)
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpMethod = "GET"
do {
let (data, _) = try await URLSession.shared.data(for: request)
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
let decodedResponse = try decoder.decode([DataFormatted].self, from: data)
results = decodedResponse
} catch let jsonError as NSError {
print("JSON decode failed: \(jsonError)")
}
}
}
JSON Data:
{
"delivery_points":[
{
"organisation_name":"THE BAKERY",
"department_name":"",
"line_1":"1 HIGH STREET",
"line_2":"CRAFTY VALLEY",
"udprn":"12345678",
"dps":"1A"
},
{
"organisation_name":"FILMS R US",
"department_name":"",
"line_1":"3 HIGH STREET",
"line_2":"CRAFTY VALLEY",
"udprn":"12345679",
"dps":"1B"
}
],
"delivery_point_count":2,
"postal_county":"POSTAL COUNTY",
"traditional_county":"TRADITIONAL COUNTY",
"town":"BIG CITY",
"postcode":"AA1 1AA"
}
try something like this:
struct DataFormatted: Codable {
var deliveryPoints: [DeliveryPoint]
var deliveryPointCount: Int
var postalCounty: String
var traditionalCounty: String
var town: String
var postcode: String // <-- postcode
}
struct DeliveryPoint: Codable {
var organisationName: String
var departmentName: String
var line1: String?
var line2: String?
var line3: String?
var udprn: String
var dps: String
}
and use it like this:
let apiResponse = try decoder.decode(DataFormatted.self, from: data)
EDIT-1: here is the code I used for testing:
// -- here, default values for convenience
struct DataFormatted: Codable {
var deliveryPoints: [DeliveryPoint] = []
var deliveryPointCount: Int = 0
var postalCounty: String = ""
var traditionalCounty: String = ""
var town: String = ""
var postcode: String = "" // <-- postcode
}
struct DeliveryPoint: Hashable, Codable { // <-- here
var organisationName: String
var departmentName: String
var line1: String?
var line2: String?
var line3: String?
var udprn: String
var dps: String
}
struct ContentView: View {
#State private var results = DataFormatted()
var body: some View {
VStack{
Text(results.postalCounty)
Text(results.town)
List {
ForEach(results.deliveryPoints, id: \.self) { point in
Text(point.organisationName)
}
}
}
.task {
await loadData()
}
}
func loadData() async {
let apikey = "your-key" // <-- here
guard let url = URL(string: "https://pcls1.craftyclicks.co.uk/json/rapidaddress?key=\(apikey)&postcode=aa11aa&response=data_formatted") else {
print("Invalid URL")
return
}
do {
let (data, _) = try await URLSession.shared.data(from: url)
let decoder = JSONDecoder()
decoder.keyDecodingStrategy = .convertFromSnakeCase
results = try decoder.decode(DataFormatted.self, from: data) // <-- here
} catch {
print("JSON decode failed: \(error)")
}
}
}
I'm making a contact list app for a project to learn programming, and am currently creating the method to add a contact to a database through a Rest API I created.
The code below is for the Add Contact view, and I followed some tutorials on how to take data from a form, convert it to JSON and send it to a database via a POST request.
This is the code I've gotten, and what should be happening is that I should see a print of responseJSON, but that isn't happening, in fact nothing is happening and when I run the checkDetails function by pressing the button on the view I get the prints of body and jsonData, but nothing else. Not even an error.
I am a bit stumped here as I haven't changed much, if anything, from the tutorials I followed, so I am guessing it is something to do with my own code.
Any help would be appreciated!
import SwiftUI
import Combine
struct AddContact: View {
#State var id = 999
#State var first_name: String = ""
#State var last_name: String = ""
#State var phone_number: String = ""
#State var address: String = ""
#State var birthday = Date()
#State var birthdayString: String = ""
#State var create_date = Date()
#State var create_dateString: String = ""
#State var updated_date = Date()
#State var updated_dateString: String = ""
#State var manager = DataPost()
var body: some View {
if manager.formCompleted {
Text("Done").font(.headline)
}
VStack {
NavigationView {
Form {
Section() {
TextField("First Name", text: $first_name)
TextField("Last Name", text: $last_name)
}
Section() {
TextField("Phone Number", text: $phone_number)
TextField("Address", text: $address)
}
Section() {
DatePicker("Birthday", selection: $birthday, displayedComponents: .date)
}
Section() {
Button(action: {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .short
birthdayString = dateFormatter.string(from: birthday)
create_dateString = dateFormatter.string(from: create_date)
updated_dateString = dateFormatter.string(from: updated_date)
print("Clicked")
self.manager.checkDetails(id: self.id, first_name: self.first_name, last_name: self.last_name, phone_number: self.phone_number, address: self.address, birthday: self.birthdayString, create_date: self.create_dateString, updated_date: self.updated_dateString)
}, label: {
Text("Add Contact")
.fontWeight(.bold)
.multilineTextAlignment(.center)
})
}.disabled(first_name.isEmpty || last_name.isEmpty || phone_number.isEmpty || address.isEmpty)
}
}.navigationTitle("New Contact")
.navigationBarTitleDisplayMode(.inline)
}
}
}
class DataPost: ObservableObject {
var didChange = PassthroughSubject<DataPost, Never>()
var formCompleted = false {
didSet {
didChange.send(self)
}
}
func checkDetails(id: Int, first_name: String, last_name: String, phone_number: String, address: String, birthday: String, create_date: String, updated_date: String) {
let body: [String: Any] = ["data": ["id": id, "first_name": first_name, "last_name": last_name, "birthday": birthday, "phone_number": phone_number, "create_date": create_date, "updated_date": updated_date, "address": address]]
let jsonData = try? JSONSerialization.data(withJSONObject: body)
let url = URL(string: "https://flaskcontact-list-app.herokuapp.com/contacts")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("\(String(describing: jsonData?.count))", forHTTPHeaderField: "Content-Length")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = jsonData
print(body)
print(jsonData)
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
print(error?.localizedDescription ?? "No data")
return
}
let responseJSON = try? JSONSerialization.jsonObject(with: data, options: [])
if let responseJSON = responseJSON as? [String: Any] {
print(responseJSON)
}
}
task.resume()
}
}
Ok, this is the test code I use. Although I am on macos 12.beta, xcode 13.beta, target ios 15 and macCatalyst.
Tested on iPhone ios15 and macos 12. It "should be the same" on xcode 12.5 and macos 11.4.
import Foundation
import SwiftUI
import Combine
#main
struct TestApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
struct AddContact: View {
#State var id = 999
#State var first_name: String = ""
#State var last_name: String = ""
#State var phone_number: String = ""
#State var address: String = ""
#State var birthday = Date()
#State var birthdayString: String = ""
#State var create_date = Date()
#State var create_dateString: String = ""
#State var updated_date = Date()
#State var updated_dateString: String = ""
#State var manager = DataPost()
var body: some View {
if manager.formCompleted {
Text("Done").font(.headline)
}
VStack {
NavigationView {
Form {
Section() {
TextField("First Name", text: $first_name)
TextField("Last Name", text: $last_name)
}
Section() {
TextField("Phone Number", text: $phone_number)
TextField("Address", text: $address)
}
Section() {
DatePicker("Birthday", selection: $birthday, displayedComponents: .date)
}
Section() {
Button(action: {
let dateFormatter = DateFormatter()
dateFormatter.dateStyle = .short
birthdayString = dateFormatter.string(from: birthday)
create_dateString = dateFormatter.string(from: create_date)
updated_dateString = dateFormatter.string(from: updated_date)
print("Clicked")
self.manager.checkDetails(id: self.id, first_name: self.first_name, last_name: self.last_name, phone_number: self.phone_number, address: self.address, birthday: self.birthdayString, create_date: self.create_dateString, updated_date: self.updated_dateString)
}, label: {
Text("Add Contact")
.fontWeight(.bold)
.multilineTextAlignment(.center)
})
}.disabled(first_name.isEmpty || last_name.isEmpty || phone_number.isEmpty || address.isEmpty)
}
}.navigationTitle("New Contact")
.navigationBarTitleDisplayMode(.inline)
}
}
}
class DataPost: ObservableObject {
var didChange = PassthroughSubject<DataPost, Never>()
var formCompleted = false {
didSet {
didChange.send(self)
}
}
func checkDetails(id: Int, first_name: String, last_name: String, phone_number: String, address: String, birthday: String, create_date: String, updated_date: String) {
let body: [String: Any] = ["data": ["id": id, "first_name": first_name, "last_name": last_name, "birthday": birthday, "phone_number": phone_number, "create_date": create_date, "updated_date": updated_date, "address": address]]
let jsonData = try? JSONSerialization.data(withJSONObject: body)
// "https://flaskcontact-list-app.herokuapp.com/contacts"
let url = URL(string: "https://jsonplaceholder.typicode.com/posts")!
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("\(String(describing: jsonData?.count))", forHTTPHeaderField: "Content-Length")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = jsonData
let task = URLSession.shared.dataTask(with: request) { data, response, error in
print("-----> data: \(data)")
print("-----> error: \(error)")
guard let data = data, error == nil else {
print(error?.localizedDescription ?? "No data")
return
}
let responseJSON = try? JSONSerialization.jsonObject(with: data, options: [])
print("-----1> responseJSON: \(responseJSON)")
if let responseJSON = responseJSON as? [String: Any] {
print("-----2> responseJSON: \(responseJSON)")
}
}
task.resume()
}
}
struct ContentView: View {
var body: some View {
AddContact()
}
}
I'm trying to make a detailed view of a contact from a contacts list. This is the WIP of that view. I am getting the error;
Missing arguments for parameters 'data', 'detailedData' in call
on the line with ContactDetail() in the ContactDetail_Previews struct.
I think I understand that this is because something is missing from the variables data and detailedData, but my confusion comes from how I use similar code for the actual list view of all the contacts, with no such error. I have pasted the code for the whole list view below the code for the detailed view.
Any help would be appreciated!
Contact Detail Code:
import SwiftUI
struct ContactDetail: View {
var data: Response_Detailed.Contact_Detailed
#ObservedObject var detailedData: getData_Detailed
var body: some View {
VStack {
Text(data.first_name + " " + data.last_name)
Text(data.phone_number)
Text(data.birthday)
Text(data.address)
Text(data.updated_date)
Text(data.create_date)
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.detailedData.updateDetailed_Data()
}
}
}
}
}
class getData_Detailed: ObservableObject {
#Published var data = [Response_Detailed.Contact_Detailed]()
#Published var id = 1
init() {
updateDetailed_Data()
}
func updateDetailed_Data() {
let url = "DATABASE_LINK\(id)"
let session = URLSession(configuration: .default)
session.dataTask(with: URL(string: url)!) { (data, _, err) in
if err != nil {
print((err?.localizedDescription)!)
return
}
do {
let json = try JSONDecoder().decode(Response_Detailed.self, from: data!)
let oldData = self.data
DispatchQueue.main.async {
self.data = oldData + json.data
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let data = try! encoder.encode(json)
print(String(data: data, encoding: .utf8)!)
}
}
catch {
print(error.localizedDescription)
}
}.resume()
}
}
struct ContactDetail_Previews: PreviewProvider {
static var previews: some View {
ContactDetail()
}
}
struct Response_Detailed: Codable {
struct Contact_Detailed: Codable, Identifiable {
public let id: Int
public let first_name: String
public let last_name: String
public let birthday: String
public let phone_number: String
public let create_date: String
public let updated_date: String
public let address: String
}
public let data: [Contact_Detailed]
enum CodingKeys : String, CodingKey {
case data = "data"
}
}
Contacts List View Code: (Note the same error comes up on the line with NavigationLink.
import SwiftUI
struct ContentView: View {
var body: some View {
NavigationView {
HStack {
ContactsList()
.navigationBarTitle("Contacts")
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Image(systemName: "plus")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 20.0)
}
}
}
}
}
}
struct ContactsList: View {
#ObservedObject var listData = getData()
var body: some View {
List(0..<listData.data.count, id: \.self) {i in
NavigationLink(destination: ContactDetail()) {
if i == self.listData.data.count - 1 {
cellView(data: self.listData.data[i], isLast: true, listData: self.listData)
}
else {
cellView(data: self.listData.data[i], isLast: false, listData: self.listData)
}
}
}
}
}
struct cellView: View {
var data: Response.Contact
var isLast: Bool
#ObservedObject var listData: getData
var body: some View {
VStack(alignment: .leading, spacing: 12) {
if self.isLast {
Text(data.first_name + " " + data.last_name)
.fontWeight(/*#START_MENU_TOKEN#*/.bold/*#END_MENU_TOKEN#*/)
.font(.title2)
.padding(/*#START_MENU_TOKEN#*/[.leading, .bottom, .trailing], 5.0/*#END_MENU_TOKEN#*/)
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
self.listData.updateData()
}
}
}
else {
Text(data.first_name + " " + data.last_name)
.fontWeight(/*#START_MENU_TOKEN#*/.bold/*#END_MENU_TOKEN#*/)
.font(.title2)
.padding(/*#START_MENU_TOKEN#*/[.leading, .bottom, .trailing], 5.0/*#END_MENU_TOKEN#*/)
}
}
.padding(.top, 10)
}
}
class getData: ObservableObject {
#Published var data = [Response.Contact]()
#Published var limit = 15
#Published var skip = 0
init() {
updateData()
}
func updateData() {
let url = "DATABASE_LINK?skip=\(skip)&limit=\(limit)"
let session = URLSession(configuration: .default)
session.dataTask(with: URL(string: url)!) { (data, _, err) in
if err != nil {
print((err?.localizedDescription)!)
return
}
do {
let json = try JSONDecoder().decode(Response.self, from: data!)
let oldData = self.data
DispatchQueue.main.async {
self.data = oldData + json.data
self.limit += 15
self.skip += 15
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let data = try! encoder.encode(json)
print(String(data: data, encoding: .utf8)!)
}
}
catch {
print(error.localizedDescription)
}
}.resume()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
struct Response: Codable {
struct Contact: Codable, Identifiable {
public let id: Int
public let first_name: String
public let last_name: String
public let updated_date: String
}
struct Pagination_Data: Codable {
public let skip: Int
public let limit: Int
public let total: Int
}
public let data: [Contact]
public let pagination: Pagination_Data
enum CodingKeys : String, CodingKey {
case data = "data"
case pagination = "pagination"
}
}
Your ContactsList is giving the only variable an initial value.
#ObservedObject var listData = getData()
Not IAW Apple documentation but the = getData() is the initial value
https://developer.apple.com/documentation/swiftui/managing-model-data-in-your-app
BTW you should change it to
#StateObject var listData = getData()
Your ContactDetail View has two variables without an initial value
struct ContactDetail: View {
var data: Response_Detailed.Contact_Detailed
#ObservedObject var detailedData: getData_Detailed
No = sign so a struct creates an init that looks like this
init(data: Response_Detailed.Contact_Detailed, detailedData: getData_Detailed)
So in your Preview you need to provide the initial values
ContactDetail(data: /.../, detailedData: /.../)
The /.../ symbolizes where you will provide sample data
I am currently building a public transportation app in SwiftUI Xcode 12 beta, where you input origin, destination, time of arrival, and date. The idea is to then convert the origin and destination input (private func FetchOriginCoordinates and private func FetchDestCoordinates) into coordinates using an API. This API will then give that data to another API that plans the route.
The problem is that the route planner API doesn't give any output. I have tried printing self.Trips.append(...) but the list is completely empty. I believe this is because the code doesn't reach that part of the code. I have put some print statements in the code in order to understand where the error is occurring. When I call the API my output in the console is this:
Test 2021-04-16 22:33:19.271246+0200 OnTrack2.0[6592:428359] [] nw_protocol_get_quic_image_block_invoke dlopen libquic failed Optional(http://api.sl.se/api2/TravelplannerV3_1/trip.json?key=40892db48b394d3a86b2439f9f3800fd&originExtId=300101115&destExtId=300101426&Date=2021-04-18&Time=09:20&searchForArrival=1) [] 10767 bytes Level 1 Level 1.5
This means that the code only reaches the print("Level 1.5") statement. But never the following print statements such as print("Level 2") or print(self.trips). Why is that and how do I solve it?
import Foundation
struct NominationStructure: Decodable {
var lat: String
var lon: String
enum CodingKeys: String, CodingKey {
case lat = "lat"
case lon = "lon"
}
}
struct NearbyStopsStructure: Decodable {
var stopLocationOrCoordLocation: [stopLocationOrCoordLocationStructure]
}
struct stopLocationOrCoordLocationStructure: Decodable {
var StopLocation: StopLocationStructure
}
struct StopLocationStructure: Decodable {
var mainMastExtId: String
}
struct JSONStructure: Decodable {
var Trip: [TripStructure]
}
struct TripStructure: Decodable {
var LegList: LegListStructure
}
struct LegListStructure: Decodable {
var Leg: [LegStructure]
}
struct LegStructure: Decodable {
var Origin: StationStructure
var Destination: StationStructure
var Product: ProductStructure
var name: String
var type: String
var dist: String
}
struct StationStructure: Decodable {
var time: String
var name: String
var date: String
}
struct ProductStructure: Decodable {
var catIn: String
}
struct LocationInfo {
var iD = String()
var input = String()
var lat = String()
var lon = String()
var name = String()
var time = String()
var date = String()
var vehicleType = String()
var transportType = String()
var dist = String()
var legName = String()
}
//One way is to call your functions when you know a step is completed
enum TripFetchStatus: String {
case start
case fetchedOriginCoordinates
case fetchedOriginId
case fetchedDestinationCoordinates
case fetchedDestinationId
case fetchingTrip
case done
case stopped
}
class PlanTripViewModel: ObservableObject {
//Apple frowns upon frezzing a screen so having a way to update the user on what is going on
//When a step is complete have it do something else
#Published var fetchStatus: TripFetchStatus = .stopped{
didSet{
switch fetchStatus {
case .start:
print("Test")
FetchOriginCoordinates { (cors) in
self.origin.lat = cors[0].lat
self.origin.lon = cors[0].lon
self.fetchStatus = .fetchedOriginCoordinates
}
case .fetchedOriginCoordinates:
self.FetchOriginID { (stops) in
self.origin.iD = stops.stopLocationOrCoordLocation[0].StopLocation.mainMastExtId
self.fetchStatus = .fetchedOriginId
}
case .fetchedOriginId:
FetchDestCoordinates { (cors) in
self.dest.lat = cors[0].lat
self.dest.lon = cors[0].lon
self.fetchStatus = .fetchedDestinationCoordinates
}
case .fetchedDestinationCoordinates:
self.FetchDestID { (stops) in
self.dest.iD = stops.stopLocationOrCoordLocation[0].StopLocation.mainMastExtId
self.fetchStatus = .fetchedDestinationId
}
case .fetchedDestinationId:
//Once you have everthing in place then go to the next API
FetchTrip()
case .fetchingTrip:
print("almost done")
case .done:
print("any other code you need to do")
case .stopped:
print("just a filler")
}
}
}
#Published var trip: LocationInfo = LocationInfo()
#Published var dest: LocationInfo = LocationInfo()
#Published var origin: LocationInfo = LocationInfo()
#Published var arrivalTime = String()
#Published var travelDate = String()
#Published var tripIndex = Int()
#Published var Trips: [Dictionary<String, String>] = []
public func FetchTrip() {
Trips.removeAll()
let tripKey = "40892db48b394d3a86b2439f9f3800fd"
let tripUrl = URL(string: "http://api.sl.se/api2/TravelplannerV3_1/trip.json?key=\(tripKey)&originExtId=\(self.origin.iD)&destExtId=\(self.dest.iD)&Date=\(self.travelDate)&Time=\(self.arrivalTime)&searchForArrival=1")
print(tripUrl)
URLSession.shared.dataTask(with: tripUrl!) {data, response, error in
if let data = data {
print(data)
print("Level 1")
do {
print("Level 1.5")
if let decodedJson = try? JSONDecoder().decode(JSONStructure.self, from: data) {
self.tripIndex = decodedJson.Trip.count - 1
print("Level 2")
for i in 0..<decodedJson.Trip[self.tripIndex].LegList.Leg.count {
self.trip.transportType = decodedJson.Trip[self.tripIndex].LegList.Leg[i].type
if self.trip.transportType == "WALK" {
self.origin.name = decodedJson.Trip[self.tripIndex].LegList.Leg[i].Origin.name
self.origin.time = String(decodedJson.Trip[self.tripIndex].LegList.Leg[i].Origin.time.prefix(5))
self.origin.date = decodedJson.Trip[self.tripIndex].LegList.Leg[i].Origin.date
self.dest.name = decodedJson.Trip[self.tripIndex].LegList.Leg[i].Destination.name
self.dest.time = String(decodedJson.Trip[self.tripIndex].LegList.Leg[i].Destination.time.prefix(5))
self.dest.date = decodedJson.Trip[self.tripIndex].LegList.Leg[i].Destination.date
self.trip.vehicleType = decodedJson.Trip[self.tripIndex].LegList.Leg[i].Product.catIn
self.trip.dist = decodedJson.Trip[self.tripIndex].LegList.Leg[i].dist
print(self.origin.name)
print(self.dest.name)
self.Trips.append(["Origin": self.origin.name, "Destination": self.dest.name, "OriginTime": self.origin.time, "DestTime": self.dest.time, "OriginDate": self.origin.date, "DestDate": self.dest.date, "TransportType": self.trip.transportType, "VehicleType": self.trip.vehicleType, "Distance": self.trip.dist])
}
else {
self.origin.name = decodedJson.Trip[self.tripIndex].LegList.Leg[i].Origin.name
self.origin.time = String(decodedJson.Trip[self.tripIndex].LegList.Leg[i].Origin.time.prefix(5))
self.origin.date = decodedJson.Trip[self.tripIndex].LegList.Leg[i].Origin.date
self.dest.name = decodedJson.Trip[self.tripIndex].LegList.Leg[i].Destination.name
self.dest.time = String(decodedJson.Trip[self.tripIndex].LegList.Leg[i].Destination.time.prefix(5))
self.dest.date = decodedJson.Trip[self.tripIndex].LegList.Leg[i].Destination.date
self.trip.vehicleType = decodedJson.Trip[self.tripIndex].LegList.Leg[i].Product.catIn
self.trip.legName = decodedJson.Trip[self.tripIndex].LegList.Leg[i].name
print(self.origin.name)
print(self.dest.name)
self.Trips.append(["Origin": self.origin.name, "Destination": self.dest.name, "OriginTime": self.origin.time, "DestTime": self.dest.time, "OriginDate": self.origin.date, "DestDate": self.dest.date, "TransportType": self.trip.transportType, "VehicleType": self.trip.vehicleType, "LegName": self.trip.legName])
}
}
}
} catch {
print(error)
}
}
}.resume()
print(self.Trips)
}
//Simple version just to replicate put your code within
private func FetchOriginCoordinates(completion: #escaping ([NominationStructure]) -> ()) {
let scheme = "https"
let host = "nominatim.openstreetmap.org"
let path = "/search"
let queryItemCountry = URLQueryItem(name: "country", value: "Sweden")
let queryItemCity = URLQueryItem(name: "city", value: "Stockholm")
let queryItemStreet = URLQueryItem(name: "street", value: self.origin.input)
let queryItemFormat = URLQueryItem(name: "format", value: "json")
var urlComponents = URLComponents()
urlComponents.scheme = scheme
urlComponents.host = host
urlComponents.path = path
urlComponents.queryItems = [queryItemCountry, queryItemCity, queryItemStreet, queryItemFormat]
URLSession.shared.dataTask(with: urlComponents.url!) {data, response, error in
if let data = data {
do {
if let decodedJson = try? JSONDecoder().decode([NominationStructure].self, from: data) {
DispatchQueue.main.async {
completion (decodedJson)
}
}
} catch {
print(error)
}
}
}.resume()
}
private func FetchDestCoordinates(completion: #escaping ([NominationStructure]) -> ()) {
let scheme = "https"
let host = "nominatim.openstreetmap.org"
let path = "/search"
let queryItemCountry = URLQueryItem(name: "country", value: "Sweden")
let queryItemCity = URLQueryItem(name: "city", value: "Stockholm")
let queryItemStreet = URLQueryItem(name: "street", value: self.dest.input)
let queryItemFormat = URLQueryItem(name: "format", value: "json")
var urlComponents = URLComponents()
urlComponents.scheme = scheme
urlComponents.host = host
urlComponents.path = path
urlComponents.queryItems = [queryItemCountry, queryItemCity, queryItemStreet, queryItemFormat]
URLSession.shared.dataTask(with: urlComponents.url!) {data, response, error in
if let data = data {
do {
if let decodedJson = try? JSONDecoder().decode([NominationStructure].self, from: data) {
DispatchQueue.main.async {
completion (decodedJson)
}
}
} catch {
print(error)
}
}
}.resume()
}
private func FetchOriginID(completion: #escaping (NearbyStopsStructure) -> ()) {
let nearbyStopsKey = "8444f9a2f75f4c27937a7165abd532a0"
let stopIDUrl = URL(string: "http://api.sl.se/api2/nearbystopsv2.json?key=\(nearbyStopsKey)&originCoordLat=\(self.origin.lat)&originCoordLong=\(self.origin.lon)&maxNo=1")
URLSession.shared.dataTask(with: stopIDUrl!) {data, response, error in
if let data = data {
do {
if let decodedJson = try? JSONDecoder().decode(NearbyStopsStructure.self, from: data) {
DispatchQueue.main.async {
completion (decodedJson)
}
}
} catch {
print(error)
}
}
}.resume()
}
private func FetchDestID(completion: #escaping (NearbyStopsStructure) -> ()) {
let nearbyStopsKey = "8444f9a2f75f4c27937a7165abd532a0"
let stopIDUrl = URL(string: "http://api.sl.se/api2/nearbystopsv2.json?key=\(nearbyStopsKey)&originCoordLat=\(self.dest.lat)&originCoordLong=\(self.dest.lon)&maxNo=1")
URLSession.shared.dataTask(with: stopIDUrl!) {data, response, error in
if let data = data {
do {
if let decodedJson = try? JSONDecoder().decode(NearbyStopsStructure.self, from: data) {
DispatchQueue.main.async {
completion (decodedJson)
}
}
} catch {
print(error)
}
}
}.resume()
}
}
I am new to swift (I am more used to python) and I am trying to send a POST request to a server and use the JSON response. I have been able to make the POST request and retrieve data and print it using this code that I got from a tutorial but the variable server.authenticated wasn't changing and after I made some changes now I'm getting two errors: Instance member 'authenticated' cannot be used on type 'HttpAuth' and 'String' is not convertible to 'Any'
Could someone please help?
import SwiftUI
import Combine
struct ServerMessage : Decodable {
let status, message: String
}
class HttpAuth: ObservableObject {
var didChange = PassthroughSubject<HttpAuth, Never>()
#Published var authenticated = false {
didSet{
didChange.send(self)
}
}
func checkDetails(username: String, password: String) {
guard let url = URL(string: "https://example.com") else { return }
let body: [String: String] = ["username": username, "password": password ]
let finalBody = try! JSONSerialization.data(withJSONObject: body)
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.httpBody = finalBody
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
URLSession.shared.dataTask(with: request) { (data, response, error) in
guard let data = data else { return }
let finalData = try! JSONDecoder().decode(ServerMessage.self, from: data)
print(finalData)
if finalData.status == "ok" {
DispatchQueue.global().async {
HttpAuth.authenticated = true //ERROR: Instance member 'authenticated' cannot be used on type 'HttpAuth'
print("correct credentials")
}
}
}.resume()
}
}
struct loginView: View {
#State private var username: String = ""
#State private var password: String = ""
#State var server = HttpAuth()
var body: some View {
VStack{
TextField("Username", text: $username)
.textFieldStyle(RoundedBorderTextFieldStyle())
.frame(width: 200, height: nil)
.multilineTextAlignment(.center)
.disableAutocorrection(true)
.accessibility(identifier: "Username")
.autocapitalization(.none)
SecureField("Password", text: $password)
.textFieldStyle(RoundedBorderTextFieldStyle())
.frame(width: 200, height: nil)
.multilineTextAlignment(.center)
.disableAutocorrection(true)
.accessibility(identifier: "Password")
Button(action: {
self.server.checkDetails(username: self.username, password: self.password)
//print(self.username + ", " + self.password)
}) {
HStack{
Spacer()
Text("Login").font(.headline).foregroundColor(.white)
Spacer()
}.padding(.vertical, 10)
.background(Color.red)
.padding(.horizontal, 40)
}
if self.server.authenticated {
Text("Correct Credentials")
} //ERROR: 'String' is not convertible to 'Any'
}
}
}
struct loginView_Previews: PreviewProvider {
static var previews: some View {
loginView()
}
}
You need to pass HttpAuth to your loginView like
struct loginView_Previews: PreviewProvider {
static var previews: some View {
loginView().environmentObject(HttpAuth())
}
}
then in your LoginView declare EnvironmentObject of HttpAuth
struct loginView: View {
#State private var username: String = ""
#State private var password: String = ""
#EnvironmentObject var server : HttpAuth
...