Russian (Pусский) translation by Ellen Nelson (you can also view the original English article)
Ещё в 2012 году Apple представила iCloud вместе с iOS 5. В то же время компания объявила, что у разработчиков будет доступ к iCloud через ряд API. Сначала у разработчиков было три варианта:
Однако, эти API не идеальны. Основным недостатком является отсутствие прозрачности. Интеграция Core Data, в частности, привела к разочарованию и путанице даже среди самых опытных разработчиков. Когда что-то идёт не так, разработчики понятия не имеют, что или кто виноват. Может это проблема в их коде, а может Apple.
CloudKit
На WWDC 2014 Apple представила CloudKit, совершенно новую структуру, которая напрямую взаимодействует с серверами iCloud от Apple. Структура сопоставима с рядом решений PaaS (платформа как служба), вроде Firebase. Как и Firebase, Apple предоставляет гибкий API и панель управления, предлагающую разработчикам заглянуть в данные, хранящиеся на серверах iCloud Apple.
То, что мне больше всего нравится в CloudKit, так это приверженность самой Apple данной структуре. По данным компании, iCloud Drive и iCloud Photo Library построены на CloudKit. Это показывает, что структура CloudKit и её инфраструктура являются крепкой и надёжной.
Для разработчика, такой знак доверия и приверженности важен. В прошлом, Apple иногда выпускала API-интерфейсы, которые достали ошибками или отсутствием ключевых функций просто потому, что компания тогда ещё не съела собаку на этом. Это не относится к CloudKit. И это многообещающе.
Зачем использовать CloudKit?
Хранилище ключ-значение и хранилище документов, каждое используются по-своему, и Apple подчёркивает, что CloudKit не заменяет или не отменяет существующие API iCloud. То же самое и для Core Data. Например, CloudKit не предлагает локальное хранилище. Это означает, что приложение, работающее на устройстве без сетевого подключения, практически бесполезно, если оно полагается только от CloudKit.
Apple также подчёркивает, что обработка ошибок имеет решающее значение при работе с CloudKit. Например, если операция сохранения завершается неудачей, и пользователь не уведомляется, он может даже не знать, что его данные не были сохранены и потерялись.
CloudKit — отличное решение для хранения структурированных, а также неструктурированных данных в облаке. Если вам нужно решение для доступа к данным на нескольких устройствах, то CloudKit, безусловно, можно рассматривать.
На WWDC 2015 года Apple сделала то, чего ожидали или надеялись некоторые разработчики. Он объявил о веб-сервисе для CloudKit. Это означает, что CloudKit можно использовать практически на любой платформе, включая Android и Windows Phone.
Цены Apple довольно конкурентоспособны. Начало работы с CloudKit бесплатное, и оно остаётся бесплатным для большинства приложений. Опять же, CloudKit, безусловно, стоит рассмотреть, если вы планируете хранить данные в облаке.
Понимание CloudKit
Разработчики, борющиеся с Core Data, часто не знакомы со основами данной структуры. Если вы не нашли времени, чтобы узнать и понять стек Core Data, то вы неизбежно столкнётесь с проблемами. То же самое и с CloudKit.
Прежде чем мы начнём работу над примером приложения, использующим CloudKit, я хочу потратить несколько минут на ознакомление с рядом ключевых концепций структуры CloudKit и инфраструктуры. Начнём с контейнеров, баз данных и песочницы.
Конфиденциальность и политика сдерживания
Apple ясно даёт понять, что конфиденциальность является важным аспектом CloudKit. Первое, что нужно знать — у каждого приложения свой контейнер в iCloud. Эта идея очень схожа с тем, как у каждого приложения iOS есть своя песочница. Однако, есть возможность делить контейнер с другим приложением, если эти приложения относятся к аккаунту одного разработчика. А это, как вы понимаете, открывает ряд интересных возможностей для разработчиков.
Контейнер CloudKit содержит несколько баз данных. У каждого контейнера есть одна общедоступная база (на изображении: public database) данных, которую можно использовать для хранения данных, к которым нужен доступ каждому пользователю вашего приложения. В дополнение к общей базе, в контейнере также есть частная база (на изображении: private database) данных для каждого пользователя приложения. Частная база пользователя используется для хранения данных, которые относятся к конкретному пользователю. Сегрегация и инкапсуляция данных является ключевым компонентом инфраструктуры CloudKit и iCloud.
Хотя контейнер приложения может содержать множество баз данных (БД), с точки зрения разработчика контейнер содержит только две БД: общедоступную (public database) и частную базу (private database) пользователя, который в данный момент подключён к своему аккаунту iCloud. В 2017 году, Apple представила третью базу — коллективную базу данных (shared database), предоставляя приложениям возможность делиться подмножеством общих записей с частной БД другого пользователя, предлагая им сотрудничество по открытым записям.
Я расскажу об учётных записях iCloud немного позже.
Записи и зоны записи
Базы данных в контейнере приложения хранят записи. Это не очень отличается от традиционной базы данных. На первый взгляд записи, хранящиеся в базе данных CloudKit, кажутся не чем иным, как обёртками для словаря пар ключ-значение. Они могут выглядеть как прославленные словари, но это только часть истории.
У каждой записи есть тип записи и несколько полей метаданных. Метаданные записи отслеживают, когда была создана запись, какой пользователь создал запись, когда запись была обновлена последний раз, и кто обновил эту запись.
Класс CKRecord
представляет определённую запись, и это довольно мощный класс. Значения, которые вы можете сохранить в записи, не ограничены типами списка свойств. В записи можно хранить строки, числа, даты и массив двоичных данных (blob), но класс CKRecord
ещё обрабатывает данные о местоположении — CLLocation
, как тип данных первого класса.
В записи можно хранить даже массивы поддерживаемых типов данных. Другими словами, для экземпляра CKRecord
, массивы строк или чисел не являются проблемой.
Записи собраны в зоны записей. Зона записей группирует связанные записи. У каждой общей и частной базы данных есть зона записи по умолчанию, но при необходимости можно создать собственные зоны записи. Зоны записи — это отдалённая тема, о которой мы не будем подробно обсуждать в этой серии.
Взаимосвязи
Взаимосвязи между записями управляются экземплярами класса CKReference
. Давайте посмотрим на пример, чтобы лучше понять, как работают эти связи. Приложение, которое мы создадим в этой серии, будет управлять несколькими списками покупок. В каждом списке может быть ноль или больше элементов. Это означает, что каждый элемент должен иметь ссылку на список, к которому он принадлежит.



