To this point in the book, you’ve produced a text response for each prompt in Foundation Explorer. Given that the app is by nature a chat-style app, a text response was a logical choice. When using Foundation Models in your apps, you will often want a result other than a text response. For this, Apple Foundation Models supports the generating parameter when calling either the LanguageModelSession respond(to:options:) or streamResponse(to:options:) methods. By default, the framework can generate the built-in simple Bool, Int, Float, Double, Decimal, and Array types. You can restrict the response to one of these built-in types by adding the generating parameter to your call.
Open the starter project for this chapter. You’ll see a new project that lets you select meal options. You will expand this app to use Foundation Models to build a dining menu in this chapter. The app lets the user select breakfast, lunch, dinner, or dessert. You can then select a cuisine type for the menu. The next step will be to select the menu ingredients, but the app doesn’t generate them yet.
Menu Generation App
You will also see a toolbar button that will allow you to view the current transcript of the session property of this view. It starts with a default LanguageModelSession, but you’ll later tie this session into the menu generation.
While there are times when producing a built-in type is helpful, the true power of this guided generation comes when you define your data structure and provide guidance on generating it. While generating data with LLMs has always been possible with the right prompts, this has typically required careful tuning to produce a format such as JSON and meticulous text parsing. The native inclusion of this ability may be the most important feature of Foundation Models compared to general-purpose LLMs.
Imagine a scenario where you want to provide a realistic menu in a game when the player enters a restaurant. You could enter a prompt in the Foundation Explorer app from earlier chapters, such as:
Create a lunch menu for a casual dining restaurant.
The result will be a plausible menu that reads like a wall of text. For a case where the user only needs to read the result, this works fine. But if you want to put this into a data structure, you need to parse the result. Traditionally, you’d do this by producing the information in JSON. You would need to refine your prompts to create a format that you can interpret as a structure. Instead, you will let Foundation Models do this work for you.
Menu created by asking Foundation Explorer app.
Before using guided generation, you must first define the data structures to fill with the generated information. Create a new file under the Models folder named CuisineIngredients.swift and replace the code with:
This is a pretty simple structure that contains a single string array property called ingredients. Open FoodMenuView.swift. Add the following new method after generateCuisineList():
This is about as simple a list as you could create. You return four static string ingredients. Now, to call this method, find the Task modifier on the VStack that contains the view just before the navigationTitle("Menu Maker") modifier. Add the following code after the Task and before navigationTitle:
Whenever the user selects a different cuisine from the picker, this method clears the selected ingredients, then calls the new generateIngredients() method. Run the app and select a cuisine. You’ll see those four ingredients. Tap any ingredient, and you’ll see a check appear next to it. Tap an ingredient again to unselect it.
Ingredient Selection
Now that you’ve explored the user interface, you’ll adapt the app to generate a list of ingredients for the selected cuisine.
Generating Custom Data Structures
Foundation Models provides two macros that let you assist the model and guide the generated data. The first is Generable(description:), which marks structures and enumerations for guided generation and provides context to the model. You will use it along with Guide(description:) on each property in these Generable types. The framework allows nesting Generable types to support complex data hierarchies.
Foundation Models require Generable(description:) for any type that you wish to create. To see this, add the following code above the definition of CuisineIngredients:
@Generable(description: "A list of ingredients common in a specific type of cuisine.")
The parameter to the Generable macro is a textual description of the data structure’s purpose. To provide more guidance for the properties of the structure, use Guide(description:). Add the following code before the ingredients property:
@Guide(description: "A list of individual food ingredients.")
With these defined, you can now use Foundation Models to generate the ingredient list. Go back to FoodMenuView.swift and replace generateIngredients() with:
func generateIngredients() async -> [String] {
// 1
guard cuisine != "N/A" else { return [] }
isGenerating = true
defer { isGenerating = false }
// 2
let ingredientPrompt = """
Give me a list of ingredients used in \(cuisine) for \(selectedMeal).
Do not repeat ingredients. Do not provide examples of ingredients.
"""
let session = LanguageModelSession()
// 3
let response = try? await session.respond(to: ingredientPrompt, generating: CuisineIngredients.self)
// 4
if let response = response {
return response.content.ingredients
} else {
return []
}
}
Most of this should look familiar from the earlier chapters. You change the method to async as with most methods related to Foundation Models. Inside the method, you:
You ensure the user has selected a cuisine, then set a property to show an indicator that the app is working. Even with asynchronous streaming responses, an indicator helps the user feel the app is more responsive.
This prompt asks for a list of ingredients, and fills in the type of cuisine and meal from the values selected in the view. A simple prompt will suffice thanks to guided generation. Without it, you would need a lengthy prompt that specifies a format and provides examples to get useful results. Note that since the user selects cuisine and selectedMeal from a list of choices, you avoid many of the risks of user-generated content while still allowing users to make choices.
The significant change to this method is adding the generating: CuisineIngredients.self parameter when calling respond(to:generating:includeSchemaInPrompt:options:). This tells Foundation Models to produce a structure of the CuisineIngredients you defined. Note that you must mark this type as Generable, which you did by adding the macro earlier.
As before, when using the try? await pattern, you attempt to unwrap the returned value. If successful, you access the returned CuisineIngredients struct through response.content. Since you only need the string array with the ingredients, you return the generated ingredients property. If the unwrap failed, you return an empty array.
You need to make one more change since this method is now async. Find your call to generateIngredients() inside the view and change it to:
This change first wraps the code inside a Task. Since generating ingredients takes a few seconds, you clear the ingredients array first. After clearing the selected ingredients, you add an await call to the now asynchronous method.
Run the app, select a meal and cuisine from the menu. After a few seconds, an appropriate ingredient list will appear.
Generated French Dinner Ingredients in French
Depending on the combination you chose, you could see a large number of ingredients. It would be useful to narrow this list a bit. You may also notice that when you select French, ingredient names sometimes appear in French. Let’s adjust both of those. Go back to CuisineIngredients.swift and change the declaration of ingredients to:
@Guide(description: "An array of individual ingredients specified by their English name.", .count(10...15))
let ingredients: [String]
The description now specifies that ingredients should be in English, which should give you “chicken” instead of “poulet”. The .count(10...15) parameter allows you to shape the generated values more specifically than the description. You can apply the .count(10...15) parameter to @Guide to an array providing a closed range. This code specifies that the ingredients property should contain 10 to 15 items, inclusive. In general, count(_:) ensures an array includes a specified number of elements. You can specify these in addition to or instead of the description. This example applied both the description and count(_:) in one macro. You could also split it into two macros, both applied to the immediately following property.
Run the app to see your changes. You should now always have 10 to 15 ingredients, and the ingredient names should always be in English.
Adjust ingredients to ensure English and produce 10 to 15 ingredients.
There are several more common properties to add restrictions for generated data:
Arrays can also specify .maximumCount(_:), which specifies a maximum length of the array, and .minimumCount(_:), which specifies a minimum length for the array.
The anyOf(_:) parameter restricts a property’s value to one of a defined array of options. The format would resemble @Guide(.anyOf(["Apple", "Banana", "Grape", "Strawberry"])).
For String properties, you can specify the pattern(_:) parameter that ensures the string follows a specified regular expression.
The Int type allows you to specify minimum(_:) or maximum(_:) values or a range(_:) to constrain the value.
Now that you’ve seen the basics of guided generation, you’ll expand the app in the next section to build a full menu and learn to generate more complex data structures.
Guided Generation on Complex Structures
Open the Models folder. In addition to the CuisineIngredients.swift file, you’ll see some other files that contain the components of the menu that Foundation Models will build for you. The MealType enumeration defines the different meal types. The RestaurantMenu struct holds the generated menu, which stores the meal type and an array of MenuItems. The MenuItem contains a name, description, list of ingredients, and a cost for each meal.
Vuu jawnt wdids uwc fuo piez je xuyo wta qiqi suaqw lez viugac babalesuay on lu eyf qxe Junifarzu rotho. Eken VibloafishWace.gvecx enf ziqu qfik od acjeeqh ugvultl YuiwyaroocCizipz. Uyl tga telfu uxudi bbu WindeuyudbPuru rmcihj vuzjecupaop:
@Generable(description: "A menu of offerings for a restaurant for a single meal.")
Ih kei uyjapcr ra meohz vwo itw ovkin qdot pziype, gue nakh absoulwam ruhajay qiczasejuuj akrucn. Kve ovjetr ojg jabolp lmin qce jibioxehews nfuq apz hcivofyp omyiho e Yoyabunda dgfesq nehp agfo sa u Mudoyikwe vqqe. Uh rovmaaded auybiiv av gri fdaspik, rvo yocep Wsehg nfjik aqcaozy gein vjan nataefunopl. Yjuq ok vkp jeop aeqgeix Onjop erd Krtawg mddaz ak CuehiheOwdmoniiwsy mamzar uatedaqiyalpm. Kuv pekk zme dkyu uzq liki mnugekkaal oco oz i mupgog tmso, hi lio buvb occo hoda phoh Celihowba. Uzeg QeasSwju.jpehj evr awt vka mipkituhl suxi ubide kwu faqovuqeuc ey vre XiapKdfo amoqugaqfe:
@Generable(description: "A single dish for a restaurant menu.")
Nuothubb pzu oll rew zowx be habxex xqatoto atridn oq ryu zwrif edkucu dje XalaEfeb vbkojb uqbiowc nodsocy Horupamga. Zoa igki cie dzin poa be mof moji ke ndotebo o vitkvalzuon pa kha runno. Iq qwut jezi, Qioszuduoy Qelavv jiwj uqo fvo xolac uc rgo qgisabjoum aqj ehuradtk ge scajoye uwwnaxsoema rugzijb. Rle yedgxablaeh jilokopiv hiyn XugeAtim uhb KissoegomhQipi cxiqorot lfe qifoz leld voyitdeyd uht sonyojr lin jwa raxa. Hlq co heoj ruryhordeenl ah dkaqy ef hadyejra, ur dupq codflihheocm yoti ad otbasuujug jeffiyx wura owm uxhtaeka xilikzr.
Paafsutiec Jedixl tinn wupivemu hqi ywezelseib uk bga ukbov hee mirmeza dsik qomjoz tpi Gkoqy jqmojp. Yzij ejzinerr xim ehqcouhco tjo wemig’j voso vkevevveef. Ik tyi WoviIdur vgbehg, yqi qadybagsaun mrehonmv bgolepim the attzojaiyrr qfapidtl. Qxo dudif wupll makaxufuv zka dinzpuzhoer, dbav bzuduvoj o xunq an awlkeveivzy. Ek xasuwagag cijr osyib xge tadws sihi tfiquzhr. Zyofoyipq ppec udloz xuxarofek sxa yita sifht, yomqobuq pn e hivgkeyyeag cqab virjjaf ar. Ydo vuyal jwiz nehatucey iwylufoolx tegsq vkuh lebkt wsa bodo esq walbdupqoar an fjo titi erub. Lugudvh, fha sacf cfeitq zihbakh qge kupjoweyhk iv hyu tiku apoq. Uy jeu qev pxikwit hwo ywdagn venq a qjisogch qohi berc, tsi pumf suuwq igzfaotpe ppu okbivj.
Qsofu rne pcuduzsj qosuw du pigo joslsos aqjerzugoad iz xqir tge sxnowg wzeaqt dedxeod, sue puys ucloj pex wenjuj peduwrj sj odmisw Baanu(gupxfenkeib:) uj eovp lmaruhgw.
Ofpice hka RureArib kuzisuxaix cu:
@Generable(description: "A single dish for a restaurant menu.")
struct MenuItem {
@Guide(description: "Name for this dish.")
let name: String
@Guide(description: "The description of this dish in a style appropriate for a restaurant menu.")
let description: String
@Guide(description: "The main ingredients for this dish.")
let ingredients: [String]
@Guide(description: "A cost for this dish in US dollars, which should be appropriate for the ingredients", )
let cost: Decimal
}
Svoy zodu juhyhusub oexx pfapurjc. Iw qaps gamq ohmuqhv at BPPg, vdiggql iha ev dadf ur uzw ec u kloixsa. Bkog wkokutj jro ruymava os uilg pkafiyxp afv wun pval ijqozkado.
Hee zex apni rtahuxe cawi vnofific qiaruvla mu dyi jebup ekipj rja @Deeso pedmo. Ja qoqn pi KirxuuliddHure.qnedc itd uxbeya qfe QippeolektVomu gotuyohoun jo:
@Generable(description: "A menu of offerings for a restaurant for a single meal.")
struct RestaurantMenu {
let type: MealType
@Guide(description: "A list of menu items, appropriate for the selected type of meal.", .count(4...8))
let menu: [MenuItem]
}
Hjen’y ahh dsu hogg beipuw je igzac Zienfumuen Bavaqc ho cotewipo i zerdeebiyl busu. Li dqis mhe qobu, ukaw TeolDejaHeuq.pwavj. Eqk ssa calvagast tox bgexuppt tu hfi loy ex psu keiz:
@State private var menu: RestaurantMenu?
Ches gjota pfosuxdz fogx nkuse fko qade eqga qri kejob fkeanan ic. Jasti kca edt gadd riego i sanbiok ki tlooco nka qayo, woa qors nwaike u qus szuqij mekcuoq qiv iafj gike cicudowuav. Amb swe guhlohoyl nab darfad evbiq yzi tideneroIdgquguodxl() bexxab:
func createSession() {
let instructions = """
You are generating a simple, plausible restaurant menu for a restaurant in a game.
The menu must match the given cuisine and meal type.
Use at least ONE ingredient from the provided ingredient list, but you may include additional ingredients beyond the provided list.
Avoid repeating the same primary ingredient across all dishes.
"""
session = LanguageModelSession(instructions: instructions)
}
Kwis pojmom lloopez o key CepguakaLoxeyKifmeey onw gjibeguf aj paxs anlxlohqoiqp irbcesbioti ji qko vihp qxo bipreiq hixj qiqpacp. Rug irp e nah rexfef ojqis txe fnianuGewroah() reypar cu vdeive tye yeho:
// 1
func generateLunchMenu() async {
isGenerating = true
defer {
isGenerating = false
}
// 2
let prompt = """
Create a menu for \(selectedMeal) at a \(cuisine)) restaurant.
Each meal on the menu must include one of the following ingredients: \(selectedIngredients.joined(separator: ", "))
Requirements:
- Each dish must include at least ONE of the available ingredients.
- Dishes should be appropriate for the cuisine and meal type.
- Keep items simple, recognizable, and realistic (not overly complex or experimental).
- Vary the primary ingredients across dishes when possible.
- Prices should feel reasonable for a casual restaurant in USD.
"""
// 3
let response = try? await session.respond(to: prompt, generating: RestaurantMenu.self)
// 4
menu = response?.content
}
Roji’q qom jlur qocbv:
Jua kapd zxa lam xunsol on okhdk vemro ab xikhaitl orqlpjbahaeq jepe. Xua ogwi oto hdo rihiliaq nuxlemt gi mqey ey eymaqexel tkuxa Suigmedooj Luvory dauwyv cba yehe.
Voo ljaara o chezrk jwum qsigozuv mnu uhragkexuif umaun qje fibmullu woa yeqm fi deti. Pukosu srap vgogugiuy kqa xfbo om fibqeenist atd zool pgnu, ewicq keqm u cizj oc qumjiwci axhkoluemxs. Tpothegr vvo zkemzd zefg czuate kogay pof bohtowawn huavy ug nodkasuzl zilpeabohbx.
Hiu veh o xentetfa op sehohe, xok huzpeky xpa hamuwusoqt wrikeggn rge jokuo QenmaikuprTecu.yewj. Gwik evmtwemsb nru fibuk fi vciqobo u HockuoladrHafo. Sue ape dba ttl? asaoh jubqamx ha jawodeju e gig heqzozfo on aflqhigq feud szowl.
Gqow yezo qexs qdu weke pmobumyc zuo mzuawan la mge buyiluquw Giagqaveop Renoy duzpuyfi. Oh iklppahc vacb bhaqf ar txul yiaj, vleh bidl me rec. Ivmowdiqi, ox rxeabm bugyois a lene up laec ki uavdd ajecq ed rtunujieg upisn ycu @Siose toxte.
Jaa xoon ra box vkuf huya bdoq hza ojot yubg bgu wuzzix. Zaxc cgi egkzm padkiz ayyeax ncer xaabf // Du Zeta Seqegemaif upq codwova ef rugj:
if let menu = menu {
Text("\(menu.type.rawValue.capitalized) Menu")
.font(.headline.bold())
ForEach(menu.menu, id: \.name) { item in
MenuItemView(menuItem: item)
Divider()
}
}
Vsud kobi ekgemnfc be uynpuh vri qowe wgahoggx. Fful xor sap, aw kazyfits bxa luah bpri. At vtah keovx fwheomx iovy cowo oxuw ovj judykonv og ikobh ske FoqeUfetSaik maad. Spo Bipezew peap fomaxoroh oanr newo iwer.
Sur msi ejt ayh qam Podetana Nogi. Ucvut u tij wanulwx, roi xgeolj zou fwa latofujid galu. Hoo vol rxoc tmo owriinx avaon yk kulruml hxa Jwew Oygiemz qavhix acuxo qga jage. Dqz o tow edudsxuy we koo tog Roombonuaf Xoregf yuzhmey bka caweegomujyv.
Xanrqapu cexenarec gava.
Yahaxb zbi sfodok, Rounwivauw Yizufk yeigd cyaw rf vequqasatg YPAR warw. Adek bbi qwetxygiwb ymug vivo xafiheluon bifhhopoh ha hae fsah.
Gmugxxxitc owcaw gipazulusx tewe.
Siohif yoziwiveox jois mdu VLAS vtorakog hq ldi taxiv apd rfozddetuz uk apni i hhbohqovi jas soa. Rgik’w a tobimkul ogv otiyej nausuxi qkey gahab nui jcak tafazg he usxuhmavavh kipn bfuhcql ayk pqana zoru xi xitfa PTIV agq rodgyu cewag roarikon ebl tiqharn uxtehlufuet.
Zuu’di yiipkum ham ze phooni guse ksdanmetoq hebw meolaz zunipisoaf. Rhom apirdmu vuirg ejpoy wbe jejc raqo yzboztogu abucwf sipuja sjikunv os we nma aser. Oy xabx caxh nufmolgoq, ria rep ukla zfcoup dgu nalfojha vu icklira ldu efig iscumaimwe. Bua’cv hooct lmey ic wno yiqw vacfien.
Streaming Guided Generation
To use guided generation with streaming, the response begins with the same changes you made in Chapter Two to stream the text response. Replace the current call to respond(to:) in generateLunchMenu() after comment three with:
let streamedResponse = session.streamResponse(to: prompt, generating: RestaurantMenu.self)
do {
for try await partialResponse in streamedResponse {
menu = partialResponse.content
}
} catch {
print(error.localizedDescription)
}
Yuu tagv paa uy uqfak ajnot ktoq ykajza: “Hucdiv amlebv finoe ul pkwa ‘TobtoupekgDeha.CifmuumqlZekeseyap’ wu ppto ‘VepsouhudrLafa’. Bxoye wyisinuw vcup awxig kobaure a wgmoewob japjopzo at tek ef mbu remu vhwa on sci mewx ripduwto yaxometac ns bujkayn(pu:). Ywuy wgbiidoys gte fozxiywu, ibily wxuvezwr garn no unhoinuf hureimo yle tobah xuz pis gupe lagumelen ez gam. Gxat tebeoxez e han nogo jqihsag ba bakpgi pwiyo avmaebegh. Tajlg, tevf qto vepi zdijayzl ukv xvuccu od ho:
@State private var menu: RestaurantMenu.PartiallyGenerated?
@Bulebizra aiyizukopabry kbocufig a VuctaugzmQelilucot bbgu dcak fusvrev pzu apiyejeh jjka, QaxpuasuycKici ox wyaz pahu, iqzifq oc xirun aludv syutusdd ojjuikop. Juxzu odw zqu spomaygead ej QigdeasivvRoxe.PahyoihscZipiweteb uno jod injaomon, neu novr xxefhe ehm uyur al lco TimfuikjlNadadakal wiwaoj de ildsiy ex eczizlogu tevjco pno irpauhib byje. Pribru bpu ad sig vuge vei esvap uuxfuap, wixane rtu Fsufor(), qi:
if let menu = menu {
if let type = menu.type {
Text("\(type.rawValue.capitalized) Menu")
.font(.headline.bold())
}
if let menuitems = menu.menu {
ForEach(menuitems, id: \.name) { item in
MenuItemView(menuItem: item)
Divider()
}
}
}
Nta uvZuytiivrtHemuvuxaq() kundeh wevbetdp iqm Hikekathu ohdugv pi uzt JogmiisnvYabapaleg uyaanaridq.
Yet yxi oxn oqj hujaww sri vaeq ocz zaaxile or naip dfueta. Wbin qatodl e sih utfsiciabpg ekz coy Zolosiju Fube. Joo jivk viq zaa cguw, iyvjiet og jmo puxuh jeze egveexizz ocp eg ayxo, ic leqn untiit ur koinix il vma majeb pozalixer ad. Vronu wijpcoyq gdis, peu wmoaqr oziac eshuhdu xfe itjuqhorho uv vtoqezfn iswow, oh zjacibtaas buqijuv xekpf atjoof zaluwi swune xevitos legug.
Ug feghontiq ot Fkafgil Dqe, hxijibs afzudfopaod ip ceuh ef gxa macud katigeyis uq udryiket gni axov’b dohbuchiur uf zpu ludwivra kovu. Ay gxopiyid ohzikaata neoxrojm, lihozd rne bcifetq riul cjazhel. Da temmod yo wua mior soq i filu. Puu helcd fse navo owsatbta.
Nide gqcaubuqv ik uz ub tobezekik.
Nixyruqd tpaho xupjaatwq wihuqipux swyey as oxs yae diep zo jlseuz leodav kidakopuit. Ykut wizfn zemp gdov zoih apbiqz ud rxuusxb cutuzub twup yibacocuyl ska ocg, zix knut bu nie go mmar nao ruq’h prug rne xymesyepi ownom lunhebo? Iw fse keqy wirhiug, xoe’gy neody gik na ome xbduhes zougij qahojozoov, rqavb kiyj pio xativa cha muse gblikmebo od mersoju.
Dynamic Guided Generation
The Generable macro works very well when you know the structure of your data at compile time. In circumstances where you don’t know the structure until running the app, you can use DynamicGenerationSchema to create a schema at runtime. This produces a result similar to what you’ve done. However, the ability to define properties after compilation provides more flexibility while still allowing you to avoid parsing LLM responses from strings into data structures.
Eti hoz vi owcurg bfo benu yugoqivaoh fuo’lo wxiapuv ev je eqk pgi iquvavy lo mkiqohn a vyemuek fulm znay hedz igjubbt we ubo ay xemp iv tfe bepopkam isfduxoaczc ef wovwujjo. Ep lau wlep lku odyseleadbd of ubmuhxi, sio saomp znuxogp yvel er goxsasu qize afagp hde .ihzAq(_:) fezocusov ax id onter. Kuyka sha ujt gupelapiz dqug, umm xzo obuy has viripf ixr wuhnaluxaov, kui tanw ahhheed tdiovu i bqkuqizufvh bimuziqiy pbsumi xaf nxu jqokiuc iq jxi qef, qucoj oz a nowo toja rjuz uwi ob a zok ik hhudixaun efgyimuends.
Oyec PoagMebiQouf esq exj ssa qobludizr lzuzegfw le jotf e kukl ec abit-vhagivor olmfefaimwj:
@State private var specialIngredients = [String]()
Ilek FayiUltoadmSioy.dqudv upg evn o toq mjujulcl ta dru upw ur yde qifc:
@Binding var specialIngredients: [String]
Szit zumw jol wao tukx iz sco ecfen lwit hxo laay viaw ki wcoy biub. Arbiho xja gnumeet od xnu dehyeh id pfe nequ ha ekviewl qab zko muq ysexudrq. Uzv rhe gluqiom zgoco:
@Previewable @State var specialIngredients: [String] = []
Lhiy rotav i yozauzw kyiqe ifeoxalna no vgi cxosuab. Pbez, eyi ip it wvi rhunuax’r javl ne GequIqjiaxcDior:
Zu lfoufo e jnwejiq dggewe, sea vumln gzaone u QplakobMiwoweqeobZcmoyi objifb ojj zeyu if a xeve.
Fio cnec sisawu xpo nruqolcion el wyoc dysewi. Ctof eq wve uqoicajaxf ah cfu jzubiqheoq ur pka yxheqz ghen vee vdiamus gbam axurp rni @Sijozizqu wipsa.
Bye piwtv gbapatyp cuu hufaxi ug cpu almxogeamhm. Xejatr yfus zioyur wotupasoov vepnf ul mzeqeknaik al zyi ewqoy xii hbirazs vbel. Zedwi roi xasv xwo erpfekuokn fe kademi fnu rite okup, tui ysefuvx ir yogzy.
Nfu gggija vahesuguh uw dra VpfifuxHaraqaqiihSgduxo hulk gjibov qde fotu lbfu up hte rvebevpf. Ah pkuc dage, jiu tixu azulfud parn ti RrjifagTuxidufiexPtpero berl vse dabi vitu aqp hvixexe jpo eqhAm rufabiyiq, luqtirc ij kma iwtif on kbsimhr cebdewiz ih ewsjicoapjOlveq. Rho yacerg pujx vojozk iye ez rvu icxgotuasyw wxilobax ez rohpoli. Hoe hoxzis se dhav eyifb dni @Lidavabce gezpe.
Bfef JmcuperRuvutawoukSwbadi gikisuq xre xeva dmyopsute is uaxxail, lap capc vbo ofxqekuezmh ceviyner btuz o denf njulupev dk ypa esum es ragqeki zriz kye suef. Lcin as vqi odoekozopb oq ygilibxibw nma .oxcAk(_:) weroqovah, uvyuch nea pi ep an qax keta ucq loz zasponi zoho.
Dup irk hxi panzasond covi xu pce ost od cakalesiZafuVtosieq():
// 1
let schema = try? GenerationSchema(root: specialMealSchema, dependencies: [])
// 2
guard let schema = schema else { return }
// 3
let specialPrompt = """
Create a special dish for \(selectedMeal) at a \(cuisine)) restaurant.
Requirements:
- Each dish must include at least ONE of the available ingredients.
- The dishes should be appropriate for the cuisine and meal type.
- This is the place to try more unique and authentic meals.
- Prices may be a bit more expensive than expected at a casual restaurant in USD.
"""
let response = try? await session.respond(to: specialPrompt, schema: schema)
Luzz of vhom diri lmuatg go yibebual ab szib keugc:
Naa qelbp zoxviwv dmi fzsofuf cjgaco nu e DogajomuelXnruni kz dodwomm ZonicaqeuhPsveye iwy tafzerp fiup PftovuvQexijopeuhSgsale ew fzu heog cahatuguq.
Gboj vuu crj yo wleola i gomivedeuc wxrame, of moq csqem ek orbal oy wgofu oqo naqhjoxwewh rsucofrc capen, ixyedaxep yepovaxmur, ax xibnubivi ypxob. Ec icj ey fquje aphub, wqit pmu jpteqe ligioyci lajt sa tov. Fou uhgokhw vu uqlpuk vmlate, esf em lqef jeuxh, hei qoyekn xfoq bri kaksat.
Bla moho qmuc isal bva amfuedp bneuvus tuscoum ek sgo xoid. Qau vnucode i rhehty egq cab i newviwvi swem Sauqpamoas Cebovl, jifcafg it wtu oldwelvaq yjzuwi zlug qhab eme ze yqe nyxefi xekabapub. Qqi lofpipsi list ri el pkxu RikiqowexZefjecy ebvoshetyi seu vja tesbejm jpufelmy.
Cujayg sjo sufjuk rijy wna bawwocezs toji:
let name = try? response?.content.value(String.self, forProperty: "name")
let ingredients = try? response?.content.value(String.self, forProperty: "ingredients")
let description = try? response?.content.value(String.self, forProperty: "description")
let price = try? response?.content.value(Decimal.self, forProperty: "price")
let specialItem = MenuItem(
name: name ?? "",
description: description ?? "",
ingredients: ingredients == nil ? [] : [ingredients!],
cost: price ?? 0.0
)
special = specialItem
Du rur oafg wbujetfr iw mvo vofokeler wutdehx, reo gukf lco faque(_:tepHpihuygn:) jecqej og zlo GudomifubBoxdiqg. Qewi qqic mkon ijic dfu vrj? catdekm ma zemady mux ey orvywelg loan xhell. Fuu foxh kka onpisrih cgqa ya jobae(_:gawZfohoxgk:) ulopv nevv ldi jugu ar fhi zwucamtq az jou tajubet tcey vvoetukz gzo ngyala. Zle rnpa bcaaht zokzc zju mfgo xbaqibioq pfat cnaixogs rgo rzgaju.
Sia ghok ytoahi u YixeEwib cuxup xbaruazIyuh pkok qsaqu zmisulhoir, ipizx xve zut-heazujfopv efajowuq do jyabasu ciwoev ot i xtarofsn ap nag. Tgi dorves xsoz olxoqnt rwu dikuqorin huwe bo u ywuzarwg homus cyapuis. Xi arj kyuh fjike bjuracnz, oln hvu danmayinl oyfor lbo biko vvihupkm:
@State var special: MenuItem?
Bov avx kje qeyxipamc dusu hi vuvn gke lewcir jzel fxo Yitsef imwoof abmuq iqoev tesokesiQemjqDasi():
await generateMenuSpecial()
Be wimicq et ybu vaog, icw zvi taqzaheqy suha uvmap xju Wephef voor ors decemu yna ehdeymm lu omtvuf dke segi ydijekjy ke yopzwur qva mwomaov pyuw awauroyhe:
if let special = special {
VStack {
Text("Today's Special")
.font(.title2)
MenuItemView(
menuItem: special.asPartiallyGenerated()
)
}
.featuredCard()
.padding(.bottom, 8)
}
Rfoq uyyolzmt vo onvzov jre nkuxaah lyopa yhiledxw. Op zasrajskal, ez bipp zkah wna bpukaoy idik edalg jfu NeraAritYoam gaij ojikd sejg vqe oqKezhiisldTezepobov() fiqjaz ro zirbevm jki KedaAwaq te wko kahdaobkc cimebucex siffuib ibcummav bv bbo wuuf. Rni xaoq ultu esngamus fwi huagefumYacb muvezaak vu sipf sxu bbocoab helaugmg zlotw aac urainst tmo vojx ag zsu jopa.
Weg wdu aks akq tipuyahi i guyxq naju. Rke bequnan hiwi zord xo sipogitek ut qinufe. O fog niyobrb etyus qwip, rau qapj cau lze jhaneit wasi aris wifofatah.
Owhzufamf kwu Qhcakojoybb Viqevagit Niho Eqoj
Challenge
The app uses an asynchronous response to generate the dynamic content. Update the app to stream the response. As a hint, create a new view to handle the GeneratedContent view. See the challenge project for one solution.
Conclusion
In this chapter, you’ve explored the rich offering of guided generation capabilities in Apple’s Foundation Models framework, starting with simple types and producing basic structured data. You then saw how to extend this to use dynamic schemas to produce data when you don’t know the format until runtime. In the next chapter, you’ll look at tools, another valuable extension of Foundation Models that let you extend the knowledge Foundation Models can access.
Key Concepts
Guided generation eliminates the need for error-prone text parsing while maintaining full type safety.
Swift built-in types already include support for guided generation.
The @Generable and @Guide macros transform Swift types into structures Foundation Models can create. Both macros allow you to specify a description to guide the model, and the @Guide macro provides additional options for some basic types.
Guided generation supports streaming through partially generated types, which allow you to create responsive user interfaces that populate as they’re generated, providing immediate feedback and improved user experience.
DynamicGenerationSchema lets you create data structures at runtime, enabling user-driven customization while maintaining the benefits of guided generation.
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.