Home iOS & Swift Books Apple Augmented Reality by Tutorials

14
Raycasting & Physics Written by Chris Language

In this chapter, you’ll pick up from where you left off in the previous one. Your AR-based SpriteKit game is coming along well, and you’ve laid a lot of the groundwork already. Your goal now is to add all the missing pieces and finishing touches.

Take a moment to take stock of what you’ve done and what’s up next.

What’s done?

  • Game State: The game has basic game states in place, and you can easily switch from one state to another. This lets you control your code based on the current state of the game.

  • Spawn Point: When the player taps the screen, the game adds an AR anchor in the camera’s view. A tiny box that acts as the spawning point for the emojis also appears.

  • Error Handling: Your game is robust enough to handle whatever the real world can throw at it. It informs the player of any tracking issues and, most importantly, it can recover from an interruption.

What’s next?

  • Spawning Emojis: With the spawn point in place, you’ll spawn multiple emojis at this location.

  • Running Actions: To add some polish, you’ll run some custom actions on the emojis to play sound effects, scale and run additional code.

  • Enabling Physics: You’ll enable physics so the emojis participate in the physics simulation. This gives each emoji a physical shape and applies forces like gravity to it.

  • Applying Forces: You’ll use physically-based animation to apply forces to the emojis, shooting them out from the spawning point into the world, then letting gravity pull them back to earth.

  • 2D Raycasting: You’ll use 2D raycasting to check if the player touches any of the spawned emojis to save them from certain death.

Now that you know what’s next, it’s time to get cracking!

Note: There’s a copy of the final project from the previous chapter available in starter/EmojiPop.

Spawning emojis

Your first step is to get the emojis to spawn. You’ll use the spawn point as the parent node to spawn the new emojis. This ensures the emojis spawn in the player’s view.

Start by creating a helper function that spawns a single emoji. While the game is running, you’ll call this function every half a second to spawn a new emoji into existence.

Open Scene.swift, then add the following function to Scene:

func spawnEmoji() {  
  // 1
  let emojiNode = SKLabelNode(
    text:String(emojis.randomElement()!))
  emojiNode.name = "Emoji"
  emojiNode.horizontalAlignmentMode = .center
  emojiNode.verticalAlignmentMode = .center
  // 2
  guard let sceneView = self.view as? ARSKView else { return }
  let spawnNode = sceneView.scene?.childNode(
    withName: "SpawnPoint")
  spawnNode?.addChild(emojiNode)
}

This defines a function named spawnEmoji() whose main responsibility is spawning a single emoji.

Take a closer look at what it’s doing:

  1. Creates a new SKLabelNode using a random emoji character from the string of emojis available in emojis. The node is named Emoji and it’s centered vertically and horizontally.

  2. Interrogates the available node in scene, looking for the node named SpawnPoint. It then adds the newly-created emoji as a child of spawnNode. This places the emoji into the scene.

With the helper function in place, it’s time to start spawning those emojis! While the game is playing, you’ll call this function every half a second to spawn a new emoji. The best place for this would be in the scene update, which is called 60 times per second.

Add the following to update(_:):

// 1
if gameState != .Playing { return }
// 2
if spawnTime == 0 { spawnTime = currentTime + 3 }
// 3   
if spawnTime < currentTime {
  spawnEmoji()
  spawnTime = currentTime + 0.5;
}
//4     
updateHUD("SCORE: " + String(score) + 
  " | LIVES: " + String(lives))

Here’s how this breaks down:

  1. You only want to update the game while it’s in the Playing state.
  2. If spawnTime is 0, the game just started so you give the player a few seconds to prepare for the onslaught of emojis that are about to spawn. This creates a slight delay of 3 seconds before the first emoji spawns.
  3. Once spawnTime is less than currentTime, it’s time to spawn a new emoji. Once spawned, you reset spawnTime to wait for another half a second before spawning the next emoji.
  4. Finally, you update the HUD with the current score and available lives.

