JSON Parser

This commit is contained in:
Ingo Rohlf 2025-10-24 14:35:06 +02:00
parent 164d8792ca
commit b91ab9d571
10 changed files with 345 additions and 31 deletions

View file

@ -13,8 +13,9 @@ struct TheSwiftWeek: App {
// ContentView()
ContentView()
DataView()
//OpenURLView()
// ContentView()
}.modelContainer(for: Unicycle.self)

59
DataView.swift Normal file
View file

@ -0,0 +1,59 @@
//
// DataView.swift
// TheSwiftWeek
//
// Created by Ingo Rohlf on 24.10.25.
//
import SwiftUI
import Foundation
struct MenuCard: Codable {
let items: [MenuItem]
}
struct MenuItem: Codable {
let name: String
let detailText: String
// let price: Double
enum CodingKeys: String, CodingKey {
case name
case detailText = "description"
}
}
struct DataView: View {
let url2 = URL(string: "https://git.irohlf.de/api/swagger")!
private let url = URL(string: "http://127.0.0.1:8080/menu")!
private let jsonDecoder = JSONDecoder()
@State private var menu = MenuCard(items: [])
var body: some View {
Text(url.absoluteString)
.onAppear{
Task {
let (data, response) = try await
URLSession.shared.data(from: url)
guard let string = String(
data: data,
encoding: .utf8
) else { return }
if let response = response as? HTTPURLResponse, response.statusCode == 200 {
menu = try! JSONDecoder().decode(MenuCard.self, from: data)
}
print("fertig geladen")
print(string)
}
}
Text("asdlfaksdf")
}
}
#Preview {
DataView()
}

View file

