Jak udělat kontaktní formulář v iOS ve Swiftu a SwiftUI
Kontaktní formulář ve SwiftUI, který vám každou zprávu pošle e-mailem přes vaši vlastní schránku – bez backendu a bez SMTP hesla v aplikaci. Používá Swift balíček Frontmail.
Aktualizováno:
Do aplikace pro iOS přidáte kontaktní formulář ve SwiftUI se Swift balíčkem Frontmail. Aplikace obsahuje jen veřejný klíč; e-mail odešle Frontmail přes vaši schránku, takže v aplikaci není žádné SMTP heslo a nemusíte udržovat žádný server.
Budete potřebovat: bezplatný účet Frontmail, projekt v Xcode se SwiftUI a zhruba patnáct minut.
1. Připojte schránku a vytvořte šablonu
- E-mailové služby → Přidat službu: připojte Gmail, Outlook, SMTP nebo transakční službu, zaškrtněte Nastavit jako výchozí a klikněte na Ověřit a uložit.
- Šablony → Z galerie → Kontaktní formulář → Použít šablonu. Povinné parametry:
name,email,message; nepovinné:phone,topic. - Na záložce Kód pro web zkopírujte ID šablony (
tpl_…).
2. Povolte mobilní aplikace
- Zabezpečení → Mobilní aplikace → Povolit mobilní aplikace → Uložit. Aplikace
neposílají hlavičku
Origin, takže do té doby API odpovídáorigin_not_allowed. - Šablona → Nastavení → Ochrana → Vyžadovat CAPTCHA (Turnstile). Aplikace používají sdílený widget Turnstile od Frontmailu; klíče od Cloudflaru nepotřebujete.
3. Přidejte balíček
V Xcode zvolte File → Add Package Dependencies…, zadejte
https://github.com/frontmail-dev/frontmail-swift a přidejte knihovnu Frontmail do targetu
aplikace. Pak si z části o Turnstile v dokumentaci iOS SDK zkopírujte do projektu TurnstileView –
malý UIViewRepresentable kolem WKWebView, který patří vám.
4. Postavte formulář
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 parametry funguje libovolná struktura Encodable, takže názvy polí za vás zkontroluje
kompilátor. Chyby sítě a odpovědi 5xx se opakují s jedním idempotency key – ani na nestabilním
připojení nevzniknou duplicitní e-maily.
5. Vyzkoušejte to
Spusťte aplikaci v simulátoru nebo na zařízení, vyplňte formulář a klepněte na Send. E-mail dorazí do vaší schránky a objeví se v sekci Historie v dashboardu.
origin_not_allowed– volba Povolit mobilní aplikace je vypnutá nebo neuložená.captcha_required– zatím chybí token Turnstile; Send zůstane neaktivní, dokud kontrola nedoběhne.invalid_template_params– povinné pole je prázdné nebo e-mail není platný.
Když vám dojdou kredity, zpráva se přijme se stavem
.helda po dobití se odešle automaticky – nic se neztratí.
Další kroky
- Potvrďte uživateli přijetí zprávy šablonou Automatická odpověď: díky za zprávu z galerie.
- Pošlete
SendOptions(locale: Locale.preferredLanguages.first), aby se použila jazyková verze šablony v jazyce uživatele.


