Advertisement
  1. Code
  2. CloudKit

Делаем приложение со списком покупок, используя CloudKit: Начало

Scroll to top
Read Time: 11 min
This post is part of a series called Building a Shopping List Application With CloudKit.
Building a Shopping List Application With CloudKit: Adding Records

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), предоставляя приложениям возможность делиться подмножеством общих записей с частной БД другого пользователя, предлагая им сотрудничество по открытым записям.

diagram of public private and shared databasesdiagram of public private and shared databasesdiagram of public private and shared databases

Я расскажу об учётных записях iCloud немного позже.

Записи и зоны записи

Базы данных в контейнере приложения хранят записи. Это не очень отличается от традиционной базы данных. На первый взгляд записи, хранящиеся в базе данных CloudKit, кажутся не чем иным, как обёртками для словаря пар ключ-значение. Они могут выглядеть как прославленные словари, но это только часть истории.

У каждой записи есть тип записи и несколько полей метаданных. Метаданные записи отслеживают, когда была создана запись, какой пользователь создал запись, когда запись была обновлена последний раз, и кто обновил эту запись.

Класс CKRecord представляет определённую запись, и это довольно мощный класс. Значения, которые вы можете сохранить в записи, не ограничены типами списка свойств. В записи можно хранить строки, числа, даты и массив двоичных данных (blob), но класс CKRecord ещё обрабатывает данные о местоположении — CLLocation, как тип данных первого класса.

В записи можно хранить даже массивы поддерживаемых типов данных. Другими словами, для экземпляра CKRecord, массивы строк или чисел не являются проблемой.

Записи собраны в зоны записей. Зона записей группирует связанные записи. У каждой общей и частной базы данных есть зона записи по умолчанию, но при необходимости можно создать собственные зоны записи. Зоны записи — это отдалённая тема, о которой мы не будем подробно обсуждать в этой серии.

Взаимосвязи

Взаимосвязи между записями управляются экземплярами класса CKReference. Давайте посмотрим на пример, чтобы лучше понять, как работают эти связи. Приложение, которое мы создадим в этой серии, будет управлять несколькими списками покупок. В каждом списке может быть ноль или больше элементов. Это означает, что каждый элемент должен иметь ссылку на список, к которому он принадлежит.

diagram of shopping list and itemsdiagram of shopping list and itemsdiagram of shopping list and items

Важно понимать, что элемент сохраняет ссылку на список. Хотя для элементов списка можно создать массив экземпляров 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:

Обязательно посмотрите их, если вы новичок в разработке для iOS или на языке Swift.

Настройка проекта

Пришло время начать писать код. Запустите Xcode и создайте новый проект на основе шаблона Single View Application.

Choose the Single View Application templateChoose the Single View Application templateChoose the Single View Application template

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

Configure the projectConfigure the projectConfigure the project

Подключение iCloud

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

Select the correct teamSelect the correct teamSelect the correct team

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

Enable iCloudEnable iCloudEnable iCloud

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

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

Enable custom containersEnable custom containersEnable 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, в консоли увидите что-то похожее на:

Code output in consoleCode output in consoleCode output in console

Это должно было дать вам представление о платформе CloudKit. Его современный API интуитивно понятен и прост в использовании. В следующем уроке мы углубимся в возможности CloudKit API.

Вы можете клонировать образец всего проекта с GitHub (метка #introduction).

В заключение

Теперь у вас должно быть правильное понимание основ CloudKit. Остальная часть этого цикла будет ориентирована на создание приложения списка покупок. В следующем уроке мы начнём с добавления возможности добавления, редактирования и удаления списков покупок.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.