Інтеграція Apple Intelligence в iOS 19 (2026) з Swift (SwiftUI + App Intents...)

Автор Vagfo, Бер. 02, 2026, 01:14 PM

« попередня та - наступна тема »

Vagfo

Вступ
Apple Intelligence у 2026 році принесло Writing Tools, Image Playground, Genmoji та нові можливості Siri. Для розробників найпотужніша частина:
  • App Intents для визначення дій по всій системі
  • Core ML для роботи з локальними моделями
  • MLX (відкритий фреймворк ML від Apple) для легких LLM
  • Foundation Models API (відкрито для розробників у 2026 році)

У цьому посібнику ми створимо з нуля Локальний ШІ-Асистент Додаток:
  • Ввід тексту користувача → генерація відповіді локальним LLM
  • App Intents з ярликом Siri "Запитай у додатку"
  • Core ML / MLX з маленькою моделлю, що працює офлайн (наприклад, Phi-3-mini або TinyLlama)
  • SwiftUI + Observation + async/await

Вимоги
Xcode 17+, ціль iOS 19+, Mac з Apple Silicon (для MLX та перетворення моделей)
Завантажте моделі з Hugging Face та перетворіть на Core ML (coremltools або mlx-examples)

1. Налаштування Проекту
Новий проект → App (SwiftUI) → ціль iOS 19
Додайте Swift Packages (Package Dependencies):

2. Перетворення Моделі (зробіть один раз)
У терміналі (на Mac з Apple Silicon):
pip install mlx mlx_lm coremltools transformers
Приклад: Перетворіть Phi-3-mini-4k-instruct на Core ML
from mlx_lm import load, quantize
import coremltools as ct

# MLX з завантаженням та квантизацією (опціонально, щоб було менше)
model, tokenizer = load("microsoft/Phi-3-mini-4k-instruct")

# Перетворення на Core ML (легкий спосіб з mlx_lm)
# Альтернатива: huggingface-cli download microsoft/Phi-3-mini-4k-instruct
# Потім перетворіть з coremltools

Вихід: Phi3Mini.mlpackage → Перетягніть у Xcode (до Assets або папки Resources)

3. Завантаження Моделі та Інференс (Swift)
AIAssistant/Model/LLMInference.swift
import Foundation
import CoreML
import MLX
import MLXLLM // З пакету MLX Swift

@Observable class LLMInference {
    private var model: LLMModel?
    private var tokenizer: Tokenizer?
    var isLoading = true
    var error: Error?

    init() {
        Task { await loadModel() }
    }

    private func loadModel() async {
        do {
            guard let modelURL = Bundle.main.url(forResource: "Phi3Mini", withExtension: "mlmodel") else { throw NSError(domain: "Модель не знайдено", code: -1) }

            let config = MLModelConfiguration()
            config.computeUnits = .all // Neural Engine + GPU + CPU

            let mlModel = try MLModel(contentsOf: modelURL, configuration: config) // Обгортка з MLX або прямий інференс Core ML

            // Альтернатива з MLX (більш гнучка)
            // let (mlxModel, mlxTokenizer) = try await loadModelFromBundle("...")
            // клас обгортки
            self.tokenizer = ...
            self.isLoading = false
        } catch {
            self.error = error
            self.isLoading = false
        }
    }

    func generate(prompt: String, maxTokens: Int = 200) async throws -> String {
        guard let model, let tokenizer else { throw NSError(domain: "Модель не знайдено", code: -1) }

        // Простий цикл інференсу (в реальності повинен підтримувати стримінг)
        let inputTokens = tokenizer.encode(text: prompt)
        var outputTokens = inputTokens

        for _ in 0..<maxTokens {
            let logits = try await model.predict(input: outputTokens)
            let nextToken = logits.argmax()
            outputTokens.append(nextToken)
            if nextToken == tokenizer.eosTokenId { break }
        }
        return tokenizer.decode(tokens: outputTokens)
    }
}

4. Інтеграція з Системою через App Intents
AIAssistant/Intents/AnswerQuestionIntent.swift
import AppIntents

struct AnswerQuestionIntent: AppIntent {
    static var title: LocalizedStringResource = "Відповісти на Запитання Локальним ШІ"
    @Parameter(title: "Запитання")
    var question: String

    func perform() async throws -> some IntentResult & ReturnsValue<String> {
        let inference = LLMInference() // Чекати завантаження моделі (в реальності зробіть singleton)
        try await Task.sleep(for: .seconds(2))
        let response = try await inference.generate(prompt: question)
        return .result(value: response)
    }
}

Додайте до Info.plist або визначте в IntentHandler.

5. Головний Інтерфейс (SwiftUI)
ContentView.swift
struct ContentView: View {
    @State private var inference = LLMInference()
    @State private var prompt = ""
    @State private var response = ""
    @State private var isGenerating = false

    var body: some View {
        NavigationStack {
            VStack(spacing: 20) {
                if inference.isLoading {
                    ProgressView("Завантаження локальної моделі...")
                } else if let error = inference.error {
                    Text("Помилка: \(error.localizedDescription)")
                        .foregroundStyle(.red)
                } else {
                    TextEditor(text: $prompt)
                        .frame(height: 120)
                        .border(Color.gray.opacity(0.3))
                        .padding()

                    Button("Генерувати Локальним ШІ") {
                        Task {
                            isGenerating = true
                            do {
                                response = try await inference.generate(prompt: prompt)
                            } catch {
                                response = "Помилка: \(error.localizedDescription)"
                            }
                            isGenerating = false
                        }
                    }
                    .buttonStyle(.borderedProminent)
                    .disabled(isGenerating || prompt.isEmpty)

                    if isGenerating {
                        ProgressView()
                    }

                    ScrollView {
                        Text(response)
                            .frame(maxWidth: .infinity, alignment: .leading)
                            .padding()
                    }
                }
            }
            .padding()
            .navigationTitle("Локальний ШІ-Асистент")
        }
    }
}

6. Поради для Просунутого Рівня (2026)
  • Streaming response: Оновлення UI токен за токеном (з AsyncSequence)
  • Інтеграція Foundation Models API (хмарна частина Apple Intelligence)
  • Функції подібні до Writing Tools: Обраний текст перефразуйте, перепишіть тощо.
  • Додайте маніфест конфіденційності (для App Intents та ML)
  • Квантизація моделі: 4-біт або 8-біт для швидкості та меншого споживання пам'яті