πŸ†• iOS 26

πŸ€– μ˜¨λ””λ°”μ΄μŠ€ AI 챗봇 λ§Œλ“€κΈ°

Foundation Modelsλ₯Ό ν™œμš©ν•΄ ν”„λΌμ΄λ²„μ‹œλ₯Ό λ³΄ν˜Έν•˜λŠ” AI 앱을 λ§Œλ“­λ‹ˆλ‹€. λͺ¨λ“  μ²˜λ¦¬κ°€ κΈ°κΈ° λ‚΄μ—μ„œ μ΄λ£¨μ–΄μ§‘λ‹ˆλ‹€.

✨ Foundation Models νŠΉμ§•

β€’ ν”„λΌμ΄λ²„μ‹œ 보호 (데이터가 κΈ°κΈ°λ₯Ό λ– λ‚˜μ§€ μ•ŠμŒ)
β€’ μ˜€ν”„λΌμΈ 지원 (λ„€νŠΈμ›Œν¬ λΆˆν•„μš”)
β€’ Swift λ„€μ΄ν‹°λΈŒ async/await API

πŸ’¬ κΈ°λ³Έ μ‚¬μš©λ²• (LanguageModelSession)

import FoundationModels

// μ„Έμ…˜ 생성 (μ‹œμŠ€ν…œ ν”„λ‘¬ν”„νŠΈ μ„€μ •)
let session = LanguageModelSession(
    instructions: "μΉœμ ˆν•œ AI μ–΄μ‹œμŠ€ν„΄νŠΈμž…λ‹ˆλ‹€"
)

// 응닡 μš”μ²­
let response = try await session.respond(to: "μ•ˆλ…•ν•˜μ„Έμš”")
print(response.content)

πŸ”„ 슀트리밍 응닡

// 슀트리밍으둜 μ‹€μ‹œκ°„ 응닡 λ°›κΈ°
let stream = session.streamResponse(to: prompt)
for try await partial in stream {
    response = partial.outputSoFar  // μ§€κΈˆκΉŒμ§€ μƒμ„±λœ ν…μŠ€νŠΈ
}

πŸ”§ Tool Calling

@Tool
struct WeatherTool {
    static let description = "ν˜„μž¬ 날씨λ₯Ό μ‘°νšŒν•©λ‹ˆλ‹€"

    @Parameter(description: "λ„μ‹œ 이름")
    var city: String

    func execute() async -> String {
        return "\(city) 날씨: λ§‘μŒ, 23Β°C"
    }
}

πŸ’¬ SwiftUI 챗봇 κ΅¬ν˜„

import SwiftUI
import FoundationModels

struct ChatView: View {
    @State private var messages: [Message] = []
    @State private var inputText = ""
    @State private var isGenerating = false
    @State private var streamingText = ""

    // LanguageModelSession μ‚¬μš©
    @State private var session = LanguageModelSession(
        instructions: "μΉœμ ˆν•œ AI μ–΄μ‹œμŠ€ν„΄νŠΈμž…λ‹ˆλ‹€. ν•œκ΅­μ–΄λ‘œ λ‹΅λ³€ν•˜μ„Έμš”."
    )

    var body: some View {
        VStack {
            ScrollView {
                ForEach(messages) { message in
                    MessageBubble(message: message)
                }
                // 슀트리밍 쀑인 응닡 ν‘œμ‹œ
                if !streamingText.isEmpty {
                    MessageBubble(message: Message(role: .assistant, content: streamingText))
                }
            }

            HStack {
                TextField("λ©”μ‹œμ§€ μž…λ ₯", text: $inputText)
                    .textFieldStyle(.roundedBorder)

                Button("전솑") {
                    sendMessage()
                }
                .disabled(isGenerating || inputText.isEmpty)
            }
            .padding()
        }
    }

    func sendMessage() {
        let userMessage = Message(role: .user, content: inputText)
        messages.append(userMessage)
        let prompt = inputText
        inputText = ""
        isGenerating = true
        streamingText = ""

        Task {
            do {
                // 슀트리밍 응닡
                let stream = session.streamResponse(to: prompt)
                for try await partial in stream {
                    streamingText = partial.outputSoFar
                }
                // μ™„λ£Œ ν›„ λ©”μ‹œμ§€ 배열에 μΆ”κ°€
                messages.append(Message(role: .assistant, content: streamingText))
                streamingText = ""
            } catch {
                print("였λ₯˜: \(error)")
            }
            isGenerating = false
        }
    }
}

struct Message: Identifiable {
    let id = UUID()
    let role: MessageRole
    var content: String
}

enum MessageRole {
    case user, assistant
}

βš™οΈ λͺ¨λΈ μ„€μ • & μ—λŸ¬ 처리

do {
    // Temperature μ‘°μ • (0.0 = 일관성, 1.0 = μ°½μ˜μ„±)
    let result = try await model.generate(
        prompt: prompt,
        temperature: 0.7,
        maxTokens: 500
    )
} catch let error as FoundationModelsError {
    switch error {
    case .modelNotAvailable:
        print("λͺ¨λΈμ΄ λ‹€μš΄λ‘œλ“œλ˜μ§€ μ•Šμ•˜μŠ΅λ‹ˆλ‹€")
    case .insufficientMemory:
        print("λ©”λͺ¨λ¦¬κ°€ λΆ€μ‘±ν•©λ‹ˆλ‹€")
    default:
        print("였λ₯˜ λ°œμƒ: \(error)")
    }
}

πŸ’‘ HIG κ°€μ΄λ“œλΌμΈ
AI κΈ°λŠ₯ μ‚¬μš© μ‹œ "μ˜¨λ””λ°”μ΄μŠ€μ—μ„œ 처리됨"을 λͺ…μ‹œν•˜μ„Έμš”. μ‚¬μš©μžλŠ” ν”„λΌμ΄λ²„μ‹œλ₯Ό μ€‘μš”ν•˜κ²Œ μƒκ°ν•©λ‹ˆλ‹€. 처음 μ‚¬μš© μ‹œ AIκ°€ λ‹€μš΄λ‘œλ“œλ˜λŠ” λ™μ•ˆ μ§„ν–‰ 상황을 λ³΄μ—¬μ£ΌλŠ” 것이 μ’‹μŠ΅λ‹ˆλ‹€.

πŸ“¦ ν•™μŠ΅ 자료

πŸ“š
DocC νŠœν† λ¦¬μ–Ό
πŸ’»
GitHub ν”„λ‘œμ νŠΈ
🍎
Apple HIG 원문