@ -6,11 +6,25 @@ struct ListView: TabContent {
static var id = UUID()
@State var bikes = Bike.all
@State private var search: String = ""
@FocusState var focusedField: UUID? // 1
var body: some View {
// let filterdBikes = {$bikes.filter { $0.name.contains(search ?? "") }}
if bikes.isEmpty {
ContentUnavailableView("No bikes found", systemImage: "bike")
} else {
NavigationStack {
List {
Section {
ForEach($bikes) { $bike in
// HStack {
// TextField("New Bike", text: $bike.name)
@ -20,6 +34,7 @@ struct ListView: TabContent {
// Text("\(bike.price, specifier: "%.2f")")
// ColorPicker("", selection: $bike.color)
// }
HStack {
BikeView(bike: bike)
Spacer()
@ -28,7 +43,8 @@ struct ListView: TabContent {
}
}
.onDelete { bikes.remove(atOffsets: $0) }
} footer: {
}
footer: {
Button("Add a new Bike") {
let bike = Bike(name: "", color: .white)
bikes.append(bike)
@ -36,6 +52,8 @@ struct ListView: TabContent {
}
.frame(maxWidth: .infinity, alignment: .center)
}
}.searchable(text: $search)
}
}
}
}

View file

@ -16,6 +16,14 @@
"comment" : "A button that adds a new bike to the list of bikes.",
"isCommentAutoGenerated" : true
},
"API Info" : {
"comment" : "The title of a section that displays information about the API.",
"isCommentAutoGenerated" : true
},
"asdlfaksdf" : {
"comment" : "A placeholder text.",
"isCommentAutoGenerated" : true
},
"Betrag" : {
"comment" : "A text field for entering an amount.",
"isCommentAutoGenerated" : true
@ -52,6 +60,10 @@
"comment" : "A word that is displayed when the number is a multiple of 3.",
"isCommentAutoGenerated" : true
},
"Loading..." : {
"comment" : "A text that appears while waiting to fetch data from the API.",
"isCommentAutoGenerated" : true
},
"My Vehicle" : {
"extractionState" : "manual",
"localizations" : {
@ -69,10 +81,38 @@
}
}
},
"No bikes found" : {
"comment" : "A message displayed when there are no bikes to display in the list.",
"isCommentAutoGenerated" : true
},
"Open TSW Web (Link)" : {
"comment" : "A link that opens the TSW website.",
"isCommentAutoGenerated" : true
},
"Open TSW Web (OpenURL)" : {
"comment" : "A button that opens the TSW website using the `openURL` action.",
"isCommentAutoGenerated" : true
},
"Path: %@" : {
"comment" : "A text showing the path part of the URL.",
"isCommentAutoGenerated" : true
},
"Paths" : {
"comment" : "A section header in the Swagger API view, indicating that it lists the available API paths.",
"isCommentAutoGenerated" : true
},
"please update to iOS 26.0 or later" : {
"comment" : "A message to inform the user that they need to update their iOS version to 26.0 or later to use this feature.",
"isCommentAutoGenerated" : true
},
"Query: %@" : {
"comment" : "A label displaying the query string from a URL.",
"isCommentAutoGenerated" : true
},
"Scheme: %@" : {
"comment" : "A label displaying the scheme of the URL.",
"isCommentAutoGenerated" : true
},
"Show Alert" : {
"comment" : "A button that triggers an alert when pressed.",
"isCommentAutoGenerated" : true
@ -85,6 +125,10 @@
"comment" : "A text field for entering the total amount to be split.",
"isCommentAutoGenerated" : true
},
"Swagger API" : {
"comment" : "The title of the view displaying information about the Swagger API.",
"isCommentAutoGenerated" : true
},
"Swapping %@ <-> %@" : {
"comment" : "A label displaying the current state of the swap operation, showing the values of the two variables before and after the swap.",
"isCommentAutoGenerated" : true,
@ -97,6 +141,14 @@
}
}
},
"Text: %@" : {
"comment" : "A label displaying the absolute string representation of a URL.",
"isCommentAutoGenerated" : true
},
"Title: %@" : {
"comment" : "A label displaying the title of the API.",
"isCommentAutoGenerated" : true
},
"Train %@ has ^[%lld minute](inflect: true) delay." : {
"comment" : "A localized string resource describing a train. The first argument is the name of the train. The second argument is the delay in minutes.",
"isCommentAutoGenerated" : true,
@ -128,6 +180,10 @@
}
}
},
"Version: %@" : {
"comment" : "A label displaying the version of the API.",
"isCommentAutoGenerated" : true
},
"Vorher %@ <-> %@" : {
"comment" : "Displays two text fields with the text \"Vorher\" and \"Nachher\" followed by a button that, when pressed, swaps the text in the two fields.",
"isCommentAutoGenerated" : true,

44
OpenURLView.swift Normal file
View file

@ -0,0 +1,44 @@
//
// OpenURLView.swift
// TheSwiftWeek
//
// Created by Ingo Rohlf on 24.10.25.
//
import SwiftUI
struct OpenURLView: View {
@Environment(\.openURL) private var openURL
let url = URL(string: "https://carrascomolina.com/tsw.htm")
@State private var scheme: String = "?"
@State private var path: String = "?"
@State private var query: String = "?"
@State private var text: String = "?"
var body: some View {
Link("Open TSW Web (Link)", destination: url!)
Button("Open TSW Web (OpenURL)"){
if let url {
openURL(url)
}
}
.onOpenURL { url in
scheme = url.scheme ?? ""
path = url.path ?? ""
query = url.query ?? ""
text = url.absoluteString
}
// Beispielanzeige der Werte
Text("Scheme: \(scheme)")
Text("Path: \(path)")
Text("Query: \(query)")
Text("Text: \(text)")
.padding()
}
}
#Preview {
OpenURLView()
}

View file

@ -14,8 +14,11 @@ let train = Train { Bike.trek }
#Playground {
// bike.price
// train.makeSomeNoise("Choo Choo!")
for bike in Bike.all {
bike
}
// for bike in Bike.all {
// bike
// }
let numbers = ["1","2","3","4","3","asd"]
let intNumbers = numbers.compactMap{ Int($0) ?? -1}
let uniqNumbers = numbers.map{ Int($0)}
}

