Работа с базами данных в Swift: PostgreSQL и MongoDB

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

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

Начать курс

Введение: Базы данных в экосистеме Swift

Swift давно вышел за рамки простой разработки мобильных приложений. С появлением серверных фреймворков, таких как Vapor и Perfect, язык стал полноценным инструментом для создания бэкендов. Однако работа с базами данных на Swift имеет свою специфику. В отличие от Python или JavaScript, здесь нет встроенных драйверов — всё решается через сторонние пакеты.

В этой статье мы разберём, как подключаться к двум популярным СУБД: реляционной PostgreSQL и документо-ориентированной MongoDB. Вы узнаете, какие библиотеки использовать, как настроить подключение и выполнять базовые CRUD-операции. Примеры ориентированы на iOS/macOS, но код будет работать и в серверном окружении.

PostgreSQL в Swift: классика через Vapor и Fluent

Выбор библиотеки

Для работы с PostgreSQL в Swift стандартом де-факто является Fluent — ORM от фреймворка Vapor. Fluent поддерживает PostgreSQL, MySQL, SQLite и MongoDB (через отдельный драйвер). Если вам нужна работа без Vapor, используйте PostgresClientKit — низкоуровневый драйвер.

Настройка подключения

Добавьте зависимость в Package.swift:

.package(url: "https://github.com/vapor/fluent-postgres-driver.git", from: "2.0.0")

Базовая конфигурация в configure.swift:

import Fluent
import FluentPostgresDriver
import Vapor

public func configure(_ app: Application) throws {
    app.databases.use(.postgres(
        hostname: "localhost",
        port: 5432,
        username: "swift_user",
        password: "secret",
        database: "swift_db"
    ), as: .psql)
    
    // Добавляем миграции
    app.migrations.add(CreateUser())
}

CRUD-операции на примере модели User

Создадим модель и миграцию:

final class User: Model, Content {
    static let schema = "users"
    
    @ID(key: .id)
    var id: UUID?
    
    @Field(key: "name")
    var name: String
    
    @Field(key: "email")
    var email: String
    
    init() { }
    
    init(id: UUID? = nil, name: String, email: String) {
        self.id = id
        self.name = name
        self.email = email
    }
}

struct CreateUser: Migration {
    func prepare(on database: Database) -> EventLoopFuture<Void> {
        database.schema("users")
            .id()
            .field("name", .string, .required)
            .field("email", .string, .required)
            .create()
    }
    
    func revert(on database: Database) -> EventLoopFuture<Void> {
        database.schema("users").delete()
    }
}

Пример запроса в контроллере:

// Создание пользователя
let user = User(name: "Алексей", email: "alex@example.com")
try await user.save(on: req.db)

// Поиск всех пользователей
let users = try await User.query(on: req.db).all()

// Фильтрация по email
let filtered = try await User.query(on: req.db)
    .filter(\.$email == "alex@example.com")
    .first()

Важно: Fluent использует асинхронность через EventLoopFuture или async/await (начиная с Vapor 4.60+). Убедитесь, что ваша среда поддерживает Swift 5.5+.

MongoDB в Swift: гибкость документов с MongoKitten

Выбор драйвера

Для MongoDB основная библиотека — MongoKitten. Она поддерживает все современные версии MongoDB, включая Atlas (облачная версия). Альтернатива — MongoSwift от MongoDB Inc., но MongoKitten считается более "свифтовой" по стилю.

Подключение к базе

Добавьте в Package.swift:

.package(url: "https://github.com/OpenKitten/MongoKitten.git", from: "7.0.0")

Пример подключения (для iOS/macOS):

import MongoKitten

let connectionString = "mongodb://localhost:27017/swift_db"
let db = try await MongoDatabase.connect(to: connectionString)

// Получаем коллекцию
let usersCollection = db["users"]

CRUD-операции с документами BSON

MongoKitten работает с типом Document — аналогом словаря:

// Вставка документа
let newUser: Document = [
    "name": "Мария",
    "email": "maria@example.com",
    "age": 28
]
try await usersCollection.insert(newUser)

// Поиск всех документов
let allUsers = try await usersCollection.find().drain()

// Поиск с фильтром
let query: Document = ["age": ["$gt": 25]]
let filteredUsers = try await usersCollection.find(query).drain()

// Обновление
try await usersCollection.updateMany(
    where: ["name": "Мария"],
    to: ["$set": ["age": 29]]
)

// Удаление
try await usersCollection.deleteOne(where: ["email": "maria@example.com"])

Для работы с Codable используйте расширение:

struct User: Codable {
    let name: String
    let email: String
    let age: Int
}

let user = User(name: "Иван", email: "ivan@example.com", age: 35)
let doc = try BSONEncoder().encode(user)
try await usersCollection.insert(doc)

Сравнение PostgreSQL и MongoDB для Swift-проектов

Блоги

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

Критерий PostgreSQL (Fluent) MongoDB (MongoKitten)
Тип данных Реляционные, строгая схема Документы, гибкая схема
Производительность Высокая для сложных JOIN Высокая для простых запросов
Миграции Встроенные через Fluent Ручное управление
Транзакции Полная поддержка ACID Поддержка с версии 4.0
Сложность для Swift Средняя (требует Vapor) Низкая (работает standalone)