Введение: Зачем Swift разработчику базы данных?
При разработке нативных приложений под iOS и macOS часто возникает необходимость хранить данные не только локально (Core Data, UserDefaults), но и на удаленном сервере. Это может быть серверная аналитика, логгирование, синхронизация между устройствами или полноценный бэкенд для приложения. Два самых популярных типа баз данных — реляционные (PostgreSQL) и документоориентированные (MongoDB).
В этой статье мы разберем, как подключиться к PostgreSQL и MongoDB из Swift-приложения, используя популярные библиотеки и драйверы. Особое внимание уделим практике: установке зависимостей, написанию запросов и обработке ошибок.
1. Работа с PostgreSQL из Swift
PostgreSQL — мощная реляционная СУБД, идеально подходящая для проектов, где важна целостность данных и сложные SQL-запросы. В Swift мы можем работать с PostgreSQL двумя основными способами:
- Через ORM Fluent (часть фреймворка Vapor) — высокоуровневый подход, удобный для веб-приложений.
- Напрямую через драйвер (например,
PostgresNIO) — низкоуровневый контроль, подходит для iOS/macOS приложений.
Рассмотрим второй вариант, так как он не требует Vapor и подходит для обычных приложений.
1.1. Подключение к PostgreSQL через PostgresNIO
Добавьте зависимость в Package.swift:
dependencies: [
.package(url: "https://github.com/vapor/postgres-nio.git", from: "1.18.0")
]
Пример подключения и выполнения запроса:
import PostgresNIO
import NIOCore
import NIOPosix
func queryPostgres() async throws {
let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1)
defer { try? eventLoopGroup.syncShutdownGracefully() }
let config = PostgresConnection.Configuration(
host: "localhost",
port: 5432,
username: "swift_user",
password: "secret",
database: "test_db",
tls: .disable
)
let connection = try await PostgresConnection.connect(
on: eventLoopGroup.next(),
configuration: config
)
defer { try? connection.close() }
let rows = try await connection.query("SELECT id, name FROM users LIMIT 5;", logger: nil)
for try await row in rows {
let id = try row.decode(Int.self, column: "id")
let name = try row.decode(String.self, column: "name")
print("ID: \(id), Name: \(name)")
}
}
Этот код устанавливает соединение с локальным сервером PostgreSQL, выполняет SELECT и выводит результаты. Важно: никогда не храните пароли в коде — используйте переменные окружения или Keychain.
1.2. Вставка данных с параметрами
Для защиты от SQL-инъекций используйте параметризованные запросы:
let name = "Alice"
let age = 30
try await connection.query(
"INSERT INTO users (name, age) VALUES ($1, $2);",
params: [.string(name), .int(age)],
logger: nil
)
2. Работа с MongoDB из Swift
MongoDB — NoSQL база данных, хранящая данные в виде JSON-подобных документов (BSON). Для Swift существует отличный драйвер MongoSwift.
2.1. Установка MongoSwift
Добавьте в Package.swift:
dependencies: [
.package(url: "https://github.com/mongodb/mongo-swift-driver.git", from: "1.3.0")
]
2.2. Подключение и CRUD операции
Пример подключения к локальной MongoDB и вставки документа:
import MongoSwift
func mongoExample() async throws {
let client = try MongoClient("mongodb://localhost:27017")
defer { try? client.syncClose() }
let db = client.db("test_database")
let collection = db.collection("users")
// Вставка документа
let document: Document = ["name": "Bob", "age": 25, "city": "Moscow"]
let insertResult = try await collection.insertOne(document)
print("Inserted document ID: \(insertResult?.insertedID ?? "")")
// Поиск
let query: Document = ["name": "Bob"]
if let found = try await collection.findOne(query) {
print("Found: \(found)")
}
// Обновление
let update: Document = ["$set": ["age": 26]]
try await collection.updateOne(filter: query, update: update)
// Удаление
try await collection.deleteOne(query)
}
Обратите внимание: MongoSwift использует BSON-документы, которые в Swift представлены типом Document (словарь с особыми правилами сериализации).
2.3. Работа с Codable (MongoKitten)
Если вы хотите использовать нативные Swift-структуры с Codable, рекомендую библиотеку MongoKitten. Она предоставляет более «свифтовый» интерфейс:
import MongoKitten
struct User: Codable {
let _id: ObjectId
var name: String
var age: Int
}
func mongoKittenExample() async throws {
let db = try await MongoDatabase.connect(to: "mongodb://localhost:27017/test_db")
let users = db["users"]
// Вставка
let newUser = User(_id: ObjectId(), name: "Charlie", age: 28)
try await users.insert(newUser)
// Поиск с фильтром
let cursor = try await users.find(["age": ["$gt": 20]], as: User.self)
for try await user in cursor {
print("User: \(user.name), \(user.age)")
}
}
3. Сравнение PostgreSQL и MongoDB для iOS/macOS
Выбор между SQL и NoSQL зависит от задачи. Сведем ключевые отличия в таблицу:
- PostgreSQL — строгая схема, транзакции, сложные JOIN-запросы. Идеально для финансовых систем, аналитики, любых данных с четкой структурой.
- MongoDB — гибкая схема, быстрая разработка, встроенная репликация. Подходит для логов, пользовательских профилей, каталогов товаров.
Для iOS-приложений, которые общаются с сервером через REST API, база данных обычно находится на сервере. Но если вы пишете серверную часть на Swift (например, с Vapor), то обе СУБД поддерживаются отлично.
3.1. Производительность и асинхронность
Оба драйвера (PostgresNIO и <