Module 7 - Note de cours

← Retour au module principal

Intégration d'une API dans une application iOS avec SwiftUI

Introduction

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 :

  1. Présentation de l'architecture MVVM
  2. Création des modèles de données
  3. Construction de l'interface avec SwiftUI
  4. Développement du ViewModel pour récupérer les données via l'API
  5. Intégration et affichage des données dans l'application

Architecture de l’application

Modèle MVVM

Le modèle MVVM (Model-View-ViewModel) vous aide à organiser votre code en séparant trois parties distinctes :

Communication et états

Pour assurer la communication entre la View et le ViewModel et pour gérer les états, SwiftUI propose plusieurs annotations :

Les Vues

ContentView

La ContentView est la vue principale de l'application. Elle comporte plusieurs éléments essentiels :

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

Gestion des données (Modèles)

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 : WeatherViewModel

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.

Récupération des données

La méthode fetchWeather(city:) du ViewModel effectue les étapes suivantes :

  1. Construction de l'URL : On crée l'URL en insérant le nom de la ville et les paramètres requis (nombre de prévisions, langue, unités, et votre clé API).
  2. Envoi de la requête : À l'aide de URLSession, on envoie une requête réseau asynchrone.
  3. Réception et décodage des données : Lorsque les données sont reçues, on utilise JSONDecoder pour les transformer en instances de nos modèles.
  4. Mise à jour de l'interface : Grâce à 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
                  }
              }
                  

Processus d'intégration de l’API

Voici un résumé étape par étape du processus d'intégration de l'API dans l'application :

  1. Saisie de la ville :
    • L'utilisateur saisit le nom d'une ville dans un TextField.
    • À la validation (événement onSubmit), la méthode fetchWeather(city:) est appelée dans le ViewModel.
  2. Construction et envoi de la requête :
    • Le ViewModel construit l’URL avec les paramètres requis (nom de la ville, clé API, etc.).
    • Une requête réseau est envoyée via URLSession.
  3. Réception et décodage des données :
    • Les données JSON reçues sont décodées en objets Swift conformes à nos modèles.
    • En cas d’erreur, un message est affiché dans la console pour le débogage.
  4. Mise à jour de l’interface :
    • Le ViewModel met à jour la propriété weatherData.
    • La vue se rafraîchit automatiquement grâce à @StateObject et @Published.
  5. Affichage des informations météo :
    • La vue principale (ContentView) affiche les informations telles que le nom de la ville, la température, la description du temps, etc.

Bonnes pratiques et points complémentaires

Conclusion

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.