ChatGPT API на Swift: интеграция за 30 минут

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Почему Swift и ChatGPT — это любовь с первого коммита

Представьте: вы пишете приложение на Swift, и вдруг понимаете, что ему не хватает «мозгов». Пользователь задаёт вопрос, а ваш код молчит, как рыба об лёд. Знакомо? До недавнего времени добавить интеллект в iOS-приложение было сложно: нужно было либо писать свою NLP-модель, либо подключать тяжёлые библиотеки. Теперь всё иначе.

ChatGPT API на Swift — это не просто модная фишка, а реальный инструмент, который может превратить ваше приложение в умного помощника. Хотите сделать AI-ассистента, генератор контента или просто чат-бота? Всё это можно реализовать за один вечер. В этой статье я покажу, как подключить OpenAI API в Swift с нуля, без лишней магии и сложных архитектур. Поехали!

Что нам понадобится?

Прежде чем прыгать в код, давайте разберёмся с инструментами. Для работы с ChatGPT в iOS вам нужно всего три вещи:

  • API-ключ OpenAI — регистрируетесь на platform.openai.com, создаёте ключ в разделе API Keys. Храните его как зеницу ока — не коммитьте в Git!
  • Xcode 15+ — чтобы использовать современный Swift с async/await.
  • Базовое понимание SwiftUI или UIKit — я покажу пример на SwiftUI, но логика работы с сетью универсальна.

Готовы? Тогда открывайте Xcode, создавайте новый проект (iOS App, SwiftUI, язык — Swift) и поехали писать код.

Архитектура запроса: как «говорить» с ChatGPT на Swift

OpenAI предоставляет REST API. Это значит, что мы будем отправлять HTTP-запросы и получать JSON-ответы. Всё общение идёт через endpoint https://api.openai.com/v1/chat/completions. Нам нужно отправить POST-запрос с телом, содержащим массив сообщений (роль + контент), и получить ответ от модели.

Давайте создадим модель данных для запроса и ответа. Это идеальный пример использования Decodable в Swift.

// Модель для одного сообщения
struct Message: Codable {
    let role: String      // "system", "user" или "assistant"
    let content: String
}

// Структура запроса к API
struct ChatGPTRequest: Codable {
    let model: String
    let messages: [Message]
    let temperature: Double?

    init(model: String = "gpt-4o-mini", messages: [Message], temperature: Double = 0.7) {
        self.model = model
        self.messages = messages
        self.temperature = temperature
    }
}

// Структура ответа
struct ChatGPTResponse: Codable {
    struct Choice: Codable {
        let message: Message
        let finishReason: String?

        enum CodingKeys: String, CodingKey {
            case message
            case finishReason = "finish_reason"
        }
    }

    let choices: [Choice]
}

Обратите внимание на CodingKeys — в Swift имена свойств принято писать в camelCase, а API возвращает snake_case. Это стандартный приём для работы с API в Swift.

Сетевой слой: URLSession + async/await

Теперь самое интересное — отправка запроса. Я буду использовать современный async/await, чтобы код был чистым и читаемым. Никаких замыканий и делегатов — только красота.

class ChatGPTService {

    private let apiKey: String
    private let session = URLSession.shared
    private let baseURL = URL(string: "https://api.openai.com/v1/chat/completions")!

    init(apiKey: String) {
        self.apiKey = apiKey
    }

    func sendMessage(_ text: String) async throws -> String {

        // Формируем сообщения: system prompt + запрос пользователя
        let systemMessage = Message(role: "system",
                                    content: "Ты — полезный ассистент. Отвечай кратко и по делу.")
        let userMessage = Message(role: "user", content: text)

        let requestBody = ChatGPTRequest(messages: [systemMessage, userMessage])

        // Настраиваем URLRequest
        var urlRequest = URLRequest(url: baseURL)
        urlRequest.httpMethod = "POST"
        urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
        urlRequest.setValue("Bearer \(apiKey)", forHTTPHeaderField: "Authorization")
        urlRequest.httpBody = try JSONEncoder().encode(requestBody)

        // Отправляем запрос
        let (data, response) = try await session.data(for: urlRequest)

        // Проверяем статус код
        guard let httpResponse = response as? HTTPURLResponse,
              (200...299).contains(httpResponse.statusCode) else {
            throw URLError(.badServerResponse)
        }

        // Декодируем ответ
        let chatResponse = try JSONDecoder().decode(ChatGPTResponse.self, from: data)

        // Возвращаем текст ответа
        return chatResponse.choices.first?.message.content ?? "Нет ответа"
    }
}

Всё гениальное просто. Мы создали класс ChatGPTService, который принимает API-ключ и умеет отправлять сообщения. Обратите внимание на system prompt — это ваша возможность задать модели стиль поведения. Хотите, чтобы ChatGPT отвечал как пират? Просто поменяйте текст промпта.

Подключаем к UI: SwiftUI View

Теперь давайте сделаем красивое окно чата. Минимум кода — максимум эффекта. Мы создадим ChatViewModel, который будет управлять состоянием, и простую SwiftUI View.

import SwiftUI

@MainActor
class ChatViewModel: ObservableObject {

    @Published var messages: [Message] = []
    @Published var isLoading = false

    private let service: ChatGPTService

    init(apiKey: String) {
        self.service = ChatGPTService(apiKey: apiKey)
        // Добавляем приветственное сообщение от ассистента
        messages.append(Message(role: "assistant", content: "Привет! Чем могу помочь?"))
    }

    func send(_ text: String) {
        guard !text.trimmingCharacters(in: .whitespaces).isEmpty else { return }

        // Добавляем сообщение пользователя
        let userMessage = Message(role: "user", content: text)
        messages.append(userMessage)
        isLoading = true

        Task {
            do {
                let reply = try await service.sendMessage(text)
                let assistantMessage = Message(role: "assistant", content: reply)
                messages.append(assistantMessage)
            } catch {
                let errorMessage = Message(role: "assistant", content: "Ошибка: \(error.localizedDescription)")
                messages.append(errorMessage)
            }
            isLoading = false
        }
    }
}

struct ChatView: View {

    @StateObject private var viewModel = ChatViewModel(apiKey: "ВАШ_КЛЮЧ_ЗДЕСЬ")
    @State private var inputText = ""

    var body: some View {
        VStack {
            // Список сообщений
            ScrollViewReader { proxy in
                ScrollView {
                    LazyVStack(spacing: 12) {
                        ForEach(Array(viewModel.messages.enumerated()), id: \.offset) { index, message in
                            MessageBubble(message: message)
                                .id(index)
                        }

                        if viewModel.isLoading {
                            ProgressView()
                                .padding()
                        }
                    }
                    .padding()
                }
                .onChange(of: viewModel.messages.count) { _ in
                    withAnimation {
                        proxy.scrollTo(viewModel.messages.count - 1, anchor: .bottom)
                    }
                }
            }

            // Поле ввода
            HStack {
                TextField("Введите сообщение...", text: $inputText)
                    .textFieldStyle                

Блоги

Рекомендаций по книгам