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