Важно понимать, что элемент сохраняет ссылку на список. Хотя для элементов списка можно создать массив экземпляров CKReference
, более удобно и рекомендуется, хранить внешний ключ с элементом, а не списком. Это рекомендует даже Apple.
Способ, которым CloudKit управляет связями, довольно простой, но он предоставляет возможность автоматически удалять дочерние записи, когда удаляется родительская запись. Чуть позже в этой серии мы более подробно рассмотрим связи.
Активы
Я также хотел бы упомянуть класс CKAsset
. Несмотря на то, что в записи можно хранить blob, неструктурированные данные (например, изображения, аудио и видео) должны храниться как экземпляры CKAsset
. Экземпляр CKAsset
всегда связан с записью и соответствует файлу на диске. В этом выпуске мы не будем работать с классом CKAsset
.
Авторизация
Я уверен, вы согласны с тем, что CloudKit выглядит довольно привлекательно. Однако есть важная деталь, о которой мы ещё не говорили — авторизация. Пользователи авторизируются через свои учётные записи iCloud. Пользователи, которые не вошли в учётную запись iCloud, не могут записывать данные в iCloud.
Хотя это верно для любого из iCloud API, имейте в виду, что в этом случае приложения, которые полагаются исключительно на CloudKit, не будут очень функциональными. Все, что сможет сделать пользователь — это доступ к данным в общей (публичной) базе данных, если разработчик это разрешил.
Чтение данных
Пользователи, которые не вошли в свои учётные записи iCloud, могут читать данные из общедоступной базы данных. Само собой разумеется, что частная база данных недоступна, поскольку iCloud не знает, кто использует приложение.
Чтение и запись
При входе в систему пользователи могут читать и записывать в общую и их частную базы данных. Я уже упоминал, что Apple очень серьёзно относится к конфиденциальности. Поэтому, записи, хранящиеся в частной базе данных, доступны только пользователю. Даже вы, как разработчик, не можете видеть данные, которые пользователи хранят в частных базах данных. Это не лучший момент в том, как Apple работает с бэкендом вашего приложения, но это точно победа для пользователя.
Список покупок
Приложение, которое мы собираемся построить, будет управлять списками покупок. Каждый список покупок будет иметь имя, ноль или более элементов. После создания приложения со списком покупок, вы должны довольно комфортно чувствовать себя при использовании структуры CloudKit в собственном проекте.
Что потребуется
Для этого урока я буду использовать Xcode 9 и Swift 4. Если вы используете более старую версию Xcode, тогда имейте в виду, что вы используете другую версию языка программирования Swift. Это означает, что вам нужно будет обновить исходный код проекта, чтобы отвечать требованиям компилятора. Изменения в основном незначительны, но важно знать об этом.
Поскольку CloudKit — это дополнительная тема, я предположу, что вы знакомы с Xcode и языком программирования Swift. Если вы новичок в iOS разработке, я рекомендую сначала прочитать ознакомительный урок или взять один из наших курсов по разработке на Swift:
- SwiftCreate iOS Apps With SwiftMarkus Mühlberger
- Design PatternsSwift Design PatternsDerek Jensen
Обязательно посмотрите их, если вы новичок в разработке для iOS или на языке Swift.
Настройка проекта
Пришло время начать писать код. Запустите Xcode и создайте новый проект на основе шаблона Single View Application.



