Что такое Realm и зачем он нужен?
Realm — это мобильная база данных, созданная как альтернатива Core Data и SQLite. В отличие от традиционных реляционных баз, Realm работает как объектно-ориентированное хранилище: вы работаете с нативными объектами Swift, а не с SQL-запросами или NSManagedObject. Realm хранит данные в собственном формате, что обеспечивает скорость работы, близкую к работе с оперативной памятью.
Основные преимущества Realm:
- Скорость: Realm работает в 10-100 раз быстрее, чем SQLite на мобильных устройствах.
- Простота: Вы работаете с обычными Swift-объектами, наследуя их от Realm.Object.
- Автоматическое обновление: Результаты запросов (Results) автоматически обновляются при изменении данных.
- Live-объекты: Объекты Realm автоматически синхронизируются с базой данных.
- Кроссплатформенность: Realm работает на iOS, macOS, tvOS и watchOS.
Realm идеально подходит для приложений, где требуется быстрый доступ к данным на устройстве: todo-списки, мессенджеры, кэширование данных из сети, приложения для заметок и многие другие.
Установка Realm в Swift
Realm для Swift устанавливается через Swift Package Manager (SPM), CocoaPods или Carthage. Рассмотрим два самых популярных способа.
Установка через Swift Package Manager (рекомендуется)
- В Xcode откройте проект:
File → Add Packages... - Вставьте URL:
https://github.com/realm/realm-swift.git - Выберите версию (рекомендуется последняя стабильная) и нажмите
Add Package. - В появившемся окне выберите пакет
RealmиRealmSwiftдля вашей целевой сборки.
Установка через CocoaPods
Добавьте в Podfile:
target 'YourApp' do
use_frameworks!
pod 'RealmSwift'
end
Затем выполните в терминале:
pod install
Импорт в коде
import RealmSwift
Быстрый старт: первая модель и запись данных
Создадим простую модель задачи и сохраним её в базу данных.
import RealmSwift
// 1. Определяем модель
class Task: Object {
@Persisted var id: String = UUID().uuidString
@Persisted var title: String = ""
@Persisted var isCompleted: Bool = false
@Persisted var createdAt: Date = Date()
// Первичный ключ (опционально, но рекомендуется)
override static func primaryKey() -> String? {
return "id"
}
}
// 2. Сохраняем объект
let task = Task()
task.title = "Купить продукты"
task.isCompleted = false
let realm = try! Realm()
try! realm.write {
realm.add(task)
}
// 3. Читаем все задачи
let tasks = realm.objects(Task.self)
for task in tasks {
print("\(task.title) - \(task.isCompleted ? "Выполнено" : "Не выполнено")")
}
Основные методы и функции Realm
Ниже приведены ключевые методы для работы с Realm. Каждый метод сопровождается описанием, параметрами и рабочим примером.
1. Realm() — Инициализация базы данных
Что делает: Создаёт или открывает существующую базу данных Realm. По умолчанию файл базы данных находится в директории Documents приложения.
Параметры: configuration: Realm.Configuration? — опциональная конфигурация (URL файла, схема, версия миграции).
// Стандартная инициализация
let realm = try! Realm()
// Инициализация с кастомным URL
let config = Realm.Configuration(fileURL: FileManager.default
.urls(for: .documentDirectory, in: .userDomainMask)
.first?.appendingPathComponent("myrealm.realm"))
let customRealm = try! Realm(configuration: config)
2. realm.write(_:) — Запись данных в транзакции
Что делает: Выполняет блок кода внутри транзакции записи. Все изменения объектов Realm должны происходить внутри такого блока.
Параметры: withoutNotifying: [NotificationToken] — опциональный массив токенов уведомлений, которые не должны срабатывать.
let realm = try! Realm()
let task = Task()
task.title = "Новая задача"
try! realm.write {
realm.add(task)
}
// Можно выполнить несколько операций в одной транзакции
try! realm.write {
realm.add(task1)
realm.add(task2)
task3.title = "Обновлённый заголовок"
}
3. realm.add(_:update:) — Добавление объекта
Что делает: Добавляет новый объект в базу данных. Если объект с таким же первичным ключом уже существует, можно обновить его.
Параметры:
_ object: Object— объект для добавления.update: Realm.UpdatePolicy— политика обновления:.error(выбросить ошибку при дубликате),.modified(обновить только изменённые поля),.all(обновить все поля).
let task = Task()
task.id = "unique-id-123"
task.title = "Новая задача"
try! realm.write {
realm.add(task, update: .modified)
}
4. realm.objects(_:) — Получение всех объектов типа
Что делает: Возвращает Results<T> — ленивую коллекцию всех объектов указанного типа. Результаты автоматически обновляются при изменении данных.
Параметры: _ type: Object.Type — тип объектов (например, Task.self).
let allTasks = realm.objects(Task.self)
print("Всего задач: \(allTasks.count)")
// Фильтрация через NSPredicate
let completedTasks = realm.objects(Task.self)
.filter("isCompleted == true")
// Сортировка
let sortedTasks = realm.objects(Task.self)
.sorted(byKeyPath: "createdAt", ascending: false)
5. Results.filter(_:) — Фильтрация запросов
Что делает: Применяет предикат к результатам запроса, возвращая отфильтрованную коллекцию.
Параметры: _ predicate: NSPredicate или строка с форматом предиката.
// Фильтр по строковому формату
let tasksFromToday = realm.objects(Task.self)
.filter("createdAt >= %@", Calendar.current.startOfDay(for: Date()))
// Фильтр с IN
let specificTasks = realm.objects(Task.self)
.filter("id IN %@", ["id1", "id2", "id3"])
// Комбинированный фильтр
let complexFilter = realm.objects(Task.self)
.filter("title CONTAINS[c] %@ AND isCompleted == false", "важно")
6. realm.delete(_:) — Удаление объектов
Что делает: Удаляет объект или коллекцию объектов из базы данных.
Параметры: _ object: Object или Results<Object> или List<Object>.
// Удаление одного объекта
if let task = realm.objects(Task.self).first {
try! realm.write {
realm.delete(task)
}
}
// Удаление всех выполненных задач
let completedTasks = realm.objects(Task.self)
.filter("isCompleted == true")
try! realm.write {
realm.delete(completedTasks)
}
// Удаление всех объектов типа
try! realm.write {
realm.delete(realm.objects(Task.self))
}
7. realm.object(ofType:forPrimaryKey:) — Поиск по первичному ключу
Что делает: Быстрый поиск объекта по его первичному ключу. Возвращает опциональный объект.
Параметры:
ofType: Object.Type— тип объекта.forPrimaryKey: Any— значение первичного ключа.
if let task = realm.object(ofType: Task.self, forPrimaryKey: "unique-id-123") {
print("Найдена задача: \(task.title)")
} else {
print("Задача не найдена")
}
8. Results.sorted(byKeyPath:ascending:) — Сортировка результатов
Что делает: Сортирует коллекцию по указанному ключу. Можно сортировать по нескольким ключам.
Параметры:
byKeyPath: String— имя свойства для сортировки.ascending: Bool— порядок сортировки (true — по возрастанию).
// Сортировка по дате создания (новые сверху)
let sortedByDate = realm.objects(Task.self)
.sorted(byKeyPath: "createdAt", ascending: false)
// Сортировка по двум ключам
let sortedByStatusAndDate = realm.objects(Task.self)
.sorted(by: [
SortDescriptor(keyPath: "isCompleted", ascending: true),
SortDescriptor(keyPath: "createdAt", ascending: false)
])
9. realm.add(notificationBlock:) — Уведомления об изменениях
Что делает: Подписывается на изменения в базе данных. Возвращает токен уведомления, который нужно сохранить для отписки.
Параметры: block: (Realm.Notification, Realm) -> Void — замыкание, вызываемое при изменениях.
// Наблюдение за изменениями всей базы
let notificationToken = realm.observe { notification, realm in
print("База данных изменилась: \(notification)")
}
// Наблюдение за изменениями конкретной коллекции
let tasks = realm.objects(Task.self)
let tasksToken = tasks.observe { changes in
switch changes {
case .initial:
print("Начальная загрузка: \(tasks.count) задач")
case .update(_, let deletions, let insertions, let modifications):
print("Изменения: удалено \(deletions.count), добавлено \(insertions.count), изменено \(modifications.count)")
case .error(let error):
print("Ошибка: \(error)")
}
}
// Не забудьте отписаться, когда объект наблюдения больше не нужен
// notificationToken.invalidate()
// tasksToken.invalidate()
10. Realm.Configuration — Настройка базы данных
Что делает: Позволяет настроить различные параметры базы данных: путь к файлу, версию схемы, блок миграции, шифрование.
var config = Realm.Configuration()
config.fileURL = FileManager.default
.urls(for: .documentDirectory, in: .userDomainMask)
.first?.appendingPathComponent("myapp.realm")
config.schemaVersion = 2
config.migrationBlock = { migration, oldSchemaVersion in
if oldSchemaVersion < 2 {
// Миграция: добавляем новое поле
migration.enumerateObjects(ofType: Task.className()) { oldObject, newObject in
newObject?["priority"] = 0
}
}
}
// Применяем конфигурацию
Realm.Configuration.defaultConfiguration = config
let realm = try! Realm()
Реальный практический пример: Приложение для заметок
Создадим полноценное приложение для заметок с категориями и тегами.
import RealmSwift
// Модели данных
class Note: Object {
@Persisted var id: String = UUID().uuidString
@Persisted var title: String = ""
@Persisted var content: String = ""
@Persisted var createdAt: Date = Date()
@Persisted var modifiedAt: Date = Date()
@Persisted var category: Category?
@Persisted var Также в библиотеке
Конфигурация приложений Swift: инструменты и библиотеки для iOS/macOS
Работа с YAML в JavaScript (Node.js): парсинг, генерация и валидация
Генерация QR-кодов в Node.js: библиотека qrcode и примеры