feat: 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
This commit is contained in:
Louis Gallet 2023-09-02 17:19:40 +02:00
parent 752a1f8b32
commit 701d163547
Signed by: lgallet
SSH Key Fingerprint: SHA256:qnW7pk4EoMRR0UftZLZQKSMUImbEFsiruLC7jbCHJAY
7 changed files with 222 additions and 6 deletions

View File

@ -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 = "<group>"; };
2C5BDAC22AA3611000DBEC93 /* velibtracker.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = velibtracker.entitlements; sourceTree = "<group>"; };
2C5BDAC42AA3611000DBEC93 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
2CDC317D2AA3654800BF98B9 /* VelibStationStruct.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VelibStationStruct.swift; sourceTree = "<group>"; };
2CDC317F2AA3672600BF98B9 /* StationComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StationComponent.swift; sourceTree = "<group>"; };
2CDC31812AA387AB00BF98B9 /* fetchVelibData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = fetchVelibData.swift; sourceTree = "<group>"; };
/* 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 = "<group>";
@ -69,6 +78,22 @@
path = "Preview Content";
sourceTree = "<group>";
};
2C5BDACB2AA3632C00DBEC93 /* components */ = {
isa = PBXGroup;
children = (
2CDC317F2AA3672600BF98B9 /* StationComponent.swift */,
);
path = components;
sourceTree = "<group>";
};
2CDC317C2AA3653200BF98B9 /* struct */ = {
isa = PBXGroup;
children = (
2CDC317D2AA3654800BF98B9 /* VelibStationStruct.swift */,
);
path = struct;
sourceTree = "<group>";
};
/* 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;

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Bucket
uuid = "E086F483-B1A2-457D-A7AA-DFBB29D4A987"
type = "1"
version = "2.0">
</Bucket>

View File

@ -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()

View File

@ -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])
}
}

View File

@ -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()
}

View File

@ -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()
}

View File

@ -14,4 +14,7 @@ struct velibtrackerApp: App {
ContentView()
}
}
init() {
fetchVelibData()
}
}