Задайте название (name) вашему проекту и идентификатор организации (organization identifier). Получаемый идентификатор пакеты будет использоваться для создания идентификатора контейнера вашего приложения по умолчанию. Этот идентификатор должен быть уникальным для всех учётных записей разработчиков, поскольку они разделяют одно глобальное пространство имён. Поэтому важно следовать рекомендациям Apple и использовать доменное имя в обратную сторону.



Подключение iCloud
Следующий шаг — включение iCloud и CloudKit. Выберите проект в Project Navigator слева и выберите цель для вашего приложения из списка целей. Откройте вкладку General и установите правильную команду разработчика в поле Team. Чтобы избежать каких-либо проблем на следующем шаге, убедитесь, что ваша учётная запись разработчика имеет необходимые разрешения для создания App ID.



Затем, сверху откройте вкладку Capabilities и установите переключатель в положение включено для iCloud. Xcode потребуется время, чтобы создать для вас App ID. Он также добавит необходимые права на идентификатор приложения (App ID). Если это не сработает, убедитесь, что команда установлена правильно, и у вас есть необходимые разрешения для создания идентификатора приложения.



Включить CloudKit настолько просто, как отметить флажок с надписью CloudKit. По умолчанию, ваше приложение будет использовать контейнер для вашего приложения по умолчанию. Этот контейнер автоматически создаётся для вас при включении CloudKit.
Если вашему приложению требуется доступ к другому контейнеру или нескольким контейнерам, установите флажок Specify custom containers и отметьте, к каким контейнерам требуется доступ к вашему приложению.



