Créer un Widget Multi-Plateforme avec WidgetKit : iOS, macOS et watchOS

Découvrez comment créer un widget universel avec WidgetKit qui s'adapte parfaitement à iOS, macOS et watchOS.
Créer un Widget Multi-Plateforme avec WidgetKit : iOS, macOS et watchOS

Les widgets sont devenus un élément essentiel de l’expérience utilisateur sur les plateformes Apple. Introduits avec iOS 14, ils permettent d’afficher des informations essentielles directement sur l’écran d’accueil. Avec WidgetKit, nous pouvons désormais proposer des widgets sur iOS, iPadOS, macOS et watchOS, chacun ayant ses propres tailles et contraintes.

Dans cet article, nous allons concevoir un widget iOS universel, capable de s’adapter aux différentes tailles et plateformes, tout en optimisant son affichage selon l’environnement. A savoir qu'il est aussi possible de réaliser des Widget sur Android avec Jetpack Glance.

Des questions sur les widgets iOS ? Contactez nous !

Mise en place du projet

Pour commencer, il faut créer une extension WidgetKit dans Xcode :

Une fois cette extension ajoutée, nous allons structurer notre widget autour des trois composants principaux :

Définition des données du widget

Notre widget affichera un message personnalisé ainsi que la date actuelle. Nous définissons une structure SimpleEntry qui représente les données affichées à un moment donné.

import WidgetKit
import SwiftUI

struct SimpleEntry: TimelineEntry {
    let date: Date
    let text: String
}

Ensuite, nous implémentons le TimelineProvider, qui est responsable de la mise à jour des données du widget.

struct Provider: TimelineProvider {
    func placeholder(in context: Context) -> SimpleEntry {
        SimpleEntry(date: Date(), text: "Chargement...")
    }

    func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> Void) {
        completion(SimpleEntry(date: Date(), text: "Aperçu rapide"))
    }

    func getTimeline(in context: Context, completion: @escaping (Timeline<SimpleEntry>) -> Void) {
        let entry = SimpleEntry(date: Date(), text: "Bonjour, Widget!")
        let timeline = Timeline(entries: [entry], policy: .atEnd)
        completion(timeline)
    }
}

Adapter l'affichage en fonction de la taille et de la plateforme

Un widget peut être affiché sous différentes tailles selon la plateforme. Sur iOS et iPadOS, nous avons trois tailles : small, medium, large. Sur macOS, nous avons une taille supplémentaire : extraLarge. Quant à watchOS, il ne prend en charge que les accessory widgets sous watchOS 10.
Nous allons utiliser l’environnement widgetFamily pour ajuster l’affichage.

struct WidgetView: View {
    var entry: Provider.Entry
    @Environment(\.widgetFamily) var widgetFamily

    var body: some View {
        switch widgetFamily {
        case .systemSmall:
            smallWidget
        case .systemMedium:
            mediumWidget
        case .systemLarge:
            largeWidget
        case .systemExtraLarge:
            extraLargeWidget
        case .accessoryCircular:
            accessoryCircularWidget
        case .accessoryRectangular:
            accessoryRectangularWidget
        case .accessoryInline:
            accessoryInlineWidget
        default:
            smallWidget
        }
    }

    var smallWidget: some View {
        ZStack {
            Color.blue
            Text(entry.text)
                .foregroundColor(.white)
                .font(.headline)
        }
    }

    var mediumWidget: some View {
        HStack {
            smallWidget
            VStack {
                Text("Info supplémentaire")
                    .foregroundColor(.white)
                    .multilineTextAlignment(.center)
                Text(entry.date, style: .time)
                    .foregroundColor(.white)
            }
            .frame(maxWidth: .infinity, maxHeight: .infinity)
            .background(Color.green)
        }
    }

    var largeWidget: some View {
        VStack {
            Text("Résumé des données")
                .font(.headline)
                .foregroundColor(.white)
                .padding()
                .background(Color.red)

            HStack {
                smallWidget
                mediumWidget
            }
        }
    }

    var extraLargeWidget: some View {
        VStack {
            Text("Résumé complet")
                .font(.title)
                .foregroundColor(.white)
                .padding()
                .background(Color.purple)

            VStack {
                Text("Détails avancés")
                Text(entry.date, style: .date)
            }
            .padding()
        }
    }

