Jak zrobić formularz kontaktowy na iOS w Swifcie i SwiftUI
Formularz kontaktowy w SwiftUI, który wysyła Ci każdą wiadomość e-mailem przez Twoją własną skrzynkę – bez backendu i bez hasła SMTP w aplikacji. Korzysta z pakietu Frontmail dla Swifta.
Zaktualizowano:
Dodasz formularz kontaktowy do aplikacji na iOS w SwiftUI z pakietem Frontmail dla Swifta. Aplikacja zna tylko klucz publiczny; e-mail wysyła Frontmail przez Twoją skrzynkę, więc w aplikacji nie ma hasła SMTP, a Ty nie musisz utrzymywać żadnego serwera.
Potrzebujesz: darmowego konta Frontmail, projektu w Xcode ze SwiftUI i około piętnastu minut.
1. Podłącz skrzynkę i utwórz szablon
- Usługi e-mail → Dodaj usługę: podłącz Gmail, Outlook, SMTP albo dostawcę e-maili transakcyjnych, zaznacz Ustaw jako domyślną, Zweryfikuj i zapisz.
- Szablony → Z galerii → Formularz kontaktowy → Użyj szablonu. Parametry wymagane:
name,email,message; opcjonalne:phone,topic. - Skopiuj ID szablonu (
tpl_…) z zakładki Kod.
2. Zezwól na aplikacje mobilne
- Bezpieczeństwo → Aplikacje mobilne → Zezwól na aplikacje mobilne → Zapisz.
Aplikacje nie wysyłają nagłówka
Origin, więc do tego czasu API odpowiadaorigin_not_allowed. - Szablon → Ustawienia → Ochrona → Wymagaj CAPTCHA (Turnstile). Aplikacje korzystają ze wspólnego widżetu Turnstile Frontmail; klucze Cloudflare nie są potrzebne.
3. Dodaj pakiet
W Xcode wybierz File → Add Package Dependencies…, wpisz
https://github.com/frontmail-dev/frontmail-swift i dodaj bibliotekę Frontmail do targetu
aplikacji. Potem skopiuj do projektu TurnstileView z sekcji o Turnstile w dokumentacji iOS SDK –
to mały UIViewRepresentable wokół WKWebView, który jest Twój.
4. Zbuduj formularz
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
}
}
}
Jako parametrów możesz użyć dowolnej struktury Encodable, więc nazwy pól sprawdza za Ciebie
kompilator. Błędy sieci i odpowiedzi 5xx są ponawiane z jednym kluczem idempotencji – żadnych
zduplikowanych e-maili przy niestabilnym połączeniu.
5. Przetestuj
Uruchom aplikację w symulatorze lub na urządzeniu, wypełnij formularz i stuknij Send. E-mail trafi do Twojej skrzynki i pojawi się w panelu w sekcji Historia.
origin_not_allowed– opcja Zezwól na aplikacje mobilne jest wyłączona albo niezapisana.captcha_required– nie ma jeszcze tokenu Turnstile; Send pozostaje nieaktywny, dopóki weryfikacja się nie zakończy.invalid_template_params– wymagane pole jest puste albo e-mail jest nieprawidłowy.
Gdy skończą Ci się kredyty, wiadomość zostanie przyjęta ze statusem
.heldi wysłana automatycznie po doładowaniu – nic nie przepada.
Co dalej
- Potwierdzaj użytkownikowi otrzymanie wiadomości szablonem Automatyczna odpowiedź: dziękujemy za wiadomość z galerii.
- Wysyłaj
SendOptions(locale: Locale.preferredLanguages.first), żeby użyć wersji językowej szablonu w języku użytkownika.