Возможно, вы заметили, что Xcode автоматически связал вашу цель с облачной структурой CloudKit. Это означает, что всё готово к использованию CloudKit в вашем приложении.
Замараем наши ручки
В следующем уроке этого цикла мы добавим возможность добавлять, редактировать и удалять списки покупок. Однако, чтобы закончить этот урок, я хочу, чтобы вы замарали ваши ручки, показав вам, как взаимодействовать с CloudKit API. Всё, что мы сделаем, это получим запись текущего пользователя, вошедшего в систему.
Откройте ViewController.swift и сверху добавьте оператор импорта, чтобы импортировать среду CloudKit.
1 |
import UIKit |
2 |
import CloudKit |
Чтобы извлечь запись пользователя, сначала нужно получить идентификатор записи. Давайте посмотрим, как это работает. Я создал вспомогательный метод — fetchUserRecordID
, который содержит алгоритм получения идентификатора записи пользователя. Мы вызываем этот метод в методе viewDidLoad
контроллера вида.
1 |
override func viewDidLoad() { |
2 |
super.viewDidLoad() |
3 |
|
4 |
// Fetch User Record ID
|
5 |
fetchUserRecordID() |
6 |
}
|
Реализация fetchUserRecordID
немного интереснее, чем viewDidLoad
. Сначала мы получаем ссылку на стандартный контейнер приложения, вызывая defaultContainer
в классе CKContainer
. Затем, вызываем fetchUserRecordIDWithCompletionHandler(_:)
для defaultContainer
. В качестве единственного аргумента, этот метод принимает замыкание (closure).
1 |
private func fetchUserRecordID() { |
2 |
// Fetch Default Container
|
3 |
let defaultContainer = CKContainer.default() |
4 |
|
5 |
// Fetch User Record
|
6 |
defaultContainer.fetchUserRecordID { (recordID, error) -> Void in |
7 |
if let responseError = error { |
8 |
print(responseError) |
9 |
|
10 |
} else if let userRecordID = recordID { |
11 |
DispatchQueue.main.sync { |
12 |
self.fetchUserRecord(recordID: userRecordID) |
13 |
}
|
14 |
}
|
15 |
}
|
16 |
}
|
Замыкание принимает два аргумента: дополнительный экземпляр CKRecordID
и дополнительный экземпляр NSError
. Если ошибка (error
) равна нулю (nil
), мы безопасно разворачиваем recordID
.
Важно подчеркнуть, что замыкание будет вызываться в фоновом потоке. Это означает, что вам нужно быть осторожным при обновлении пользовательского интерфейса приложения из замыкания, вызванного CloudKit. Например, в fetchUserRecordID
я явно указываю на вызов fetchUserRecord(_:)
в основном потоке.
В fetchUserRecord(_ :)
, мы получаем запись пользователя, сообщая CloudKit, какую запись мы хотим получить. Обратите внимание, что мы вызываем fetchRecordWithID(_:completionHandler:)
для объекта privateDatabase
, свойства объекта defaultContainer
.
Метод принимает экземпляр CKRecordID
и обработчик завершения. Последний принимает дополнительный экземпляр CKRecord
и экземпляр NSError
. Если мы успешно получаем запись пользователя, то напечатаем её в консоли Xcode.
1 |
private func fetchUserRecord(recordID: CKRecordID) { |
2 |
// Fetch Default Container
|
3 |
let defaultContainer = CKContainer.default() |
4 |
|
5 |
// Fetch Private Database
|
6 |
let privateDatabase = defaultContainer.privateCloudDatabase |
7 |
|
8 |
// Fetch User Record
|
9 |
privateDatabase.fetch(withRecordID: recordID) { (record, error) -> Void in |
10 |
if let responseError = error { |
11 |
print(responseError) |
12 |
|
13 |
} else if let userRecord = record { |
14 |
print(userRecord) |
15 |
}
|
16 |
}
|
17 |
}
|
Когда запустите ваше приложение в Xcode, в консоли увидите что-то похожее на:



Это должно было дать вам представление о платформе CloudKit. Его современный API интуитивно понятен и прост в использовании. В следующем уроке мы углубимся в возможности CloudKit API.
Вы можете клонировать образец всего проекта с GitHub (метка #introduction
).
В заключение
Теперь у вас должно быть правильное понимание основ CloudKit. Остальная часть этого цикла будет ориентирована на создание приложения списка покупок. В следующем уроке мы начнём с добавления возможности добавления, редактирования и удаления списков покупок.