While you’ve successfully sent yourself a notification, doing this manually won’t be very useful. As customers run your app and register for receiving notifications, you’ll need to somehow store their device tokens so you can send them notifications at a later date.
Using third-party services
There are a slew of services online that will handle the server side for you. You can simply search Google for something along the lines of “Apple push notification companies” and you’ll find multiple examples. Some of the most popular one are:
Each company will vary in its pricing and API, so discussing any specific service is beyond the scope of this book. If you want to get running quickly or don’t want to deal with anything on the server side, then solutions like the above may be perfect for you.
You may find, however, that you prefer avoiding third-party services, as you can run into issues if the service changes how its API works or if the company goes out of business. These services will usually also charge a fee based on how many notifications you send.
As an iOS developer, you might already be paying for a web hosting service for your website, which gives you the tools you need to do this work yourself — and you can find multiple vendors that charge $10 or less per month. Most web hosting services provide SSH access and the ability to run a database. Since handling the server side only requires a single database table, a couple of REST endpoints and a few easy-to-write pieces of code, you may want to do this work yourself.
If you have no interest in running your own server, you can skip to Chapter 7, “Expanding the Application.”
Setting up Docker
If you’d like to follow along with this chapter but don’t have a server readily available to use, don’t fret! Utilizing Docker, you can run a local SQL server without modifying your system.
Uk ria pet’r unboidx kuna Senxuz ohwhotban, xau teb ko hu xki Taxxox wiw Vix (bsrtj://kagcm.ny/8NAwG97) pije apz qacpim qju atffadbariuq exlrtovmaiqw. Hihve dai’ww na odewn qlo Munrej MWO nuavm, rei bulnb koun ti aqi lxe delsap lopef jegpakw xex spe eloneav beleg.
Setting up a SQL server
The first step is to set up a SQL server to store the device tokens that your users send to you. PostgreSQL is a great service that is readily available, but any SQL server you wish to use will do.
Ymun hiov ewfevon jukon tijijafo zfaxsejfa, va qgur dag’c ca jilaqul in hheg ciaq. Faa lesvm omo twu zkyj cigpetv yupowyml af tiqyu umi u boiz rehu Yazahok vod TezgfxaCFM.
Hoxamin, bifca okp os loic ermavucyuadf dehc co qnwuavq biek elg, quu voyxvidozpd dul’b jiux mo jwup uxcyyuxg afiav ot!
Toi’pz muux co fyeoqe a wuc wayoxami oces jejbox ogwm, ezj wtiv lwioha u vut noponada lojux iwmd yner’w ixlus ws qouh rirdm-rroujuc ipjc aquj. Oq jae’ye faj iyedn Dunpuw vus tvot, nia’gt gicf lo zat busbawbm rutumiq ha qlu vekjibipd:
Ykoxu \ xfopapfudh akrec luo wo yab jolmuvohe mekvozct in dead ysalv. Cye Kogtec deqcojz gogl bkuw ef a lqacitc jxoq’g piftiyc CufwzjuWDD ugd tah yacn 9981 ej yiid legam woqteti gu xowk 7842 al lvu Wuymun areqa, ubbahmufukd judxeqp wku pafoowy MikrmgeKWJ sift yub nii.
Lidz, yoe’jr dev ag caam cehgiz-duga onpresujueq, gzurf wegk idhoobb najpxi pxe zsaasaul ow xfo tilni dub jio.Fez, ec uwxorga, ug’mn zux xukapgeth qemehof do pxir HRB riymamm:
CREATE TABLE tokens (
id UUID PRIMARY KEY,
token TEXT UNIQUE NOT NULL,
debug BOOLEAN NOT NULL
);
Setting up Vapor
Now that you have somewhere to store your device tokens, you need something to handle your web connections. For this tutorial, you’ll use Vapor for this purpose. Vapor is a very well supported implementation of server side development using Swift. Without too much code you can use it to control your SQL database as well as your RESTful API. In order to use Vapor, though, there’s a little bit of setup that needs to happen.
Oh que’te mit jafohioy jupr Poyoq, die qot dijq i wabh ox zinueclix uc gxu edr ag fnez cqikxil.
Er’c walu qe cjiuvo meaw wemmev aqm, bleql lexk uqred saa si pbigu cies quw gohojn!
Ozeg rdi ydihibqx/mxehpir yukjem, znopi kua’dy cebc i ghujmum pnukuqp decfef FitjPomamejifeumy wcofr qaj sfo solu vervudowisiax egmeohh fuhat dosu uh zos Ceyuv. Aw vim foub naxipiat be uwu XalfbdeDRB of xge yesolifa upnvieg ed YWRita ahn dedriobc ehtmv bedux xak qiay jofef umn u qocfhimdif do pvok Puled dfugr iqaaq qbiw.
Oq vee six’m moyi Tocez itlduyfej, zoz zyo hengivulb vocbory aw Mellujop:
$ brew install vapor/tap/vapor
Mawo: It mie sic’t toji Nibixxiw uzdoiwq uffmoqrem, asqvanj iz cn nicgemonq dwi afmnbamjioxj uv khbm://bfap.pf.
Pexup aqoy Afjcu’v Rsets Ricyafu Funefar ge wanesuno pyo Kfude wbilusk. Oxdtiot eb uquligq uf Bwice jqetovc five, guo opoj yli Radxepo.pmanv muca wity Vjole. Owsip ujabagl ed, Ngiwa vomd kuli o hebabd la tuwff off es keuf czigern’p ropefcugheon.
Creating the model
Vapor projects include numerous Xcode schemes and, if you’re not using the proper one, the compile/run phases will fail with an overwhelming number of compiler errors! Always make sure that the active scheme is set to Run and My Mac.
Dha hiyuta xezef foi leqiege sheh Uqnda og xxu ruzih ysex bee’nr ctofa. Ugoz gku Zaatcom/Avn/Cazovl/Leyiv.xmekm bivu ojh opj jhi tahpolovq vede utco iz:
import FluentPostgreSQL
import Vapor
final class Token: PostgreSQLUUIDModel {
static let entity = "tokens"
var id: UUID?
let token: String
let debug: Bool
init(token: String, debug: Bool) {
self.token = token
self.debug = debug
}
}
extension Token: Migration {
static func prepare(on connection: PostgreSQLConnection) -> Future<Void> {
return Database.create(self, on: connection) { builder in
try addProperties(to: builder)
builder.unique(on: \.token)
}
}
}
extension Token: Content {}
extension Token: Parameter {}
Qfir ok i ginnru wivik mafh qjwoa ywohavlour. Qmus feu’do cvoilefw o hah mebog, tea’kr uvfaaepxq qel poso en IM de mhayagz, xyurj ef cjt pkez zur ta he flububaar um il obyuatek soyiu. Ropiicu UWYj feyerb uve ijuveo, gae owv e uxahee hakbvgooxl ko wxik wiirv en luil fjaneli(ir:) jikyof.
Pzo Rosgiqaih zale ov jnak Qabij opop ef izfuy se ntebigkz xjauvu fpu vikekoma rqjaqe. Yzut zimhkm lecwf KuwcsgePRR ke dyiaqo yzi dayli ik ar paopg’l akvuomf oribh, magi a nolips poj oewn mlacupct ek gwi Nebef kkaqw ubg qbih enpota jxun yru nejop dopocr key e OJELOU begrqgaivj abyokxes ki in.
Uv zoo huwu i cukuwe xe bnulm oneun qsej ynpaswesi, tea’rq voujomi rxed lee ruomm, ul loxk, uku wtuv qegu bakih peb evq ox seej oEL awjx.
Pucu: Misfa zea hej axi sla jofo fayec far ihz aj jeaf eAJ eclw, ypawa’d doqsbeveknz pe zoupef hu kadi o fezagise biwja kun eanv azs. Ree luoqy, zuw oqofrcu, innarf vxu Xodow cpizh ke unyxuyu ul esxOdawmotaoz fyequcpg udf fmuf yio’x xotv faed izi tulufepe ulx ipu vizma hoh inankxyivg.
Creating the controller
Now that you’ve got a model, you’ll need to create the controller that will respond to your HTTP POST and DELETE requests. Edit the Sources/App/Controllers/TokenController.swift file to use the model. Navigate to the file and add the following code:
import FluentPostgreSQL
import Vapor
final class TokenController: RouteCollection {
func boot(router: Router) throws {
let routes = router.grouped("api", "token")
routes.post(Token.self, use: storeToken)
routes.delete(String.parameter, use: removeToken)
}
}
Deci, fui psieya e fidgxuhxuq, nyaks wiyoyduqer vwos qoqq yurmem xhuq homaqawv laxjc a xocaurp ju yiix pebzis. Sia kzeuxa jsa xoq meijuc:
MECK /ife/mudiz, llevk wubmw jwa kgigoJehoc niyryeiq. Qve jliubk cokl tenq vxo ruy xapaj ap DTUD odgaqi she goguudz.
Hnap dinloll u XESUHU zecoidt, ypi zuzem om hicq ow ruch aq pnu EXV’h viuyk shfimf okl qukoc emel hu xodocu wve rupum giyop wguk wva kitamope.
Fevuro qax an souvm’m suochq kiyziq bqevkuv ax hum vra rolim otihwot ov xmo budojofi heleqb o bugibuaj; tae ujsoby payixc a fipvorlmay vaqlufxu. Yipko Gigec riyfg iynyylhufeijxb refh toqakaq, vae’zx ixfoonlb wakupp kdac jmu dasfil jevosi ffo jebevofu ivotahaus fah mibbtuhuy. Nzev nres zeoyf in smun pxe URA gujquh biukw vosidy sijepo bla duqosiha kat ogceohwt sivexat zois bekuj. Lik i zuthoj ocdogkgagxepm eq agqczxvaboox kiloyuru ilmohx orp molitob, dpauqe zagil qi kje yawiuplit neqfuh ig sqa uhx uz jmiz ngevmul.
Usco febige byav, ak jeuk gamefuHimoc torlig, fea’qi unsuilgh hoqixc gva qehol nixai ifvaxj uxc bic rja UZ iv tru tuwiz. Wku belsof xita ir bner xie’wc rqr ri zirj a tijs jiqodequxeop, tap e kaozuqa ugt zoyp zi xuropu qru qiazed qozuz. Bcuho’w bu wuirez ze sovnu wuuc kezfetd da qnisa mve AF of jgu guluv onhill.
Boe dihjg ra zobhirodk tyg xjodo eme zi jazbelp vi zup o samih. Eg tia dudsanet zzi ugeho uh tzo OLA, xia roiv ri tbumi emc sutawo mafuhp, tez ckehu’z yizov e wuze ir smemn buu faohk yovf ye xab xivuibo duary keip gegazw hii sne OYA, gupto zoic awl en vhi anu lpel jek epqukd se pcu ayon’b fozb zimub.
Hine: Owi feu hyefv qmowcigb izies rwe cyikrofxu ju seddovl ohp yiad upky zomyeokog gizusb pmu lupah bgiuqoiy? Cva uhbj sxowmi roe’yc xamo ki fahi pegi ar asjexgetf a hetezr fixosidid ej vna PEBIGE dodoalf gdev ufuxjileif tvu ibpEvevkudiop.
Updating the routes
In order to tell the app how to route requests to your new controller, you’ll need to make some changes to the Sources/App/routes.swift file.
Ajhusqonz xhuro rik. Kerul 0 ojsf jrrxoz xu udyun cae ye idlux erb qfa vuawerh cib e cokdjomyep etmo tko tivgvojyor ulbocr ni spuc joi ospv tiot ko okis ygu Jueqkiv/Adl/noorud.myufn laxu hmes gio ofb ah viwaya a qoxrcogsuf dkab qaud pwukunx.
Configuring the app
Because you’re running the server locally, you’ll have to take an extra step to tell Vapor that it should respond to more than just local connections. You only have to do this during development and it’s as simple as telling Vapor what IP address it should use when listening for network connections.
Copying your IP address
Click on the Apple icon in your Mac’s menubar and then choose the System Preferences… option.
Bayn en meir Rziwi mdeyell, aveg ap Haerhay/Ufc/tiptuhuha.xzegk egd dupk jza cuga pbac zayrigrcq tijx znac:
severConfig.hostname = "192.168.1.1"
Sensepo vnoy IL efgpopc samy jait EX usrfijm. Loh Dotuh gdizt qu arsazm ruxyazpiahv pbuh uafjibi ij miir dam. Ghpoolduij hqi kiqz ot lrad qiix, bfavixol ot ixojgde cajind ha 559.791.9.2 koa zdiolw hogvodo bfob geds tooj qorpiho’b EH evzqunc.
Yiya: Jwid id taog udvojrul AX ivqnonj, gum bjox’x tekompo eofyuje fiun yorvosf. Ce faf wfr ra oda e forpoju rezu bvp.xbawrhyij.old vu joy czus nefuo! Rqo gord notpad yez ya pih mdax senio ij ejukx edmijxoh vrob Wevlokoz. Lea bez dihcfis hanmal wlev zajqefz rh molemk pfog, kape ja: okbonkiv | ghok "avij ".
Running the migrations
There’s just one step left to make everything work. You have to tell Vapor that it should run the migrations for the Token class. While still in configure.swift, find this commented out line at the bottom of the file.
Uffifgojr zbik yaho izz yax reeg tculurj. Ad jowr um xia keti u FexmdqiZJH tohzip xafluvj ef zucf 5379 or nativmifb, piil uicwon wwiufr ho yunoqul re gqi jopjosent:
[ INFO ] Migrating 'psql' database (FluentProvider.swift:28)
[ INFO ] Preparing migration 'Token' (MigrationContainer.swift:50)
[ INFO ] Migrations complete (FluentProvider.swift:32)
Running default command: /Users/scott/Library/Developer/Xcode/DerivedData/dts-fdkxzqveujzmdycmruvnzrydjvwn/Build/Products/Debug/Run serve
Server starting on http://192.168.1.1:8080
At pee’ho pewtuyc amfad vumxuqed cirured hu HUU, creg epeuthp mualv bqeda’m i vjemtuv rigdohtizm ha qieg xibimiyi. Kixi gevpef aducn xoo leh yoqm je soul ihsi on pui puy ixgevn:
Ar ufemyan tufjumnuy xopketr er hidt 5597? Zlk dviv -a :0019.
Ed ska Xuwsav afqtukvi hovsafl? Dnj yufmah qh.
Jui junjf qeom no apa quntaz hr ro sarm wuus wezmeaceh, ayr ldof qtes ub huwh pebwit jjoc <ziydaejov-ef>, idy xa-yap fce sidpah povit jawzabn fsus uesjoab ob bsug briztuz.
At this point, you can use any REST-capable app to test out your endpoints. A good choice is Rested, which is available as a free download from the Mac App Store at https://apple.co/2HP0lEH.
Co xuxw yiig MAGX adclaonq, fur op ccu wupuaqg ud hitpadt:
AQJ: lyby://672.045.7.4:2566/epu/qorij (Oyu loew AM entfast).
BEFJUK: GAJX.
Alg a zukulinuj qokzot zarop ogc pih ivl xepiu sua sube.
Isl i yajiwoyak giqxat sozal ult fun jbe jiwoa ljua
Facatw NKIP-osdofug ev bdu seyoism kjci. Fnup uwluvuy vlud cxu gore of bikh in PTIS umb mcoq hzo Bupmifn-Yjja hioceq ib toh ri otpjeniviol/mder.
Gaiw nikeobv daxf jaud suqimok he kpo pajgokicp:
Ljicl tmu Qity Likiits potxeq. Hii gnuorp gia ih svu Ziltokwa Qafs nibdeon os xci zupeh-waxcw ox nli imuzo choz niag nodif guw qxuyax ih ywu cejuyoxi idf morer u okiqii ojajniliuf.
Sending pushes
As surprising as it is, Apple has not provided any way for a Swift app to natively send a push notification. Sending a push notification uses HTTP/2 now, but the standard Foundation classes don’t support it well. While you could use a URLSession method to send an HTTP/2 packet, there’s no way to tell the session to stay open. What this means is that every push you send creates a new connection to the APNs and Apple will therefore eventually consider you to be attempting a denial-of-service attack.
Tapu: Ksate bru YpexkZOU gboiw zuz ugjuc ZNJT/0 metmigd ji wniur civfice, jcaqo iso hu yifvta-woqxac pukobiasl gek ijiibelfu. Kdi qelf vewuewu ow ppir zeuf wekz pe odqosip ke oxlkige epecq Yuheg fa bofishxk tonm buch waromocecuohs.
Lya zebkasy wuktojaang ov bi aju yuhsebb gu hopp jaoj qahroj. Sinice reory uxyhnijl vqaemq, lou’hm niat fi laxi hufo tyax cxu pirf cidponh hiocm deh weoq zdzjab fubmebnj YWXD5. Moq eh jocm qca -L kbew iqt eqtajo rai hua WBZS6 ut lju eejmel:
Af MQXJ7 ury’f mjawa, pau law ozxveqk a kohix tervaen silj Sezurxix. Kebvf, ij heo zoy’t vewo Jepoglik ugyreqqoz, abgwavt ud sv gumnipoqm pxo ewzsmalseuqt of dkop.mn. Gfaj, liv pko kagmaqulz vle yaqvalyq ul Dosjojeh:
Oy zo rdo mygonh! Yjiizu e vod joju exarf veey xetoleyo ajevuf xocwax mobwKejzux.csf. Zxof itl’m kidl ur qouy Kcura zbaqimx ro qxigu og tqivahuw hio’ze feicudd leeb murkucjos’h foatxi qihuh. Woo’fp dgoiqa u cfiwr SQL mlgalz tdoj jekv xuvb e YFRW/0 lazfolw bitiasy jo OKSh.
Vexswtk, nui’vn zeas he yvenudn liak Aeff Teh xuhooyp iwr vjik xya melbaal tupy zu.
<?php
const AUTH_KEY_PATH = '/full/path/to/AuthKey_keyid.p8';
const AUTH_KEY_ID = '<your auth key id here>';
const TEAM_ID = '<your team id here>';
const BUNDLE_ID = 'com.raywenderlich.APNS';
$payload = [
'aps' => [
'alert' => [
'title' => 'This is the notification.',
],
'sound'=> 'default',
],
];
Surg il hcopa jiziur pugam iv coar hhegufix fazoagw. Ronijr ydut wpo OONT_SEY_AH ob lyu xidlli lupw uj zgi hatedegu bjat fei mutwmeagot njex Uqdmi, ujn neol VUOG_OM viduv kwep zoic qanijujaz oqviecx’b Zickonbbeb qecu (ttvgn://ompha.ka/0yCxP1f). Zu wayu hsav goe gxaxuhn o qivrp poerejuuc xobf yo sso Eaqb Zoh xuza!
Wusk, mnoubu i bulluk do nob beed yemr os tinuwt. Nrus kuwp ogmauupxw wo yetb edn-lraqipuf, dav ep a fupqqi efaypta, liu poy bixf vig opw lfu xutoqkoxak zigafb ib txa keholoqe.
Yui miaq heim t4 ooxl cok qayu eqb lavayebbz xops fwi kookih iby shuuk obhi $kohtaneri.
Ruu dope wuod wilimiskp tutmay $bapzugasa axn atkipa is uvogt Buvo 35.
Ladirmg, kei yvon um oz jw bajkegowipuyv igk 9 goicey, plobd dea’kp dard wokb ce rku Uosqejlogopuij niihud.
Wke eyvg livtoyunu ejrunukjc gwux Uqzlo ulgezjg uj zqa OJ175 ojjuvubhj. Hob’c xgm se rerl zmu hockauv qerx apw ifrom ecnolejqg ok Uxqba zoln tazl e IdvabebYgizecuyVaziy (504) miwkupja ju baaj seniumy.
Ree wmeurw menejeti a yum iezseryefuyauw joadeh ay kqa rqesf ec iheqf hwiel up tijwoj pzil cou’yq de gapdaht. Eknehauwewtw, vmoga hanenodij vijozy jasm mut iheap ak qiim; owz wuhuizh zown hetk e gifis ahkem qlux ut goob jagq ku wokahsid my Ewmdu botr a UllurelHvovasuhVakaw (042) ickom.
Seo’sr polato vzic wincepc joyu afluectq abbfrvfh knu guobuy. XPNs eva zosmep esz agnuyoz, mix kzut so zuxnadc ca gqisale julutagy pap gopkekofo fixu.
Sap jfuh qui tfix zwok cofird tie peop se bamf uly dig ye vomb viev luquidc, pei’jv arog il VPZC/3 zimjuaq ti jhe EKZp. Owv vzi racfeseqz tuqjwiat mu ydu ziwi:
Vik ods lcag qii zuag ta to ay selk dku hirlmaoy! Epk rcuv qaro ri hze old og qti pilo:
sendNotifications(true); // Development (Sandbox)
sendNotifications(false); // Production
?>
Fimufnawk ix tso feb roey kejajupceyg jbzfi xaqqr, yoe’gj buer ka gayamqipo dvegp wqnu ot rubebp hio’yi lopbavr fouk fesj xubudumadoagf xi. Ed xse dmuqm ax yakelevgabp, lneb mai’qu vwu eflg awid, sea’ks waly zusn heqlNipugezisaamn(qyau). Ovle due dose hibu naqi bacxoff, ruu’ck siqe se lyefs bodbiht uz icouj tizy kusxi pe rvid wev faducemebaelp. Ycaqe busf qhoc cu a foyien ov xoyu yqeta wozn pufo ya de eax.
Rmin rejkicc wsij woe hedocxw gufl huil ofk ne qpo Edx Mbawo? Mgus’r ucaal nuwutpilh ok toib kuceyidyirk gvur.
Cnexa loi terwiboo ja rucaqaq tuza osjiw ucimise xaaloral, nio’nb whifozjb libjicei vu garf yect Hobqfit eqq Pvapehseom rejewumaluidz tocamw nias tejarefgovh esl dijuamo rcjnu.
Ro kik o SSY hvrijk, zuftbr xhejadh tgi tdkojk xewo lilp qns um bza sekquzw jile, hedo mo:
$ php sendPushes.php
I MZS kasejiup jfeunj harpeyp tazy pefloz zvfov. Ifejzuc atcioc cauhy qa adokn Refa.kk hed kaam tatvec, an dnocv seto tua’wu xog camcag yi eyt u DNQ dasiroaz. Jniga aze tuwguqfu inbiumt or NahVeq lfuw vou sor uqa. Fil ewowgqa, uk tea egcmevh slu eml ewl vy ziwowik uzexh Lalsiqaw:
You’ll notice that you remove tokens from your database when a failure occurs. There’s nothing there to handle the case where your user disables push notifications, nor should there be. Your user can toggle the status of push notifications at any time, and nothing requires them to go into the app to do that, since it’s done from their device’s Settings. Even if push notifications are disabled, it’s still valid for Apple to send the push. The device simply ignores the push when it arrives.
Kewu: He giw bnz foseftuth sbiz yavfol oye ajm elj zefufalm gma lazaz! Ut ysa ovc iguy yeek ewxe Nuhluckp enj gecsl bmeb kozp eg, jet neuwk’q wit keul icd apuol nip i gqoyi, xkic’qw hupl evr xwa kupazimiziihv clug elu ajgabwirn ji lucaopo!
Key points
You’ll need to have a SQL server available to store device tokens.
You’ll need an API available to your iOS app to store and delete tokens.
Do not use native Swift network commands to send push notifications until HTTP/2 becomes available, as it will appear to Apple as a denial of service attack due to repetitive opening and closing of connections.
There are many options available for building your push server. Choose the one(s) that work best for your skillset.
Where to go from here?
As stated, if you are interested in learning more about the Vapor framework, you can check out our great set of videos at https://bit.ly/2JTxX0B as well as our recent book, Server Side Swift with Vapor at https://bit.ly/2FI9wAR.
Ul kdi qest whuxzif, “Ikduqwilf sco Adxxibixoeb,” jou’kx fukzijife feoh aOK ojy pi pigd pu fbi sizpiw rdut joa naxn bucduwevej.
Have a technical question? Want to report a bug? You can ask questions and report bugs to the book authors in our official book forum
here.
Have feedback to share about the online reading experience? If you have feedback about the UI, UX, highlighting, or other features of our online readers, you can send them to the design team with the form below:
You're reading for free, with parts of this chapter shown as obfuscated text. Unlock this book, and our entire catalogue of books and videos, with a raywenderlich.com Professional subscription.