From 701d1635471758bee5a33e2c621cf4cef7e1d8fe Mon Sep 17 00:00:00 2001 From: Louis Date: Sat, 2 Sep 2023 17:19:40 +0200 Subject: [PATCH] feat: :sparkles: Ability to view stations from the API Addition of a single component for stations; creation of structure; creation of function to retrieve bikes in real time --- velibtracker.xcodeproj/project.pbxproj | 28 +++++++ .../xcdebugger/Breakpoints_v2.xcbkptlist | 6 ++ velibtracker/ContentView.swift | 16 ++-- .../components/StationComponent.swift | 48 ++++++++++++ velibtracker/fetchVelibData.swift | 52 +++++++++++++ velibtracker/struct/VelibStationStruct.swift | 75 +++++++++++++++++++ velibtracker/velibtrackerApp.swift | 3 + 7 files changed, 222 insertions(+), 6 deletions(-) create mode 100644 velibtracker.xcodeproj/xcuserdata/louisgallet.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist create mode 100644 velibtracker/components/StationComponent.swift create mode 100644 velibtracker/fetchVelibData.swift create mode 100644 velibtracker/struct/VelibStationStruct.swift diff --git a/velibtracker.xcodeproj/project.pbxproj b/velibtracker.xcodeproj/project.pbxproj index 2abf0b3..23ca91c 100644 --- a/velibtracker.xcodeproj/project.pbxproj +++ b/velibtracker.xcodeproj/project.pbxproj @@ -11,6 +11,9 @@ 2C5BDABF2AA3611000DBEC93 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2C5BDABE2AA3611000DBEC93 /* ContentView.swift */; }; 2C5BDAC12AA3611000DBEC93 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2C5BDAC02AA3611000DBEC93 /* Assets.xcassets */; }; 2C5BDAC52AA3611000DBEC93 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 2C5BDAC42AA3611000DBEC93 /* Preview Assets.xcassets */; }; + 2CDC317E2AA3654800BF98B9 /* VelibStationStruct.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CDC317D2AA3654800BF98B9 /* VelibStationStruct.swift */; }; + 2CDC31802AA3672600BF98B9 /* StationComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CDC317F2AA3672600BF98B9 /* StationComponent.swift */; }; + 2CDC31822AA387AB00BF98B9 /* fetchVelibData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2CDC31812AA387AB00BF98B9 /* fetchVelibData.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ @@ -20,6 +23,9 @@ 2C5BDAC02AA3611000DBEC93 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 2C5BDAC22AA3611000DBEC93 /* velibtracker.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = velibtracker.entitlements; sourceTree = ""; }; 2C5BDAC42AA3611000DBEC93 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 2CDC317D2AA3654800BF98B9 /* VelibStationStruct.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VelibStationStruct.swift; sourceTree = ""; }; + 2CDC317F2AA3672600BF98B9 /* StationComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StationComponent.swift; sourceTree = ""; }; + 2CDC31812AA387AB00BF98B9 /* fetchVelibData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = fetchVelibData.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -52,11 +58,14 @@ 2C5BDABB2AA3611000DBEC93 /* velibtracker */ = { isa = PBXGroup; children = ( + 2CDC317C2AA3653200BF98B9 /* struct */, + 2C5BDACB2AA3632C00DBEC93 /* components */, 2C5BDABC2AA3611000DBEC93 /* velibtrackerApp.swift */, 2C5BDABE2AA3611000DBEC93 /* ContentView.swift */, 2C5BDAC02AA3611000DBEC93 /* Assets.xcassets */, 2C5BDAC22AA3611000DBEC93 /* velibtracker.entitlements */, 2C5BDAC32AA3611000DBEC93 /* Preview Content */, + 2CDC31812AA387AB00BF98B9 /* fetchVelibData.swift */, ); path = velibtracker; sourceTree = ""; @@ -69,6 +78,22 @@ path = "Preview Content"; sourceTree = ""; }; + 2C5BDACB2AA3632C00DBEC93 /* components */ = { + isa = PBXGroup; + children = ( + 2CDC317F2AA3672600BF98B9 /* StationComponent.swift */, + ); + path = components; + sourceTree = ""; + }; + 2CDC317C2AA3653200BF98B9 /* struct */ = { + isa = PBXGroup; + children = ( + 2CDC317D2AA3654800BF98B9 /* VelibStationStruct.swift */, + ); + path = struct; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -139,7 +164,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 2CDC317E2AA3654800BF98B9 /* VelibStationStruct.swift in Sources */, 2C5BDABF2AA3611000DBEC93 /* ContentView.swift in Sources */, + 2CDC31822AA387AB00BF98B9 /* fetchVelibData.swift in Sources */, + 2CDC31802AA3672600BF98B9 /* StationComponent.swift in Sources */, 2C5BDABD2AA3611000DBEC93 /* velibtrackerApp.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/velibtracker.xcodeproj/xcuserdata/louisgallet.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/velibtracker.xcodeproj/xcuserdata/louisgallet.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000..de2e08e --- /dev/null +++ b/velibtracker.xcodeproj/xcuserdata/louisgallet.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,6 @@ + + + diff --git a/velibtracker/ContentView.swift b/velibtracker/ContentView.swift index 6ad3a30..20e3f3f 100644 --- a/velibtracker/ContentView.swift +++ b/velibtracker/ContentView.swift @@ -9,16 +9,20 @@ import SwiftUI struct ContentView: View { var body: some View { - VStack { - Image(systemName: "globe") - .imageScale(.large) - .foregroundColor(.accentColor) - Text("Hello, world!") + NavigationView { + ScrollView { + ForEach(velibStations, id: \.stationcode) { station in + StationComponent(station: station) + .padding(.bottom, 16) + } + } + .navigationBarTitle("Stations Vélib") } - .padding() + } } + struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() diff --git a/velibtracker/components/StationComponent.swift b/velibtracker/components/StationComponent.swift new file mode 100644 index 0000000..de2eab3 --- /dev/null +++ b/velibtracker/components/StationComponent.swift @@ -0,0 +1,48 @@ +// +// StationComponent.swift +// velibtracker +// +// Created by Louis Gallet on 02/09/2023. +// + +import SwiftUI + +import SwiftUI + +struct StationComponent: View { + + let station: VelibStation + + var body: some View { + VStack(alignment: .leading, spacing: 8) { + Text(station.name) + .font(.title) + .foregroundColor(.primary) + + HStack { + Text("Capacité: \(station.capacity)") + .font(.subheadline) + .foregroundColor(.secondary) + + Spacer() + + Text("Disponibles: \(station.numbikesavailable)") + .font(.subheadline) + .foregroundColor(.secondary) + } + } + .padding() + .background(Color.white) + .cornerRadius(10) + .shadow(radius: 5) + .padding(.horizontal, 16) + .padding(.vertical, 8) + } +} + + +struct StationComponent_Previews: PreviewProvider { + static var previews: some View { + StationComponent(station: test[1]) + } +} diff --git a/velibtracker/fetchVelibData.swift b/velibtracker/fetchVelibData.swift new file mode 100644 index 0000000..7d7f413 --- /dev/null +++ b/velibtracker/fetchVelibData.swift @@ -0,0 +1,52 @@ +// +// fetchVelibData.swift +// velibtracker +// +// Created by Louis Gallet on 02/09/2023. +// + +import Foundation + + +var velibStations: [VelibStation] = [] + +import Foundation + +func fetchVelibData() { + // L'URL de l'API Vélib + let apiUrl = URL(string: "https://opendata.paris.fr/api/explore/v2.1/catalog/datasets/velib-disponibilite-en-temps-reel/records?select=stationcode,name,capacity,numdocksavailable,numbikesavailable,mechanical,ebike,coordonnees_geo")! + + // Créez une session URLSession pour effectuer la requête + let session = URLSession.shared + + // Créez la tâche de requête + let task = session.dataTask(with: apiUrl) { (data, response, error) in + // Vérifiez s'il y a des erreurs + if let error = error { + print("Erreur de requête : \(error.localizedDescription)") + return + } + + // Vérifiez si les données sont présentes + guard let data = data else { + print("Aucune donnée reçue.") + return + } + + do { + // Décodez les données en utilisant le décodeur JSON + let decoder = JSONDecoder() + let velibResponse = try decoder.decode(VelibResponse.self, from: data) + + // Stockez les données dans la variable globale velibStations + velibStations = velibResponse.results + + print("Données Vélib récupérées avec succès.") + } catch { + print("Erreur lors du décodage JSON : \(error.localizedDescription)") + } + } + + // Lancez la tâche de requête + task.resume() +} diff --git a/velibtracker/struct/VelibStationStruct.swift b/velibtracker/struct/VelibStationStruct.swift new file mode 100644 index 0000000..64258ca --- /dev/null +++ b/velibtracker/struct/VelibStationStruct.swift @@ -0,0 +1,75 @@ +// +// VelibStationStruct.swift +// velibtracker +// +// Created by Louis Gallet on 02/09/2023. +// + +import Foundation + +struct VelibResponse: Codable { + let totalCount: Int + let results: [VelibStation] + private enum CodingKeys: String, CodingKey { + case totalCount = "total_count" + case results + } +} + +struct VelibStation: Codable { + let stationcode: String + let name: String + let capacity: Int + let numdocksavailable: Int + let numbikesavailable: Int + let mechanical: Int + let ebike: Int + let coordonnees_geo: Coordinates + +} + +struct Coordinates: Codable { + let lon: Double + let lat: Double +} + +let test = [ + VelibStation(stationcode: "293", name: "Test", capacity: 3, numdocksavailable: 3, numbikesavailable: 3, mechanical: 3, ebike: 1, coordonnees_geo: Coordinates(lon: 3.5939, lat: 2.59094)), + VelibStation(stationcode: "293R", name: "Test2", capacity: 5, numdocksavailable: 3, numbikesavailable: 2, mechanical: 3, ebike: 1, coordonnees_geo: Coordinates(lon: 5.5939, lat: 3.59094)) +] + +let testAPI2 = VelibResponse(totalCount: 2, results: [ + VelibStation(stationcode: "293", name: "Test", capacity: 3, numdocksavailable: 3, numbikesavailable: 3, mechanical: 3, ebike: 1, coordonnees_geo: Coordinates(lon: 3.5939, lat: 2.59094)), + VelibStation(stationcode: "293R", name: "Test2", capacity: 5, numdocksavailable: 3, numbikesavailable: 2, mechanical: 3, ebike: 1, coordonnees_geo: Coordinates(lon: 5.5939, lat: 3.59094)) +]) + + +func fetchVelibStations(completion: @escaping ([VelibStation]?, Error?) -> Void) { + guard let url = URL(string: "https://opendata.paris.fr/api/explore/v2.1/catalog/datasets/velib-disponibilite-en-temps-reel/records?select=stationcode,name,capacity,numdocksavailable,numbikesavailable,mechanical,ebike,coordonnees_geo") else { + completion(nil, NSError(domain: "fr.louisgallet.velibtracker", code: 1, userInfo: nil)) + return + } + + URLSession.shared.dataTask(with: url) { data, response, error in + if let error = error { + completion(nil, error) + return + } + + guard let data = data else { + completion(nil, NSError(domain: "fr.louisgallet.velibtracker", code: 2, userInfo: nil)) + return + } + let decoder = JSONDecoder() + do { + let velibResponse = try decoder.decode(VelibResponse.self, from: data) + let stations = velibResponse.results + // Utilisez la liste des stations dans votre application + } catch { + print("Erreur lors du décodage JSON : \(error)") + } + print("Fetched + decoded") + }.resume() +} + + diff --git a/velibtracker/velibtrackerApp.swift b/velibtracker/velibtrackerApp.swift index 223578d..0941808 100644 --- a/velibtracker/velibtrackerApp.swift +++ b/velibtracker/velibtrackerApp.swift @@ -14,4 +14,7 @@ struct velibtrackerApp: App { ContentView() } } + init() { + fetchVelibData() + } }