Aller au contenu

Comment créer un formulaire de contact iOS avec Swift et SwiftUI

Un formulaire de contact SwiftUI qui vous envoie chaque message par e-mail via votre propre boîte mail – sans backend ni mot de passe SMTP dans l’application. Avec le paquet Swift de Frontmail.

Mis à jour:

La vidéo est en anglais ; le texte ci-dessous est le tutoriel complet.

Vous allez ajouter un formulaire de contact à une application iOS avec SwiftUI et le paquet Swift de Frontmail. L’application ne contient qu’une clé publique ; Frontmail envoie l’e-mail via votre boîte mail : pas de mot de passe SMTP dans l’application, pas de serveur à maintenir.

Il vous faut : un compte Frontmail gratuit, un projet Xcode avec SwiftUI et environ quinze minutes.

1. Connectez votre boîte mail et créez le modèle

  1. Services d’envoi → Ajouter un service : connectez Gmail, Outlook, SMTP ou un fournisseur transactionnel, cochez Définir par défaut, puis Vérifier et enregistrer.
  2. Modèles → Depuis la galerie → Formulaire de contact → Utiliser ce modèle. Paramètres obligatoires : name, email, message ; facultatifs : phone, topic.
  3. Copiez l’ID du modèle (tpl_…) depuis l’onglet Extraits de code.

2. Autorisez les applications mobiles

  • Sécurité → Applications mobiles → Autoriser les applications mobiles → Enregistrer. Les applications n’envoient pas d’en-tête Origin : d’ici là, l’API répond origin_not_allowed.
  • Modèle → Paramètres → Protection → Exiger un CAPTCHA (Turnstile). Les applications utilisent le widget Turnstile partagé de Frontmail ; vous n’avez pas besoin de clés Cloudflare.

3. Ajoutez le paquet

Dans Xcode, choisissez File → Add Package Dependencies…, saisissez https://github.com/frontmail-dev/frontmail-swift et ajoutez la bibliothèque Frontmail à la cible de votre application. Copiez ensuite la TurnstileView depuis la section Turnstile de la documentation du SDK iOS dans votre projet – un petit UIViewRepresentable autour de WKWebView, qui vous appartient.

4. Créez le formulaire

import SwiftUI
import Frontmail

let frontmail = try! Frontmail(publicKey: "pk_…") // create once, share

struct ContactMessage: Encodable {
    let name: String
    let email: String
    let message: String
}

struct ContactView: View {
    @State private var name = ""
    @State private var email = ""
    @State private var message = ""
    @State private var widget: Turnstile.Widget?
    @State private var token: String?
    @State private var resetID = 0
    @State private var info = ""

    var body: some View {
        Form {
            TextField("Name", text: $name)
            TextField("Email", text: $email)
                .keyboardType(.emailAddress)
                .textInputAutocapitalization(.never)
            TextField("Message", text: $message, axis: .vertical)
                .lineLimit(4...8)
            if let widget {
                TurnstileView(widget: widget, size: .flexible, resetID: resetID) { event in
                    if case let .token(t) = event { token = t } else { token = nil }
                }
                .frame(height: Turnstile.Size.flexible.recommendedHeight)
            }
            Button("Send") { Task { await send() } }
                .disabled(token == nil)
            Text(info)
        }
        .task { widget = try? await Turnstile.widget(client: frontmail) }
    }

    private func send() async {
        guard let widget, let token else { return }
        defer { self.token = nil; resetID += 1 } // a token works only once
        do {
            _ = try await frontmail.send(
                templateID: "tpl_…",
                params: ContactMessage(name: name, email: email, message: message),
                options: SendOptions(turnstileToken: token, turnstileKey: widget.turnstileKey)
            )
            info = "Thank you! We’ll get back to you soon."
        } catch let error as FrontmailError {
            info = error.message
        } catch {
            info = error.localizedDescription
        }
    }
}

N’importe quelle struct Encodable peut servir de paramètres : le compilateur vérifie les noms de champs à votre place. Les erreurs réseau et les réponses 5xx sont relancées avec une seule clé d’idempotence – pas d’e-mails en double sur une connexion instable.

5. Testez

Lancez l’application dans le simulateur ou sur un appareil, remplissez le formulaire et touchez Send. L’e-mail arrive dans votre boîte de réception et apparaît dans l’Historique du tableau de bord.

  • origin_not_allowed – Autoriser les applications mobiles est désactivé ou non enregistré.
  • captcha_required – pas encore de jeton Turnstile ; Send reste désactivé jusqu’à la fin de la vérification.
  • invalid_template_params – un champ obligatoire est vide ou l’e-mail n’est pas valide.

Quand vos crédits sont épuisés, le message est accepté avec le statut .held et envoyé automatiquement après la recharge – rien ne se perd.

Étapes suivantes

  • Confirmez le message à l’utilisateur avec le modèle Réponse automatique : merci pour votre message de la galerie.
  • Envoyez SendOptions(locale: Locale.preferredLanguages.first) pour utiliser une variante linguistique du modèle dans la langue de l’utilisateur.
Tous les tutoriels