98
SwaggerView.swift Normal file
View file

@ -0,0 +1,98 @@
import Foundation
import SwiftUI
import Combine
struct SwaggerAPI: Codable {
let swagger: String
let info: Info
let paths: [String: PathItem]
struct Info: Codable {
let title: String
let version: String
}
struct PathItem: Codable {
let summary: String?
let description: String?
let parameters: [Parameter]?
let responses: [String: Response]
struct Parameter: Codable {
let name: String
let info: String
let required: Bool
let type: String
}
struct Response: Codable {
let description: String
}
}
}
class SwaggerAPIService: ObservableObject {
@Published var apiData: SwaggerAPI?
func fetchAPI() {
guard let url = URL(string: "https://git.irohlf.de/swagger/api" ) else { return }
URLSession.shared.dataTask(with: url) { data, response, error in
guard let data = data, error == nil else { return }
DispatchQueue.main.async {
do {
let decodedData = try JSONDecoder().decode(SwaggerAPI.self, from: data)
self.apiData = decodedData
} catch {
print("Error decoding data: \(error)")
}
}
}.resume()
}
}
struct SwaggerContentView: View {
@StateObject private var apiService = SwaggerAPIService()
var body: some View {
NavigationView {
List {
if let api = apiService.apiData {
Section(header: Text("API Info")) {
Text("Title: \(api.info.title)")
Text("Version: \(api.info.version)")
}
Section(header: Text("Paths")) {
ForEach(api.paths.keys.sorted(), id: \.self) { path in
if let pathItem = api.paths[path] {
VStack(alignment: .leading) {
Text(path)
.font(.headline)
Text(pathItem.summary ?? "No summary available")
.font(.subheadline)
}
}
}
}
} else {
Text("Loading...")
.onAppear {
apiService.fetchAPI()
}
}
}
.navigationTitle("Swagger API")
}
}
}
#Preview {
SwaggerContentView()
}

19
TheSwiftWeek-Info.plist Normal file
View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>CFBundleURLName</key>
<string>com.example.myphotoapp</string>
<key>CFBundleURLSchemes</key>
<array>
<string>myphotoapp</string>
</array>
</dict>
</array>
</dict>
</plist>

View file