    var accessoryCircularWidget: some View {
        ZStack {
            Circle().fill(Color.orange)
            Text(entry.date, style: .time)
                .font(.caption)
                .foregroundColor(.white)
        }
    }

    var accessoryRectangularWidget: some View {
        VStack {
            Text(entry.text)
                .font(.headline)
            Text(entry.date, style: .time)
                .font(.caption)
        }
        .padding()
        .background(Color.orange)
    }

    var accessoryInlineWidget: some View {
        Text("\(entry.text) - \(entry.date, style: .time)")
    }
}

Déclaration du widget universel

Enfin, nous devons configurer notre widget pour qu’il soit disponible sur iOS, macOS et watchOS. Pour cela, nous utilisons StaticConfiguration et listons toutes les tailles compatibles.

@main
struct MyAppWidget: Widget {
    let kind: String = "MyAppWidget"

    var body: some WidgetConfiguration {
        StaticConfiguration(kind: kind, provider: Provider()) { entry in
            if #available(iOS 17.0, *) {
                WidgetView(entry: entry)
                    .containerBackground(.fill.tertiary, for: .widget)
            } else {
                WidgetView(entry: entry)
                    .padding()
                    .background()
            }
        }
        .supportedFamilies([
            .systemSmall, .systemMedium, .systemLarge, .systemExtraLarge,
            .accessoryCircular, .accessoryRectangular, .accessoryInline
        ])
    }
}

Tester le widget sur les différentes plateformes

Pour vérifier que notre widget fonctionne sur iOS, iPadOS, macOS et watchOS, il est essentiel de le tester sur plusieurs simulateurs et appareils.

  1. Tester sur iOS/iPadOS/macOS :
    • Sélectionner la cible du widget et exécuter sur un simulateur ou un appareil réel.
    • Ajouter le widget à l’écran d’accueil ou au centre de notifications.
  2. Tester sur watchOS :
    • Ajouter un widget accessoire via l’application Montre sur iOS.
    • Vérifier l’affichage des complications circulaires, rectangulaires et en ligne.

Conclusion

Grâce à WidgetKit et SwiftUI, nous avons conçu un widget universel qui s’adapte à plusieurs tailles et plateformes Apple. Cette approche permet d’offrir une expérience utilisateur cohérente sur iPhone, iPad, Mac et Apple Watch.
En allant plus loin, il est possible d’intégrer des données en temps réel, d’ajouter des interactions avec WidgetURL.
Avec cette base, tu es prêt à concevoir des widgets performants et adaptés à tous les écrans !
Pour encore plus d'intéraction et d'expérience utilisateur en live, pensez aussi à l'utiliser des Live Activities et du Dynamic Island ou le retour haptique dans tes applications mobiles.

Des questions sur les widgets iOS ? Contactez nous !

FAQ

Qu'est-ce que WidgetKit ?

WidgetKit est un framework d'Apple permettant de créer des widgets pour iOS, iPadOS, macOS et watchOS. Il offre une API unifiée pour développer des widgets qui s'adaptent à différentes tailles et plateformes.

Quelles sont les différentes tailles de widgets disponibles ?

Les widgets peuvent être de taille small (petite), medium (moyenne), large (grande) et extra large sur iOS/iPadOS. Sur watchOS, ils peuvent être circulaires, rectangulaires ou en ligne. Sur macOS, ils s'adaptent au centre de notifications.

Comment mettre à jour les données d'un widget ?

Les widgets sont mis à jour via le TimelineProvider qui définit quand et quelles données doivent être affichées. On peut programmer des mises à jour régulières ou réagir à des événements spécifiques.

Les widgets sont-ils interactifs ?

Les widgets ne sont pas directement interactifs, mais ils peuvent utiliser WidgetURL pour rediriger vers des sections spécifiques de l'application lorsqu'on tape dessus. Certaines interactions limitées sont possibles sur watchOS.

Faut-il créer des widgets différents pour chaque plateforme ?

Non, WidgetKit permet de créer un widget universel qui s'adapte automatiquement aux différentes plateformes. On utilise SwiftUI pour définir des vues conditionnelles selon la plateforme et la taille.

Publié par Laurent Bello