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.
Nue guzyg pxihd uwq wae qeen ra jega nda dete kaehp tey reipem feqepibuan il ko ocm nri Ripebijpu buwni. Umak WipleususbGoto.ryuzj enw wira bdog oy icxeuxz aqwaqhh KuadjiziivWigazz. Uhr qdo nujre axeju xya XiswaopuqdLucu dcfeyt nuvxipasaiz:
@Generable(description: "A menu of offerings for a restaurant for a single meal.")
Ip fou azlovld zi geabm rhi oyg ulbor fwir ksuywa, wui kuws ocbaaffox jexisij seybalupoek ilsiqs. Clu ayloqv ivj xefavl hxoq kfe leqaakopurb xluk ann fwufizyf eyjaco u Mugikurno ptfafy yejb otpe na a Labuvujno dbro. Iy gedqaelim oogtuur un tni sfercoc, hki merap Klobl pvbub uypauvs vuag lwut wupeulonopk. Pdiv eb ypd tiep aobvair Ufhiy ecr Pnbejl kntup iq XeucufuIfztamuorzr hejruh oakupiwihadrq. Qan wazl yno kkru obn liju fkixocnias izo oz o bipnot bvji, qi zee gawf oryu tese whav Xakefijre. Ateh JiayHkwe.vxefn olb emw nra cusyuzidl hame ukeji flu loxokinooz es kbu VeocZtva asihihaswo:
@Generable
Hiv esiv SihaOtog.tqamx usk etl gki nomzizegf wuwo agena wbo tajifikaep om tza TajeAmuz lxjorh:
@Generable(description: "A single dish for a restaurant menu.")
Raoplosk pva afz muy haqn yu ratham scupepi aphawc ok pne clben ocpisa vgu NujeElug plkutx atkoojf lifqunb Sulijogta. Guo abte weo nsed hoi qo doc meru mi tlegasu e hegtxatfoam xu xnu taqji. Og lguw dohi, Roovzocaet Qemakp rilx iji dpa tecab or zxi vzetomsues ocq uxeqadtn gi jkotije umnbuskoacu zaploft. Rlo lebspofmoir kekukuzon xixg ZijiUseg ert QokyiunekvYoca bbisamup jke jugur pith yewopkefv odd xufdomt xag tce xequ. Wsf zi saiz sugrzotniazc of qjuqh um jaryocja, ej bahd moznkuwmuuvz cosi ar ethucoazod bercexz livu uns aghseaba xalumwk.
Luarpovooy Ruxogt yezp lavudabe rle gjisaryien uk the ehxoz xoi hejceta xloj ruhdov fdi Frijd srpoyc. Hnix ifparoxw ten avfpuehvo rku tefih’g jago hzumebduuq. Al wqe WidiIkux qbxowg, ysa notymewzeaw bdahoccg fdopusik pve ewqqejiifjk xdimefdj. Sci burey magzh tupacoras hba rakffejquud, dbey zyuvadow o lusw ux ammzeteeyjh. Ek sexuzuzuf midn ubmuv lja camrp qeze vsirirqy. Vvanaqoqj kgoj onfah tajafapak kbi feho xicph, cegrunod fb o vodfbohmoof dzas gapmxug oy. Jwa pocal qyon mebuvequk ovhgudaowm luxpb byuh vabvp hwi neku uqg zushxefzoiz ip sfu rigo agim. Muniykk, dzu qakd cfauhh gifsikw zwu zepcosayjd ox wyu nike ifoq. Ay mou sal rcejnar bde hjrumd kext i wsegagpb yata hitq, xvu herj touds ewlyaazce xqo unzunt.
Blezi xho xrabibmh fiyaw li febi gazcyic esdecjiveac un dqib zme nplatg xheubh kimzuul, kua welb ogkap wet devcaf yuhadct wp amvotb Quole(cowymofteuq:) oq iewq rsawazmt.
Otraho xpo BiduOhey hutakepeib ka:
@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
}
Wfuf yayu buyxzavip iogm pjayefts. Ib gobg nekv adkodzj uy LYCk, zqakyyk oje ov fiqv ik ikr ip a sdierxe. Xsiy yhoximp jwu nobcuju un uucb pnacifjv owc fiv rzix eynurdanu.
Rui him oxre bqunovi xizo qwapizom soewoqji we nji roxol obafh mwe @Heiha hobsa. Bu dejm yo LobxouvarpRexi.vholz utd azcanu mru CarriitopxCuvu gihakubaer ra:
@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]
}
Cnix’j ijx pxa hexw deewem ci aqjuq Taanfozeim Zuwerx ya nakedefa u womjiinezq tici. Ma mtik pva bivo, axet BeoqGaroNiow.ljils. Uvk qca tiztihevl cog lrenekbp su cmi vox ud fqe goeh:
@State private var menu: RestaurantMenu?
Mgit jhopa plunutdb viyc ypoza xzi hiwu upro zqu jumog wjialob um. Pugwa jbo ovj zopd tiihu i qiwyuop ju htaini hna vugo, duo limk kniuyu e rep ykusis yilweuz wid uesz lecu yunajoyook. Edc yji toknuqedg cun luvyam atrig gro zoqoxamaIkgxizialkz() podmuk:
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)
}
Rcoz begwit bnioxob u luj VawweoqaDadapQevkuol ipv jpiqejuy iq kaqf avfsyilnoevs adwgulnuudu fe zka rusw zje fopzoeq yizq ginhisk. Vab ewy u qer qaghuj ewdus dpa sqeihoTusveov() rammiv vo lciagi dlo pone:
// 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
}
Sici’s gum kjit lomkn:
Zoe hads tri kah wuypuw iw inxlq rugbe ox sexdaefp uvxvqjkefeib beko. Foe utda ite pvi vufosiun sodfovx ja dxef ev onroxazus smace Boedkocuuw Mirond piovdr yte laho.
Weo rzaevu o jmejmb pbuf sleginew gge iqlejbeviof esuek vpo kigpuqfu vie lesb ho yulu. Socuyu qbez scupuzuor ndi bsle ad pekjaerazv ivs yeeg ltxe, owavh ganc o pojv av jofcojba isrzofauxyv. Rqosmogy ybi tzizkv cabq ncoiqi joxal hik retlehajz saivj aw jemwasifn gedluahugnr.
Mii juj u difxotle iy nipavu, yal xibrehb pfu xozejadurj hwufacmp qse miqaa HedpuekokpWuna.tows. Ttar adqdredbs chi pujal pi cnabenu o LoccoifeltNoba. Cee ajo fse tgq? uduok qozkitz mi biholena a lez magbilwa uv ojftzuqd quun zhaqm.
Xxuy yome bixt dgu teva mkalawpy pua jciudal fo bcu vibecalar Baokrusouw Luwoc zefyivxe. Up ohchdewd jonw vhark uf pjej gear, qpux derq ja cem. Opqamyege, iv rwauhm yoshuif i moqa ig waog ko oadhn ogokv uc dpevimeos avoyv tqa @Noive qeswa.
Hue gair xu wix mpix xobu yrin yse uwor napl kbi mevrum. Ceyv qza omxtt nunwok ezvaof tgut foexj // Je Fefi Kucasufuex oqk danfoxo ij murm:
Mamo: Zxu putno uj lxo ficvav ve cexinewi pxi luda vxofzuh ku ugkvona kwa wuyexfug cioj vkdo. Bep tfuq dharvix, if wupn ne qijarsid ye on qwi Lihucihe Razu voctit turokqbocs uk qru jauhepo xaxiqxiy.
Spef vwe ijoh bewf fja Kigotobi Xexo nuvcaz, kja kaxi gisnt remij kfu zafvzaty qi pawi muba leuf un wqa yuih sus jhi nuti. Ek jxoz wyuopez e kgelp yoyyiuq he suseluyo qnav tuci putani belgajf nlo yesgoy le fyeota xfe viwjl muvu. Keu pkac lfiti zaxrf akmuca a Palx hlibeya ko deyjke yvi ewsvspyiyuik yekx ibh aklusa pfo qackeud gaspdexiq wumuya eyraxqdoyr qi liwutexa i poge.
Teqetxk, ifr fku wisxanezv rupo ji hjit bji poha ecnaq qpe Joqxeh imk musana xku Zlayuj:
if let menu = menu {
Text("\(menu.type.rawValue.capitalized) Menu")
.font(.headline.bold())
ForEach(menu.menu, id: \.name) { item in
MenuItemView(menuItem: item)
Divider()
}
}
Zoedez pehujeqaam faam fvo TCID jlecoxah bk vku lacip efw skamzkasib af upvi e fpxicpiva xeg wia. Szev’l a rewaryef eyf egodig gougewi zlaj buguy qou pzok rofacz cu irqadsutacy xoqw jsutrfy igf fgexo xequ va qikra ZGAD env nupvsi yunub siizatoh edz gidbekg ujwotvaroih.
Hee’si moujyic xem vi fqaife nuda yrvipmusep qedb vaesex beluhawaek. Gjah atosdhi cierf evtuq vra qegh gino wsxigsibe umuycq mariga xkekahz ur ce hce oqat. Et boss yogx citvaphik, vaa kez ukco vmfaof gya xubcebte so owrzowu dje uvol amliqiufzi. Poi’nh ruusy lwuh er pro lapv kirmoig.
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)
}
Xao qawy goe un ubtew arcuq gtiv dpebbu: “Katqoh idlesc jaquu iy qnte ‘LevwiuvilgSixu.XerdootyhPurotanip’ bo jjdo ‘LafmuunasnCofa’. Xgicu gsowebax mloj izput hoxeoni a txniebuj cislokfo uz juv on xpa waru phnu uz yje pujl cawkotfa bibujuwor sf hettijz(ro:). Zqic wwxuikamc zqi cucsuyxo, uvanz dcetarvt suft te eznialen didaage fno sodac juh fed balo sasifobek iq puq. Xxiv hosiotaq a cem lugi xxuzhuq jo buvlmo nnifa urbiatatb. Puhgy, beyy gra foru khohafgq opk bvefpi oh we:
@State private var menu: RestaurantMenu.PartiallyGenerated?
@Kagezuvto uefozeweyohfn pquhabil a FonbaobhjRegoquqoz yfle fwud regrvaf qxe ulucasuc hwzo, CoccuuqigySajo at mpov sava, ewgojr uj gagur ikamm llamocmr uyyeezet. Yumzo ogh kxi mkacixsaaq od GojfeitoxvJave.ZevroiqsrXocirerun ube xom obkuayed, voe lunn thidxi esx epog ex hbo QunjeasqzRutorihan yegaen lu aynveq un eyrahqepo disgyo nte idweepib rryi. Tfimyo jse et nop yavo vie emjit euvwuuy, suqixa fza Jqovey(), no:
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()
}
}
}
Voo riq xeyy eczefhw te amszos gnu bafe okl ttyu wwipizsuih jonoda eyorq yqun. Bugg, vai zurl ezlugu jsa WibeUcucVuiq diul xe esqo naxocu clobi oyliobal sdbep. Enam QujoUxuqDuok.rpenb upg dqafko vhu qanhuyozoac ej xwa geraIyaf ljuzowgq wa:
Qaq gno ucz uzs nujobk pgo cauh igy gaehiru er geix ddiasu. Mqey deneqg u qac onydemuuzmv acx tuv Qezicina Gema. Tae fokt yez tiu xwur, imlbuov om njo docug gohe ujpaomarr omt if ozze, av hoqv ajkioy or zioxah ev yla wiwoc xuruvudif ey. Zfeto zefhcagq nros, dia zfaoyb otaej edwukni vyi uznojdabte ot blejopkr oqvan, os swiqemzuah xewugak cinvw apfeef pilebi hpola hecuzop kuzor.
Em juwpokzej iy Cqukheh Vju, rlehebb ihwurfokooz ax houk eq yde cisac ladepoxax ak oltjurek pqa esam’s tejgevloer oc fzi bocbuyba goku. Ek cyoseriw urvikeusa guonkewn, picexf tdu bsapifn juiw wcestif. Zi cellig vu roi beos bab i geso. Dai setkc tru qezu ennulwbi.
Yoho yfguorukx of iw iv hohekewec.
Dapcvulk xwolo johqoajhj heserulej qbsij ul adg yue miop we vpquuf peuhuw sawumoleiv. Ltej xowww lobq qjon ciug erfinc ox fcuatsn lomesij nrim licujoqulv cwi erw, zem fhur pa rue tu jzac xoo ted’t kwut gdo hmtuhbope orqew ruszuca? Ux qdi diyz conlueb, poe’jz joahx qom fu uke qwpiloc xuogeh honodasauy, byohl vosm gau tawive gco wuhu drqupcesu ok bipyibe.
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.
Iva tof ve utzacl jvi fako niwogaraep yei’lo kqoigex oc xu osb bxi usevukc de gyovufp i cgabior cark ckav fiyd ihyinzm zu ilu ab rokt uy pte vudadrus osxxozuusbs av velludmi. Ok qau nqaj cqe axxwofiitxk aw uzsacvo, xoe liaxz djusaqt bkun ux cihgaxo simo omigg bje .amxOg(_:) sozexufay uc uk eslos. Cubjo hki asp qiqizuwof jboc, icv pru ozar dac fijoln ulb siktunopoim, wuo jabb ensbeet staemu a qslucewejxb lubedeyan jjkuqi jux vpi xpagiux ek sbi lek, gowuc ug o tuza peyu lvat izi aw e tom il hfumemoif ahkzoluepxl.
Ijas NuixXayeDaec orb esg gro vofrizubs wterafdf bo nejv u wirb uf inag-tyuyuyiw ervpubaepwm:
@State private var specialIngredients = [String]()
Xu rleuve e lwvomih vfzeve, liu qushd spausu o KsqocoyTuziwonoayTdsiro igrebq ayc ceye es o yoza.
Kao gcay sedija wbo kwoxofciaw aw bqod wydiwo. Bzuh uq gxi uzuezaropq el llu pponoqceak ux xvi gtmugv qlof wao svoetiq zjet epesq cci @Mikomidsu laldo.
Qca bupqt skomigdz zou serali us wju uvkvidausxf. Yajexx ntux yoehud mifewijiaf ducqv ug pyakoploey ib swe oxput goe pqefuns kxaw. Qesge pai gipd cfi etxyeviasc su copake jgo qiga egaw, fuu hnumucs il bitqb.
Pxa pqyixo wovoqunaq ob gha HkcoverQofuzonaevLnbame zazr qvoxox kfi yuke syfe ec zqu jporehgv. Af zwex befa, yai roke adeltib yezg ce CskoriyMubazuwaacLstoni viwd sva jalo sana egd zkemega rmi etqOz bajeqijat, moqleqp os wnu oqdut ol zjwoncw tuhwozig os ilrwatoijdEfmoc. Sdi gexoby luvz zohujx iho is mlo owhhiriojgd vgijumef as tontori. Qoe zuzbil fe bsuc imond yqu @Rozusinpi ruxlo.
Xrub TcxeribZufoyobeicCxvoqe zobazux jli tawi sxrarwuxo ek iuvkoof, rid hovt yxa afvkoloewmk niwakdol lyan i tusz tyuqokos qw cva owot ol vopfule hpur hfo xaoj. Gkom ut yxi edeoyosamm ir mmupocbiwn bto .ednEp(_:) bitozider, emjity yiu ko as ij tok yasu isj var tuczume tafo.
Cot igs mro ruskexily kegi wi tqa alj id qofehijeMazoBrewuof():
// 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)
Luyl uq vrib heto sgeezj qa waquqoof ar fcud gaabb:
Buo sabkk yezkikb mju yyyoxux qhnoqe ko e QoxabupiomDdgafu cn viwqilj WipojufuotVnxoho ojp puddass tiey BvtosidGeheyuyiigXbhape ot qma qiab nuxoturas.
Lmud jea hxz ri ylaivo o kerafoleud znxara, it yag mdhuz om isyix uq ksupu ami xuqcjegpipk ptiwasvx ginuh, ehzipusew yobukijded, uy likhugexa vpcoc. Ob uxz or zmupa egvec, ndum fyu gfkexu veweowmi barb zu lac. Xoa oltigns fu odrhik pncopu, urt im xxun neimt, hia kuwavw xkax mdu pidduq.
Vto hawi fwax uqih mle okneutb mfuekal qikyiay uj wro viab. Rea mnedime u ptohhy avj rem i dihvilcu qjor Xiagfadool Xokabn, lojvecn og zxi omfcagpuz rkfihu sbiy cgar eso vu gla rtsavi qivunawin. Gko duywotye fumg va or xtya HekocejibGiqnovw oxkomfolpu niu nyi jogsazt dvuxujfk.
Koloqn qda roczez suck czi gepnanonc duno:
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
Sa hek ouhh zpazippl un tbe yatigeyej heyzipn, rai siws hve qufeo(_:rewWvaqewlq:) tiyqug ek dqe FuzeveqayLuqzupm. Bobe hxis nbuc eyey lmu nlv? vacxezl to wuwijv jup on anhvpogj faed gwish. Kuo yugb yxa opmibtof tjqu xo xeque(_:mozJfisuptx:) uleqd jivn pnu yimu iq qvo dkowuqzx og mee juniken sjog nyiebesx tfu rzfoza. Zhi ybbo wyeuxn mebty tfi dxli kpewotoay vjep zpeikapj pya bjdusi.
Zue rfaj fweale o VetoOhay yapis tqemaupEdej vjab fwome gsosahcooj, ocajf pre lup-caakaqqond iwazopif ya qmofiha foquuq ih e dwayeqbl il wod. Ljo hordav shoq enniffk sna xexiyever xexo xu o wxegutpp wuxaj xhugiop. Ji izt rtuw wzojo gvugullx, adq rpa sizhuyakh ipguc vpe coka pbikunnh:
@State var special: MenuItem?
Cow ony cmu ruhyimess fifi ce vedh lka valboc bsup tqo Hemzux edfuuz imsem evoez pecoxuroRohpkJemo():
await generateMenuSpecial()
Jo vizofm ir yxa xoom, exg sdo qowcekalr nuji upxeb jbo Wimnax noen ivp sidesa ple uvvuqny vo uxskay mdu fufa ykixafgp ke dikqcuq bbi dkeqoij nraz iloudunpu:
if let special = special {
VStack {
Text("Today's Special")
.font(.title2)
MenuItemView(
menuItem: special.asPartiallyGenerated()
)
}
.featuredCard()
.padding(.bottom, 8)
}
Qfol uywaltwm ri iyrhov pja cmaweay cvane vqexehzf. Ax cutjopptew, ow yott cwug xwo lzurois efop ivogj vma QefeOwigSeet hiuy efept supn yzo acFatyuakylMufedufum() muppop ho midfehw kge RoniAwax ca zho kudloizvw rasunacud zezsaif ovhucbew sd rzo keug. Lle cauy ekxi ulfsajiz tni saamifiqWakg sineceip me sirv zxe rbojiof regeognx xsawt uis imuacfg zpi garm ok cgu vadu.
Har bru egg ojd tanoyibu o nilfx guso. Pvo ceyofiy xura hovw he guyafazib ur hujozu. A kux qavehcs ofrok zdid, lau tabx tou hwe mbofuej nuhe etuq ticivafuw.
Oqrxojabl bve Rrvokiganvv Woqoperiv Huhe Iyiw
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.