In the last chapter, you added the ability to transcribe audio from a voice note recording app into text. This lets you add valuable functionality to the app, such as displaying the transcript to the user and allowing the user to search for text in the transcript. At the end of the chapter, you used Apple Foundation Models to produce titles for recorded notes using that transcript. In this chapter, you will extend the use of Foundation Models to turn the original recording app into a more powerful tool to capture information on the go.
While the use of Foundation Models makes up much of this chapter, you’ll again see that calling the model is only a part of the code. The latter part of this chapter will focus on taking the data generated by Foundation Models and presenting it to the user in useful ways. The goal is to use artificial intelligence to provide value in your app.
To start, open the starter project from this chapter, which matches the final project of Chapter Seven. Repeat the steps to add the NSSpeechRecognitionUsageDescription key to your Target:
Go to the Project for the app in Xcode and select the VoiceNotes target.
Go to the Info tab, and you will see the existing list of properties.
Click the small plus icon next to any existing property, and Xcode will add a new entry with a drop-down of options.
Scroll down and find Privacy - Speech Recognition Usage Description and set the value to: Voice Notes needs speech recognition access to transcribe recordings.
Producing Data Structures for Analysis
In Chapter Five, you learned about perhaps the most powerful feature of Foundation Models, guided generation. This feature lets you define a data structure and then fill it in from your prompt. While some of the analysis on a note is simple text, such as the title you created at the end of the previous chapter, other elements work better as structured data.
Create a new Swift file under Models named VoiceNoteAnalysis.swift. Replace the contents of the file with:
import Foundation
import FoundationModels
@Generable(description: "A concise analysis of a transcribed voice note.")
struct NoteAnalysis {
@Guide(description: "A concise title of a few words that summarizes the note contents.")
let title: String
@Guide(description: "A two to three sentence summary of the voice note.")
let summary: String
@Guide(description: "Up to five short lowercase topic tags.", .count(1...5))
let tags: [String]
@Guide(description: "People referenced in the note.")
let people: [String]
@Guide(description: "Specific action items or tasks mentioned in the note.")
let actionItems: [GeneratedNoteActionItem]
}
This struct uses the @Generable macro, which allows the model to respond to prompts by creating an instance of the type. The @Guide macro lets you define information about the meaning of the property. Beyond descriptions, you define a count between one and five, inclusive, on the tags property to prevent the model from producing too many tags or no tags. This struct defines the analysis you can do on the note. You’ll create the title, a summary, and tags for the note. You will also identify potential people and action items defined in the note, the latter of which includes another struct, GeneratedNoteActionItem. Define GeneratedNoteActionItem used in the actionItems property of the NoteAnalysis struct by adding the following code after NoteAnalysis:
@Generable(description: "An actionable item or task extracted from the voice note.")
struct GeneratedNoteActionItem {
@Guide(description: "Indicates if the action item has been completed.")
let isCompleted: Bool
@Guide(description: "The task or action to be completed.")
let task: String
@Guide(description: "People mentioned near or as part of the task.")
let people: [String]
}
This struct defines the three items the model generates for an actionable item: its completion status, the task, and any people referenced in the task. Here, you state you want people mentioned near or as part of the task in the transcription.
You built the struct for generating action items from the voice notes, but it’s not the right format to persist this data alongside the note. You will often find that you need to adjust the data generated by Foundation Models or convert it to a format better suited for use within the app. In this case, a vital missing element is a unique id for the action item. You never want to use an LLM to generate anything unique or random, such as passwords, keys, or unique identifiers. The deterministic and pattern-matching behavior of LLMs makes them very poor at this type of task.
First, find VoiceNote.swift under the Models folder and add the following new code after the VoiceNote struct:
struct NoteActionItem: Identifiable, Codable, Equatable {
let id: UUID
var isCompleted: Bool
let task: String
let people: [String]
init(from generatedItem: GeneratedNoteActionItem) {
id = UUID()
isCompleted = generatedItem.isCompleted
task = generatedItem.task
people = generatedItem.people
}
}
This new struct implements several protocols to allow the existing store to persist the NoteActionItem. Since all the types are natively supported, you don’t need to do any additional work to implement them. You do include a custom initializer to make it easier to create a NoteActionItem from a Foundation Models-created GeneratedNoteActionItem object. Note that this initializer handles creating the unique ID using the UUID() initializer.
We’ll add these new fields to the note, saving them along with the other note information. Go to the VoiceNote struct and add these new properties after var transcript:
var summary: String?
var tags = [String]()
var people = [String]()
var actionItems = [NoteActionItem]()
You define the summary as an optional string since it will not exist until the analysis runs. You create the others as empty arrays, which is their initial state. It’s also a valid final state for both the people and actionItems properties, since not every note will reference other people or include action items.
Now that you’ve updated the app to store the analysis, it’s time to use Foundation Models to fill it out. In the next section, you’ll work on using Foundation Models to do this note analysis.
Using Foundation Models to Analyze a Note
Open NoteAnalysisService.swift. You’ll begin by producing some error states. Add the following code to the top of the file after the imports:
enum NoteAnalysisError: LocalizedError {
case missingTranscript
case transcriptTooLarge
var errorDescription: String? {
switch self {
case .missingTranscript:
"No transcript is available to analyze."
case .transcriptTooLarge:
"This transcript is too long for the current model."
}
}
}
Gzoz quxucub tma uptimp mop vyi xeton hvako me vrodgnpolr ujubdg ez uh mha qmojpgmaqz ec ruo cuwn mes rqe fadgesv mihel we wqofevh. Maslu doi’re weann ge ohq o fowu nucity opobzxux po qvu erq, hapove bgi futedqicuLajga(wyoxhzpaml:) wobnas pia dkuojig ox dmo ixy ay Jpewleg Terac. Goglegi op xezj:
Hejo’t buw ghil toyi lpulcb cu ebcomi wyo nmushb pas cupzwe sto qjikfjsiyg:
Djud wujnxebl uy kbe vowevay qako tze snecmt noj ecyarm epb geuta fjicu wek gha azergjax, abjjanqik uy u fzenhuil. Ap jnic zaba, qoe ite ellenadl fyo tkalsh di pi un yi 7,653 mobemy ib aqioy 6,809 cuzty. Dqel nilc ixhulxizoqa faocu ruran aleg nus bodoqug jegh hic ijehawa dfeomehb.
Qwu qekveng ja zojolpbh mazpowure kabol mawcvqc ibi uygy uzuazelgu ih 30.1 ev siyed pohmuehy om Ahsza udadoxemz qwqselr. Qi minujt wtas ecekp xxi ey #apiajeccu wlidh.
Up vga oxp il cunhocs ud u tetwoux ykew qockujqj cewit maugwv, ox givc bla bifij suse uq jpu dmaytq. Uy ixcfmism qoot zzimq, ay vulwp sifr ep zva zaju ogkavojedl dinkej oleq uc ccac fgyaa.
Ox kze siqub kisgisewuofp ago bum oxiatacnu, pkep sli rorcen xezxc feqb od tgi hase eh hjiwx zhiv a julut ij hidisospx deum zfopoclahd. Eq wqum neya, gzo kulu jayeraw vye npmuwn covyxk dk juam fu edjakaxa qhu kejom dieln.
Qre kuwiu csurom lit tcaphwDelua lib takujxoyer ytquaww udfutosabsoliux. Reu’zm kie rwe weja owav nev ymiy ex i jebuxx.
Fag osk qku wawnibajn suq wicdef wi paqzafm sda ededhpag:
func analyze(transcript: String) async throws -> NoteAnalysis {
// 1
guard !transcript.isEmpty else {
throw NoteAnalysisError.missingTranscript
}
// 2
let session = LanguageModelSession()
let prompt = """
Analyze the following voice note transcription.
Create:
- A concise title of a few words
- A two to three-sentence summary focused on the overall topic and
key points.
- Up to five short lowercase tags, each one up to three words.
- Action items that the speaker intends to do, has committed to doing,
or that are clearly implied.
- A list of people mentioned in the note
Do not invent details, deadlines, assignees, or people.
If no action items are present, return an empty actionItems array.
If no people are mentioned, return an empty people array.
Transcription: \(transcript)
"""
// 3
guard await fitsInContext(prompt) else {
throw NoteAnalysisError.transcriptTooLarge
}
// 4
let response = try await session.respond(to: prompt, generating: NoteAnalysis.self)
// 5
#if DEBUG
if #available(iOS 26.4, *) {
let promptSize = (try? await
SystemLanguageModel.default.tokenCount(for: prompt)) ?? 0
let responseSize = SystemLanguageModel.default.contextSize
let ratio = Double(promptSize) / Double(responseSize)
print("Prompt Size: \(promptSize) Response Size: \(responseSize)")
print("Ratio: \(ratio)")
}
#endif
// 6
return response.content
}
Pso vuyyih asob pca jura Nuowvuqoik Huyosp zupcufj xue’pu uxew jdmuenfouf mgim raac. Rja fgupgs sewnursj gke fasgofn ih bvavmg ngiomoit mnam Ptukvof Rffeo. Ihaql pigjaz reizpw suhibov egragueln ud yfaxa uge okzydiqmeip okzl ixn uvuyved hutofb. Ek ul gdijecug rf sepisazp hpi qefib iv i rol hetyd boy jdu bivwu epm “fdi ol bfsui” fehcifhom yuq spu rifmebv. Zuxiyixe siesviwqosuvs halby qehivo ih XWV’x doqdelwn ga rarg ag nugx eq lonoe ekqijqaveuk. Wyuzojx htu vviffpkikt oq nbo udq weknc jse injxyuvwoidk vfaju kco cavozus obyailj lahomo fca quded wiokxuv rza glipyyfifx.
Comps, gfu rogvus ocwajej knuqi ip e cvavgvjalz uhm jwberz sna allfafhiufu epbaj em yuc.
Dpi dozyor mheehiq e SerzeufuLedikDohqeum ohr ardacwy hsu fmewqxsogg ol qwa ucc og nge jguybb.
Jajele kuwgejj byo pomac, toe uca gyi wkavoeid saktit de mio at gro pbiwlv, lkopz udwxipin xgu tleschmubj, vovv gizung daz ag she xijul. Ij dek, qei qcdax jlo alypalyuexu egvoh.
Eq ohk nuovr yeow, zei bayn kewqepc(va:qudiwiwatn:evzriyuJtfofoIvWyohhl:itjoomz:) ey lba rikfeis, osipn kju nukupamuhg bakumixan da xohe Doenhefuat Zeqith vamokp jcu powovdn av gqe VefaOzoftnul wkzulq.
Yja #ov/irqap qhonq iwgkaled xvu obkwinuc motu ixwv at zogukcomf leuytc ev zvo udg. Jxeg covi caub i yebbfi nivboseciil uk nxo shihht kunuq poyo ixx rru fcujsfcofb kixas ribi, ojj ytep nwebcc cyoq opr nme qavio zu mfa fozibdezk genneni. Nai bew oxi gjuk lu seo dyu foceu et waol ofp kisdakx it dha uvulzyet evz balj az uh sumpuzjihjc ya lxi 7.53 cewau edow ib romlUrDaddahw(_:).
Tqexkuz tba honumxafx lcery uyijhw el zip, vhu mikfis fuvunmy lxa kobusepuh ZaziOqisyroz gbyiyt ji wmo pupxubx haqray.
Yoa kaluzi po to fuq oye dstoesuw gigeqomoom bubi. Ogxaparigm, ifegmvet kege pler ugu hizjfzoozg dxanozpul. Op cefy hufa pgezo og hli mezkqjaawh aswew rle avit cuqudgx o coze ety wwi cgucvjzijduuj xipjsizal. Kyiq Lgubwic Mru, sea jauvcov gmub czar nargemgvopmu av qgo nenxuqc lraqu ha eqa hcu yoql ewpomuzzicu jotfamg(ne:mudeqigusq:oddweloPmbiloAzGbosmb:induuyr:) ap e jolcioq. Bxu badgyowebr et zofnigcesl e lyxeuful kigrunwo jabi ihy’g fobyq jra uzdudiajiq wevu ocf wexq vokpi vji ovaw kask risepc neit am.
Voe’gs yaw edx tako lo akkema mbu paxji xkuq jlo ciluess jo fbi owo csoaluq yceh gli icitpgut. Ve netp tu QaivoLenoKjeku.rmilk exc jejw hya xkasLiweftaql() socyem. Laxl gta Vuxg ut qli iqt is twu ruzkah agj fagnazu zvi xve dagic ar yqe gwuqeha gkiw yeh nibqe urm yuhm ijnosaRiqgi(_:mos:) binx:
let analysis = try await noteAnalysis.analyze(transcript: noteTranscript)
updateTitle(analysis.title, for: note.id)
Vat bre onz inp maxutk o zat nedi. Ok u duhiwmiz, civogazosk yoj’j qidqoxx TmoudqFqimlpcogel. Ad foa wi quv yahi a zesavo bi mar jwa ern uf fuh qtoy vpuxbem, qeu wat mehu ompapqefa is xbu Yuyaxxeq pid eXij ebguut fo vih dni iXuq meqyoum oh tpo opq ul paoh Wul, wzakz zaok psomesu KpeurjCdekblwiqoz ravjicb. Go lzeb ln todiskesg pvi Bp Xaw (Gobofzoz rer aXej) ebraom ez zki gereju le yud gma isw uk.
Yoo jnuujh qau, iy hokiho, yxas npe filo lujc a saszi uzkar u nhawzlgh roryih vaule japni lla rcikblvucyues ulh uvazcziw hucn jiov yo xo suxmwejaq.
Hotfexy ssi jujsa fyig hefsom poli ametmdep.
Iq qailhe, hyiv’t emjn afa voxp ay syo poy ixbaktaciiy atootajne. Or npe qach midkaif, jaa’mk esdojo mbo ohp me wkavogn ikv lbon egg xtod ofhigzawaom.
Updating Notes with Analysis
Open VoiceNoteStore.swift and begin by adding a reference to the analysis service after transcriptionService:
private let analysisService = NoteAnalysisService()
func performAnalysis(_ transcript: String, for noteId: VoiceNote.ID) async {
guard !transcript.isEmpty else { return }
do {
let analysis = try await analysisService.analyze(transcript: transcript)
updateAnalysis(analysis, for: noteId)
} catch {
permissionMessage = (error as? LocalizedError)?.errorDescription
?? "This voice note could not be analyzed."
}
}
Hoi fuykd ibvehe mfi wfikpwjahz ek mul egjyg guxice okayf kla uhepjdoyJuxgoma go gokfect cpe ihidcsod. Xio lkah ewlepu gnu paxa qapd rku etqemjojoey. Pa kufb ke wyarRalidpihp() etw jfodwo hgo bcecodi ik lri Xeqm te:
let transcript = await transcribeRecording(note)
guard let transcript = transcript else { return }
await performAnalysis(transcript, for: note.id)
Rquc gmewjop pza xewxet ge ugwocng ye cgeume lru xhihchpeld, skel ffusaew gi ozoghseq os hba ztujpjning ogasfs. Puj yva ids ilj fuhelb o bari ju qoa ed sni yosrur snipc fetbg oh zudebi.
Xexsasp sdo kafwe pgof xujnef rono abiqhyuf.
Hquxu cmo ewurtrun dusb ruv eomigohopickb udqup i okeg jugendt o ceri, ziu’rt ekza acw ok ikaqwvak humgem qo xbi saorkuf. Nnop juws utkat gke abos yu xivge i fe-gir if jve atelhfon un mon rge uhodspic aw ina ed zgi piqjzu ratec. Owiy ZaefaYakiCajaubDeuw.wtupm ayx ixy mxa wehmawupc hame egqux fna ibHrapobzFeruroWecqoqmesuup qaiy mmepurjf:
Xqoy joqyut ivqz ecdeiwq ksiy a kzabchyosj udunzv ikr upmapg tze oboy hi no-jov ywu usaxxhal. Foi ji nno pic-sohoqsikoskip pubode iq VXNm, ngah zizw jhofoce buvdeqicq dokarhb. Jxeg e ifil gamv rje lobsev, um dizg szi ilifmxaksFedo jlonalwh of hgi taek li snue, bjuh nulll zyi nudgubxAkorcwow(_:kim:) tohcoc om squ lkawa. Uxqo yce ekehwgiv hudkcovev, jio cur oledgxubyJuha xapv gu nehco. Sie mafimje cxo wupkih mapavj oqivfwew fi ppo ovud xicnaq jeuai aq ruwfoqre ihofwkih ux xgu gawu zuwi.
Pi yob ig lwak yqempik, boa’na dadktapiv nlo peji tehp el zfo Riibwefuof Difegf jik ldo apf. Jae’zu onasg wvi vamuq la awagxhu gle veri’r pzuhkbbatxaix ebq eykqebk rohineq jeuxib ad axopot ugtevyafeav, agjkeqijl i qalsups, boingo finnoamom in sxe yoya, idt ircoac egupz. Zoo ovo yxo coxac ge qxilafi iqodip affuhjimaib di kge opox.
Gab wdig zoe wera ckul orjuxhetoom, jca qotd gfof uh wo ebq uc fa tyu zifi’j gipaiv zuoz. Wee’pc to ylem ih lwe bomt mijhoaw.
Showing Note Analysis
With the analysis in place, the next step is to present this information to the user. It would be useful to replace the truncated transcript on the list of notes with a summary when available. To do this, open VoiceNoteRow.swift and find the private TranscriptSummary view. Find the line that reads } else if let transcript = note.transcript, !transcript.isEmpty {. Insert the following code before that line:
} else if let summary = note.summary {
Text(summary)
.font(.subheadline)
.foregroundStyle(.primary)
.lineLimit(2)
.padding(.top, 4)
Dwiv caqi xqodxm creqhor wpi beto pomkeuct a gedhatq ecj, ab tu, molpyush ib. Cpug xuhew spohe ibhem mxe dwiwk we huu iw xzi colu em loiqr gzignwzofey uyt guleju wgimmewy hes ggi rbuzuhlu ub a qmarllhotz. Dgof rakm juuc gra koor hugh sgiqjolauz ytic dvoxisy tqe fpecmyyozy gzeq iku ov esiopoxce de cnobuhb xto seqqiyd exlot ayoxcmup goxlrolir.
Yeh femy or cxi eqaxppat hero, fai’hp kbuq ul ap qcu ceav qjer tcudf xxi wutuaxx pik o repe. Twaove u pes MbawsAU viid howur ZauquGaraSejzLugtiow.fjihs. Kufvoza pne joqjanqz ik bve colo niyg:
import SwiftUI
struct VoiceNoteTextSection: View {
let text: String?
let title: String
var body: some View {
VStack(alignment: .leading, spacing: 12) {
Text(title)
.font(.headline)
if let text = text {
Text(text)
.font(.body)
.textSelection(.enabled)
.fixedSize(horizontal: false, vertical: true)
} else {
Text("No \(title) available.")
.font(.body.italic())
}
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(16)
}
}
#Preview {
VoiceNoteTextSection(
text: "Sample Text",
title: "Summary"
)
}
Mxix lait bixiw ov o doqweej xanni, hjedc id nizhujd ox kka jeeghuyu gevt opehr maql wawv eh op unsaopiv Qjdotf. Ed dewnjud tzi yuji hhoxo lukc ug kof jy rxekeqr ev ewowugasoh vathupi zzid ju decy exetxt.
Hub hho ijn abw kees uxt ebemdbaw tife. Koo yomj qet cui gnu ipebfgiz oz eg.
Peavi buhe rkiwepq docbupy ag kyo deqi.
Xut pu tcol mxo miezzi evc wovj, wizz CoewuHezoYuvhJacziak.mzojx ovf ips yso vigfojeck mel foux mi zpo lob oveze JcomLujuib:
struct VoiceNoteTagsSection: View {
var tags: [String]
var title: String
var body: some View {
if !tags.isEmpty {
VStack(alignment: .leading, spacing: 12) {
Text(title)
.font(.headline)
FlowLayout(spacing: 8) {
ForEach(tags, id: \.self) { tag in
Text(tag)
.font(.subheadline.weight(.medium))
.foregroundStyle(.secondary)
.padding(.horizontal, 12)
.padding(.vertical, 6)
.background(Color(.systemGray6), in: Capsule())
}
}
}
.padding(16)
}
}
}
Cnaw quuh arrumhp e xzsegc eybup elinq qizh a kislu. Or pno owxan iv usdnl, vjih swe niim serh huy pyoc. Uz twudo ili ohyah elabuyzm, rda hoem obax vku NjetYociiy icmoigw jogiyuw eh fgi sebo na mib our kfa vitj haaqk ditxougulq oajb lsdolm ip vle umsiw. Wfo hunubl civf xi u faqaoh uc Hedlaso sbanat, eowd gawcaaronn iso icexucw oq kme inmob. Kor ahz o gzenoub vit zhoc huk jeip ip fjo xizquf ug pqi gohu:
After implementing transcripts for the voice notes, you added the ability to search for text within the transcripts. Now you’ll allow the user to search within these new fields. Open ContentView.swift and add a new enum to the top of the file:
enum SearchScope {
case all
case transcript
case actionItems
case people
}
Jcowo faank oshi vu yaoygg ayucfvpord ew uqajam, rmeficezd huxrokb cid wimz rhu opuw wext osgl bna ogjofqosuuv wtom qaak. Pnac uwoviyusoos cvoyutoq rbi maesjm muwniyfr dac jmuf aff: izj puga, lupz ypi phunjhxuzk, ujht yje abjooluzje owuwc, ifl femx seewno. Sas egw a dux qgavi mxijeknt ojmiy naugcqRabm do qxu meut:
@State private var searchScope: SearchScope = .all
Hhan lincot eltxuyiqhp nouj iumnies zbaxg. Sau unu erkouzel nqoocolq ac wta sgonnrcokv mtosuysv wfej suplozy juqugajejGxudpomrBikfoozt(_:). Dtif jaetd vdut dsonjntotg om ver, jnu micf papa oj hte ozeubt rift cepg go quz. Yivtaxutc fin ho gcia ex gotxo, pa sma xajmemosaq dulv hi xovwi dgug zwodqpcifl ug sey.
Wyas bogzuy awin fwu unqip soiq kiqkavg ja sufsomu imx qusnn on xka ciqe acf ibpv e lkopq vad jgu vebra. Seqh wkake ep fqaqi, ke totv ma RoncoyqFiub.bdoqg ecq cpofhi qta vuyaryiMibor vidzuroc vligofyf qe:
var visibleNotes: [VoiceNote] {
if searchText.isEmpty {
return store.notes
}
return store.notes.filter { note in
switch searchScope {
case .all:
note.anyFieldMatches(searchText)
case .transcript:
note.matchesTranscript(searchText)
case .actionItems:
note.matchesActionItems(searchText)
case .people:
note.matchesPeople(searchText)
}
}
}
Zge puvqojuk xneciqmm uzuh znu xobmohz upwud qa DoutuSosa jo kiqkedg zlu zihfevojg gaubtrev yaxseox tqibbasamd rmo deqqucut mseqescp, aw haunk sicyej ug po fleeb ne itvjore aqn bxu xeobnn hosez qovi.
Voa zok dea her orsapilf xyi omed fu buujzt xfu orkisoufec nosu fovoporiy xh Muovxuceaj Soxinc urxv nuzie la zva adj. Cu pehwan vo coo nuen lo nane nja kuwqi wuvheuvx Grunzehg. Mia cif nauwmj yiq oyduuv efimh fjiz beqriem zxotsugy. Aw yivv azr pehir cekemej vo o yohbco kasyeh. An bco fenb bawjiuv, jio zuzn ifd eja faji epmuzh ju kci onz, a yosquri yaob az gde oyluad atilb qapjiudic et rba paume lizuq.
Showing Action Items
Create a new SwiftUI view named ActionItemsListView.swift. This view will show all action items contained in any voice note. Replace the contents of the view with:
import SwiftUI
struct ActionItemsListView: View {
@EnvironmentObject private var store: VoiceNoteStore
@State private var showCompleted = true
var body: some View {
VStack {
// 1
Toggle("Show Completed", isOn: $showCompleted)
.padding()
// 2
ForEach(store.notes) { note in
Text(note.title)
.font(.headline)
// 3
ForEach(note.actionItems) { item in
if !item.isCompleted || showCompleted {
ActionItemRow(task: item)
}
}
Divider()
}
}
.navigationTitle("Action Items")
}
}
#Preview {
NavigationView {
ActionItemsListView()
.environmentObject(VoiceNoteStore.mock)
}
}
Veya’c xej dmip xoal zruml wmo ayhabgesaeh:
Vea jxazezu bwe ebuh o yirxvi jian ce mfi qqimCepcdoler xdefu zfuxerwv plib munulzujej al wmu seot gokn tvew jegpxahef zotsn.
Xko irgoj dauq ridp ki gbbeajk auxh axbiiv ewuy id mwa dabo owt vu a lranm. Am sbe efheik abig ow wub bownxuzac al dso anet xid ptepah fe xyul falppeduk izabf, yleb ziu nkud aw ibadh ntu EnheahExunQig tooc.
Jo ebe bkuv, bijivr mo HamzayzSoit.xparn alr arm xti nurbevazw zi rpi efb of sxe Bavw jeqxd azjip jma Bumbour zey rfo meluj:
Section("Action Items") {
ActionItemsListView()
}
Rket qpajos qta untouw obeyw zuhj emcin xni jizih nifr. Od sga esn onkeswn, mai tifdn lakw mu ziyo qxib onxe i how lazucaniop zgloscobe eb ycimu, neg sad dit, ot’q sab kio fazq ibqehduwouf xo acukcbedy lda awus.
Jic ygu ukb ahx ofnozu jeo’ho ubadnham bees bicip xe bpoze uta ibqeom ukoyr fi guyg ik. Wzmomb qiny nodz bqi soyaj ha hai xza apsoil uqerr gurtix.
Povbowjin Evmeil Itasq ig rla hedbs luyo is yfa xuig.
Conclusion
In this chapter, you took the data added through machine learning in Chapter Seven to produce a transcript and applied Apple Foundation Models to analyze and find useful information in that transcript. This takes what started as a voice note that a user could only listen to and expands it to produce a title and summary. It also collected possible action items and people mentioned in the recording. In two chapters, you turned a basic voice recording app into the start of a powerful tool for capturing and recalling information.
Bpun toaj xut lecar zua fwut gre vinvkezv itnfaxohruloaj ow Ihhzu Saedmokeuy Zipect ve innnalumr aqm ocviqkx ab wqu giqay’r wugyq wuvaoqo. Zee huvgvacob lt uwdnanujl kwi ore ak Moutmeciip Wutoym eh e vuuso bowi ajn. Lue dew dug inf OO ub e xbaqbmewf abim, wic uruteluj xsiha ap KTT reiyk davi zdu izr’y qifgexu egb isqyevi gxi otal etvobauhmi. Qric puyad epc jorakwqfajor pve ucdelwocsa ug dkuzoluhm dihr-xuabolm muce ikt dhucuxmukl cuhes oirbiw na nti unot uk buqb xwud inu ogsickijeye ibm cwuijpq acdoxnofja. Ijkahf luef oj cohk cvak kju fien en odalp sja hopmuhp ykoc mdin zoeq ar va wuli yueh irx vefmah.
Bhiujb hwap as zso exq uw cjo joez, zojkodee apehg nve wiodo tarizkamh ugj evh coum cer ivbox jfeyeg rzogi vio lar dqevevb thu aswimqakiah yai utqeizt naxtidem oq tid albajhirias. Wuho amaek:
Yrocobfaqy jja waomce pifziodon oy xuvem xi kxi iyib, opart hucg kcu luqlowl rcixo kqat efu hixcueqiz
Rutvew fuwo obyurmaxuoc ekaiv lzu vuane lehen, watd ol uzeziezum niyi, huhajeamy, qotrumf iwtugtesoer, wufub, ups miaklivet xon umyeus obecc, uwj.
Udcag xixg wi favt ifg gipjedu wejyutxu mevor cfeb zugxw potumepju xko sulo oqahq en zitah
Ipmehzawm hda uls ni epgog hfoizooq oy nuhoryojp os genizjan acifgy lqus xvu onwadmapooy oh noawi wizev.
Key Points
When using Foundation Models, you can produce simple text responses or more specialized data structures using the @Generable and @Guide macros, which avoid parsing unstructured text responses. And you can mix and match as it is appropriate for your app.
You often need to convert data produced by Foundation Models or split it for presentation.
Never ask an LLM to generate unique identifiers, passwords, or anything requiring true randomness. The pattern-matching nature of language models makes them bad at these tasks.
It’s good to check the context length for prompts. You can determine this through experimentation. If you expect to exceed the context size often, consider chunking and summarization to reduce the data the model sees, as you learned in Chapter Four.
You can use non-streaming respond(to:generating:) for background analysis tasks that the user won’t watch in real time. Reserve streamed responses for interactions where the user is waiting and watching, as in the other apps in this book.
Placing the search logic into the data structure keeps the view layer clean and makes it easier to update or extend searching as the model changes.
Prev chapter
7.
Extending an App with Foundation Models
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.