Pendahuluan: Era Baru AI On-Device di Apple
Kalau kamu mengikuti WWDC 2025, pasti tahu bahwa Apple baru saja meluncurkan sesuatu yang cukup menggemparkan — Foundation Models framework untuk iOS 26. Dan jujur, ini salah satu update paling menarik yang pernah saya lihat dari Apple dalam beberapa tahun terakhir.
Framework ini memberikan akses langsung ke model bahasa besar (LLM) yang berjalan sepenuhnya di perangkat pengguna. Tidak perlu koneksi internet. Tidak perlu server cloud pihak ketiga. Privasi terjaga, latensi rendah, dan fitur AI tetap jalan meskipun offline. Lumayan kan?
Berbeda dengan layanan AI berbasis cloud seperti ChatGPT atau Claude yang mengirim data ke server eksternal, Foundation Models framework memanfaatkan kekuatan Apple Silicon untuk menjalankan model dengan 3 miliar parameter langsung di iPhone, iPad, atau Mac. Framework ini menggunakan API Swift yang elegan dan terintegrasi secara native dengan ekosistem Apple — jadi buat developer iOS, ini memang terasa seperti "rumah sendiri".
Nah, dalam panduan ini kita akan bahas semuanya dari awal sampai akhir. Mulai dari setup, generasi teks dasar, output terstruktur dengan @Generable, pembatasan pakai @Guide, streaming respons, sampai penggunaan Tools buat memperluas kemampuan model. Setiap konsep disertai contoh kode yang bisa langsung kamu coba.
Persyaratan dan Ketersediaan
Sebelum mulai ngoding, ada baiknya kita pahami dulu apa saja yang dibutuhkan:
- Platform: iOS 26+, iPadOS 26+, macOS Tahoe+, dan visionOS 26+
- Hardware: Perangkat yang mendukung Apple Intelligence (Apple Silicon)
- Development: Xcode 26 atau lebih baru
- Kapasitas Token: Kombinasi input dan output maksimal 4096 token
- Bahasa: Model bekerja paling optimal dengan instruksi dalam bahasa Inggris
Satu hal penting yang harus kamu ingat: model ini bukan pengganti ChatGPT. Ia nggak dirancang untuk pengetahuan umum atau penalaran tingkat lanjut. Model ini dioptimalkan untuk tugas-tugas seperti generasi teks, ringkasan, ekstraksi entitas, klasifikasi, dan pemahaman teks. Jadi jangan harap dia bisa menjawab "siapa presiden pertama Indonesia" dengan akurat ya.
Memeriksa Ketersediaan Model
Karena framework ini cuma jalan di perangkat tertentu, kamu wajib cek ketersediaan model sebelum menggunakannya. Untungnya, Apple sudah menyediakan API yang cukup straightforward:
import FoundationModels
let model = SystemLanguageModel.default
switch model.availability {
case .available:
// Model siap digunakan
print("Model tersedia dan siap digunakan")
case .unavailable(let reason):
switch reason {
case .deviceNotEligible:
print("Perangkat tidak mendukung Apple Intelligence")
case .appleIntelligenceNotEnabled:
print("Apple Intelligence belum diaktifkan di Pengaturan")
case .modelNotReady:
print("Model sedang diunduh atau disiapkan")
@unknown default:
print("Model tidak tersedia")
}
}
Dalam aplikasi SwiftUI, kamu bisa manfaatkan pengecekan ini untuk menampilkan UI yang sesuai dengan kondisi perangkat:
struct ContentView: View {
let model = SystemLanguageModel.default
var body: some View {
switch model.availability {
case .available:
AIFeatureView()
case .unavailable(.appleIntelligenceNotEnabled):
Text("Aktifkan Apple Intelligence di Pengaturan untuk menggunakan fitur ini.")
case .unavailable(.deviceNotEligible):
Text("Perangkat Anda tidak mendukung fitur AI on-device.")
default:
Text("Fitur AI sedang disiapkan. Coba lagi nanti.")
}
}
}
Memulai dengan LanguageModelSession
LanguageModelSession adalah "pintu masuk" utama kamu untuk berinteraksi dengan model bahasa on-device. Session ini mempertahankan konteks percakapan dan mendukung berbagai mode generasi — teks biasa, output terstruktur, maupun streaming.
Membuat Session Sederhana
Cara paling gampang untuk memulai? Buat session tanpa konfigurasi macam-macam:
import FoundationModels
let session = LanguageModelSession()
let response = try await session.respond(to: "Jelaskan apa itu protocol-oriented programming dalam satu paragraf.")
print(response.content)
Simpel banget. Kode di atas membuat session default, kirim prompt ke model, dan respons dikembalikan sebagai objek Response yang punya properti content berisi teks yang dihasilkan.
Session dengan Instruksi Kustom
Kalau kamu mau kontrol lebih atas perilaku model, bisa kasih instruksi sistem. Yang menarik, instruksi ini diprioritaskan di atas prompt pengguna — jadi ini juga membantu dari sisi keamanan:
let session = LanguageModelSession(instructions: {
"""
Kamu adalah asisten pemrograman Swift yang berpengalaman.
Berikan jawaban yang ringkas dan langsung ke intinya.
Selalu sertakan contoh kode ketika memungkinkan.
Gunakan bahasa yang mudah dipahami oleh pemula.
"""
})
let response = try await session.respond(
to: "Bagaimana cara menggunakan guard statement?"
)
print(response.content)
Menggunakan Model Khusus
Selain model default, Apple juga menyediakan adapter khusus yang sudah di-fine-tune untuk kasus penggunaan tertentu:
// Model default untuk tugas umum
let generalModel = SystemLanguageModel.default
// Model khusus untuk klasifikasi dan tagging konten
let taggingModel = SystemLanguageModel(useCase: .contentTagging)
let session = LanguageModelSession(model: taggingModel)
Model .contentTagging sudah dioptimalkan untuk tugas ekstraksi dan klasifikasi. Jadi kalau use case kamu memang di area itu, hasilnya bakal lebih akurat dibanding model default.
Prewarming Session
Memuat model ke memori itu butuh waktu (dan pengguna nggak suka nunggu). Untuk mengatasi ini, kamu bisa melakukan prewarming — yaitu memuat resource model ke memori sebelum pengguna beneran butuh:
let session = LanguageModelSession()
// Panggil prewarm saat kamu yakin pengguna akan menggunakan fitur AI
// Misalnya saat view muncul di layar
session.prewarm()
Strategi yang bagus adalah memanggil prewarm() di event .onAppear pada view yang mengandung fitur AI. Dengan begitu, model sudah siap saat pengguna mulai berinteraksi.
Konteks Percakapan dan Multi-Turn
Salah satu hal yang bikin LanguageModelSession powerful adalah kemampuannya menyimpan konteks percakapan. Setiap kali kamu panggil respond(to:) di session yang sama, riwayat percakapan sebelumnya ikut disertakan:
let session = LanguageModelSession(instructions: {
"Kamu adalah tutor pemrograman Swift yang ramah dan sabar."
})
// Percakapan pertama
let response1 = try await session.respond(to: "Apa itu optional di Swift?")
print(response1.content)
// Model akan mengingat konteks percakapan sebelumnya
let response2 = try await session.respond(to: "Berikan contoh penggunaannya")
print(response2.content)
// Masih dalam konteks yang sama
let response3 = try await session.respond(to: "Bagaimana cara unwrapping yang aman?")
print(response3.content)
Fitur multi-turn ini sangat berguna untuk bikin pengalaman chat interaktif atau asisten kontekstual. Tapi perlu diingat, setiap pesan tambahan mengonsumsi token dari batas 4096. Jadi untuk percakapan yang panjang, kamu perlu pantau penggunaan token-nya.
Output Terstruktur dengan @Generable
Nah, ini dia salah satu fitur paling keren dari Foundation Models framework — Guided Generation. Intinya, kamu bisa minta model menghasilkan output yang mengikuti struktur Swift type yang sudah kamu definisikan. Caranya? Pakai macro @Generable.
Konsep Dasar @Generable
Dengan menandai struct pakai @Generable, compiler secara otomatis menghasilkan JSON schema dan logika parsing. Model kemudian pakai constrained decoding untuk menjamin output mengikuti schema tersebut. Keren, kan?
import FoundationModels
@Generable
struct RingkasanArtikel {
let judul: String
let poinUtama: [String]
let sentimen: String
}
let session = LanguageModelSession()
let response = try await session.respond(
to: "Ringkas artikel berikut: [teks artikel di sini]",
generating: RingkasanArtikel.self
)
print("Judul: \(response.content.judul)")
print("Poin utama:")
for poin in response.content.poinUtama {
print("- \(poin)")
}
print("Sentimen: \(response.content.sentimen)")
Kenapa ini lebih baik dari parsing JSON manual? Beberapa alasan:
- Type safety: Kamu kerja langsung dengan Swift types, bukan string JSON yang rentan error
- Keandalan: Constrained decoding menjamin struktur output pasti valid
- Prompt lebih sederhana: Nggak perlu repot menjelaskan format output di prompt
- Performa lebih baik: Model menghasilkan output lebih cepat karena schema membatasi ruang pencarian
Struktur Bersarang (Nested Structures)
Kamu juga bisa bikin hierarki @Generable types untuk memodelkan data yang lebih kompleks. Contohnya, sebuah resep masakan:
@Generable
struct Resep {
let nama: String
let deskripsi: String
let bahan: [Bahan]
let langkah: [String]
let waktuMasakMenit: Int
}
@Generable
struct Bahan {
let nama: String
let jumlah: Double
let satuan: Satuan
}
@Generable
enum Satuan: String, Codable {
case gram, mililiter, sendokMakan, cangkir, buah, potong
}
let session = LanguageModelSession(instructions: {
"You are a cooking assistant. Generate recipes with precise measurements."
})
let response = try await session.respond(
to: "Berikan resep nasi goreng sederhana",
generating: Resep.self
)
let resep = response.content
print("Resep: \(resep.nama)")
print("Waktu masak: \(resep.waktuMasakMenit) menit")
print("Bahan-bahan:")
for bahan in resep.bahan {
print(" - \(bahan.jumlah) \(bahan.satuan.rawValue) \(bahan.nama)")
}
Perhatikan bahwa enum juga bisa pakai anotasi @Generable, asalkan conform ke String dan Codable. Ini kasih type safety tambahan buat field yang nilainya memang terbatas.
Membatasi Output dengan @Guide
Macro @Guide itu ibarat "pagar pembatas" untuk output model. Kamu bisa kasih petunjuk dan batasan pada properti @Generable supaya hasilnya lebih terkontrol dan sesuai kebutuhan.
Deskripsi dan Petunjuk
@Generable
struct UlasanFilm {
@Guide(description: "Judul lengkap film termasuk tahun rilis")
let judul: String
@Guide(description: "Skor rating dari 1 sampai 10", .range(1...10))
let rating: Int
@Guide(description: "Genre utama film", .anyOf(["Aksi", "Drama", "Komedi", "Horor", "Sci-Fi", "Animasi"]))
let genre: String
@Guide(description: "Ulasan singkat dalam 2-3 kalimat")
let ulasan: String
}
Jenis Batasan yang Tersedia
Ada beberapa jenis batasan yang bisa kamu pakai dengan @Guide:
description(_:)— Petunjuk bahasa natural buat bantu model memahami konteks properti.anyOf(_:)— Membatasi nilai ke daftar opsi tertentu (pakai constrained decoding).range(_:)— Membatasi nilai numerik ke rentang tertentu.count(_:)— Menetapkan jumlah elemen tepat untuk array
Ini contoh penggunaan beberapa batasan sekaligus — perhatikan bagaimana setiap properti dibatasi dengan cara yang berbeda:
@Generable
struct RencanaLatihan {
@Guide(description: "Nama program latihan")
let namaProgram: String
@Guide(description: "Tingkat kesulitan", .anyOf(["Pemula", "Menengah", "Lanjutan"]))
let tingkatKesulitan: String
@Guide(description: "Durasi latihan dalam menit", .range(15...120))
let durasiMenit: Int
@Guide(description: "Daftar 5 latihan yang harus dilakukan", .count(5))
let latihan: [Latihan]
}
@Generable
struct Latihan {
let nama: String
@Guide(description: "Jumlah repetisi", .range(5...30))
let repetisi: Int
@Guide(description: "Jumlah set", .range(1...5))
let set: Int
}
Dengan @Guide, kamu dapat jaminan bahwa output model selalu memenuhi batasan yang ditentukan. Ini sangat berguna untuk fitur yang butuh data terstruktur dengan format konsisten.
Streaming Respons untuk UI Responsif
Pernah lihat efek "mengetik" di aplikasi chat AI? Nah, Foundation Models framework juga mendukung itu melalui streaming. Respons dikirim secara inkremental saat model menghasilkan output, jadi pengguna nggak perlu nunggu sampai semuanya selesai baru bisa lihat hasilnya.
Streaming Teks Biasa
let session = LanguageModelSession()
let stream = session.streamResponse(to: "Tulis cerita pendek tentang robot yang belajar memasak")
for try await partial in stream {
// Setiap partial berisi teks yang telah dihasilkan sejauh ini
print(partial)
}
Streaming dengan Output Terstruktur
Yang menarik (dan agak mind-blowing menurut saya), streaming juga bisa dipakai dengan @Generable types. Saat streaming output terstruktur, setiap elemen yang di-stream bertipe T.PartiallyGenerated — semua propertinya jadi opsional karena mungkin belum sepenuhnya dihasilkan:
@Generable
struct CeritaPendek {
let judul: String
let paragraf: [String]
let moral: String
}
let session = LanguageModelSession()
let stream = session.streamResponse(
to: "Tulis cerita pendek dengan moral yang baik",
generating: CeritaPendek.self
)
for try await partial in stream {
// partial bertipe CeritaPendek.PartiallyGenerated
// Semua properti opsional
if let judul = partial.judul {
print("Judul: \(judul)")
}
if let paragraf = partial.paragraf {
for p in paragraf {
if let teks = p {
print(teks)
}
}
}
}
Integrasi dengan SwiftUI
Streaming dan SwiftUI itu pasangan yang cocok banget. Berikut contoh integrasi menggunakan pattern @State:
import SwiftUI
import FoundationModels
struct ChatView: View {
@State private var prompt = ""
@State private var responseText = ""
@State private var isGenerating = false
private let session = LanguageModelSession(instructions: {
"You are a helpful and friendly assistant."
})
var body: some View {
VStack {
ScrollView {
Text(responseText)
.padding()
.frame(maxWidth: .infinity, alignment: .leading)
}
HStack {
TextField("Ketik pesan...", text: $prompt)
.textFieldStyle(.roundedBorder)
Button("Kirim") {
Task {
await generateResponse()
}
}
.disabled(prompt.isEmpty || isGenerating)
}
.padding()
}
}
private func generateResponse() async {
isGenerating = true
responseText = ""
let currentPrompt = prompt
prompt = ""
do {
let stream = session.streamResponse(to: currentPrompt)
for try await partial in stream {
responseText = partial
}
} catch {
responseText = "Terjadi kesalahan: \(error.localizedDescription)"
}
isGenerating = false
}
}
Hasilnya? Pengguna bisa lihat teks muncul secara real-time saat model menghasilkannya. Pengalaman yang jauh lebih responsif dibanding nunggu loading spinner berputar.
Memperluas Kemampuan Model dengan Tools
Secara default, model bahasa on-device cuma bisa generate teks berdasarkan pengetahuan yang sudah dilatih. Tapi dengan Tool protocol, kamu bisa memperluas kemampuannya — akses data eksternal, lakukan komputasi, atau interaksi dengan API. Dan semuanya tetap berjalan di perangkat, jadi privasi tetap aman.
Membuat Tool Kustom
Sebuah Tool terdiri dari beberapa komponen wajib:
name— Identifier unik untuk tooldescription— Penjelasan kapan dan bagaimana model harus pakai tool iniArguments— Struct@Generableyang mendefinisikan parameter inputcall(arguments:)— Fungsi yang dieksekusi saat model memutuskan untuk menggunakan tool
import FoundationModels
struct CariCuacaTool: Tool {
let name = "cariCuaca"
let description = "Mencari informasi cuaca terkini untuk kota tertentu."
@Generable
struct Arguments {
@Guide(description: "Nama kota yang ingin dicari cuacanya")
let kota: String
}
func call(arguments: Arguments) async throws -> ToolOutput {
// Dalam implementasi nyata, kamu akan memanggil API cuaca
// Ini hanya contoh dengan data statis
let dataCuaca = """
Cuaca di \(arguments.kota):
Suhu: 28°C
Kondisi: Cerah berawan
Kelembaban: 75%
"""
return ToolOutput(dataCuaca)
}
}
Mendaftarkan dan Menggunakan Tools
Setelah bikin tool, tinggal daftarkan ke session lewat parameter tools:
let cuacaTool = CariCuacaTool()
let session = LanguageModelSession(
tools: [cuacaTool],
instructions: {
"""
You are a weather assistant.
When asked about weather conditions, always use the cariCuaca tool.
Provide the information in a friendly and conversational manner.
"""
}
)
let response = try await session.respond(
to: "Bagaimana cuaca di Jakarta hari ini?"
)
print(response.content)
Model bakal secara otomatis memutuskan kapan harus pakai tool berdasarkan prompt dan deskripsi tool. Saat model memanggil tool, framework mengeksekusi fungsi call(arguments:) dan mengembalikan hasilnya ke model untuk diproses lebih lanjut. Cukup elegan prosesnya.
Tool dengan Akses Data Nyata
Berikut contoh tool yang lebih realistis — mengakses data kontak lokal:
struct CariKontakTool: Tool {
let name = "cariKontak"
let description = "Mencari kontak berdasarkan nama dari daftar kontak pengguna."
let kontakStore: KontakStore
@Generable
struct Arguments {
@Guide(description: "Nama atau bagian nama kontak yang dicari")
let kueriNama: String
}
func call(arguments: Arguments) async throws -> ToolOutput {
let hasil = await kontakStore.cari(nama: arguments.kueriNama)
if hasil.isEmpty {
return ToolOutput("Tidak ditemukan kontak dengan nama \"\(arguments.kueriNama)\"")
}
let daftarKontak = hasil.map { kontak in
"\(kontak.nama) - \(kontak.nomorTelepon)"
}.joined(separator: "\n")
return ToolOutput("Kontak ditemukan:\n\(daftarKontak)")
}
}
Menggunakan Beberapa Tools Sekaligus
Kamu bisa daftarkan beberapa tools ke satu session. Model akan pilih tool yang tepat berdasarkan konteks percakapan — nggak perlu kamu tentukan secara manual:
let session = LanguageModelSession(
tools: [
CariCuacaTool(),
CariKontakTool(kontakStore: store),
KonversiMataUangTool(apiClient: client)
],
instructions: {
"""
You are a personal assistant that can help with weather information,
contact lookup, and currency conversion. Use the appropriate tool
based on the user's request.
"""
}
)
GenerationOptions: Mengontrol Perilaku Generasi
Mau kontrol lebih detail soal bagaimana model menghasilkan output? Di sinilah GenerationOptions berperan:
let options = GenerationOptions(
sampling: .greedy,
temperature: 0.8,
maximumResponseTokens: 200
)
let response = try await session.respond(
to: "Tulis paragraf pembuka novel fiksi ilmiah",
options: options
)
Mode Sampling
Ada tiga mode sampling yang bisa kamu pilih, dan masing-masing punya karakter yang berbeda:
.greedy— Selalu pilih token dengan probabilitas tertinggi. Hasilnya deterministik dan konsisten. Cocok buat tugas yang butuh keakuratan tinggi seperti ekstraksi data..random(top:seed:)— Top-k sampling, pilih secara acak dari k token teratas. Nilaitopyang lebih besar = output lebih beragam..random(probabilityThreshold:seed:)— Top-p sampling (nucleus sampling), pilih dari token yang probabilitas kumulatifnya mencapai threshold tertentu. Lebih adaptif dibanding top-k.
// Untuk output kreatif — gunakan sampling acak dengan temperature tinggi
let opsiKreatif = GenerationOptions(
sampling: .random(top: 40, seed: nil),
temperature: 1.2
)
// Untuk ekstraksi data — gunakan greedy dengan temperature rendah
let opsiPresisi = GenerationOptions(
sampling: .greedy,
temperature: 0.1,
maximumResponseTokens: 500
)
Soal temperature, anggap saja seperti "tingkat kreativitas" model. Nilai rendah (mendekati 0) bikin output lebih fokus dan predictable, sedangkan nilai tinggi (mendekati 2.0) bikin output lebih kreatif tapi kadang bisa jadi agak "liar".
Studi Kasus: Membangun Fitur Ringkasan Artikel
Oke, sekarang saatnya kita gabungkan semua konsep yang sudah dipelajari ke dalam sebuah contoh nyata. Kita akan bikin fitur ringkasan artikel lengkap dengan analisis sentimen — sesuatu yang realistis dan bisa langsung kamu adaptasi ke project sendiri.
import SwiftUI
import FoundationModels
// MARK: - Model Data
@Generable
struct RingkasanHasil {
@Guide(description: "Ringkasan artikel dalam 3-5 kalimat")
let ringkasan: String
@Guide(description: "3 sampai 5 poin utama dari artikel", .count(5))
let poinUtama: [String]
@Guide(description: "Sentimen keseluruhan artikel",
.anyOf(["Positif", "Negatif", "Netral"]))
let sentimen: String
@Guide(description: "Estimasi waktu baca dalam menit", .range(1...60))
let estimasiWaktuBaca: Int
}
// MARK: - ViewModel
@Observable
class ArtikelViewModel {
var ringkasan: RingkasanHasil?
var sedangMemproses = false
var pesanError: String?
private let session: LanguageModelSession
init() {
self.session = LanguageModelSession(instructions: {
"""
You are an article analysis assistant. When given article text,
provide a concise summary, extract key points, determine overall
sentiment, and estimate reading time. Be accurate and objective.
"""
})
}
func ringkasArtikel(_ teksArtikel: String) async {
sedangMemproses = true
pesanError = nil
do {
let response = try await session.respond(
to: "Analyze and summarize this article:\n\n\(teksArtikel)",
generating: RingkasanHasil.self
)
ringkasan = response.content
} catch {
pesanError = "Gagal meringkas artikel: \(error.localizedDescription)"
}
sedangMemproses = false
}
}
// MARK: - View
struct ArtikelRingkasanView: View {
@State private var viewModel = ArtikelViewModel()
let teksArtikel: String
var body: some View {
VStack(alignment: .leading, spacing: 16) {
if viewModel.sedangMemproses {
ProgressView("Sedang meringkas artikel...")
} else if let ringkasan = viewModel.ringkasan {
VStack(alignment: .leading, spacing: 12) {
Label("Waktu baca: \(ringkasan.estimasiWaktuBaca) menit",
systemImage: "clock")
Label("Sentimen: \(ringkasan.sentimen)",
systemImage: "face.smiling")
Text("Ringkasan")
.font(.headline)
Text(ringkasan.ringkasan)
Text("Poin Utama")
.font(.headline)
ForEach(ringkasan.poinUtama, id: \.self) { poin in
HStack(alignment: .top) {
Image(systemName: "checkmark.circle.fill")
.foregroundStyle(.green)
Text(poin)
}
}
}
} else if let error = viewModel.pesanError {
Label(error, systemImage: "exclamationmark.triangle")
.foregroundStyle(.red)
}
}
.padding()
.task {
await viewModel.ringkasArtikel(teksArtikel)
}
}
}
Contoh di atas menunjukkan bagaimana semua fitur bekerja sama: instruksi kustom, @Generable untuk output terstruktur, @Guide untuk membatasi nilai, dan integrasi dengan SwiftUI lewat @Observable. Ini bukan cuma teori — kamu bisa langsung pakai pattern ini di aplikasi nyata.
Best Practices dan Tips Optimasi
Setelah paham API-nya, sekarang mari kita bahas hal-hal yang sering bikin developer tersandung (termasuk saya sendiri waktu pertama kali coba).
1. Jangan Lebay dengan Properti @Generable
Model akan mengisi semua properti dari tipe @Generable, mau dipakai atau enggak. Jadi, hanya sertakan properti yang benar-benar diperlukan. Semakin sedikit properti, semakin cepat inferensi dan semakin hemat token.
// Buruk - terlalu banyak properti yang nggak diperlukan
@Generable
struct HasilAnalisisBuruk {
let ringkasan: String
let sentimen: String
let kategori: String
let subKategori: String
let entitas: [String]
let bahasa: String
let kataPenting: [String]
let statistik: String
// Terlalu banyak! Model harus mengisi semuanya
}
// Baik - hanya properti yang benar-benar dibutuhkan
@Generable
struct HasilAnalisisBaik {
let ringkasan: String
let sentimen: String
let poinUtama: [String]
}
2. Tulis Instruksi dalam Bahasa Inggris
Ini tips yang sering diabaikan. Meskipun model bisa memahami berbagai bahasa, performanya paling optimal dengan instruksi dalam bahasa Inggris. Triknya: tulis instruksi sistem dalam bahasa Inggris, tapi biarkan prompt pengguna dalam bahasa apa pun.
let session = LanguageModelSession(instructions: {
// Instruksi dalam bahasa Inggris untuk performa optimal
"You are a helpful assistant. Respond in the same language as the user."
})
// Prompt pengguna bisa dalam bahasa Indonesia
let response = try await session.respond(to: "Jelaskan cara kerja SwiftUI")
3. Perhatikan Urutan Properti
Ini detail kecil yang bisa bikin perbedaan besar. Karena model menghasilkan output secara berurutan, letakkan properti "dasar" sebelum properti yang bergantung padanya:
@Generable
struct AnalisisTeks {
// Ini dihasilkan pertama dan jadi konteks untuk properti berikutnya
let teksAsli: String
// Dihasilkan setelah model memproses teks asli
let sentimen: String
// Dihasilkan berdasarkan pemahaman dari dua properti sebelumnya
let rekomendasi: String
}
4. Tangani Error dengan Baik
Jangan lupa siapkan penanganan error yang informatif. Pengguna harus tahu apa yang terjadi kalau sesuatu nggak beres:
func prosesPermintaan(_ prompt: String) async {
let model = SystemLanguageModel.default
guard case .available = model.availability else {
tampilkanPesan("Fitur AI tidak tersedia di perangkat ini")
return
}
do {
let session = LanguageModelSession()
let response = try await session.respond(to: prompt)
tampilkanHasil(response.content)
} catch is CancellationError {
tampilkanPesan("Permintaan dibatalkan")
} catch {
tampilkanPesan("Terjadi kesalahan. Silakan coba lagi.")
}
}
5. Gunakan Prewarming Secara Strategis
Jangan panggil prewarm() terlalu awal — nanti malah makan memori percuma. Panggil saat kamu yakin pengguna akan segera menggunakan fitur AI:
struct FiturAIView: View {
let session = LanguageModelSession()
var body: some View {
NavigationLink("Buka Asisten AI") {
AsistenView(session: session)
}
.onAppear {
// Prewarm saat user mendekati fitur AI
session.prewarm()
}
}
}
Batasan dan Pertimbangan Penting
Sehebat apa pun Foundation Models framework, ada beberapa batasan yang perlu kamu ketahui (supaya nggak kecewa di tengah jalan):
- Batas Token 4096: Total input dan output gabungan dibatasi 4096 token. Untuk percakapan panjang atau dokumen besar, kamu perlu kelola konteks secara cermat.
- Bukan untuk Pengetahuan Umum: Model ini nggak dirancang untuk menjawab pertanyaan faktual tentang dunia. Gunakan untuk tugas pemrosesan teks — ringkasan, klasifikasi, generasi terstruktur.
- Ketergantungan Hardware: Cuma jalan di perangkat Apple Silicon yang mendukung Apple Intelligence. Siapkan fallback untuk perangkat lama.
- Masih Beta: API masih bisa berubah sebelum rilis final, jadi pantau terus catatan rilis Apple.
- Performa Bahasa: Model paling optimal dengan bahasa Inggris. Untuk bahasa lain, hasilnya bisa bervariasi.
Kesimpulan
Foundation Models framework di iOS 26 itu, secara jujur, langkah besar Apple dalam membawa AI ke tangan developer. Dengan API Swift yang intuitif, output terstruktur lewat @Generable dan @Guide, streaming responsif, serta Tool protocol yang extensible — framework ini membuka peluang besar buat bikin aplikasi cerdas yang tetap menjaga privasi pengguna.
Kunci suksesnya? Pahami batasan dan kekuatan model ini. Gunakan untuk tugas pemrosesan dan generasi teks, bukan sebagai pengganti layanan AI cloud. Optimalkan penggunaan token, tulis instruksi yang jelas (dalam bahasa Inggris!), dan selalu tangani kasus ketidaktersediaan model dengan baik.
Developer yang menguasai Foundation Models framework dari sekarang bakal punya keunggulan tersendiri saat iOS 26 dirilis nanti. Jadi, mulai eksperimen sekarang — contoh-contoh kode di artikel ini bisa jadi titik awal yang bagus. Selamat ngoding!