L'objectif de cette application est de montrer comment consommer une API (ici l’API OpenWeather) dans un projet iOS natif en utilisant SwiftUI. L'application permet de récupérer et d’afficher les prévisions météo d'une ville saisie par l'utilisateur. Nous utiliserons le modèle de conception MVVM pour séparer la logique métier (récupération et traitement des données) de l'interface utilisateur.
Dans cette fiche, nous allons détailler chaque étape du développement :
Le modèle MVVM (Model-View-ViewModel) vous aide à organiser votre code en séparant trois parties distinctes :
WeatherData,
Forecast, Main, Weather, Wind et
City pour correspondre aux données JSON.
ContentView est la vue principale, et nous avons aussi des
composants comme MeteoParHeure et MoreDetailMenu pour afficher
différentes parties de l’information.
WeatherViewModel gère la récupération des données de l’API, leur décodage et met à
jour les vues.
Pour assurer la communication entre la View et le ViewModel et pour gérer les états, SwiftUI propose plusieurs annotations :
@StateObject : Crée et observe le ViewModel dans une vue. Dès que les données
changent, la vue se rafraîchit automatiquement.
@Published : Utilisé dans le ViewModel pour signaler que certaines propriétés,
comme weatherData, ont changé.
@State : Gère l’état local d’une vue, par exemple le contenu d’un
TextField.
@FocusState : Permet de gérer le focus (l'endroit où l'utilisateur tape) sur un
champ de saisie.
La ContentView est la vue principale de l'application. Elle comporte plusieurs éléments essentiels :
LinearGradient, pour un rendu esthétique.
TextField qui permet à l'utilisateur de saisir le nom d'une ville.
Lorsque l'utilisateur valide sa saisie (en appuyant sur "Entrée"), la méthode
fetchWeather(city:) du ViewModel est appelée pour récupérer les données.
Exemple de code pour ContentView :
import SwiftUI
struct ContentView: View {
// Création et observation du ViewModel
@StateObject var weatherViewModel = WeatherViewModel()
// Variable pour stocker la saisie de l'utilisateur
@State private var cityTextInput = ""
// Gérer le focus sur le TextField
@FocusState private var focus: Bool
var body: some View {
ZStack {
// Arrière-plan en dégradé
LinearGradient(gradient: Gradient(colors: [.blue, .blue, .blue, .white]),
startPoint: .top, endPoint: .bottom)
.ignoresSafeArea()
VStack {
if let weatherData = weatherViewModel.weatherData {
// Affiche le nom de la ville et le pays
Text("\(weatherData.city.name), \(weatherData.city.country)")
.font(.system(size: 30))
.foregroundColor(.white)
.padding(.top, 50)
// Ici, d'autres composants pour afficher les données météo seront ajoutés
} else {
// Affichage par défaut quand aucune donnée n'est disponible
VStack {
Spacer()
Image(systemName: "sun.and.horizon.fill")
.symbolRenderingMode(.multicolor)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 300)
TextField("Ville", text: $cityTextInput)
.textFieldStyle(.roundedBorder)
.padding(.horizontal)
.onSubmit {
weatherViewModel.fetchWeather(city: cityTextInput)
}
.focused($focus)
.autocorrectionDisabled(true)
Spacer()
}
.onAppear {
focus = true
}
}
}
.padding(.horizontal, 10)
}
}
}
Les modèles sont des structures qui représentent les données que nous recevons de l’API.
Pour faciliter la conversion des données JSON en objets Swift, ces structures doivent adopter le
protocole Decodable.
Exemple de structure pour WeatherData :
struct WeatherData: Decodable {
let list: [Forecast] // Liste des prévisions météo
let city: City // Informations sur la ville
}
struct Forecast: Decodable {
let main: Main // Informations principales (température, humidité, etc.)
let weather: [Weather]// Description du temps et icônes associées
let wind: Wind // Informations sur le vent
let dt: TimeInterval // Date de la prévision en timestamp Unix
let dt_txt: String // Date au format texte (facultatif)
}
Le ViewModel sert d'intermédiaire entre les données (les modèles) et l'affichage (les vues). Il récupère les données depuis l'API, les décode et les met à disposition pour que la vue puisse les afficher.
La méthode fetchWeather(city:) du ViewModel effectue les étapes suivantes :
URLSession, on envoie une
requête réseau asynchrone.
JSONDecoder pour les transformer en instances de nos modèles.
DispatchQueue.main.async, on
met à jour la propriété weatherData sur le thread principal pour que l'interface se
rafraîchisse.
Exemple de code pour la méthode fetchWeather(city:) :
class WeatherViewModel: ObservableObject {
// Propriété publiée pour notifier la vue des mises à jour des données
@Published var weatherData: WeatherData?
// Méthode pour récupérer les données météo pour une ville donnée
func fetchWeather(city: String) {
// Remplacer {VOTRE_API_KEY} par votre clé API OpenWeather
guard let url = URL(string: "https://api.openweathermap.org/data/2.5/forecast?q=\(city)&cnt=4&lang=fr&appid={VOTRE_API_KEY}&units=metric") else {
return
}
// Envoi d'une requête réseau asynchrone
URLSession.shared.dataTask(with: url) { data, response, error in
// Vérifier que des données ont bien été reçues
guard let data = data else { return }
do {
// Décoder le JSON en instances de WeatherData
let decodedResponse = try JSONDecoder().decode(WeatherData.self, from: data)
// Mise à jour sur le thread principal pour rafraîchir l'interface
DispatchQueue.main.async {
self.weatherData = decodedResponse
}
} catch {
// Afficher une erreur dans la console en cas de problème de décodage
print("Erreur lors du décodage du JSON: \(error)")
}
}.resume() // Lancer la tâche réseau
}
}
Voici un résumé étape par étape du processus d'intégration de l'API dans l'application :
TextField.onSubmit), la méthode
fetchWeather(city:) est appelée dans le ViewModel.
URLSession.weatherData.@StateObject et
@Published.
ContentView) affiche les informations telles que le nom
de la ville, la température, la description du temps, etc.DispatchQueue.main.async pour
mettre à jour l'interface sur le thread principal, ce qui est essentiel en SwiftUI.
icon(iconCode:) (non
montrée ici) mappe les codes d’icônes de l’API aux icônes natives d’iOS pour améliorer
l'apparence de l'application.
Ce projet illustre concrètement comment intégrer une API externe dans une application iOS utilisant SwiftUI. En adoptant le modèle MVVM, nous séparons la logique métier (récupération et traitement des données) de l'interface utilisateur, ce qui rend l'application modulaire, facile à maintenir et à faire évoluer.