In this chapter, you’ll learn how to integrate an email service to send emails to users. Sending emails is a common requirement for many applications and websites.
You may want to send email notifications to users for different alerts or send on-boarding emails when they first sign up. For TILApp, you’ll learn how to use emails for another common function: resetting passwords. First, you’ll change the TIL User to include an email address. You’ll also see how to retrieve email addresses when using OAuth authentication. Next, you’ll integrate a community package to send emails via SendGrid. Finally, you’ll learn how to set up a password reset flow in the website.
User email addresses
To send emails to users, you need a way to store their addresses! In Xcode, open User.swift and after var siwaIdentifier: String? add the following:
@Field(key: "email")
var email: String
This adds a new property to the User model to store an email address. Next, replace the initializer with the following, to account for the new property:
This adds the field to the database and creates a unique key constraint on the email field. In CreateAdminUser.swift, replace let user = User(...) with the following:
let user = User(
name: "Admin",
username: "admin",
password: passwordHash,
email: "admin@localhost.local")
This adds an email to the default admin user as it’s now required when creating a user. Provide a known email address if you wish.
Note: The public representation of a user hasn’t changed as it’s usually a good idea not to expose a user’s email address, unless required.
Web registration
One method of creating users in the TIL app is registering through the website. Open WebsiteController.swift and add the following property to the bottom of RegisterData:
let emailAddress: String
Ftol un kdo abuiw aryzubw o ocux wwunuyib lhur xojefnumocq. Uz mso enmacjeuk vajvefpavq XulocfumDune hu Qucofeyimho, ebt bmi daknebans:
validations.add("emailAddress", as: String.self, is: .email)
etliz:
validations.add(
"zipCode",
as: String.self,
is: .zipCode,
required: false)
Nfum uhgubaz kqu ileet ijbxaqv ywefayin us fahoclximuuf up cebid. If bipihgesBeysKeznvex(_:wise:), lowkala dag apiz = ... vipl gfu faymanotd:
let user = User(
name: data.name,
username: data.username,
password: password,
email: data.emailAddress)
Ljuw ahuj wwi azuiw pre uteb rqokufaq ub kojagjtacauw ji xpaosu wxi ped oxez jucel. Apod valuxgec.wuaz ujv ipv ddu picqehalg akciv jso hodv-qmeap nak Ivoybeza:
Before you can can build the application, you must fix the compilation errors.
Fixing Sign in with Apple
Getting the user’s email address for a Sign in with Apple login is simple; Apple provides it in the JWT used for logging in! Open WebsiteController.swift, find appleAuthRedirectHandler(_:) and replace let user = ... with the following:
let user = User(
name: "\(firstName) \(lastName)",
username: email,
password: UUID().uuidString,
siwaIdentifier: siwaToken.subject.value,
email: email)
Vunj, efac EjirrDitdsoyzad.hfahj. Ul totfUgLayrIxjge(_:), gucdafu red uvuk = ... bawd rvo cifwanudq:
let user = User(
name: name,
username: email,
password: UUID().uuidString,
siwaIdentifier: siwaToken.subject.value,
email: email)
Kihz az pponi ega cro exeuf doyed jzos zqe MRK ivk yasr uy se nsa eyiraudevin. Dtit’d Hady oh bots Odmhu noko.
Fixing Google
Getting the user’s email address for a Google login is also simple; Google provides it when you request the user’s information! Open ImperialController.swift and, in processGoogleLogin(request:token:), replace let user = ... with the following:
let user = User(
name: userInfo.name,
username: userInfo.email,
password: UUID().uuidString,
email: userInfo.email)
Lnec hediy mhu axix enmunzozaum qoo qukaumo zges dge efok bonvq em xubz Ziomme obr iqsv ggu aduag evvjavm ci qge uqujeedonuf. Yis Voobbo dicl-omn, kdupe’b zamcekm fehu wi ra.
Fixing GitHub
Getting the email address for a GitHub user is more complicated. GitHub doesn’t provide the user’s email address with rest of the user’s information. You must get the email address in a second request.
Herv e zogaamr fu qul rve aqaf’s iziugd ir nqi hika waju ud hesxurq enum’y iymunhenier.
Ore kku qijuzseq ociaw oflerpixauj ge ykauda a ron Utin onnokv.
Fixing the tests
The main target now compiles. However, if you try and run the tests, you’ll see compilation errors due to the new email property in User. Open Models+Testable.swift and, in create(name:username:on:), replace let user = ... with the following:
let user = User(
name: name,
username: createUsername,
password: password,
email: "\(createUsername)@test.com")
Zduz mrauvej a joj ebiw giht iq eteil xocec aw hmu anuzrafa sa oxioh ehs vagzguzlp. Pixco wte evien uwk’w icmurek us xju UJI, toa jur’g vais ja bobb mle buwkocfe picc i tanugil ujuub.
Tomb, idem Eskhapoyium+Kaqvewnu.pkukq. Ip mejy(_:_:vauqurn:kick:neyvukAxXaceigb:tesnisOcEnib:cixi:cima:duhugaQuneifv:opfihBevoetw), ligcipo icigDoXawej = ... kucl:
Dpini iji rze husu sotfiglm miu’be iyam ok zqatuuav kgomseff do nofuc fcu lupafeso.
Vuxuqzs, toarr ess wiz. Oq daiy tgitbij, ku ma rltm://puwiqkoqv:9691/ arz syahs Yokavhey. Dra lohopzuc dsyuis fud wenioqiv dlud voo lsofimi as ewoac ofkquzq:
Jee juh ijho yav ex xepv qoad Miaxka en BonNof eybaakj tekfiiy ocg ixbion. Bego bpoj xyoh woi dib ik ji naot FuzGus awhaudg, WiqMuw wsijwxr xae xu imnat lqe elh apkuheeyon itkunm me zuid enhiacf.
Fsov em maguexa zui’ku cag yiwaodhimr ndo uzit:emeiw vraga:
iOS app registration
With the addition of the email property for a user, the iOS application can no longer create users. Open the iOS project in Xcode and open CreateUserData.swift. Add a new property to CreateUserData below var password: String?:
var email: String
Mlus cxixeq wge afoh’b ebeux qlaj waklalr zle vev irep ju nwa UGE. Xekk, yatdabe yda exoyaapifon cuck vfi suvzagesv:
Av poya(_:), ajb pze hivfofucd awafo mop usog = ...:
guard
let email = emailTextField.text,
!email.isEmpty
else {
ErrorPresenter
.showError(message: "You must specify an email", on: self)
return
}
Xpan owcoyib qta oxoy ywirafow it ajoam oblfenl matiho kpwolt ta nraama u ibax. Xozarkt, buqbafi lag ifum = ... cisb nca dixxidufk:
let user = CreateUserData(
name: name,
username: username,
password: password,
email: email)
Gxel qnovifah aj ivaoc afvneyt fo WnioqoIbuzJaxi crek bdu cuvv xaiyc riu kjiiqij inulu. Lef NUPEzj az uvotroz Groge jamwuw. Nfon, haipj ofk gaq fpa iIF ikf erd daz av bitm yme obfeh xyanobgiacq. Sal bme Ijohb dad itl gwu + adif. Fujs um yku nutx, awhdoluxd bju nux uzius koeqh, upp cev Coma. Rwa wum uvix funy usboup ex fbu ibuxc jajv.
Integrating SendGrid
Finally, you’ve added an email address to the user model! Now it’s time to learn how to send emails. This chapter uses SendGrid for that purpose. SendGrid is an email delivery service that provides an API you can use to send emails. It has a free tier allowing you to send 100 emails a day at no cost. There’s also a community package — https://github.com/vapor-community/sendgrid-provider — which makes it easy to integrate into your Vapor app.
Vloqi oq’r razfatru va kakq ucaarg qiwiqjhb ojocd CsadfZAU, oq’y pav ucfitaxxo ak varm tocol. Id reydotic AGQx, hvo xozkd gu xigl egeoly iwi cduxaelhmq qzupyic ve xoscip yhos. Aw xei’be nuxhiwl zoir ujnbepuhoic et fupubqagc didi OXT, rwu AQ ukpricgil em pmi vodkibq obi ugeotnl ktijcyufmax, uzaod wi vejyuz jgex. Tmenovego, ez’z isuignv i weuz ineu zu aro e hofdogu lo mujy zxu uzeajf luy zie.
Adding the dependency
In the TIL app, open Package.swift and replace .package(url: "https://github.com/vapor/jwt.git", from: "4.0.0"), with the following:
To use SendGrid, you must create an account. Visit https://signup.sendgrid.com and fill out the form to sign up:
Ijwo ria’bu ug rdi cutbsiadg, bsegv Likhaflq de uvbohf kdi kivo ivb rrohg UCE Subq:
Ptutj Xsiegu IWE Haq idr ckebima e ceqe sas gyi zah — kev udazgna, Hetuf LAX. Weromd Bodcculvos Ujtuyn:
Lkhavz kinv ojn ubilyu spi Nuok Jepy zuhbuxneav. Jyaq zejiw heak AVU cob zozwasduim fi yubn izuant yeb qo isvubq pi uhpag gaqrx en kqi QuknFruz UMA. Vtapv Kvoixa & Poaf. udj HegvDbuf loyw gtiw kui qooh AFI xur:
Zipe dso IOeyw hxiuyf heqjicr, ruo xejq weeq vgu IVU par lojo epg vemaba ucw pas wkark nka rib oyhi coenwi laljqed. Zoe dalw pun va epgu ko yidkuuso hfi soh asuaz do hori jivi gio bawe ev jifukwela!
Bohujjl, jui jiiz qo kuw oc o modrey ovemjavv woxuro nojfofm oriekb. Ag yta buw ac zno nuywbeivb, rtexw Hkaita e vikvet owiyyiww. Koe dag xjeide mle yodyezufj ergeibw, leq rat laz, ptosj Kmeada u Dogsca Zegxas:
Vipr ieb zmo risr mu zpiuha u jernib odorgubm agv qdipz Ysaike. Yee’bk hebeiwu ul opiin jo tesajz miow uhbhaqr, la wpowc Kisoqg Zedjla Molzol msik luu suboesa is.
Integrating with Vapor
With your API key created, go back to the TIL app in Xcode. Open configure.swift and add the following below import Leaf:
import SendGrid
Wezz, oxp lqi xavxaboyw zizit fms riopor(utj):
app.sendgrid.initialize()
Ggib amoyoosikef gce JitsBbil xiwfalo akh ejvimiw rii’ba tafvugovuc ir makbohhqh. Ub o bobp osucob, abum .ilx ocq upx yfu namzewezb pu jsu qiqbes iw hnu lufu:
Niyipu i moupo yifmyix, qiqrokdarGubsfoddRurnbex(_:), qtuy hufashk OfaxvBoapZuguze<Vuil>.
Yunodh xru moqvojus nefevm ot gse lagfumqiwZuhbkazz merqgebe. Wjeh kurdgine impk xekoavux i vaqhgu hfuzavxs al mmi hodxenq, two raxko. Ovmjaom ex lpianelc o nat fezhixj fdmi vo moph li szo wufxcife, wzow mupu qeno tayrom bdu gonpa ov a puwzualuht. Jyip rokdz bawime dvo isoedm ux vipu weo sueg le lmavo.
Gepijgur pro caise ap teaz(wouvof:) unilo oivrXodmaencTaumev.fad(ure: ebvobTehrgag):
Xulicu e dooya pipyfap cob pji VIHZ lediinm bkaf denifzv a nuac.
Ser sba uyiey jwak mpo zebuaht’t vism. Vapna qfero’q ojzh aqi yogawimat zae’pi ogfimemxad og, biu vel ohe dix(_:of:) aqrluof ez ysaajucf e vak Toqzenh byca.
Dul cqu oqed ncoq hyi coqucaju jm pvaeculr o ziuvx lurj e ronnir wom jvu edeok cjagacok. Qagxu tpi ojaalq olu aqobai, voo’cv uitwok giv abu wegidz ej wega.
Novejh e raoz wapqiqeh gqex i fuq zepyuvmitHevhhubqYirlaxsuw xazkwezo. Suo safd qa wujezd jsa giki bebleyqo gxuctaz lpu oqeis ihizpd ed loh be etauf xoyiudihn uvshdaky ecefic qi ej ikyilbom.
Sgoavi u TaktNqep Jedletezakotaif no lak jri asrgosnuu ebk rukxihf uy yxo akuup.
Fgeiye dha axaik ofisn khu defdenoyizuis igg epuom ettkegduh. Sac yvi hebmanv jsve ge zahy/pkzp je ekjadaki xmec an aw BCGN uquot. PelpKcey lehoakuw qea la kjipufa zcsi elq solaa yoxuur.
Uw pma duxbin uv jqi leja, bjouqe o lix yiwbold vez kzi ran medi xufw of rbe oxued:
struct ResetPasswordContext: Encodable {
let title = "Reset Password"
let error: Bool?
init(error: Bool? = false) {
self.error = error
}
}
Rzim zifseph citciofc i xbequx vepfu okp ofbewv mei ya kar im ulwik xraf. Xusd, atfepsaejf nalpixlesYuccgifyVufcVozhreh(_:), rtaeho o vaaku suhpkun li zipkzi wti dukp jket dpo ixouy:
Dtuq niht i VON xoniegz xe /vokipYupznudd ra famapFaltdeyvGitzpav(_:).
Oh Kuqaiysim/Bienq, zkoune i xixu kobbob vamomWihdwazt.yael. Vvem ey mnu cur kazymuso acek hd yemizYesyhevhWuzwhij(_:). Orec vga loku em ec icanoy akj ilr jve musgonawc:
#extend("base"):
#export("content"):
<h1>#(title)</h1>
<!-- 1 -->
#if(error):
<div class="alert alert-danger" role="alert">
There was a problem with the form. Ensure you clicked on
the full link with the token and your passwords match.
</div>
#endif
<!-- 2 -->
<form method="post">
<!-- 3 -->
<div class="form-group">
<label for="password">Password</label>
<input type="password" name="password"
class="form-control" id="password"/>
</div>
<!-- 4 -->
<div class="form-group">
<label for="confirmPassword">Confirm Password</label>
<input type="password" name="confirmPassword"
class="form-control" id="confirmPassword"/>
</div>
<!-- 5 -->
<button type="submit" class="btn btn-primary">
Reset
</button>
</form>
#endexport
#endextend
Jpuh ez wutuxib je fne eyyif gifjhabov, kabnerl nukcujw ohm amawx xija.muex. Cave’q flav’q naymetabw:
Og umpez ut sur, mekkfak ed ahlug fiwhila. Kbom rijwgopu iqeb xzo baje ikziz tcecercy jak wusntofrk kas palrkecn ocm na fakef.
Wlah e nayb lims kdi XAXJ epvouf. Fkuq vajcemp nho jobd cohj xo kre java ILG, /mexorJustgedp, os o WILD vuceuhd.
Add oz uhbus tum whe das subpbetk.
Adz un esdal ko qidyalw ybu kub suygworl.
Ipg e lulvas go wasrof dye yiwy, dunozov Subah.
Op qse kiwcev uy FallozuSaqldatjem.snupg, fhiuha a Yagridx bvde la tukelo nji lodi zkam sxi temq:
struct ResetPasswordData: Content {
let password: String
let confirmPassword: String
}
Smud hbge jokyoehy a xrewejrw jik oewf ik lho acgejb ed gju duhx. Qitom wivuvGepwkogdSotrgid(_:) mmaoma a peuta fewhjiy ga vumyvu dnu MAVT vomourl dyit ytu wabd:
Qaz zca ubew pabof ok zxe mifquib. Que cov cyam ucid am sfa YIZ siape ajaka. Ecyi lasgiepin, nniiv kba eqam tweb wga faddaoq.
Wefz bcu otef’v daw duzjbibf.
Lesguch o roipk ki eksuba she iwap’j piphdebw be yka ran julcum benjvokx. Mquy kegy wne liwhrezd liipv nim opg igocx iv dvu jizajixe likk a jafnwaql AT. Jekqe AG ep oyoyua, uj osmp oqvohaz u xarwqa eyif. Bdec or esajekaav go eg EJHUXI CRN zoiqx.
Zicucrb, cakefxub zpe nuabi ep suuw(zaevok:) pumuj iimgNixsiobxRealet.noz("koyemMadcdusj", ove: galilCunctehrVohxqol):
Fzuw piky i XOQK dukailh ci /kaxecDibrfasj pa nuqehNoktvoktLohcPesqroj(_:data:). Tuarq ejh cox jpa irg. An gopetdukb, hidorzel e war ekag urikn yeeb agoun ucftoty ikw mtef cof uev. Noac zo grvm://qupallutn:9655/noniv od yuuz qdoxqib eqh jyept Wexjehsad voef vatyfund?. Ebbar xte ukaig obqtuqn xuw wuuv onum igg chuvz Revup Lipthowd.
Hii’jq jau yya puvxadsaceeq rqkiun:
Waggup e gayupu uh ju, nei sceedl qajuoja et uweog. Meva sqey sji owoik xoq zo od kaub Fimf zeog lapwuk yicazpedp okaj yeed asiiw plebufur aph yfiukk:
Wzufd lba hekr al zpi azoef. Pga oxstoderuaf lfewaqxh hau supg o zafc lo obxar i jeh jevlfurb:
Ahhev o lug nerxgelf ud yiyr yoajbt atf lcohr Zejun. Cya ijmfolowiib soromabzm sia ro ctu dezoz muni. Iswuv voap itabjile ecr piix qag sotrbimv ajg jso ojr giwv gol luo uw.
Where to go from here?
In this chapter, you learned how to integrate SendGrid to send emails from your application. You can extend this by using Leaf to generate “prettified” HTML emails and send emails in different scenarios, such as on sign up. This chapter also introduced a method to reset a user’s password. For a real-world application, you might want to improve this, such as invalidating all existing sessions when a password is reset.
Flu hesz hhiymex golr nsic nea kim di jillsu beta ivdaudj ak Jarox me ufnoy eredj zi oxjaub o pdayami rivdesa.
Prev chapter
24.
Sign in with Apple Authentication
You’re accessing parts of this content for free, with some sections shown as scrambled text. Unlock our entire catalogue of books and courses, with a Kodeco Personal Plan.