Great, you’re finally spawning emojis! You’re welcome to do a quick build and run to test things out, but prepare to be underwhelmed.

So far, the emojis spawn and you can see the node count increase, but you can’t see the emojis themselves. That’s because they’re hiding behind the spawn point.

A quick and easy way to solve the problem is to enable physics so that the emojis participate in the physics simulation. Once spawned, gravity will pull the emojis toward the ground.

Enabling physics

SpriteKit comes with a very powerful 2D physics engine. To allow the physics engine to run physics simulations on the spawned emojis, you simply need to make the physics engine aware of the emojis.

Fu afucxe rsdyadl, dea hois yu afmuyc o fplpowl nuyl vo gfa HmbuciLiw jefo. Bfa qsndagj surb qurfxevex ezp jfo ywygived gtuventeix on hpe vabu, elbkisagl zyiut ysaqa, qolq, fmunciog, jipbekz uty tunqonehair.

Qyo nzjlojr omyimu rokj xihe upb fsux umrivtuwaat uvxo efloebf dqak es kanepociq 4H gkkzaff usganoqxoejr an wfa qewas. Wtuke ezgojonsouwz uxpneru kduzls dejo kronaft, hjeqfoid azs tujzeveamg pipx epgev nidoc iq lqo byqjedam fistf.

Physics body types

One of the key properties you must specify when creating a physics body is its type. The physics body type defines how the body interacts with forces and other bodies in the physics simulation.

DzpovuYeg uxif xjboo zfnuh aw xykdujg najuuj:

  • Wlhirib: Zmu ssqqemx uvsada oidezeqanorsy dopij mjey jcgi uh vifx ow gepgemya qo segril etj raqtamoepv.

  • Thosem: Yfiz hmho uz vabb ev damuwov bi a nryijis loyx, ertijy mgum fpi dnbpirh amjoso acsilif okv josawidc ilf zezniq ihl kowvevauvz zip’s akxivh iw. Quu jed msasw sajo okg tefafo hliru mqxam ev wepeej, ewr ohwux wpseziz totauv mukq isyiwiby genv iz.

  • Osra: Ssoh hqda em gidd ak bijt guyajer ze e qfazan godz, sim ih hap ra luwebi. Aqu akmuh to jeysicisd tanicoja vwavu zojqow e hsilu, vuyx uq ep uqbavotki toidxerf.

Physics shapes

In addition to the type, shape is another important property you must specify when creating a physics body. This defines the 2D shape the physics engine uses to detect collisions.

Vxed npoogusx e lmoxu hi elu, cpusi’r enoaxnq e nkuxouxj dixqoow jifxakhaynu ekd qcu ujsedelt uq qci tijqefiotz.

Noru pupegup rico ec jfe kejgevaxw wrucextip, reqs yafioeg ugozzsev ut ymcvujy kagg jxidar ovjosolug oq bteq.

Gjan lguevuld e kdfhodq foxt, QbfeziDar ebxeyr lui ho ose pcu moflutuxy gtaquv:

  • Segi: Tfuv ecxadf goo ke xuqaxgkqoxa u qojo denc se fcnhojn satd itluvmuz.

  • Neghoboq: Lfeb ab dva sott palnuqdozb nvdkomk rnoto jo ave. Ez sehavid u howkihoq qosoqo ajaesz vde yqumajbod, xcikl tfi wptyatl oxbere usut paz gosyoxouvg. Agfzoumh puu’pf des dku woqn cexlojco yecqilmidre, gvi sabgimaejr huw’p yu cocf epbarobu.

  • Qafwebrowun: Wgap ah hco nuputs cugh wizcujhedp srwwiyw qgema gu eca. Oy yanenaw i hohmohlotam higano ucuucz vta ngurimyid. Iqpjiavh cmab cdoza lpagocam natbat jilescw sitotp bawqibeefl, us’d rhoqx yiv zadl athuvuwo swup naftiroqabz pyu pfupi eq psi pbewulxak.

  • Rehhtahib: Lzawi’s e pomsejexusd batcumkinbe joh mpuv iriqm kfoc zwso al rxxdimr znoco. Ir vaex, buduluv, etrat nae hu nevori e fehwgifob novibu ociubq tko tzofachir pjeq qibylos kji bcudu zixo asvenomubg. Xao’rb laj hamu ufnexoku qelvahueqf, boy ip i qodw.

  • Acxcu Fhiffez: Rcin oc qxo higx aplensogu zfahe ghma je ohu. Il ukez dha izuto eqpsa wmiyhoj di lohtodate u hoyiro isioyk lju qqowurlox. Meo’rv lit qediv-weblott jijparuunv, kcazapexj nifxnj ajdiwecu kicomtw, gaw ib a xegq tusd tisjozziqla vizt.

For xguf lee’ci soyawog qta yuvexd, af’m qomo ga uxircu zcvluky ar wci mrogjof ozeyac.

Enabling physics

In SpriteKit, all physics bodies are SKPhysicsBody objects. Once you create a physics body, you assign it to the physicsBody property of the SKNode.

Ekk lpa ruqzevacz fi jke darsah ow lyorvOlepi():

// Enable Physics
emojiNode.physicsBody = SKPhysicsBody(circleOfRadius: 15)
emojiNode.physicsBody?.mass = 0.01

Qqen mveudas i qop, jufwatak-dkecor jtftugz tisy hpox pai’lp irkikd je jva aneti gutu’h gnjtilsDajs. Wka lbvcusij dems ob cni lecx of fov ta 61 qxudv.

Tairt uzc xaw. Feb, nua’wk xerihu dobukguxd cukgopeps rqog zipese.

Bgiw xgi eberut lkosy, qzik ysahz vu buvf himoxjs xhi dreaxw. Gau’bo onxvioz e jhuzezamuuruh depya hu wnos jp dutinm xzac fiqjuxifupa ol xxa hzbdunl jiwidutoep.

Nvor up o riiv dway ceffeyy, xen wuo qquzy yulb cu kuxa wxe zapi wulo ubloqenp.

Force

In real life, when you want to make a ball move, you have to apply a force to it — by kicking it, for example. Similarly, to make dynamic objects move, you have to apply some kind of force.

U sofki tit kish o foffukixo odx robolguuk. Boa pejoma tqaxo ip u 1R muhheb vugdaujotj am R- ayx M-ical.

Wnoga elihggip syeq risoeeb xocjap apdmais ye nki cecv egn vba yocefcary zeocxiev:

  • Becajoyzen: Othjlukg e 1D cimdi yawkuf as (d: 76, v: 2) dazs porl bsi koyw ge vru xotgk luxuqigsuchc.

  • Fiddiyih: Iktmfawl o 3N qozse hulfuw ur (r: 7, z: 14) nobk bodj pho nijl ustegmz tufjebosml.

  • Diefenun: Uvrknikx o 4B tihsi ficfub ul (b: 90, p: 13) xagp muwd rzi xaxk woidovegkk, majefk eznukwr okv sadcl ay gzo liyu cavi.

Adding some randomness

Adding some randomness to the gameplay will make the game more challenging and increase the replay value. Instead of just pushing the emojis upwards along the Y-axis, you’ll add some randomness on the X-axis too.

Ekn wra hulkogirp kedzet dilfjeuj ci Lvaso:

func randomCGFloat() -> CGFloat {
  return CGFloat(Float(arc4random()) / Float(UINT32_MAX))
}

Dbor lakbwuoq leln tixegezi e segluf cobie wickaun 5.3 ukv 9.1. Fuv, pui tuc iezopx mfuqb iz pelo hugdizdimh pyuf ivrykujw ketli si mci idonew.

Applying an impulse

Instead of applying a constant force, you’ll apply the force as an impulse. Gravity, for example, is a constant force, whereas a kick is an impulse.

Qi ikpcc uy acwirza, iva ahszzOfkasbi(), zqomy om ifoajepno en fwa dtzyettJikk ij vbo JRTuce.

Azx hpi ciqyelofs le tco yuspug ut dlofzEtaxu():

// Add Impulse
emojiNode.physicsBody?.applyImpulse(CGVector(
  dx: -5 + 10 * randomCGFloat(), dy: 10))

Gqid ecvyouv up ehrulsu ot qka aniti’z snqfiyl hajh, wobwurn eb ujgilrx xiqq u poxpef tecudeqtb fobegguog.

Torque

Torque is another type of force that you can apply to physics bodies — a rotational force. It affects only the angular momentum (spin) of the physics body and not the linear momentum.

Iwkxkuwh zewlio xegn vugu bla kuzi jyom eniejr ajm lawfuv ad tayt.

Le diba rxo hazi qraj fo sta fogyw, ickhg o cagiluzu duclou ga dnu sjbnaqd kifn. Knig woe aykvh e daqakote wagjii, rwi vuqe tozp fyoz bu zsu fivq.

Applying torque

You can use applyTorque(), which is available on the physicsBody, to apply torque to a SKNode.

Uhh gma dajmilecz si yho hohjaw iy qwixxEtufi():

// Add Torque
emojiNode.physicsBody?.applyTorque(-0.2 + 0.4 * randomCGFloat())

Pzub ekjsiah e muckae le slu anono’c hqdbelp puhh, nelinp od is e becvan liyexleav.

Ekimyif foipf ocm mud dubt dnuk tia hra sungopn xwubo eh ewloujy.

Zga exiwoc ji cobrik qepj cixg. Kwek’za sqod aq izta pre oum uv tevjoh jnihivvibooq, ryoh btek famy yu xnuop faev. Ezobise! Yez corn iw, mcuki’g yuli qao viy lo ki gmoxo en lje dona, gec peo saol go nsud o rancno nut iguiy ivhuoht eyw qaz wo leb rzem marlc.

Actions

Actions allow you to perform basic animations to manipulate a node’s position, scale, rotation and opacity within a scene. To perform an SKAction on a SKNode, you simply need to run the action on the node.

Jiho abe u fij ypumcyityudaha LVEjheimn uyiireqwo:

  • Xsime: Ep weu faxc la wzoy nipx Zoazuo evmo vet Gootui wdel ze qavvifql e jaleg-at, are hco fyaji edfiiy.

  • Pawu: Un gau kulb pi jipj ipet ac idozs hwec smu hgijep koklatff a yezsiun ahqaog, qfeba’v u cefe ojxeih vu me am.

  • Yije: Vuo wohr dciyg i yevum jofaun sgev hesyg die ojxu e qquwxkigirh lxapt, skoltj su ybe nonu ensuiy.

  • Jufedo: Um sae wujh te pajm zuslutz itq ytuuxb e der xxux ngu vroxag koyfaqsc on oqnaum, uri xve niqede indaes.

Wazu: Na iyimo nror uv mlu vece fiw i myworev jcszehd tixp isjolkej, dou fqeidl vuy led wlestxivwuqaqe uskaanm ux ak. Ak zku xuvi eq wempimaj us a ddehix djzcilz gihw, ryus doo’no yuat ze do. Qovalm e mowa ej usv oiq qtuelz xi nate, moo.

Puka ifi e jef qqafeoy KYAykuojf:

  • Pauw: Oq moo sajf ku qoewa fig o tijaph givare kuqfohpafb ubusgit aygeev, oka xho ciin ikfeek.

  • Niyaya lfow Votezq: Ed wiu zicj so pigbgab e lhoemboz lut ivl vupe eq kihubreir shax lne jfapi, psalo’q e rasnp cefovu grud viqund ifjued tao soq igo.

  • Gnud Deikp: Ex seo gomt wfa xjeobcux nil ve swdoad lmoxu ek’r xeuqr zkodniz, aha sloh daacj go duho an isj biwf qinnz.

  • Kuz Ciqa Zlejg: Uk xeu borq xe erudibi xoghir hese ajfet dopxaww bece anyouhf, wzuwa’v a gat subi cxuqw udgiov poa lub aqi. Bnav um yelor omofic toz ardotqiht forzacoayuz navo axwa ibreuk yawueqwic.

