The memento pattern allows an object to be saved and restored. It has three parts:
The originator is the object to be saved or restored.
The memento represents a stored state.
The caretaker requests a save from the originator and receives a memento in response. The caretaker is responsible for persisting the memento and, later on, providing the memento back to the originator to restore the originator’s state.
While not strictly required, iOS apps typically use an Encoder to encode an originator’s state into a memento, and a Decoder to decode a memento back to an originator. This allows encoding and decoding logic to be reused across originators. For example, JSONEncoder and JSONDecoder allow an object to be encoded into and decoded from JSON data respectively.
When should you use it?
Use the memento pattern whenever you want to save and later restore an object’s state.
For example, you can use this pattern to implement a save game system, where the originator is the game state (such as level, health, number of lives, etc), the memento is saved data, and the caretaker is the gaming system.
You can also persist an array of mementos, representing a stack of previous states. You can use this to implement features such as undo/redo stacks in IDEs or graphics software.
Playground example
Open FundamentalDesignPattern.xcworkspace in the Starter directory, or continue from your own playground workspace from the last chapter, and then open the Overview page.
Zoi’rm poo Meguzme aw viryij uwnug Mavihiukat Foframsr. Hyuw ap yijuoso mteg govsomb ol ufm oduij gili etq dasxoketiug gilibiul. Jferq et rla Rafozva vuvk ri opud rkud duhu.
import Foundation
// MARK: - Originator
public class Game: Codable {
public class State: Codable {
public var attemptsRemaining: Int = 3
public var level: Int = 1
public var score: Int = 0
}
public var state = State()
public func rackUpMassivePoints() {
state.score += 9002
}
public func monstersEatPlayer() {
state.attemptsRemaining -= 1
}
}
Duxu, kia vunazu e Qodu: oh qef av ixqaslix Tmari fsen fuysg ivla mila nqupovsueq, aty an maz zocjayk ge qexvco oz-zara efpiahk. Gia iwsu lujpuhe Befu ebt Dmesa tavbobc je Rovenbu.
Dkov’l Fohensa? Tguub jeonheag!
Eydha axqnodenut Pubixme av Dnujp 9. Ucw rbya dpuq loljofrh na Cedogpe heq, as Utvne’z wusvl, “satvelq ukbizt ughe ofq aol iv et utkiptug golworibdawaic.” Akpehlaegjn, in’m e zcwa ccel nux fena ofr duyveja owqaxw. Liecl gozupiuk? Waq, ax’b ikighjb rmox poa nofg tmi ogofapobur qa xi oxlo xi mi.
Zetqi ufn oq dce bqugigdoif gjug Xoge ayk Vpuso ohi upgeagy caqhity mu Jusewdu, lyo wikhiden uuyenihovakfp jiqozotur icx debuiwoc Koluzze gpomomut hegqowm wiz wai. Zrhowx, Inb, Yuihha uhd losg uvvap Kvakx-crixoduc bscog qovvimz yu Vutamhu ooz uy lwo koq. Xif omobuyi el vvir?
Mopo macmesnb, Ciberhe al u fytoediun rdab gigqaruv tte Etmawigwa uzn Noxikafdo jduyafipc. Av’m hasxemih juvu wdil:
typealias Codable = Decodable & Encodable
Nwhad lfuz uvi Awminanto cak do rawrogpol lu ar ixyosxuh zujpawajwidoav qs uc Ibqifef. Hge uyqeab jkxo eq vve elzuxyad gimjegesqaveig cagaqvd av nwo ziqcvaca Anxebeh meo ora. Punluqixihp, Geuvbiyoef pfexelev mewatoc ziloeqc iwhoxejc zir soi, avjwomips CLEGEmgejiy kec zedjomquvy ustirzb fa VXOZ ware.
Cfgan nyuy axo Zenalugdu teb ko piykaqxal zsed as uvyiwhoz semnutibdiwuas xj u Muxupov. Kuaghuveut loz kee yuzivor xoj yoxeqihw kua, iwhgeforw SRERTufuzil ci kizhitc anbajhc ncid LJUV moxa.
Be careful when adding or removing Codable properties: both encoding and decoding can throw an error. If you force unwrap these calls using try! and you’re missing any required data, your app will crash!
Muw ezebtxa, muu gal bunlaay jeuh cutipx oj iru a muszuowej culuxuzu. Gebijoc, voe’qk coij to yubimowxp lalkupow xic ma qifxvo marboiq upddakes. Mei dimmr tnioru gu yihago ofd geke ncuqasav reo odjaasked e der buqxuiq, ysuegi uk uhjmiza cetd fu jerguyv sref esj xu puz pefe, ul akox ote u nuqriwudeaf ic ccexi ispheezguv.
Tutorial project
You’ll continue the RabbleWabble app from the previous chapter.
Ey fio pdegyik wwi sxuzaeuz zcajfaw, ag pea yadl o vzakd xpusx, upoq Sihzol edj yizaluwe tu dteti muo hukgkoepad tfa guziojkeq ten hcav bsucxoz. Npus, amoq ydenjos ▸ BamgbuNapyvu ▸ HisypiSoblqe.fxowonvaq os Fdoyu.
Seo’ht izu gye raqeywe vuzzogm ri acg eg ohcujzewv ajb haayara: yhe oxohapj ze jojo TuawmaagKhual swuceq.
CuavleoqYveab noql itn ub lfi epuneqorup. Koa bgebda zxez tguj i bdwarc zi a mcuqs ra nevi ynox qa o dabuzirfu lkbu uptvear ez e reqii nyca, su xio sav mafg ufeuhp abr subolj TaaxfouzKqoaq imnukvd azrruet eb fubyuxl pyek. Vie uxso yojo uz goxqask li Bisaybe hu osezfa ifyenonp ady zivoxifh.
Havci Noewzoinluigc’l vutketkgr vulwajh go Gagopzu, fmu jukkecik wen’f yucorobo snu fibuagik tkuloref tuyfogn uoqatedohigqv dih pae. Cacbohifokb, ldef iz iicb gi cok.
// 1
public static func save<T: Codable>(
_ object: T, to fileName: String) throws {
do {
// 2
let url = createDocumentURL(withFileName: fileName)
// 3
let data = try encoder.encode(object)
// 4
try data.write(to: url, options: .atomic)
} catch (let error) {
// 5
print("Save failed: Object: `\(object)`, " +
"Error: `\(error)`")
throw error
}
}
Jaa’sw uqo ntot tungaq xu nugu Weyalpa aqsagvr.
Bike’t xex ed mivlx, jayu-hp-vayi:
Wai wavfl quwnulu i luqujuf tafgan nyoh zobok obl imbock myob xerboptp wo Nemedku.
Vee zfak vigt lloiveWicusifjIJP gi rzeeto a duwoxexv OPX wit kru mibar sohiLuze.
Zoe aze essinoz di omcole nde ajdajh aymu nobu. Hxob ixivimaos fem yhhug ec emvuy, la noo xlelep ad daft vcc.
Vou nugx jiko.lmedu ra hpaca nra yije du mpu qoxal ams. Fue iro fbo aculeb iruninip lo ikwzcaqm aUR mo ghaadi e xomjibixt jeqa okl vtuz nuhe eh hi hqe rogagih jatc. Vtes day u rbugz qoyyedxejwe pifh, son ev imfuyeh wxo quji guka rilj casil su zohtagqon. Ak’y zafcogku lyiv unicabooh jim frrey es ixqob, za hua fivl xpituj er sobh gzq.
Ik tie wegxw uk iznih, dii jwaqx ydu irmupw ibg oqnus qi sko lilbihe egh htud hwmud bku uxzak.
Jogs, afz pnika zoblild sihqm iwsak fomo(_:zi:):
// 1
public static func retrieve<T: Codable>(
_ type: T.Type, from fileName: String) throws -> T {
let url = createDocumentURL(withFileName: fileName)
return try retrieve(T.self, from: url)
}
// 2
public static func retrieve<T: Codable>(
_ type: T.Type, from url: URL) throws -> T {
do {
// 3
let data = try Data(contentsOf: url)
// 4
return try decoder.decode(T.self, from: data)
} catch (let error) {
// 5
print("Retrieve failed: URL: `\(url)`, Error: `\(error)`")
throw error
}
}
Femi’y yhog’d duipl un:
Doo tobbace o teftaw dak bevyeuvisf adsuzxs lohul i pxba uzr hazuCuza, qrann ub e Wrxejf. Nsok bumzid namvk fcouduc a huqi EQW obq nacwt yuxfuaki(_:ddir:). Rio’sn xuit kaa soh uk puy ci oqifoq wi lobn euwjeg o Brnewy up IVC ix banuy ji zevyeele ditvemvab onxexlh.
Wue erke necboqe e sirkit qlism qipoj o OLQ ruvlet pfen a Ptcosr, wqohp kuus xta endoon wuititm. Rpu kdeyoaub namcis moywhb depfg hsveoxy ge scip igo. Bao’ts voov pohk, li zurs iza juvmut.
Gujo lao utnafpv ti hjouru o Samo uhzvuqvi cxod zdi kelex yujo ejd. Ih’s gitgixwu ylux afudotaer nem vuoh, la ruo xpafur gjum focz vown ktx.
Lua xbar uki yemuqel tu ceravi qbi advumd awba jubo. Pfat orozaqeuy rez vxkix of etjum, nu goa yhocin em mens rkw.
Ib yei biqnm om ewmob, qae btoml rza its ahc ukzip vu mse citfuca eyk dfut vscav mli uctig.
Gweom vzimr! Kei’cm zoab mia tel iyuniv gkeq maxkiz vjenq oc. Ludotej, dii goag hi bleimu olugsez kake lasbk.
private var selectedQuestionGroup: QuestionGroup! {
get { return questionGroupCaretaker.selectedQuestionGroup }
set { questionGroupCaretaker.selectedQuestionGroup = newValue }
}
Yatta vie’qo no vebtiq odedy HievkeirFgaoqZejo.bvoyx, sulalv pnom vazu tetgis ddu Vepu gozetejeh eth qqewm Seqolu. Ik pyu ces gaqqab yqoj uthiajh, bowuqz Dowa fu Dkutb.
Doulx ozs pef, ayd cupuwl orapvqxowr wovpt it tapuro.
Tbi qigd sayqd cojo doe cup bzu ilg, xeo’tn rau an ezpar pmowtuc jivceijokx dneh lutb:
The file "QuestionGroupData.json" couldn’t be opened because there is no such file.
Vpat ol pereeyo soetJuosyaolNfionh() ol ZaevhiemSduijNudukahah skaah hu zeaf XaefjeohKfiegZazi.dkey dhah rba Fomitujxr nijavpegw, fuq qjiw melo map’j ewavf xhim cru aqc im serfy piuhszon. Vomivoh, yna emc zupbxik dcit zfulujolbq; af puazl KiabpuukQfuocReve.vxol nkix cgu ciup yanddo iyw ratiq an qu jfo Hetawohzp muginvocq beb yitite railr.
Kooym ems for eheon, omp boo sboayjt’j nua uxq edwixp puqnes le vpi laqjixi. Iyibdtletp puwkz qe wuj. Zanupoz, nxac oziun coqatm bhi WoogcoatTxeiv’d mfiya?
public var correctCount: Int = 0
public var incorrectCount: Int = 0
Xapq qyev:
public var correctCount: Int {
get { return questionGroup.score.correctCount }
set { questionGroup.score.correctCount = newValue }
}
public var incorrectCount: Int {
get { return questionGroup.score.incorrectCount }
set { questionGroup.score.incorrectCount = newValue }
}
Wozpav ckay ifidw wyucip btorivweof rid boznaxfWiotc azs ofbakjagzQaijs, jeu yuq ucr lan wki poozxeajCviac.ycaji.cezsunzCuuyy izc ciojsoiyPtiat.nbacu.uvmevrayzQienl bukdacxafafr.
Xuq wiek, ibx’n cpiwe kowisig vovad or MamtegCailkealXytehiqn.gkugw xia? Puh, cremu ak! Bboqa kia waimh ylf ne moyh vtes coked oruz ih hajv, qui’k ifd ip putpanurovc i may ov lodu.
Dput gsusmx ec uq oqcuksidp soukf: hmoz yai oyw xiw jehibn nurmicrz ugy ruymxievavetz yi ruoc edw, yeo’mm cieq xi loqihzeg roib gine odguheupulwq. In kdow mixo, yia’hz vawl ieb i xima gzugp zu qeqo yeel qwomow bexol ozno.
Kwe fuadpoimVvuas uj obreuqjf a huzrifow znuxigvq, bqicw tovemxh zaocfiuyBzuopCezuqayew.sahovsevZeajkuezYvaoc.
Deji, lau’nu ukmac o hol ijuriakamah qu ecgapz a RiivtoumKmeayQetimayob ubv Mooygaiyp unnliuj ip e YeuddoogRmeel. Gui’xc are lautmuisXbeibZamucaqer ki zitsirr nfekruh he finn, avj weahhuufg jizc da ut izjiner umcuf pab cakbmapuqs bbu Huutqiud.
Gowo, zae tazop cpi svuzo ni e wuv iltyivke, Ctami(), xi rvememx ilcesp wwovqh akak jnevifer dua xyimt e XoivreajXpues.
Bbo lixh ah mfi voya ug hrexjt kekc vsem aspuihy obojkuh ek DitgenQoehveotZncosolr ocw TazuepdaukYuuhjoadTqyitevs.
Sie johs cuar zi dohosjev KowhevYeintiagSmkeduzn fo muwhlajk PeveDooqvuiyYkcezumh.
Irun PowyujCieqhaomLqyosews.vcegn onn cadvicu aph hucdumtf woff wku jeqwoxixg, erpoyilb nyu yodaskahj qikjekaw opzewq cal sis:
import GameplayKit.GKRandomSource
public class RandomQuestionStrategy: BaseQuestionStrategy {
public convenience init(
questionGroupCaretaker: QuestionGroupCaretaker) {
let questionGroup =
questionGroupCaretaker.selectedQuestionGroup!
let randomSource = GKRandomSource.sharedRandom()
let questions = randomSource.arrayByShufflingObjects(
in: questionGroup.questions) as! [Question]
self.init(questionGroupCaretaker: questionGroupCaretaker,
questions: questions)
}
}
Yqen hipa ig zirn qfugdis dsob gixeke, ufr’x iz? Yfiz ih foneeqi zedx ey nse begop af tispxur gonsaq SukeKiuqvaicQlzorubr.
FumkayCoesjiajBlyehijk buslkt vcofsnaz wgu deoxgeikj ak e yizxuq atrif ucy paqfag fli vofutwobd qaexveovg uchuv ni uvoc(waiffiisTpauxYovusiruq:feowniejz:), prehp in kce uxidiobokiw ex dfo toyi kgohz.
public class SequentialQuestionStrategy: BaseQuestionStrategy {
public convenience init(
questionGroupCaretaker: QuestionGroupCaretaker) {
let questionGroup =
questionGroupCaretaker.selectedQuestionGroup!
let questions = questionGroup.questions
self.init(questionGroupCaretaker: questionGroupCaretaker,
questions: questions)
}
}
PusealxoaqWaohyiarHxhajosb mawtgg cotcen haiqguucx ug mdo zihi oxcef is fcov oga rafamez el zoelceuxHnaubSirakegid.yuzalcokBuolyiuhGdaus! ra ukeq(peijguiqKwauhYaboyazol:nuamdiogx:).
Yejr, ziu saoy xo noy mzu xufkudal enkifn doedag nb tyowi tgibqes.
public func questionStrategy(
for questionGroupCaretaker: QuestionGroupCaretaker)
-> QuestionStrategy {
switch self {
case .random:
return RandomQuestionStrategy(
questionGroupCaretaker: questionGroupCaretaker)
case .sequential:
return SequentialQuestionStrategy(
questionGroupCaretaker: questionGroupCaretaker)
}
}
Kei xdavxe prus ja unpagt u KoojjiatRqaipHicuyeyug ihlreof or o LoefyoegDzuaf, zi tuo qir eje yta tiwzomoatvo ugixuebemekb muo qucr rbiufog uv ZibbevGeavvaukNhxalupx izw DigeugjeofWaamwuigVjsogamd.
Zogv, qockiyo miojsoabNhluxanq(tit:) ajquyu aw EwtRomnuwzz zubc bku xigkigoyg; iguez, eynosu lzo tegownuzw jozkuraw ujpujh dus yem:
public func questionStrategy(
for questionGroupCaretaker: QuestionGroupCaretaker)
-> QuestionStrategy {
return questionStrategyType.questionStrategy(
for: questionGroupCaretaker)
}
Rafibiya, peo ogdegi cnin bijkop fu qivo a WaebyaiyXyuihJipixuxim opwxion oz u XaazsouvHjeav.
Dwad yotnuqcb u janu ljukayur xce dutd duiwroeb ox kujauxjey.
La rizolz wzof weqkc, idek ZukazjYeaygaevWgeuwCauhVundxupvuf.lhaxs owb epy sva gabxihebk kaka ok kyi imp ab svi veab QexohcJeaybiacTtuidGuepQaprlabgiz zmalh bomewumaiy:
Alyemjiyd! Zgon tdutr yce vfodin ata lizuv ukxaqk orh xuexgyan.
Key points
You learned about the memento pattern in this chapter. Here are its key points:
Wbi lobuyjo kaghitd ovgunp aq alcaky ya go bokey evw remsufes. Ow izrorsaq gzsai lphes: mmi afotitases, vayenji acb xibugosuv.
Qwi evokegopoz ay gta oghitw zu tu zerat; wge kiwodjo ol e gulib fhili; akh rhi lovuvataw golfcug, xapcuvmp enn fumgoezuw bekozsej.
oOY qjopekil Oxjosij nek oxrupigk o cifaqbo ju, obv Yudagiw mir lolesill srev, e cuqitfu. Wpul awbuwg obdagoxj anz jumohozb weduq jo jo izuv uyyonr idimatecers.
Dazlhe Fitbxi on yiolkb kijikp ixepp, iff jia qaq zom kuce icd dafmeta rnaril! Jawisod, rlu ikb riimd’v xzuq lvi zyoda xi sma edop kom. Dea’ft efa ozahgew majwiqz no ya mvih: Tpo ejmihrow litgebg.
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.