diff --git a/App.swift b/App.swift
index fc8aae0..83ea3c0 100644
--- a/App.swift
+++ b/App.swift
@@ -13,8 +13,9 @@ struct TheSwiftWeek: App {
// ContentView()
-
- ContentView()
+ DataView()
+ //OpenURLView()
+ // ContentView()
}.modelContainer(for: Unicycle.self)
diff --git a/DataView.swift b/DataView.swift
new file mode 100644
index 0000000..1b72825
--- /dev/null
+++ b/DataView.swift
@@ -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()
+}
diff --git a/ListView.swift b/ListView.swift
index 2542f34..aa239eb 100644
--- a/ListView.swift
+++ b/ListView.swift
@@ -4,37 +4,55 @@ struct ListView: TabContent {
static var title = "List"
static var image = "list.bullet"
static var id = UUID()
-
+
@State var bikes = Bike.all
+ @State private var search: String = ""
@FocusState var focusedField: UUID? // 1
-
+
+
var body: some View {
- 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 {
- BikeView(bike: bike)
- Spacer()
- ColorPicker("", selection: $bike.color)
+
+ // 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 {
+ BikeView(bike: bike)
+ Spacer()
+ ColorPicker("", selection: $bike.color)
+
+ }
+ }
+ .onDelete { bikes.remove(atOffsets: $0) }
}
- }
- .onDelete { bikes.remove(atOffsets: $0) }
- } footer: {
- Button("Add a new Bike") {
- let bike = Bike(name: "", color: .white)
- bikes.append(bike)
-// focusedField = bike.id
- }
- .frame(maxWidth: .infinity, alignment: .center)
+ footer: {
+ Button("Add a new Bike") {
+ let bike = Bike(name: "", color: .white)
+ bikes.append(bike)
+ // focusedField = bike.id
+ }
+ .frame(maxWidth: .infinity, alignment: .center)
+ }
+ }.searchable(text: $search)
}
}
}
diff --git a/Localizable.xcstrings b/Localizable.xcstrings
index f9fcc45..645edcf 100644
--- a/Localizable.xcstrings
+++ b/Localizable.xcstrings
@@ -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,
diff --git a/OpenURLView.swift b/OpenURLView.swift
new file mode 100644
index 0000000..6ea61d0
--- /dev/null
+++ b/OpenURLView.swift
@@ -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()
+}
diff --git a/Playground.swift b/Playground.swift
index 67f5d34..4f8e35f 100644
--- a/Playground.swift
+++ b/Playground.swift
@@ -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)}
}
diff --git a/SwaggerView.swift b/SwaggerView.swift
new file mode 100644
index 0000000..6985e17
--- /dev/null
+++ b/SwaggerView.swift
@@ -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()
+
+}
diff --git a/TheSwiftWeek-Info.plist b/TheSwiftWeek-Info.plist
new file mode 100644
index 0000000..a2bc3c4
--- /dev/null
+++ b/TheSwiftWeek-Info.plist
@@ -0,0 +1,19 @@
+
+
+
+
+ CFBundleURLTypes
+
+
+ CFBundleTypeRole
+ Viewer
+ CFBundleURLName
+ com.example.myphotoapp
+ CFBundleURLSchemes
+
+ myphotoapp
+
+
+
+
+
diff --git a/TheSwiftWeek.xcodeproj/project.pbxproj b/TheSwiftWeek.xcodeproj/project.pbxproj
index 3345335..ae1643f 100644
--- a/TheSwiftWeek.xcodeproj/project.pbxproj
+++ b/TheSwiftWeek.xcodeproj/project.pbxproj
@@ -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 = ""; };
FBA6FA632EA7715000C373EC /* Extentions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Extentions.swift; sourceTree = ""; };
FBA6FA652EA7725A00C373EC /* TrainView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrainView.swift; sourceTree = ""; };
+ FBA8A4582EAB607A00BC025C /* OpenURLView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OpenURLView.swift; sourceTree = ""; };
+ FBA8A45A2EAB627C00BC025C /* TheSwiftWeek-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "TheSwiftWeek-Info.plist"; sourceTree = ""; };
+ FBA8A45B2EAB9DA000BC025C /* DataView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataView.swift; sourceTree = ""; };
+ FBA8A45D2EABA2A700BC025C /* SwaggerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SwaggerView.swift; sourceTree = ""; };
FBE1A29C2EAA1B4300081638 /* UniView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UniView.swift; sourceTree = ""; };
FBE1A29E2EAA225600081638 /* NewUnicycleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewUnicycleView.swift; sourceTree = ""; };
FBE1A2A02EAA340F00081638 /* WebAppView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebAppView.swift; sourceTree = ""; };
@@ -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;
diff --git a/TheSwiftWeek.xcodeproj/project.xcworkspace/xcuserdata/rohing73.xcuserdatad/UserInterfaceState.xcuserstate b/TheSwiftWeek.xcodeproj/project.xcworkspace/xcuserdata/rohing73.xcuserdatad/UserInterfaceState.xcuserstate
index 4da3d6c..27403da 100644
Binary files a/TheSwiftWeek.xcodeproj/project.xcworkspace/xcuserdata/rohing73.xcuserdatad/UserInterfaceState.xcuserstate and b/TheSwiftWeek.xcodeproj/project.xcworkspace/xcuserdata/rohing73.xcuserdatad/UserInterfaceState.xcuserstate differ