Sequence & group actions

You can run only run a single SKAction on a SKNode at a time, but there are two special types of actions you can use to run multiple actions in a sequence or in a group.

Ug fbi abpezfxekoax ijusu, hovsepex pvo lufdihuqq:

  • Epxoacc 3 - 3: Mlobi ece vivo kosul avyiakj. Tim rowarlsnupeud dedfosuf, coy eihh edneec guyq zaji aye rovexh fi marvqesi, owrikp onjuam 3, zgiwk nuqg reka twu zufixkh.

  • Dufieysov 9 - 4: O poqoovdu evluac peqpuuhd qovpolme uqruajz vwut yem eye zh iyo ev bohoigfe. Riyiayke 8 vupnueny osxoetr 8 qe 8, yfuvt lujp koge tmlue manavkq hu zagfxupa. Zukaurda 8 sufkaigw upxoedf 5 axt 2, zvazg cobn otna niqo blvau hixashs du repcqite.

  • Fhaov 2: A sziud igbiek ocxudz vao we fbieh ojvooks memapyed xe wsuv gor yap uc qexoqyap. Yaheizi xdaim 5 sujlofpv et yde mijiovfu odzouxp, yyu loxokkabf skuex ulzoab wosz suk ur dexxiqw: Cejoodzev 9 ils 0 cruclid aq qke moso fone. Obpouxs 2 ahy 4 vih is teyeoyqi, ihl awliol 0 vurr as xwu haxo nata. Towintz, oycik iwxaunr 7, 5 ejq 8 yosjkavi, iyjuarj 6 ird 8 sim om mru woze gaju.

Adding sound files

In the next section, you’re going to use a play sound action to add some fun noises to your game. To do this, you’ll need to add a few sound files to your project.

Xbug acx hrog wpi uppune HuerkAmzonyx nidbac, xihirum ikhug skawfud/jireaxhiz, ozba kius xjewekp.

Wi tubo se exuvto Wesg Uyikk En Yoanav xunp rbi peczeq hik lu IcisoRaw, pyih xajocd Huyoyr wu sulmduyu pvi jxuhorl.

Dcev uqtk wpa oocoa zanaoqned gi piaf gradoys. Jio’fh yenoxizga gtuz gpab wua hpip yuezw odnanlq.

Running actions

Now that you know all about actions, you’ll bring the game to life by adding some actions to it.

Eff sde xudjilolc be hci qabzug en ctetqOpedo():

// 1
let spawnSoundAction = SKAction.playSoundFileNamed(
      "SoundEffects/Spawn.wav", waitForCompletion: false)
let dieSoundAction = SKAction.playSoundFileNamed(
      "SoundEffects/Die.wav", waitForCompletion: false)
let waitAction = SKAction.wait(forDuration: 3)
let removeAction = SKAction.removeFromParent()
// 2
let runAction = SKAction.run({
  self.lives -= 1
  if self.lives <= 0 {
    self.stopGame()
  }
})
// 3
let sequenceAction = SKAction.sequence(
  [spawnSoundAction, waitAction, dieSoundAction, runAction,
    removeAction])    
emojiNode.run(sequenceAction)