@ -25,6 +25,9 @@
FBA6FA622EA76AAD00C373EC /* BikeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBA6FA612EA76AAD00C373EC /* BikeView.swift */; };
FBA6FA642EA7715E00C373EC /* Extentions.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBA6FA632EA7715000C373EC /* Extentions.swift */; };
FBA6FA662EA7725A00C373EC /* TrainView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBA6FA652EA7725A00C373EC /* TrainView.swift */; };
FBA8A4592EAB607A00BC025C /* OpenURLView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBA8A4582EAB607A00BC025C /* OpenURLView.swift */; };
FBA8A45C2EAB9DA000BC025C /* DataView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBA8A45B2EAB9DA000BC025C /* DataView.swift */; };
FBA8A45E2EABA2A700BC025C /* SwaggerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBA8A45D2EABA2A700BC025C /* SwaggerView.swift */; };
FBE1A29D2EAA1B4300081638 /* UniView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBE1A29C2EAA1B4300081638 /* UniView.swift */; };
FBE1A29F2EAA225600081638 /* NewUnicycleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBE1A29E2EAA225600081638 /* NewUnicycleView.swift */; };
FBE1A2A12EAA340F00081638 /* WebAppView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FBE1A2A02EAA340F00081638 /* WebAppView.swift */; };
@ -51,6 +54,10 @@
FBA6FA612EA76AAD00C373EC /* BikeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BikeView.swift; sourceTree = "<group>"; };
FBA6FA632EA7715000C373EC /* Extentions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extentions.swift; sourceTree = "<group>"; };
FBA6FA652EA7725A00C373EC /* TrainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrainView.swift; sourceTree = "<group>"; };
FBA8A4582EAB607A00BC025C /* OpenURLView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenURLView.swift; sourceTree = "<group>"; };
FBA8A45A2EAB627C00BC025C /* TheSwiftWeek-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "TheSwiftWeek-Info.plist"; sourceTree = "<group>"; };
FBA8A45B2EAB9DA000BC025C /* DataView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataView.swift; sourceTree = "<group>"; };
FBA8A45D2EABA2A700BC025C /* SwaggerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwaggerView.swift; sourceTree = "<group>"; };
FBE1A29C2EAA1B4300081638 /* UniView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UniView.swift; sourceTree = "<group>"; };
FBE1A29E2EAA225600081638 /* NewUnicycleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewUnicycleView.swift; sourceTree = "<group>"; };
FBE1A2A02EAA340F00081638 /* WebAppView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebAppView.swift; sourceTree = "<group>"; };
@ -71,7 +78,9 @@
0CC14A6D2E92EC4700271E8D = {
isa = PBXGroup;
children = (
FBA8A45A2EAB627C00BC025C /* TheSwiftWeek-Info.plist */,
FBE1A2A02EAA340F00081638 /* WebAppView.swift */,
FBA8A4582EAB607A00BC025C /* OpenURLView.swift */,
FBE1A2A22EAA635600081638 /* SplitAppView.swift */,
FBA00D742EA7ACDD006F8B9A /* SwapperView.swift */,
FBA00D702EA7A830006F8B9A /* PaddingView.swift */,
@ -90,6 +99,8 @@
FB79FE4B2EA9071F0011678F /* Unicycle.swift */,
FBA6FA652EA7725A00C373EC /* TrainView.swift */,
FB2F07E82EA7CB25002BD499 /* ListView.swift */,
FBA8A45B2EAB9DA000BC025C /* DataView.swift */,
FBA8A45D2EABA2A700BC025C /* SwaggerView.swift */,
FB79FE452EA8CFD20011678F /* RangeView.swift */,
FBA6FA5D2EA63EA300C373EC /* Models.swift */,
FBA00D6C2EA78411006F8B9A /* Localizable.xcstrings */,
@ -186,8 +197,10 @@
FB79FE462EA8CFD20011678F /* RangeView.swift in Sources */,
FBA6FA662EA7725A00C373EC /* TrainView.swift in Sources */,
FBA00D752EA7ACE1006F8B9A /* SwapperView.swift in Sources */,
FBA8A45E2EABA2A700BC025C /* SwaggerView.swift in Sources */,
FB79FE4C2EA9071F0011678F /* Unicycle.swift in Sources */,
0CC14A892E92EEA900271E8D /* Playground.swift in Sources */,
FBA8A45C2EAB9DA000BC025C /* DataView.swift in Sources */,
FBA6FA642EA7715E00C373EC /* Extentions.swift in Sources */,
FB79FE4A2EA906CC0011678F /* UnicycleView.swift in Sources */,
FBE1A2A32EAA635600081638 /* SplitAppView.swift in Sources */,
@ -199,6 +212,7 @@
FB2F07E92EA7CB25002BD499 /* ListView.swift in Sources */,
FBA00D712EA7A839006F8B9A /* PaddingView.swift in Sources */,
FBA6FA622EA76AAD00C373EC /* BikeView.swift in Sources */,
FBA8A4592EAB607A00BC025C /* OpenURLView.swift in Sources */,
FBA00D772EA7C235006F8B9A /* CountView.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -338,6 +352,7 @@
CURRENT_PROJECT_VERSION = 1;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "TheSwiftWeek-Info.plist";
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
@ -370,6 +385,7 @@
CURRENT_PROJECT_VERSION = 1;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "TheSwiftWeek-Info.plist";
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES;