Статьи

Создание приложения для списка покупок с CloudKit: Введение

Еще в 2012 году Apple представила iCloud вместе с iOS 5. В то же время компания объявила, что разработчики получат доступ к iCloud через ряд API-интерфейсов. Сначала у разработчиков было три варианта:

Эти API не идеальны, хотя. Основным недостатком является отсутствие прозрачности. В частности, интеграция Core Data привела к разочарованию и замешательству даже у самых опытных разработчиков. Когда что-то идет не так, разработчики понятия не имеют, кто или кто виноват. Это может быть проблема в их коде или в коде Apple.

На WWDC 2014 Apple представила CloudKit , совершенно новую среду, которая напрямую взаимодействует с серверами Apple iCloud. Платформа сопоставима с рядом решений PaaS (платформа как услуга), таких как Firebase. Как и Firebase, Apple предоставляет гибкий API и панель инструментов, которая позволяет разработчикам взглянуть на данные, хранящиеся на серверах Apple iCloud.

Что мне больше всего нравится в CloudKit, так это приверженность Apple к этой платформе. По данным компании, iCloud Drive и iCloud Photo Library построены на основе CloudKit. Это показывает, что платформа CloudKit и ее инфраструктура являются надежными и надежными.

Как разработчик, этот знак доверия и приверженности важен. В прошлом Apple время от времени выпускала API-интерфейсы, которые страдали от ошибок или отсутствовали ключевые функции просто потому, что компания не ела собственную собачью еду. Это не верно для CloudKit. И это многообещающе.

Хранилище значений ключей и хранилище документов имеют свое применение, и Apple подчеркивает, что CloudKit не заменяет и не осуждает существующие API-интерфейсы iCloud. То же самое относится и к основным данным. Например, CloudKit не предлагает локальное хранилище. Это означает, что приложение, работающее на устройстве без сетевого подключения, практически бесполезно, если оно опирается исключительно на CloudKit.

Apple также подчеркивает, что обработка ошибок имеет решающее значение при работе с CloudKit. Например, если операция сохранения завершается неудачно и пользователь не получает уведомления, он может даже не знать, что ее данные не были сохранены — и потеряны.

CloudKit — отличное решение для хранения как структурированных, так и неструктурированных данных в облаке. Если вам нужно решение для доступа к данным на нескольких устройствах, то CloudKit, безусловно, является вариантом, который стоит рассмотреть.

В 2015 году на WWDC Apple сделала то, на что немногие разработчики ожидали или надеялись. Анонсирован веб-сервис для CloudKit . Это означает, что CloudKit можно использовать практически на любой платформе, включая Android и Windows Phone.

Цены на Apple тоже довольно конкурентоспособны . Начало работы с CloudKit бесплатное, и оно остается бесплатным для большинства приложений. Опять же, CloudKit, безусловно, стоит рассмотреть, если вы планируете хранить данные в облаке.

Разработчики, борющиеся с Core Data, часто не знакомы со строительными блоками фреймворка. Если вы не потратите время на изучение и понимание стека основных данных, вы неизбежно столкнетесь с проблемами. То же самое верно для CloudKit.

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

Apple ясно дает понять, что конфиденциальность является важным аспектом CloudKit. Первое, что нужно знать, это то, что у каждого приложения есть свой контейнер в iCloud. Эта концепция очень похожа на то, как у каждого приложения iOS есть своя песочница. Однако можно совместно использовать контейнер с другими приложениями, если эти приложения связаны с одной и той же учетной записью разработчика. Как вы можете себе представить, это открывает ряд интересных возможностей для разработчиков.

Контейнер CloudKit содержит несколько баз данных. Каждый контейнер имеет одну общедоступную базу данных, которую можно использовать для хранения данных, доступных каждому пользователю вашего приложения. В дополнение к общедоступной базе данных контейнер также содержит частную базу данных для каждого пользователя вашего приложения. Частная база данных пользователя используется для хранения данных, специфичных для данного конкретного пользователя. Разделение и инкапсуляция данных является ключевым компонентом инфраструктуры CloudKit и iCloud.

Несмотря на то, что контейнер приложения может содержать много баз данных, с точки зрения разработчика контейнер содержит только две базы данных: общедоступную базу данных и личную базу данных пользователя, которые в настоящее время вошли в свою учетную запись iCloud. С 2017 года Apple представила третью базу данных, общую базу данных , предоставляя приложениям возможность обмениваться подмножеством записей, которые совместно используются в частной базе данных другого пользователя, предлагая им внести свой вклад в эти открытые записи.

схема публичных частных и общих баз данных

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

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

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

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

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

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

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

схема списка покупок и предметов

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

CloudKit управляет отношениями довольно просто, но он предоставляет возможность автоматического удаления дочерних записей при удалении родительской записи. Мы рассмотрим отношения чуть позже в этой серии.

Я также хотел бы упомянуть класс CKAsset . Хотя в записи можно хранить CKAsset данные, неструктурированные данные (например, изображения, аудио и видео) следует хранить как экземпляры CKAsset . Экземпляр CKAsset всегда связан с записью и соответствует файлу на диске. Мы не будем работать с классом CKAsset в этой серии.

Я уверен, что вы согласны, что CloudKit выглядит довольно привлекательно. Однако есть важная деталь, которую мы еще не обсуждали: аутентификация. Пользователи аутентифицируют себя через свои учетные записи iCloud. Пользователи, которые не вошли в учетную запись iCloud, не могут записывать данные в iCloud.

Хотя это верно для любого из API-интерфейсов iCloud, имейте в виду, что приложения, которые полагаются исключительно на CloudKit, в этом случае не будут очень функциональными. Все, что может сделать пользователь, это получить доступ к данным в общедоступной базе данных, если это разрешено разработчиком.

Пользователи, которые не вошли в свои учетные записи iCloud, все еще могут читать данные из общедоступной базы данных. Само собой разумеется, что частная база данных недоступна, так как iCloud не знает, кто использует приложение.

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

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

Для этого урока я буду использовать Xcode 9 и Swift 4 . Если вы используете более старую версию Xcode, имейте в виду, что вы используете другую версию языка программирования Swift. Это означает, что вам нужно будет обновить исходный код проекта, чтобы он соответствовал компилятору. Изменения в основном незначительные, но важно знать об этом.

Поскольку CloudKit — это сложная тема, я предполагаю, что вы знакомы как с Xcode, так и с языком программирования Swift. Если вы новичок в разработке для iOS, я рекомендую сначала прочитать вводное руководство или пройти один из наших курсов по разработке Swift:

  • стриж
    Создавайте приложения для iOS с помощью Swift
    Маркус Мюльбергер
  • Шаблоны проектирования
    Swift Design Patterns
    Дерек Дженсен

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

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

Выберите шаблон приложения Single View

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

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

Следующим шагом является включение iCloud и CloudKit. Выберите проект в Навигаторе проектов слева и выберите цель для вашего приложения из списка целей. Откройте вкладку « Общие » и установите команду для правильной команды. Чтобы избежать каких-либо проблем на следующем шаге, убедитесь, что у вашей учетной записи разработчика есть необходимые разрешения для создания идентификатора приложения .

Выберите правильную команду

Затем откройте вкладку « Возможности » в верхней части и установите переключатель iCloud в положение « включено» . Xcode понадобится время, чтобы создать идентификатор приложения от вашего имени. Это также добавит необходимые права доступа к идентификатору приложения. Если это не работает, убедитесь, что команда настроена правильно, и у вас есть необходимые разрешения для создания идентификатора приложения.

Включить iCloud

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

Если вашему приложению необходим доступ к другому контейнеру или нескольким контейнерам, установите флажок « Указать пользовательские контейнеры» и проверьте контейнеры, к которым ваше приложение должно иметь доступ.

Включить пользовательские контейнеры

Возможно, вы заметили, что XCode автоматически связал вашу цель с платформой CloudKit. Это означает, что вы готовы начать использовать CloudKit в своем приложении.

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

Откройте ViewController.swift и добавьте оператор импорта вверху, чтобы импортировать инфраструктуру CloudKit.

1
2
import UIKit
import CloudKit

Чтобы получить запись о пользователе, нам сначала нужно получить идентификатор записи. Посмотрим, как это работает. Я создал вспомогательный метод fetchUserRecordID , который будет содержать логику для извлечения идентификатора записи пользователя. Мы вызываем этот метод в методе viewDidLoad контроллера представления.

1
2
3
4
5
6
override func viewDidLoad() {
    super.viewDidLoad()
 
    // Fetch User Record ID
    fetchUserRecordID()
}

Реализация fetchUserRecordID немного интереснее, чем viewDidLoad . Сначала мы defaultContainer на контейнер приложения по умолчанию, вызывая defaultContainer в классе CKContainer . Затем мы вызываем fetchUserRecordIDWithCompletionHandler(_:) для defaultContainer . Этот метод принимает замыкание в качестве единственного аргумента.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
private func fetchUserRecordID() {
       // Fetch Default Container
       let defaultContainer = CKContainer.default()
        
       // Fetch User Record
       defaultContainer.fetchUserRecordID { (recordID, error) -> Void in
           if let responseError = error {
               print(responseError)
                
           } else if let userRecordID = recordID {
               DispatchQueue.main.sync {
                   self.fetchUserRecord(recordID: userRecordID)
               }
           }
       }
   }

Закрытие принимает два аргумента: необязательный экземпляр CKRecordID и необязательный экземпляр NSError . Если error равна nil , мы благополучно разворачиваем recordID .

Важно подчеркнуть, что замыкание будет вызываться в фоновом потоке. Это означает, что вам нужно быть осторожным при обновлении пользовательского интерфейса вашего приложения изнутри замыкания, вызванного CloudKit. Например, в fetchUserRecord(_:) я явно вызываю fetchUserRecord(_:) в основном потоке.

В fetchUserRecord(_:) мы извлекаем запись пользователя, сообщая CloudKit, какая запись нам интересна. Обратите внимание, что мы вызываем fetchRecordWithID(_:completionHandler:) privateDatabase объекта privateDatabase , свойства объекта defaultContainer .

Метод принимает экземпляр CKRecordID и обработчик завершения. Последний принимает необязательный экземпляр CKRecord экземпляр NSError . Если мы успешно загрузили запись пользователя, мы распечатаем ее в консоли Xcode.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
private func fetchUserRecord(recordID: CKRecordID) {
       // Fetch Default Container
       let defaultContainer = CKContainer.default()
        
       // Fetch Private Database
       let privateDatabase = defaultContainer.privateCloudDatabase
        
       // Fetch User Record
       privateDatabase.fetch(withRecordID: recordID) { (record, error) -> Void in
           if let responseError = error {
               print(responseError)
                
           } else if let userRecord = record {
               print(userRecord)
           }
       }
   }

Когда вы запустите приложение в Xcode, вы увидите в консоли нечто похожее на:

Вывод кода в консоль

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

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

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