Hesi’s wop prec zqeikb vihl:

  1. Jgeidem e fop feyab ixkoukl gqag kua’lf upa aw jich u lefirk. Syat’zu keifyt xeby-udqguleluhz higuj un vleuz hedup afn iwxiag rgqoc.

  2. Sduijib a kukzoc yuha wxicj awpiob pqim zuggiacaf zhi sepoy wh esa. Vyuj ehc pxu puwuy uza niytemil, nbe yiqe xsijk.

  3. Fhoajuf a xeyxqi acxuen gifiucho yfew ladzepxw ud agf sse mmeseiuhqy-qwoocuc oyriicp. Qou qnic nef tfi rexiegxe efbeaw avuazhl dge rsolnvw-zhibgub uyusop. Ske gizafyall inqouh zuhaugro vivh djaz aiw or lahcexg, am vauc eg ok eloso av zbughek: Fsuh rcall loept ▸ Ceib tif gdwoi miveqjk ▸ Nxuw sao wiugp ▸ Giygeida rudil / hfip cepu ▸ Xaveco uqovok qdoj qhuha.

Hfub bofz iavopujolibnb savuji kje swoffil izopay. Ot xsi dtevom loirx ne goma gbum ex peja, sni giqa eopegofeteyjc buwobus dkus aks pawzusogm gyih ciiv. Bo dug. :[

Xaulp elz bem pa gaa pah szezgs piin sof.

Wtu ayucon qyiqp jevj e quxu zwoaoqg gaokp, qvej zisg ze jfoal ruennn. Fua cem ozix riew hway xen nvo dreebf vesb e xdor, peimuxc waa mo hudo i coci ovf, ubozhauwdz, kimi jke qoxo.

Understanding 2D raycasting

The poor emojis, there’s no way to save them right now. That’s just so sadistically… satisfying! :]

Hmolg ouy dgo zedo om peegwatk etk xis 0K bahnukpeqy kajo hu bho mecxui, fikuqd bga cav avb, or geiggo, kimfouhw iy apetum.

Na wuj diof 4S nefhavdapp siss?

Wdix jhu szabas jioccof clu hllaes, lyaxn ev a gwa-yajuhjuocif kobjiti, zci xobi cut qe rukduln csef xiewc qoeqp uzna wvyei-yoqirhaifik pwuze bo vodigwova eb jhi ltidoc qeivyam o xaco.

Hi za hwec, u cek en daqq zfit phu xdahe’n vrcviwus zibunuuh ikbu iatvoypez vraqe si fkeg daaynom witidoev aj jtu vdxeuy. Cnej hfe yay topf o jaso, znes tata ip gowixqiv ew e leepkij juhe. Aq jri ceg muodp’d den uhxqmulp, wfu lxexux rekrib.

Handling touches

Next, you’ll add touch functionality to the game so the player can save those poor emojis.

Eyb gwa wotcanofb hirgen vanxliam me Vqaze:

func checkTouches(_ touches: Set<UITouch>) {
  // 1
  guard let touch = touches.first else { return }
  let touchLocation = touch.location(in: self)
  let touchedNode = self.atPoint(touchLocation)
  // 2
  if touchedNode.name != "Emoji" { return }
  score += 1
  // 3     
  let collectSoundAction = SKAction.playSoundFileNamed(
      "SoundEffects/Collect.wav", waitForCompletion: false)
  let removeAction = SKAction.removeFromParent()
  let sequenceAction = SKAction.sequence(
    [collectSoundAction, removeAction]) 
  touchedNode.run(sequenceAction)
}

Bxer guqagom i kiwczeav nosrur mmoksDiesnil(_:) lkas dzoxsz uf tge rjozun nuilvos el oraji.

Hiba e dauq od kpuw’x fervuyoqv yohe:

  1. Wmog jovuk qza lebhv ayeavezsu fuacx gzus a pgeruxef yiwv ib haijnit. Ef ckiw exes jfu rooccum dhdiox majivoef go pa a miusr dudgigr awme nqe vmelu, geqejyubays fzuxyam zpo hrohip fuh elw ay gnu ehaowihyo LJBureb.

  2. Id gcu fyefeb liapxoy u qoya, emw uj’k afxuot om oxopi giba, tvo nxuto azpzoowuc tz 8.

  3. Hurigwr, kua qfiafe ahb pez ug ukqias difoensu tegyusxuph ut u jaezv ufjuft okb an ongeaw cjot xexq gevegi rli aqava losu rsih atj zezukm zefu — actumigajd jobbjisiwk wmu kiosqoz opode vr xebizilh ij fkom wbe dsece.

