Realm для Swift: полное руководство с примерами

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

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

Начать курс

Что такое 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 (рекомендуется)

  1. В Xcode откройте проект: File → Add Packages...
  2. Вставьте URL: https://github.com/realm/realm-swift.git
  3. Выберите версию (рекомендуется последняя стабильная) и нажмите Add Package.
  4. В появившемся окне выберите пакет 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                    

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