Hello everyone,
I'm trying to create an iOS app with Swift for the first time and I've encountered an error that I can't solve. I've searched and googled a lot but somehow I can't find anything.
So, the app displays city names and when you click on a city, it should then display categories such as restaurants or doctors. The addresses should come from a json file that is on my hosting. As soon as I write the code with the json file, the categories disappear, there is no error message from xcode. Do you have any idea what else I could try. This is the code, and the json file.
swift code:
timport SwiftUI
struct CitySelectionView: View {
let cities = ["Hamburg", "Berlin", "Köln", "München", "Frankfurt", "Stuttgart"]
var body: some View {
NavigationView {
List(cities, id: \.self) { city in
NavigationLink(destination: CategorySelectionView(city: city)) {
Text(city)
}
}
.navigationTitle("Städte")
}
}
}
struct CategorySelectionView: View {
let categories = ["Restaurants", "Bars", "Ärzte", "Supermärkte", "Freizeit"]
let city: String
@State private var restaurants: [Restaurant] = []
var body: some View {
VStack {
Text("Wähle eine Kategorie für \(city):")
.font(.title)
.padding()
List(restaurants, id: \.name) { restaurant in
VStack(alignment: .leading) {
Text(restaurant.name)
.font(.headline)
Text(restaurant.address)
.font(.subheadline)
}
}
.onAppear {
if city == "Hamburg" {
downloadRestaurantData()
}
}
}
.navigationTitle("\(city)")
}
func downloadRestaurantData() {
guard let url = URL(string: "http://www.digi-nova.de/xmlapp/hamburg/restaurants/restaurantshamburg.json") else {
print("Invalid URL")
return
}
URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else {
print("Error: \(error?.localizedDescription ?? "Unknown error")")
return
}
do {
let decodedData = try JSONDecoder().decode([Restaurant].self, from: data)
DispatchQueue.main.async {
self.restaurants = decodedData
}
} catch {
print("Failed to decode JSON data: \(error.localizedDescription)")
}
}.resume()
}
}
struct Restaurant: Codable, Identifiable {
var id = UUID()
let name: String
let address: String
let zip: String
let city: String
let phone: String
let opening_hours: String
let map_link: String
}
struct ContentView: View {
var body: some View {
CitySelectionView()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
in the code is the link to the json file
tera
2
Are you getting the data back at all or is there a "The resource could not be loaded because the App Transport Security policy requires the use of a secure connection." message in the console? Note that by default non-SSL'ed traffic is disabled, ideally you use https and if that's not good for you then you tweak the relevant keys in the app's plist (NSAppTransportSecurity) to allow either arbitrary http requests or requests to particular sites only.
Other than that I see two mistakes. The JSON file has this:
{
"restaurants":[
{
"id":1,
"name":"Restaurant A",
so at the top level there's a dictionary and the "restaurants" field of that dictionary points to an array. The fix is easy:
struct Reply: Codable {
let restaurants: [Restaurant]
}
...
let decodedData = try JSONDecoder().decode(Reply.self, from: data)
// was `[Restaurant].self`
Another error is using UUID() for the ID fields, which are Ints according to JSON:
struct Restaurant: Codable, Identifiable {
let id: Int // was UUID
To get more insight about what error is I'd do this:
print(error)
or
print((error as NSError).debugDescription)
as "error.localizedDescription" doesn't tell the whole story.
1 Like
Thank you for your help. I uploaded the json file to github to get an https link and made the customizations as you described. However, I still don't get any categories when I click on a city, the page just stays blank.
The code is now changed to:
import SwiftUI
struct CitySelectionView: View {
let cities = ["Hamburg", "Berlin", "Köln", "München", "Frankfurt", "Stuttgart", "Hannover"]
var body: some View {
NavigationView {
List(cities, id: \.self) { city in
NavigationLink(destination: CategorySelectionView(city: city)) {
Text(city)
}
}
.navigationTitle("Städte")
}
}
}
struct CategorySelectionView: View {
let categories = ["Restaurants", "Bars", "Ärzte", "Supermärkte", "Freizeit"]
let city: String
@State private var restaurants: [Restaurant] = []
var body: some View {
VStack {
Text("Wähle eine Kategorie für \(city):")
.font(.title)
.padding()
List(restaurants, id: \.name) { restaurant in
VStack(alignment: .leading) {
Text(restaurant.name)
.font(.headline)
Text(restaurant.address)
.font(.subheadline)
}
}
.onAppear {
if city == "Hamburg" {
downloadRestaurantData()
}
}
}
.navigationTitle("\(city)")
}
func downloadRestaurantData() {
guard let url = URL(string: "https://github.com/donnievedro/balkangermany/blob/main/restaurantshamburg.json") else {
print("Invalid URL")
return
}
URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else {
print("Error: \(error?.localizedDescription ?? "Unknown error")")
return
}
do {
let decodedData = try JSONDecoder().decode(Reply.self, from: data)
DispatchQueue.main.async {
self.restaurants = decodedData.restaurants
}
} catch {
print("Failed to decode JSON data: \(error.localizedDescription)")
}
}.resume()
}
}
struct Restaurant: Codable, Identifiable {
let id: Int // Changed from UUID
let name: String
let address: String
let zip: String
let city: String
let phone: String
let opening_hours: String
let map_link: String
}
struct Reply: Codable {
let restaurants: [Restaurant]
}
struct ContentView: View {
var body: some View {
CitySelectionView()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
tera
4
The new version of the code works fine for me with your old URL:
http://www.digi-nova.de/xmlapp/hamburg/restaurants/restaurantshamburg.json
(provided I tweak the ATS settings to allow http).
I can't check your new URL as it points to a file on a private repo I have no access to.
I'd recommend to print(error) to get further insights, and also:
print(String(data: data, encoding: .utf8))
to see what data is actually getting returned.