JSON Parser
This commit is contained in:
parent
164d8792ca
commit
b91ab9d571
10 changed files with 345 additions and 31 deletions
|
|
@ -13,8 +13,9 @@ struct TheSwiftWeek: App {
|
|||
|
||||
|
||||
// ContentView()
|
||||
|
||||
ContentView()
|
||||
DataView()
|
||||
//OpenURLView()
|
||||
// ContentView()
|
||||
|
||||
|
||||
}.modelContainer(for: Unicycle.self)
|
||||
|
|
|
|||
59
DataView.swift
Normal file
59
DataView.swift
Normal 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()
|
||||
}
|
||||
|
|
@ -6,20 +6,35 @@ 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)
|
||||
// .focused($focusedField, equals: bike.id)
|
||||
// .textFieldStyle(.roundedBorder)
|
||||
// Spacer()
|
||||
// Text("\(bike.price, specifier: "%.2f€")")
|
||||
// ColorPicker("", selection: $bike.color)
|
||||
// }
|
||||
// HStack {
|
||||
// TextField("New Bike", text: $bike.name)
|
||||
// .focused($focusedField, equals: bike.id)
|
||||
// .textFieldStyle(.roundedBorder)
|
||||
// Spacer()
|
||||
// Text("\(bike.price, specifier: "%.2f€")")
|
||||
// ColorPicker("", selection: $bike.color)
|
||||
// }
|
||||
|
||||
HStack {
|
||||
BikeView(bike: bike)
|
||||
Spacer()
|
||||
|
|
@ -28,14 +43,17 @@ struct ListView: TabContent {
|
|||
}
|
||||
}
|
||||
.onDelete { bikes.remove(atOffsets: $0) }
|
||||
} footer: {
|
||||
}
|
||||
footer: {
|
||||
Button("Add a new Bike") {
|
||||
let bike = Bike(name: "", color: .white)
|
||||
bikes.append(bike)
|
||||
// focusedField = bike.id
|
||||
// focusedField = bike.id
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .center)
|
||||
}
|
||||
}.searchable(text: $search)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
44
OpenURLView.swift
Normal 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()
|
||||
}
|
||||
|
|
@ -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
98
SwaggerView.swift
Normal 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
19
TheSwiftWeek-Info.plist
Normal 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>
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
Binary file not shown.
Loading…
Reference in a new issue