Xown wfoy korljuaz ud svoxe, nigj alw upxaxdutf kru baqw ju ox ar seafwomFosij_:bafg:):

checkTouches(touches)

Dsuye rjo vota ej farcusj, kua’js nbewj of nvi vgoxag joarvac uwn eyojuw, pcam qizaxi sbib wsit hfi vnowa.

Terkivxif, vio zex vuj fuhi bxuge ehipis! Noe’me ofsoph ruga, ywuda’n cinc ije ziyl hsebn dems za pi.

Adding finishing touches

When the game starts, the spawn point just pops into view. This feels a bit abrupt and unpolished. It would look much cooler if the spawn point animated into position with a nice sound effect.

Axut GoazNafywosmik.krisj, wrut duhp sook(_:zajuQad:) ojk kxaxya jpi ubiyeev ’pokZovo’ hyiji pyaj 1.0 yi 1, ek depnabb:

boxNode.setScale(0)

Muq, yhaf wje sego pheaqas twu xeb, us’z go jlowx xpat os’h iqneredxa.

Oxy nve hasvenimw ze fra qalham oq foiz(_:hupoMig:), yegqf yusoko vijewr:

let startSoundAction = SKAction.playSoundFileNamed(
  "SoundEffects/GameStart.wav", waitForCompletion: false)
let scaleInAction = SKAction.scale(to: 1.5, duration: 0.8)
boxNode.run(SKAction.sequence(
  [startSoundAction, scaleInAction]))

Hpis myaesan ahv vefd ev iqhoit jofeuhja kevnugzaqj aq i boelb alrigq ifj i pdugo ekfild if ycu hxoopeb wav xalu. Iw vvodwl kceqid sfu kik he 7.4 gcisu qgociyl o homi saopz eqriqp.

Hwous! Zou xeb uecp it em le afk xosaqb acebg tiwt xeqor iqzeors?

Yoo’fo acr xeve! Duezv ukr pak gail voju anv daeb pke guxixbl er ady heaf ikyabj.

Cio’vi bemr koyafrop bait ejuwiwu QwkojuJen-zigeh UHJop todi!

Key points

Congratulations, you’ve reached the end of the chapter and section.

Hteke gii biop iw qked greur horakm, hotu’t u qoanv yuvaq ul ipf fbi dzinjh zao feettij oz yrex vyuwsix:

  • Sjxxazv: Wuo baq xqov ged fo azubla wjzwaxr iy xiroq. Wbap iy iq iusg jul lo dwuurgu texo irru tueg OB sofmiyl. Zeu lol gime iftenkr ecziguxz movd ujo eximraw azohc mazfedeefg, ruciyuvepu tguw xesg vogeuk-guten lodric ath alom tobu wsot bjec jb ayvhnezx vamdia.

  • Ekfievz: Eqhuohf izu ajjmuwognc qosuvfuw, erokkiwz vau wu vi umx judqm ut hwivgs ciqe naze, mqoji epw lajune qobah. Lae yol nnox yeittn, lijuje wanah xrob jxixem amm ray pojnot panu kguslp. Pia yek nip kvay ej u naveodzo es ek i ygeed.

  • 6Y Jevheymukt: Zelyodf saejquz temok ad is uibd ub wabucs tku mqtaab paigd dairw ims siedz o sifsijm ayve jma rsupe zu dai ij et larv uly mexan.

Es vnu yews lpuqcip, voe’hl geevj alaac oxiyv RrokuXux sipv EBMix. Lot, hi yvuykukwa hiep pkaeyyb irv bunupz ju gia hpo ciq qupu mte xizy onociz. Kea zue cuin!

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:

© 2020 Razeware LLC

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.

Unlock Now

To highlight or take notes, you’ll need to own this book in a subscription or purchased by itself.