[{"data":1,"prerenderedAt":685},["ShallowReactive",2],{"blog-post-/blogs/swift6-on-device-llms-2026":3},{"_path":4,"_dir":5,"_draft":6,"_partial":6,"_locale":7,"title":8,"description":9,"date":10,"image":11,"alt":12,"ogImage":11,"tags":13,"published":20,"body":21,"_type":678,"_id":679,"_source":680,"_file":681,"_stem":682,"_extension":683,"sitemap":684},"/blogs/swift6-on-device-llms-2026","blogs",false,"","Swift 6 and On-Device LLMs - The Year iOS Got Smart","How Swift 6's strict concurrency, Apple Intelligence, and on-device foundation models are quietly rewriting what an iOS app can do.","14th Feb 2026","/blogs-img/blog4.jpg","Swift 6 and on-device LLMs in 2026",[14,15,16,17,18,19],"swift","swift6","ai","ios","apple-intelligence","llm",true,{"type":22,"children":23,"toc":669},"root",[24,33,39,44,50,64,69,170,175,181,194,219,224,286,291,297,310,390,395,401,406,551,572,578,583,617,623,628,653,658,663],{"type":25,"tag":26,"props":27,"children":29},"element","h3",{"id":28},"a-quiet-shift",[30],{"type":31,"value":32},"text","A quiet shift",{"type":25,"tag":34,"props":35,"children":36},"p",{},[37],{"type":31,"value":38},"It's the middle of February 2026, and something has changed about iOS development. Not loudly — Apple rarely does loud. But if you've shipped anything to the App Store this quarter, you've probably felt it. Swift 6 has settled in, Apple Intelligence is no longer an experimental side dish, and on-device foundation models have crossed the threshold from \"neat demo\" to \"thing your users expect.\"",{"type":25,"tag":34,"props":40,"children":41},{},[42],{"type":31,"value":43},"This post is my field report from the trenches: what's actually different, what I've thrown away, and what I'm still figuring out.",{"type":25,"tag":26,"props":45,"children":47},{"id":46},"swift-6-strict-concurrency-finally-for-real",[48],{"type":31,"value":49},"Swift 6: strict concurrency, finally for real",{"type":25,"tag":34,"props":51,"children":52},{},[53,55,62],{"type":31,"value":54},"Swift 6 isn't a new language. It's a stricter one. The compiler now refuses to let you write the kinds of race conditions you used to get away with, and ",{"type":25,"tag":56,"props":57,"children":59},"code",{"className":58},[],[60],{"type":31,"value":61},"@MainActor",{"type":31,"value":63}," boundaries are enforced at compile time. The first week was painful — entire view models I'd written in 2023 lit up like Christmas trees. The second week I started liking it.",{"type":25,"tag":34,"props":65,"children":66},{},[67],{"type":31,"value":68},"The pattern I keep using:",{"type":25,"tag":70,"props":71,"children":74},"pre",{"className":72,"code":73,"language":14,"meta":7,"style":7},"language-swift shiki shiki-themes dracula","@MainActor\nfinal class UniverseSplitter {\n    private(set) var universes: [Universe] = []\n\n    func split(from event: Event) async throws {\n        let response = try await aiService.generate(event: event)\n        // No more \"did I dispatch this to main?\" anxiety.\n        universes.append(response.universe)\n    }\n}\n",[75],{"type":25,"tag":56,"props":76,"children":77},{"__ignoreMap":7},[78,89,98,107,116,125,134,143,152,161],{"type":25,"tag":79,"props":80,"children":83},"span",{"class":81,"line":82},"line",1,[84],{"type":25,"tag":79,"props":85,"children":86},{},[87],{"type":31,"value":88},"@MainActor\n",{"type":25,"tag":79,"props":90,"children":92},{"class":81,"line":91},2,[93],{"type":25,"tag":79,"props":94,"children":95},{},[96],{"type":31,"value":97},"final class UniverseSplitter {\n",{"type":25,"tag":79,"props":99,"children":101},{"class":81,"line":100},3,[102],{"type":25,"tag":79,"props":103,"children":104},{},[105],{"type":31,"value":106},"    private(set) var universes: [Universe] = []\n",{"type":25,"tag":79,"props":108,"children":110},{"class":81,"line":109},4,[111],{"type":25,"tag":79,"props":112,"children":113},{"emptyLinePlaceholder":20},[114],{"type":31,"value":115},"\n",{"type":25,"tag":79,"props":117,"children":119},{"class":81,"line":118},5,[120],{"type":25,"tag":79,"props":121,"children":122},{},[123],{"type":31,"value":124},"    func split(from event: Event) async throws {\n",{"type":25,"tag":79,"props":126,"children":128},{"class":81,"line":127},6,[129],{"type":25,"tag":79,"props":130,"children":131},{},[132],{"type":31,"value":133},"        let response = try await aiService.generate(event: event)\n",{"type":25,"tag":79,"props":135,"children":137},{"class":81,"line":136},7,[138],{"type":25,"tag":79,"props":139,"children":140},{},[141],{"type":31,"value":142},"        // No more \"did I dispatch this to main?\" anxiety.\n",{"type":25,"tag":79,"props":144,"children":146},{"class":81,"line":145},8,[147],{"type":25,"tag":79,"props":148,"children":149},{},[150],{"type":31,"value":151},"        universes.append(response.universe)\n",{"type":25,"tag":79,"props":153,"children":155},{"class":81,"line":154},9,[156],{"type":25,"tag":79,"props":157,"children":158},{},[159],{"type":31,"value":160},"    }\n",{"type":25,"tag":79,"props":162,"children":164},{"class":81,"line":163},10,[165],{"type":25,"tag":79,"props":166,"children":167},{},[168],{"type":31,"value":169},"}\n",{"type":25,"tag":34,"props":171,"children":172},{},[173],{"type":31,"value":174},"What used to be \"I think this is safe\" is now \"the compiler agrees this is safe.\" It feels boring. Boring is good.",{"type":25,"tag":26,"props":176,"children":178},{"id":177},"foundation-models-on-device",[179],{"type":31,"value":180},"Foundation Models on device",{"type":25,"tag":34,"props":182,"children":183},{},[184,186,192],{"type":31,"value":185},"The headline change for indie developers is ",{"type":25,"tag":56,"props":187,"children":189},{"className":188},[],[190],{"type":31,"value":191},"FoundationModels",{"type":31,"value":193},", the framework that gives you access to Apple's on-device LLM directly from Swift. No API key. No network call. No usage cost. The model is small — by frontier standards, very small — but it's plenty for the kinds of tasks indie apps actually need:",{"type":25,"tag":195,"props":196,"children":197},"ul",{},[198,204,209,214],{"type":25,"tag":199,"props":200,"children":201},"li",{},[202],{"type":31,"value":203},"Summarising user-written content",{"type":25,"tag":199,"props":205,"children":206},{},[207],{"type":31,"value":208},"Generating short responses (titles, suggestions, micro-copy)",{"type":25,"tag":199,"props":210,"children":211},{},[212],{"type":31,"value":213},"Light reasoning over structured data",{"type":25,"tag":199,"props":215,"children":216},{},[217],{"type":31,"value":218},"Tool calls into your own app's functions",{"type":25,"tag":34,"props":220,"children":221},{},[222],{"type":31,"value":223},"Here's the part that genuinely surprised me — invoking it looks like calling any other Swift function:",{"type":25,"tag":70,"props":225,"children":227},{"className":72,"code":226,"language":14,"meta":7,"style":7},"import FoundationModels\n\nlet session = LanguageModelSession(instructions: \"You are a poetic narrator.\")\nlet response = try await session.respond(\n    to: \"Write 60 words about a person who chose silence.\"\n)\nprint(response.content)\n",[228],{"type":25,"tag":56,"props":229,"children":230},{"__ignoreMap":7},[231,239,246,254,262,270,278],{"type":25,"tag":79,"props":232,"children":233},{"class":81,"line":82},[234],{"type":25,"tag":79,"props":235,"children":236},{},[237],{"type":31,"value":238},"import FoundationModels\n",{"type":25,"tag":79,"props":240,"children":241},{"class":81,"line":91},[242],{"type":25,"tag":79,"props":243,"children":244},{"emptyLinePlaceholder":20},[245],{"type":31,"value":115},{"type":25,"tag":79,"props":247,"children":248},{"class":81,"line":100},[249],{"type":25,"tag":79,"props":250,"children":251},{},[252],{"type":31,"value":253},"let session = LanguageModelSession(instructions: \"You are a poetic narrator.\")\n",{"type":25,"tag":79,"props":255,"children":256},{"class":81,"line":109},[257],{"type":25,"tag":79,"props":258,"children":259},{},[260],{"type":31,"value":261},"let response = try await session.respond(\n",{"type":25,"tag":79,"props":263,"children":264},{"class":81,"line":118},[265],{"type":25,"tag":79,"props":266,"children":267},{},[268],{"type":31,"value":269},"    to: \"Write 60 words about a person who chose silence.\"\n",{"type":25,"tag":79,"props":271,"children":272},{"class":81,"line":127},[273],{"type":25,"tag":79,"props":274,"children":275},{},[276],{"type":31,"value":277},")\n",{"type":25,"tag":79,"props":279,"children":280},{"class":81,"line":136},[281],{"type":25,"tag":79,"props":282,"children":283},{},[284],{"type":31,"value":285},"print(response.content)\n",{"type":25,"tag":34,"props":287,"children":288},{},[289],{"type":31,"value":290},"That's the whole thing. No prompt engineering pipeline, no JSON parsing, no rate limit retries. For 80% of what I used to send to Gemini or OpenAI, the on-device model is now enough. And it's instant.",{"type":25,"tag":26,"props":292,"children":294},{"id":293},"when-on-device-isnt-enough",[295],{"type":31,"value":296},"When on-device isn't enough",{"type":25,"tag":34,"props":298,"children":299},{},[300,302,308],{"type":31,"value":301},"It's not magic. The on-device model has a small context window, it's not great at long-form creative writing, and it will not replace GPT-class models for hard reasoning. My current rule of thumb in ",{"type":25,"tag":303,"props":304,"children":305},"strong",{},[306],{"type":31,"value":307},"Elsewhere: Parallel Lives",{"type":31,"value":309}," (my AI life-simulator app):",{"type":25,"tag":311,"props":312,"children":313},"table",{},[314,333],{"type":25,"tag":315,"props":316,"children":317},"thead",{},[318],{"type":25,"tag":319,"props":320,"children":321},"tr",{},[322,328],{"type":25,"tag":323,"props":324,"children":325},"th",{},[326],{"type":31,"value":327},"Task",{"type":25,"tag":323,"props":329,"children":330},{},[331],{"type":31,"value":332},"Where it runs",{"type":25,"tag":334,"props":335,"children":336},"tbody",{},[337,351,364,377],{"type":25,"tag":319,"props":338,"children":339},{},[340,346],{"type":25,"tag":341,"props":342,"children":343},"td",{},[344],{"type":31,"value":345},"Short reflections, alter-ego names, micro-summaries",{"type":25,"tag":341,"props":347,"children":348},{},[349],{"type":31,"value":350},"On-device Foundation Model",{"type":25,"tag":319,"props":352,"children":353},{},[354,359],{"type":25,"tag":341,"props":355,"children":356},{},[357],{"type":31,"value":358},"Long narrative events, 90-word poems with structure",{"type":25,"tag":341,"props":360,"children":361},{},[362],{"type":31,"value":363},"Gemini API",{"type":25,"tag":319,"props":365,"children":366},{},[367,372],{"type":25,"tag":341,"props":368,"children":369},{},[370],{"type":31,"value":371},"Tool-call routing, classification",{"type":25,"tag":341,"props":373,"children":374},{},[375],{"type":31,"value":376},"On-device",{"type":25,"tag":319,"props":378,"children":379},{},[380,385],{"type":25,"tag":341,"props":381,"children":382},{},[383],{"type":31,"value":384},"AI image generation",{"type":25,"tag":341,"props":386,"children":387},{},[388],{"type":31,"value":389},"Replicate / cloud",{"type":25,"tag":34,"props":391,"children":392},{},[393],{"type":31,"value":394},"The win isn't \"everything moves on-device.\" The win is \"the cheap stuff stops costing you money and stops introducing latency.\" Which is huge when you're an indie dev watching every API call eat your subscription margin.",{"type":25,"tag":26,"props":396,"children":398},{"id":397},"strict-concurrency-meets-async-llm-calls",[399],{"type":31,"value":400},"Strict concurrency meets async LLM calls",{"type":25,"tag":34,"props":402,"children":403},{},[404],{"type":31,"value":405},"This is the combo I didn't expect to find delightful. Async/await was already the right abstraction for AI calls — they're slow, they can fail, they need cancellation. Swift 6's strict actor isolation makes the boundaries between \"fetching from a model\" and \"rendering the result\" impossible to mess up.",{"type":25,"tag":70,"props":407,"children":409},{"className":72,"code":408,"language":14,"meta":7,"style":7},"@MainActor\n@Observable\nfinal class EventViewModel {\n    var event: Event?\n    var isGenerating = false\n\n    func generate(from biography: Biography) async {\n        isGenerating = true\n        defer { isGenerating = false }\n\n        do {\n            event = try await aiService.generateEvent(from: biography)\n        } catch {\n            // Surface to the user, log to Sentry.\n        }\n    }\n}\n",[410],{"type":25,"tag":56,"props":411,"children":412},{"__ignoreMap":7},[413,420,428,436,444,452,459,467,475,483,490,499,508,517,526,535,543],{"type":25,"tag":79,"props":414,"children":415},{"class":81,"line":82},[416],{"type":25,"tag":79,"props":417,"children":418},{},[419],{"type":31,"value":88},{"type":25,"tag":79,"props":421,"children":422},{"class":81,"line":91},[423],{"type":25,"tag":79,"props":424,"children":425},{},[426],{"type":31,"value":427},"@Observable\n",{"type":25,"tag":79,"props":429,"children":430},{"class":81,"line":100},[431],{"type":25,"tag":79,"props":432,"children":433},{},[434],{"type":31,"value":435},"final class EventViewModel {\n",{"type":25,"tag":79,"props":437,"children":438},{"class":81,"line":109},[439],{"type":25,"tag":79,"props":440,"children":441},{},[442],{"type":31,"value":443},"    var event: Event?\n",{"type":25,"tag":79,"props":445,"children":446},{"class":81,"line":118},[447],{"type":25,"tag":79,"props":448,"children":449},{},[450],{"type":31,"value":451},"    var isGenerating = false\n",{"type":25,"tag":79,"props":453,"children":454},{"class":81,"line":127},[455],{"type":25,"tag":79,"props":456,"children":457},{"emptyLinePlaceholder":20},[458],{"type":31,"value":115},{"type":25,"tag":79,"props":460,"children":461},{"class":81,"line":136},[462],{"type":25,"tag":79,"props":463,"children":464},{},[465],{"type":31,"value":466},"    func generate(from biography: Biography) async {\n",{"type":25,"tag":79,"props":468,"children":469},{"class":81,"line":145},[470],{"type":25,"tag":79,"props":471,"children":472},{},[473],{"type":31,"value":474},"        isGenerating = true\n",{"type":25,"tag":79,"props":476,"children":477},{"class":81,"line":154},[478],{"type":25,"tag":79,"props":479,"children":480},{},[481],{"type":31,"value":482},"        defer { isGenerating = false }\n",{"type":25,"tag":79,"props":484,"children":485},{"class":81,"line":163},[486],{"type":25,"tag":79,"props":487,"children":488},{"emptyLinePlaceholder":20},[489],{"type":31,"value":115},{"type":25,"tag":79,"props":491,"children":493},{"class":81,"line":492},11,[494],{"type":25,"tag":79,"props":495,"children":496},{},[497],{"type":31,"value":498},"        do {\n",{"type":25,"tag":79,"props":500,"children":502},{"class":81,"line":501},12,[503],{"type":25,"tag":79,"props":504,"children":505},{},[506],{"type":31,"value":507},"            event = try await aiService.generateEvent(from: biography)\n",{"type":25,"tag":79,"props":509,"children":511},{"class":81,"line":510},13,[512],{"type":25,"tag":79,"props":513,"children":514},{},[515],{"type":31,"value":516},"        } catch {\n",{"type":25,"tag":79,"props":518,"children":520},{"class":81,"line":519},14,[521],{"type":25,"tag":79,"props":522,"children":523},{},[524],{"type":31,"value":525},"            // Surface to the user, log to Sentry.\n",{"type":25,"tag":79,"props":527,"children":529},{"class":81,"line":528},15,[530],{"type":25,"tag":79,"props":531,"children":532},{},[533],{"type":31,"value":534},"        }\n",{"type":25,"tag":79,"props":536,"children":538},{"class":81,"line":537},16,[539],{"type":25,"tag":79,"props":540,"children":541},{},[542],{"type":31,"value":160},{"type":25,"tag":79,"props":544,"children":546},{"class":81,"line":545},17,[547],{"type":25,"tag":79,"props":548,"children":549},{},[550],{"type":31,"value":169},{"type":25,"tag":34,"props":552,"children":553},{},[554,556,562,564,570],{"type":31,"value":555},"The whole thing is type-safe, race-free, and SwiftUI re-renders the moment ",{"type":25,"tag":56,"props":557,"children":559},{"className":558},[],[560],{"type":31,"value":561},"event",{"type":31,"value":563}," updates. No ",{"type":25,"tag":56,"props":565,"children":567},{"className":566},[],[568],{"type":31,"value":569},"DispatchQueue.main.async",{"type":31,"value":571}," anywhere in the codebase. I deleted hundreds of those calls this quarter and the apps got smaller, faster, and harder to break.",{"type":25,"tag":26,"props":573,"children":575},{"id":574},"what-this-means-for-indie-devs",[576],{"type":31,"value":577},"What this means for indie devs",{"type":25,"tag":34,"props":579,"children":580},{},[581],{"type":31,"value":582},"If you ship indie iOS apps in 2026, three things are true:",{"type":25,"tag":584,"props":585,"children":586},"ol",{},[587,597,607],{"type":25,"tag":199,"props":588,"children":589},{},[590,595],{"type":25,"tag":303,"props":591,"children":592},{},[593],{"type":31,"value":594},"You can no longer ignore Swift 6.",{"type":31,"value":596}," Xcode 16+ ships with it on by default. Migrate now or you'll migrate later with worse code.",{"type":25,"tag":199,"props":598,"children":599},{},[600,605],{"type":25,"tag":303,"props":601,"children":602},{},[603],{"type":31,"value":604},"On-device AI is a competitive advantage.",{"type":31,"value":606}," Apps that use it feel faster and respect user privacy. Apps that don't feel slow and expensive.",{"type":25,"tag":199,"props":608,"children":609},{},[610,615],{"type":25,"tag":303,"props":611,"children":612},{},[613],{"type":31,"value":614},"Cloud models are still essential",{"type":31,"value":616}," — but only for the parts where they're irreplaceable. Stop sending \"summarise this paragraph\" to GPT-4. That's free now.",{"type":25,"tag":26,"props":618,"children":620},{"id":619},"where-id-start",[621],{"type":31,"value":622},"Where I'd start",{"type":25,"tag":34,"props":624,"children":625},{},[626],{"type":31,"value":627},"If you're sitting on an app that hasn't touched any of this:",{"type":25,"tag":584,"props":629,"children":630},{},[631,636,648],{"type":25,"tag":199,"props":632,"children":633},{},[634],{"type":31,"value":635},"Open it in Xcode 16, turn on strict concurrency. Fix one warning a day for a month — you'll thank yourself.",{"type":25,"tag":199,"props":637,"children":638},{},[639,641,646],{"type":31,"value":640},"Pick one small feature in your app that calls a cloud API. Move it to ",{"type":25,"tag":56,"props":642,"children":644},{"className":643},[],[645],{"type":31,"value":191},{"type":31,"value":647},". Measure latency and cost before and after.",{"type":25,"tag":199,"props":649,"children":650},{},[651],{"type":31,"value":652},"Find one thing your app cannot do today because an LLM would be too slow or too expensive. Now do it on-device.",{"type":25,"tag":34,"props":654,"children":655},{},[656],{"type":31,"value":657},"This is the year iOS quietly got smart. The apps that lean into it will feel like the future. The apps that don't will feel like they were built last year.",{"type":25,"tag":34,"props":659,"children":660},{},[661],{"type":31,"value":662},"— Met",{"type":25,"tag":664,"props":665,"children":666},"style",{},[667],{"type":31,"value":668},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":7,"searchDepth":91,"depth":91,"links":670},[671,672,673,674,675,676,677],{"id":28,"depth":100,"text":32},{"id":46,"depth":100,"text":49},{"id":177,"depth":100,"text":180},{"id":293,"depth":100,"text":296},{"id":397,"depth":100,"text":400},{"id":574,"depth":100,"text":577},{"id":619,"depth":100,"text":622},"markdown","content:blogs:8. swift6-on-device-llms-2026.md","content","blogs/8. swift6-on-device-llms-2026.md","blogs/8. swift6-on-device-llms-2026","md",{"loc":4},1780164678387]