Упрощаем работу с CloudKit, или синхронизация в духе Zen
Введение
Облачная синхронизация — закономерный тренд нескольких последних лет. Если вы разрабатываете под одну или несколько Apple платформ (iOS, macOS, tvOS, watchOS) и задачей является реализация функционала синхронизации между приложениями, то в вашем распоряжении есть очень удобный инструмент, или даже целый сервис — CloudKit.
Нашей команде постоянно приходится прикручивать функционал синхронизации данных с CloudKit, в том числе в проектах, которые используют CoreData в качестве хранилища данных. Поэтому возникла, а затем была реализована идея — написать универсальный интерфейс для синхронизации.
CloudKit — это не просто фреймворк. Это полноценный BaaS (Backend as a Service), т.е. комплексный сервис с полноценной инфраструктурой, включающей в себя облачное хранилище, пуш-уведомления, политики доступа и многое другое, а также предлагающий универсальный кросс-платформенный программный интерфейс (API).
CloudKit прост в использовании и сравнительно доступен. Только за то, что вы являетесь участником Apple Developer Program, в вашем распоряжении совершенно бесплатно:
- 10Gb хранилище под ресурсы
- 100MB под базу данных
- 2GB трафика в день
- 40 запросов в секунду
И эти цифры могут быть увеличены, если есть такая потребность. Стоит отметить, что CloudKit не использует iCloud-хранилище пользователя. Последний используется только для аутентификации.
Эта статья — не реклама CloudKit и даже не очередной обзор основ работы с ним. Здесь не будет ничего о настройке проекта, конфигурировании App ID в вашем профиле разработчика, создании CK-контейнера или Record Type в дэшборде CloudKit. Кроме того, за рамками статьи остаётся не только backend составляющая, но и вся программная, относящаяся непосредственно к CloudKit API. Если вы хотели бы разобраться именно в основах работы с CloudKit, то для этого уже есть прекрасные вводные статьи, повторять которые нет никакого смысла.
Эта статья — в некотором смысле следующий шаг.
Когда вы уже освоились с чем-то, что давно используете, рано или поздно возникает вопрос: как автоматизировать процесс, сделать его ещё более удобным и более унифицированным? Так возникли паттерны проектирования. Так возник наш фреймворк, облегчающий работу с CloudKit — ZenCloudKit, который уже был успешно применен в ряде проектов. О нём, а именно, о новом техническом способе работы с CloudKit, и пойдет речь дальше.
Универсальный интерфейс
Создавая фреймворк, нашей конечной целью была реализация такого интерфейса, который был бы совместим с сущностями CoreData, позволяя при минимуме усилий синхронизировать — сохранять, удалять и получать данные — с учетом имеющихся связей БД, вне зависимости от сложности имеющейся архитектуры.
Фреймворк написан на Swift 3 и именно Swift-разработчики в полной мере сумеют ощутить преимущества, которые даёт его использование. Для Objective-C возможен вполне полноценный bridge, но по известным причинам аналогичные вещи будут выглядеть в нём избыточными и более громоздкими в реализации. Примеры кода в данной статье будут написаны на Swift.
Перейдём к обзору, параллельно рассматривая пример реализации.
Пример реализации
Рассмотрим в качестве введения некоторые типичные операции синхронизации: методы сохранения и удаления. Конечная реализация выглядит следующим образом:
Что же здесь происходит?
Положим, у нас есть объект event со свойством entity, где entity — это NSManagedObject. У этого NSManagedObject, как и у всякого объекта базы данных, есть поля, некоторые из которых являются свойствами, некоторые — ссылками, reference, на другие объекты NSManagedObject, образуя связи один-к-одному или один-ко-многим.
Чтобы сохранить этот объект (или удалить соответствующий ему) синхронно или асинхронно в базу данных CloudKit, пробросив при этом все связи, используется прокси-объект — iCloud, который содержит в себе соответствующие методы. Достаточно вызывать entity.iCloud.save() (асинхронное) или entity.iCloud.saveAndWait() (синхронное сохранение), чтобы все поля entity были записаны в соответствующие поля объекта CloudKit, а уникальный UUID от вновь сохраненного CKRecord (т.е. строковое свойство recordName объекта CKRecordID) был автоматически записан обратно в специально отведенное для этого поле объекта entity, образовав тем самым связь между локальным и удаленным объектом.
Если вы никогда не использовали CloudKit и всё это звучит непонятно, то проще сказать, что на любую сущность есть .iCloud.save() и этого достаточно, чтобы сохранить как сам объект, так и все его связи. Никакого больше множества идентичных методов для разных сущностей и грязи в клиентском коде. Удобно, не правда ли?
Настройка объектов синхронизации
Для того чтобы это работало, необходимо выполнить несколько условий.
В основе работы лежит широко применяемая схема маппинга свойств, которая используется во многих библиотеках, в различных веб-парсерах (таких как RestKit) и т.д. Маппинг же реализован в классической манере — посредством KVC, который поддерживается только наследниками NSObject. Отсюда, первое условие:
1) Каждый синхронизируемый объект должен быть наследником NSObject (к примеру, NSManagedObject – это отличный выбор).
2) Каждый синхронизируемый объект должен реализовать протокол ZKEntity, который выглядит следующим образом:
Если вы работаете с CoreData, то реализовывать нужно прямо в вашем (sub-)классе:
Как видно из протокола, обязательными полями являются recordType и mappingDictionary. Рассмотрим оба.
// REQUIRED (обязательные поля)
1) recordType — соответствующий тип записи, Record Type, в CloudKit.
Пример: класс Person содержит свойство recordType = “Person”. После вызова save() у его экземпляра, в дэшборде CloudKit именно в этой таблице (“Person”) будет заведена запись.
Реализация:
static var recordType = "Person"
2) mappingDictionary — словарь маппинга свойств.
Схема: [локальный ключ: удаленный ключ (поле в таблице CloudKit) ].
Пример: класс Person содержит поля firstName и lastName. Чтобы сохранять их в таблицу Person в CloudKit под теми же именами, необходимо написать следующее:
static var mappingDictionary = [ "firstName" : “firstName”, “lastName” : “lastName” ]
// OPTIONAL (необязательные поля)
Остальные поля протокола являются опциональными,
3) syncIdKey — имя локального свойства, которое будет хранить ID удаленного объекта. ID — это паспорт объекта, необходимый для связи локальный<—>удаленный.
Поле является условно опциональным. При инициализации контроллера фреймворка, о которой будет написано ниже, есть возможность указать имя свойства для всех сущностей. Однако, указанное индивидуально в классе сущности, оно имеет более высокий приоритет и при парсинге будет проверяться сначала именно оно. И лишь затем, если реализация пустая, будет использоваться универсальный ключ (см. далее).
Реализация:
static var syncId: String = "cloudID"
changeDateKey
Аналогично предыдущему, оно условно опционально. Есть возможность опустить реализацию и указать имя свойства для всех синхронизируемых объектов во время инициализации ZenCloudKit (см. далее).
Реализация:
static var changeDateKey: String = "changeDate"
references — словарь, содержащий ключи, реализующие связь *-к-одному.
Схема: [“локальный ключ”: “удаленный ключ”]
Требованием здесь является то, чтобы свойство “локальный ключ” своим типом имело класс, который удовлетворяет базовым требованиям (наследует NSObject и реализует протокол ZKEntity).
При вызове save() у локального объекта ZenCloudKit попытается также сохранить все связанные с ним.
Реализация:
static var references : [String : String] =
["homeAddress" : "address"]
referenceLists — словарь, содержащий массив объектов ZKRefList, каждый из которых несёт в себе информацию о конкретной связи *-ко-многим: тип объектов и название ключа, по которому необходимо запрашивать и сохранять этот список.
Схема: ZKRefList(entityType: ZKEntity.Type,
localSource: локальное свойство, которое возвращает массив объектов ZKEntity,
Реализация:
static var referenceLists: [ZKRefList] = [ZKRefList(entityType: Course.self,
localSource: "courseReferences",
remoteKey: "courses")]
courseReferences – это user-defined свойство, возвращающее массив объектов ZKEntity, которые вы хотели бы сохранить и ссылки на которые необходимо поместить в перечень ссылок корневого объекта.
Код (продолжение):
var courseReferences : [Course]? { get { return self.courses?.allObjects as? [Course] } set { DispatchQueue.main.async { self.mutableSetValue(forKey: "courses").removeAllObjects() self.mutableSetValue(forKey: "courses").addObjects(from: newValue!) } }
Реализация соответствующего сеттера также необходима чтобы приложение могло сохранить объекты, полученные из CloudKit. Таким образом, поле localSource объекта ZKRefList в сущности является ссылкой на обработчик (хэндлер), который управляет операциями ввода и вывода.
isWeak — опциональный флаг, который, будучи установленным (true), указывает на то, что любой другой объект, ссылающийся на экземпляр данного типа, образует слабую ссылку (аналогия с модификатором weak) в CloudKit. Это означает, что запись о нём будет удалена каскадно, как только будет удален объект, который содержит ссылку на него.
Пример: есть объект A, ссылающийся на объект B.
Если установить B.isWeak = true, объект А будет сохранен в CloudKit со “слабой ссылкой” на B. Объект B будет удален автоматически, как только вы удалите объект A.
Этот флаг является реализацией нативного API CloudKit и апеллирует к конструктору CKReference с флагом .deleteSelf:
CKReference.init(record: <CKRecord>, action: .deleteSelf)
Поэтому механика удаления — целиком прерогатива CloudKit, фреймворк же просто предлагает более удобный интерфейс. В дальнейшем этот функционал может быть расширен, чтобы каскадное удаление можно было настраивать для разных сущностей.
Реализация:
static var isWeak = true
referencePlaceholder — свойство, которое, будучи объявленным, позволяет избежать значения nil при получении объекта из CloudKit, подменяя его значением по умолчанию.
Если предполагается, что объект сущности CoreData должен всегда содержать какое-либо значение, отличное от nil, в качестве ссылки на другой объект, то всякий раз, когда данный объект будет отсутствовать в CloudKit при синхронизации, локальному свойству может быть автоматически задано значение по умолчанию.
Пример: есть класс A со свойством b и, зеркально ему, такой же Record Type в CloudKit.
В CloudKit имеется объект A, который отсутствует локально, имеющий пустую ссылку на B (значение отсутствует). При обычном сценарии в результате синхронизации вы бы получили объект A, у которого свойство b было бы nil. Но с установленным значением по умолчанию в локальном классе (referencePlaceholder = …) ZenCloudKit автоматически присвоит свойству b указанное вами значение:
A.b = referencePlaceholder,
где последний является экземпляром B.
Так, в результате полного цикла синхронизации в вашем приложении всегда будут создаваться объекты с заполненными ссылками, даже в том случае, если на всех остальных устройствах они сохранялись пустыми.
Реализация:
static var referencePlaceholder: ZKEntity = B.defaultInstance()
Обратите внимание, что referencePlaceholder указывается именно в таргет-классе. Если нужно, чтобы свойство b объекта A не оказывалось nil (A. b != nil), то именно в классе B необходимо реализовать referencePlaceholder, а не в корневом классе A, который мы получили в результате синхронизации.
// SUMMARY
На момент написания статьи это весь функционал, поддерживаемый ZKEntity. Подытожим изложенное ещё раз в виде конкретного примера.
Положим, есть класс Event:
Реализация ZKEntity может выглядеть, например, так:
Здесь:
- словарь для маппинга свойств.
- словарь для маппинга ссылок (опционально)
- CloudKit Record Type
Опущены syncIdKey и changeDateKey. В примере им соответствуют свойства syncID и changeDate. Поскольку аналогичные свойства (changeDate, syncID) присутствуют в интерфейсе других классов, они были записаны на фазе инициализации ZenCloudKit (о чём пойдёт речь далее) как универсальные, поэтому частная имплементация была опущена.
Настройка контроллера и делегата
После того, как сущности были настроены, необходимо проинициализировать контроллер и назначить его делегат. Сделать это можно различными способами, но лучше всего — отвести для этого отдельный класс и написать вызываемый инициализатор.
Для начала можно завести глобальную переменную, которая будет хранить ссылку на статический экземпляр контроллера.
Класс-делегат должен будет реализовать следующий протокол:
Прежде чем рассматривать каждый метод в отдельности, попробуем взглянуть на вариант готовой реализации (за исключением метода zenSyncDIdFinish).
Класс CloudKitPresenter в приведенном примере является делегатом ZenCloudKit. Здесь происходит инициализация и вызов callback-функций, необходимых для реализации полного цикла синхронизации. Полный цикл синхронизации — это последовательность закадровых операций, при которых осуществляется сравнение локальных и удаленных объектов по времени изменения и их актуализация на обоих концах. Для этого по каждому типу, т.е. по каждой зарегистрированной сущности ZKEntity, фреймворку необходимо предоставить три функции, реализующие соответственно создание, запрос объекта по ID (fetch) и запрос всех доступных объектов. В каждой из трёх функций в качестве параметра выступает класс ZKEntity (ofType T: ZKEntity.Type). В результате выполнения ZenCloudKit ожидает получить объекты именно данного типа.
zenAllEntities(ofType T: ZKEntity.Type)
— ожидает получить массив всех сущностей типа T
zenCreateEntity(ofType T: ZKEntity.Type)
— ожидает получить новый экземпляр T.
zenFetchEntity(ofType T: ZKEntity.Type, syncId: String)
— ожидает получить существующий экземпляр T по данному syncId (или nil если таковой отсутствует).
Например, если вы работаете с сущностями Person и Home, то параметр T в данных функциях будет равен одному из этих двух типов. Ваша задача — предоставить результат по каждому из них (новый объект, существующий и все). Сделать это можно либо осуществив проверку типа и написав код для каждого, либо при помощи интерфейсного полиморфизма.
В приведенном примере для осуществления перечисленных операций используются стандартные методы MagicalRecord для поиска существующего, создания нового и запроса всех объектов, которые работают как extension-методы (или методы категорий, выражаясь в духе Objective-C) для NSManagedObject. Это значительно упрощает реализацию. Код становится универсальным, поскольку пропадает нужда делать type-check для каждого случая T.
Функции являются конкретной реализацией generic-абстракции, хотя, строго говоря, обобщения в сигнатуре функций не используются в целях обеспечения совместимости с Objective-C.
В последней функции используется инструкция T.predicateForId(…). Это метод расширения, предоставленный ZenCloudKit, который возвращает корректный предикат поиска для данного типа T по данному syncId (чтобы избежать хард-кода и связанных с ним возможных ошибок в названии свойства, локально хранящего ID).
zenEntityDidSaveToCloud (entity: ZKEntity, record: CKRecord?, error: Error?)
— вызывается каждый раз при завершении сохранения в CloudKit. На этой фазе объект entity уже получил ID удаленного объекта, поэтому здесь можно, например, сохранить главный контекст базы данных.
Делегат реализует закрытый Singleton (sharedInstance не виден клиенту). Для того, чтобы проинициализировать и контроллер, и его делегат, достаточно где-либо извне в нужный момент вызвать метод:
В методе инициализации происходит настройка фреймворка:
Задаются стандартные для CloudKit параметры:
- имя контейнера (container)
- тип базы данных (ofType: .public/.private)
Далее следуют уже рассмотренные выше ключи syncIdKey и changeDateKey — имена свойств, хранящих ID записей и дату изменения. Необходимо отметить, что эти значения могут быть оставлены пустыми (nil). В таком случае при вызове соответствующих методов у экземпляров ZKEntity (например, save()) ZenCloudKit будет искать их имплементацию среди объявлений каждого класса. И наоборот, достаточно указать эти ключи только здесь, чтобы опустить специфичную реализацию. Если пустой окажется и общая, и частная имплементация, то вызов cloudKit.setup() выдаст в лог ошибку, и синхронизация работать не будет.
В параметр entities мы передаем массив всех типов, с которыми собираемся работать.
ignoreKeys — массив строковых ключей, обнаружив которые, ZenCloudKit должен проигнорировать объект (например, не сохранять или не удалять его).
deviceId — ID устройства. Очень важный параметр, если в синхронизации будет задействовано несколько устройств. Об уникальности этого параметра должен позаботиться разработчик. Стандартно, берётся Hardware UUID, но возможны и другие варианты.
// RECAP
Реализация настроек, описанных до сих пор, является необходимым и достаточным условием для того, чтобы работал базовый функционал, предоставленный прокси-объектом iCloud, который, в свою очередь, реализует протокол ZKEntityFunctions:
За исключением функции update(), назначение которой — обновить локальный объект из удаленного, представленного в коде как CKRecord. Эту функцию следует использовать в методе делегата zenSyncDIdFinish, который вызывается по окончании полного цикла синхронизации, который, в свою очередь, запускается следующим образом:
Первый вариант — синхронизация в стандартном режиме. Каждый последующий цикл синхронизации фиксируется ZenCloudKit; в случае успеха, сохраняется дата последней синхронизации (всё это берёт на себя фреймворк). Сохранение даты очень важно: оно позволяет отбирать только те объекты, дата изменения которых — позже даты последнего успешного цикла. В противном случае, если, скажем, у вас в БД 100 объектов, то каждый цикл включал бы бессмысленную проверку давно уже синхронизированных, не изменяющихся объектов. Это совершенно не нужная и, к тому же, ресурсозатратная операция.
Второй вариант — принудительная синхронизация (forced: true). Могут быть случаи, когда целостность данных оказывается нарушенной. Тогда вы можете в принудительном порядке проверить каждый синхронизируемый объект, игнорируя дату последнего успешного цикла, и актуализировать данные локально и удаленно. Локальные объекты будут обновлены тем, что лежит в CloudKit (если по каким-то причинам этого не произошло ранее). А в CloudKit могут быть сохранены локальные объекты, которые также почему-то не были сохранены. В зависимости от специфики вашего приложения, вы сами можете определить, в каком месте вызывать принудительную синхронизацию (например, при старте, во время длительного простоя или же отвести эту функцию в настройки). В общем случае в этом вызове нет нужды и, скорее всего, вам не придётся к нему прибегать.
Вызов метода syncEntities() на уровне контроллера делает то же самое, только применительно ко всем зарегистрированным сущностям. Параметр specific принимает конкретные типы, которые вы бы хотели синхронизовать (nil — если нужно применить ко всем).
Осталось разобрать метод zenSyncDIdFinish, сигнатура которого выглядит так:
Параметры:
T — тип сущности, объекты которой необходимо создать или обновить.
newRecords, updatedRecords — массивы CKRecord, объектов, которые необходимо создать или обновить локально. Ориентиром при поиске локального соответствия выступает уникальный ID, который стандартно хранится в свойстве CKRecord. recordID.recordName. Сущность, среди объектов которой нужно искать соответствия и экземпляр которой создать, является T.
deletedRecords — массив объектов ZKDeleteInfo, каждый из которых хранит информацию об удаляемом объекте: локальный ZKEntity-тип и ID объекта. Эти объекты могут быть различных типов, поэтому ориентироваться на тип T в данном случае не нужно. Тип удаляемого объекта следует смотреть в свойстве entityType, а ID объекта — в свойстве syncId объекта ZKDeleteInfo. Класс выглядит следующим образом:
ZenCloudKit формирует этот список перед тем, как завершить удаление, отправляя его в обработчик zenSyncDidFinish в массиве deletedRecords, чтобы вы смогли произвести необходимую локальную очистку. Как только локально всё будет успешно удалено, необходимо вызвать callback-метод finishSync(). Если этого не сделать, то в базе данных CloudKit не будет произведено никаких изменений. Такая схема принята в целях безопасности: лишь удостоверившись в том, что локальная база данных актуализирована, вы вызываете финализатор — finishSync().
Всегда вызывайте finishSync() в конце синхронизации.
Это относится не только к фазе удаления, описанной выше, но и к фазам создания и обновления.
Подытожим сказанное, рассмотрев фрагмент реализации функции zenSyncDIdFinish:
Сразу после данного фрагмента должны следовать:
— вызов finishSync()
— функции обновления UI, которые бы отразили изменившееся состояние БД (если требуется).
При помощи следующей инструкции:
мы заполняем поля локального объекта полями CKRecord, который нам доступен как аргумент в одном из массивов. Флаг fetchReferences позволяет загрузить все связи. Под загрузкой связей подразумевается реальная загрузка соответствующих объектов (приведенных в массивах references и referenceLists, описанных в протоколе ZKEntity) из CloudKit и их привязка к данному объекту entity. Если при загрузке связи обнаружится, что соответствующий локальный объект не существует (zenFetchEntity == nil), он будет автоматически создан в локальной базе данных путём вызова метода делегата zenCreateEntity.
Если образование этих связей предполагает изменение UI, об этом необходимо позаботиться дополнительно (updateEntity — в части заполнения связей — работает асинхронно и дожидаться его выполнения не стоит). В обработчике ZKRefList это можно сделать в сеттере, о чём уже говорилось:
Здесь происходит следующее:
При получении связей *-ко-многим (в результате вызова updateEntity с флагом fetchReferences = true) в сеттер teacherReferences попадает массив объектов Teacher. В главном потоке мы обновляем этот список у корневого объекта NSManagedObject, а затем вызываем методы обновления UI.
Маппинг связей *-к-одному (массив references, содержащий название свойств-ссылок на другие сущности ZKEntity) не предполагает обработчиков (get/set), поэтому, если требуется отслеживать образование этих связей, необходимо прибегнуть либо к аналогичному методу — в качестве ключей в массиве references указывать обработчики и переопределять их геттер и сеттер, — либо использовать ReactiveCocoa или иные средства для наблюдения за свойствами.
Работа со ссылками кажется богатой нюансами, и это действительно так, однако эти нюансы — закономерное следствие обвязки и автоматизации работы двух систем — CoreData и CloudKit.
Если вам нужно иметь более прямой контроль над образованием связей, обновлением UI или другими sync-related процессами, по усмотрению вы можете совместить средства ZenCloudKit и нативный CloudKit API. В методе zenSyncDidFinish передаются массивы объектов CKRecord, которые, помимо свойств, содержат объекты CKReference. Это значит, что вы можете кастомизировать парсинг, а также вручную загрузить те объекты, которые вам нужны.
На этом настройка ZenCloudKit окончена.
Нюансы использования
Стандартный способ обращения к функционалу фреймворка — через экземпляр (singleton) ZenCloudKit контроллера:
В качестве аргументов — всё те же экземпляры и классы ZKEntity.
Сокращенный вариант (через прокси-класс .iCloud) в данный момент доступен только в Swift.
Push-уведомления
Обработка push-уведомлений также может быть передана в ZenCloudKit:
Результатом его работы будет вызов метода делегата zenSyncDIdFinish, с одним из трёх заполненных массивов (newRecords, updatedRecords, deletedRecords), выполнение которого автоматически приведет к обновлению базы данных и UI (если вы позаботились об этом в теле данной функции). Напомню, что обычный сценарий обработки push-уведомлений предполагает ряд довольно монотонных действий: проверка типа уведомления (CKNotification), причины нотификации (queryNotificationReason), парсинг — определение сущности, к которой относится уведомление и лишь затем вызов соответствующего обработчика. ZenCloudKit берёт всё это на себя.
Блокировка синхронизации
Рано или поздно код вашего приложения будет наполнен инструкциями .save() или .delete() в разных местах. Если вы предполагаете возможность отключения синхронизации изнутри приложения (а не в свойствах системы), то вместо того чтобы в каждом месте клиентского кода делать проверку какого-нибудь флага, вы можете отключить синхронизацию на уровне фреймворка:
Возобновление синхронизации, как можно догадаться, достигается передачей false. И ваш код остается чистым.
Логгирование
Все основные этапы работы фреймворка логгируются. Включение/отключение флага debugMode позволяет управлять выводом в консоль служебной информации (по умолчанию true):
Настройка контейнера:
Для успешной работы приложению и ZenCloudKit необходим доступ на чтение и запись всех используемых Record Type, включая query-права на ключ modifiedDate (CKRecord). Не забудьте включить всё это в дэшборде. Кроме того, фреймворком в базе данных будут созданы таблицы под названием Device и DeleteQueue. Первая будет содержать список зарегистрированных устройств, которые обращаются к вашей базе данных. Вторая — очередь на удаление — представляет собой таблицу с мета-информацией об удаленных объектах, которые необходимо удалить на каждом устройстве (для каждого устройства — соответствующая запись). После того, как это устройство осуществит локальное удаление соответствующего объекта, запись из DeleteQueue также будет стёрта. Эти две таблицы являются служебными, к ним должен быть полный доступ на чтение и запись для каждого устройства.
Безопасность
Последним достойным внимания моментом работы ZenCloudKit является безопасность.
Процедура сохранения объектов в CloudKit стандартно сводится к двум этапам: (1) проверка на наличие искомого объекта, и только затем — (2) сохранение. Рассмотрим ситуацию, когда в кратчайший промежуток времени вы атомарно сохраняете 15 новых объектов (или один и тот же несколько раз подряд), или же это происходит в результате сбоя. В стандартном сценарии работы CloudKit это может произойти так: сначала несколько раз сработает хэндлер поиска (fetch), возвратив nil, а затем столько же раз будет вызвана команда сохранения (ведь объект не найден). В результате, не желая того, вы получите несколько экземпляров одного и того же объекта в CloudKit. Без дополнительных мер (см. GCD), это неизбежно, потому что CloudKit API основан на асинхронных блоках, последовательность которых сложно предугадать, даже выставив флаги приоритета и QoS у CKQueryOperation.
Описанного выше сценария гарантированно не случится с ZenCloudKit, который на этапе инициализации создает очередь для каждого зарегистрированного типа ZKEntity, обеспечивая строгую последовательность в выполнении операций сохранения. Если среди 15 объектов — по 3 объекта разных типов (итого 5 типов), то при их одновременном сохранении, “в одно время” будет запущен процесс сохранения для 5 объектов, без какой-либо угрозы. Также схема сводит на нет возможность отказа (DoS).
Заключение
Фреймворк создавался одним человеком в течение примерно двух месяцев. Значительная часть времени была потрачена не столько на программирование, сколько на дизайн и рефакторинг. Цель стояла простая — упростить и унифицировать выполнение типовых операций синхронизации с CloudKit, обеспечив приемлемый уровень совместимости с CoreData. Кардинальных неисправностей и серьезных багов в ходе применения на сегодняшний день обнаружено не было.
Некоторые функции в данной статье не описаны (например, управление потерянным соединением и автоматический запуск полного цикла синхронизации, по мере его восстановления). Известны также некоторые нюансы: например, на данный момент отсутствует поддержка CKAssets (однако её реализовать не сложно).
В данный момент фреймворк вместе с демо-проектом готовится на выкладку. Если вы хотели бы получить исходной код ZenCloudKit или у вас есть какие-либо вопросы или комментарии, будем рады узнать о них в комментариях к данной статье или через ЛС.
Введение в CloudKit — CoderLessons.com
Вступление
CloudKit — это фреймворк Apple, который был представлен вместе с iOS 8 в 2014 году. Его цель — предоставить простое решение для хранения данных вашего приложения в облаке с помощью iCloud, сделав его доступным для всех устройств. Вот некоторые из ключевых особенностей CloudKit:
- до 1 ПБ хранилища активов (изображения, аудиофайлы и т. д.)
- до 10 ТБ хранилища базы данных (строки, числа, массивы и т. д.)
- аутентификация пользователя iCloud
- уведомления
В этом руководстве я научу вас настраивать CloudKit и взаимодействовать с ним в приложении для iOS, создав образец приложения под названием CloudBug . CloudBug отображает список ошибок с заголовком и описанием, которые хранятся в iCloud с помощью CloudKit.
Требования
Это руководство требует, чтобы вы работали с Xcode 6+ и имели учетную запись разработчика iOS. Вам также необходимо скачать стартовый проект с GitHub .
1. Конфигурация проекта
Первое, что вам нужно сделать после открытия стартового проекта, — это изменить идентификатор и команду целевого комплекта . Выберите проект CloudBug в Навигаторе проектов и выберите цель CloudBug из списка целей.
Измените идентификатор пакета на уникальную строку, используя обратную нотацию службы доменных имен . Далее выберите подходящую команду для вашей учетной записи разработчика.
Откройте вкладку « Возможности » вверху и включите iCloud , переключая переключатель справа. Это покажет настройки iCloud для цели CloudBug .
В настройках iCloud установите флажок CloudKit, чтобы включить CloudKit для цели CloudBug.
Создайте и запустите приложение на физическом устройстве в iOS Simulator, чтобы увидеть, все ли работает без ошибок.
2. Создание типа записи об ошибке
Нажмите кнопку CloudKit Dashboard в нижней части настроек iCloud. Это откроет панель управления CloudKit в вашем браузере. После входа в систему с учетной записью разработчика должна появиться панель управления CloudKit. Прежде чем продолжить, позвольте мне пройтись по пунктам на боковой панели слева.
схема
- Типы записей функционируют аналогично обычному определению класса. Записи создаются как экземпляры классов из этих типов.
- Роли безопасности предоставляют возможность различным пользователям получать доступ к данным по-разному. Например, роль безопасности можно использовать, чтобы разрешить только пользователям уровня администратора чтение и запись данных.
- Типы подписок используются для управления различными подписками, которые есть в вашем приложении, если они были реализованы.
Публичные данные
- Записи пользователей работают так же, как обычные записи, за исключением того, что они специально связаны с типом записей пользователей , который нельзя удалить.
- Зона по умолчанию — это место, где будут храниться все ваши публичные записи и другие данные.
Частные данные
- Зона по умолчанию работает так же, как и общедоступная зона по умолчанию, за исключением того, что она доступна только пользователю, вошедшему в систему в данный момент.
Администратор
- Команда, в которой вы можете просматривать других разработчиков, сотрудничающих над проектом, и редактировать их разрешения.
- Развертывание, где вы можете посмотреть, какие изменения будут внесены в схему элементы, когда ваш проект попадает в производственную среду.
В этом уроке вы сосредоточитесь только на типах записей и общедоступной зоне по умолчанию. Следующим шагом является создание вашего первого типа записи.
Если вы выберете « Типы записей» на боковой панели слева, вы заметите, что тип с именем « Пользователи» уже существует.
Давайте создадим новый тип записи с именем Bug, который будет содержать заголовок и описание. Нажмите кнопку «плюс» вверху, чтобы создать новый тип записи.
Измените имя нового типа записи на Bug и добавьте два атрибута, Title и Description . Создайте оба атрибута типа String, отметив только опцию индекса запроса . Вот как должен выглядеть новый тип записи.
Нажмите Сохранить в правом нижнем углу, чтобы создать тип записи об ошибке.
3. Создание записей в CloudKit Dashboard
Когда вы выбираете Зону по умолчанию в Общедоступных данных , тип записи об ошибке уже должен быть выбран. Создайте новую запись, нажав « Новая запись» или кнопку «плюс» вверху.
Введите все, что вы хотите для описания записи и Поля заголовка .
Нажмите Сохранить в правом нижнем углу, чтобы создать запись. Вы увидите, что вашей записи была присвоена уникальная запись Идентификатор , Дата создания, Дата изменения , Идентификатор созданного и Идентификатор измененного . Независимо от того, на каком типе записи основана запись, она всегда будет иметь эти пять атрибутов.
4. Загрузка записей
Вернитесь к Xcode и откройте MasterViewController.swift . Вверху добавьте оператор импорта для импорта платформы CloudKit.
Обновите метод viewDidLoad
, добавив следующий фрагмент кода.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 | let container = CKContainer.defaultContainer() let publicData = container.publicCloudDatabase
let query = CKQuery(recordType: «Bug», predicate: NSPredicate(format: «TRUEPREDICATE», argumentArray: nil)) publicData.performQuery(query, inZoneWithID: nil) { results, error in if error == nil { // There is no error for bug in results { let newBug = Bug() newBug.title = bug[«Title»] as! newBug.description = bug[«Description»] as!
self.objects.append(newBug)
dispatch_async(dispatch_get_main_queue(), { () -> Void in self.tableView.reloadData() }) } } else { println(error) } } |
Давайте посмотрим, что происходит в этом фрагменте кода.
- Сначала мы получаем ссылку на контейнер по умолчанию для вашего приложения. Этот контейнер содержит типы записей и записи, которые мы создали на информационной панели CloudKit. Вы можете создать больше контейнеров с уникальным идентификатором в Target Inspector> Capabilities .
- Во-вторых, мы получаем ссылку на публичную базу данных контейнера. Формат такой же для частной базы данных.
- Затем мы создаем запрос для типа записи об
NSPredicate
экземплярNSPredicate
. Этот предикат можно использовать для фильтрации результатов запроса, например, только выборки записей, созданных или измененных после определенной даты. Для этого примера мы выбираем каждую запись типа Bug . - Далее мы сообщаем базе данных выполнить запрос, который запускает асинхронный запрос. Когда запрос завершен, блок завершения выполняется, передавая нам массив
results
и объектerror
. - Если объект
error
равенnil
, мы перебираем элементы массиваresults
и создаем локальный экземплярBug
для каждой записи. Каждый экземплярBug
добавляется в источник данных табличного представления, а табличное представление перезагружается в главном потоке.
Создайте и запустите ваше приложение, чтобы увидеть, загружена ли запись, созданная нами на панели мониторинга CloudKit, в CloudBug.
Если вы видите ошибку в консоли Xcode, похожую на приведенную ниже, убедитесь, что вы вошли в правильную учетную запись iCloud на своем тестовом устройстве. Это также верно, если вы используете CloudBug в iOS Simulator.
Нажатие на ошибку в табличном представлении должно представить контроллер подробного представления с описанием ошибки.
5. Создание записей
В то время как загрузка данных из iCloud через CloudKit может быть очень полезной, CloudBug также нужна возможность, позволяющая пользователям создавать новые записи. В MasterViewController.swift добавьте следующий фрагмент кода в метод receiveBug(_:)
.
01 02 03 04 05 06 07 08 09 10 11 | let container = CKContainer.defaultContainer() let publicData = container.publicCloudDatabase
let record = CKRecord(recordType: «Bug») record.setValue(bug.title, forKey: «Title») record.setValue(bug.description, forKey: «Description») publicData.saveRecord(record, completionHandler: { record, error in if error != nil { println(error) } }) |
Первые две строки должны быть знакомы. Мы получаем ссылку на контейнер по умолчанию и публичную базу данных. Мы создаем экземпляр CKRecord
, устанавливаем значения для ключей Title и Description и сохраняем запись в iCloud.
Обратите внимание, что объект record
возвращаемый из iCloud в обработчике завершения saveRecord(_:completionHandler:)
включает в себя значения, которые вы устанавливаете вручную, а также пять атрибутов по умолчанию, которые мы видели ранее на панели мониторинга CloudKit.
Создайте и запустите свое приложение, а также опубликуйте новую ошибку, нажав кнопку «плюс» в правом верхнем углу, заполнив поля заголовка и описания. Нажмите « Опубликовать» и сохраните новую запись в iCloud в базе данных CloudKit.
Еще раз зайдите на панель мониторинга CloudKit и перейдите в зону по умолчанию в разделе « Общие данные» . Если все прошло хорошо, Вы должны увидеть две записи об ошибках в базе данных.
6. Удаление записей
Давайте закончим этот урок, позволив пользователям удалять ошибки. Добавьте следующий фрагмент кода в tableView(_:commitEditingStyle:forRowAtIndexPath:)
метод чуть выше objects. removeAtIndex(indexPath.row)
.
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 | let container = CKContainer.defaultContainer() let publicData = container.publicCloudDatabase
let bug = self.objects[indexPath.row] let query = CKQuery(recordType: «Bug», predicate: NSPredicate(format: «(Title == %@) AND (Description == %@)», argumentArray: [bug.title, bug.description])) publicData.performQuery(query, inZoneWithID: nil, completionHandler: { results, error in if error == nil { if results.count > 0 { let record: CKRecord! println(record)
publicData.deleteRecordWithID(record.recordID, completionHandler: { recordID, error in if error != nil { println(error) } }) } } else { println(error) } }) |
Чтобы удалить запись, мы создаем запрос, как мы делали ранее. Однако для удаления записи мы создаем предикат, соответствующий заголовку и описанию ошибки. Обработчик завершения возвращает результаты запроса, который мы используем для удаления соответствующей записи из общедоступной базы данных.
Создайте и запустите приложение и удалите одну из ошибок, проведя вправо или нажав кнопку « Изменить» .
Еще раз зайдите на панель управления CloudKit, чтобы увидеть, действительно ли выбранная вами ошибка была удалена.
Вывод
В этом руководстве вы создали свое первое приложение CloudKit, связав его с iCloud. Вы узнали, как создавать типы записей и записи на панели инструментов CloudKit и как загружать их в свое приложение. Помимо добавления и удаления записей в инструментальной панели CloudKit, мы также рассмотрели добавление и удаление записей с использованием инфраструктуры CloudKit. Если у вас есть какие-либо комментарии или вопросы, оставьте их в комментариях ниже.
Swift, CloudKit, HomeKit и Metal — новые инструменты для разработчиков, представленные на WWDC 2014
Apple посвятила заключительную часть своей презентации на конференции WWDC новшествам в комплекте программного обеспечения для разработчиков (SDK), среди которых отметился новый язык программирования Swift и инструменты разработки для сервисов здоровья, домашней автоматизации, iCloud и взаимодействия приложений.
Абсолютным фаворитом презентации стал Swift — новый язык программирования для собственного фреймворка Apple. Анонс Swift вызвал шквал оваций в зале, а в компании его называют современным, быстрым, мощным и безопасным языком для разработки приложений.Swift — это результат последних исследований в области языков программирования, объединенный с многолетним опытом разработки программных продуктов Apple. Свойства, унаследованные от Objective C, выраженные в простом синтаксисе делают новый API (интерфейс программирования приложений) простым для восприятия и освоения. Унаследованные типы делают код чище и помогают избежать ошибок, в то время, как модули обходятся без заголовков и поддерживают пространство имен. Необходимая память выделяется автоматически, а синтаксис избавляет разработчика даже от необходимости расставлять точки с запятой.
Swift также предоставляет отдельную панель в приложении, которая позволяет видеть результаты написанного кода в режиме реального времени.Купертиновцы также предоставили инструменты для работы с iCloud. Новая платформа CloudKit избавляет разработчиков от необходимости дорогостоящих и ресурсоемких разработок серверной части приложения, предоставляя свои вычислительные мощности бесплатно, хоть и с чрезвычайно высокими требованиями.
Используйте широкие возможности iCloud и разрабатывайте свои приложения с помощью новой платформы CloudKit. Теперь вы можете с легкостью сохранять и получать доступ к данным хранящимся в iCloud, подобно тому как вы работает с базой данных. CloudKit позволяет авторизоваться в iOS-программах сторонних разработчиков через идентификатор Apple, не передавая личной информации приложению.
Следующим новшеством стал анонс API, который нацелен собрать разрозненные сторонние приложения в единую инфраструктуру и позволяет одному приложению использовать функции другого. Так, например, Safari сможет использовать приложение Bing для перевода интернет-страниц. Приложения из App Store теперь смогут размещать свои интерактивные виджеты в центре уведомлений.Apple решила смягчить свою политику закрытости и открыла доступ к некоторым элементам iOS. В «программу лояльности» попал Touch ID, позволивший контролировать доступ к сторонним приложениям по отпечатку пальцев. Также разработчики смогут заменять стандартную клавиатуру при необходимости.HomeKit — платформа, созданная с целью объединения домашних устройств автоматизации в одну систему. Ранее все устройства, выпускаемые различными разработчиками, управлялись собственными приложениями и использовали собственные протоколы. Apple намерена создать единый протокол для всех систем, а многочисленные договора с компаниями-лидерами в этой сфере лишь укрепляют позиции Apple на рынке умных домов. Будущая площадка позволит управлять устройствами через специальное приложение или команды Siri. Например, после фразы: «Siri, я иду спать» в доме гаснет свет и блокируются двери.
Для разработчиков игр Apple представила библиотеку Metal, которая позволяет более экономно использовать ресурсы для максимально реалистичных и ресурсоемких игр на iOS. Также компания представила обновление XCode с живым рендерингом и визуальной отладкой.Все вышеперечисленные новшества включены iOS 8 beta, которая доступна разработчикам уже сегодня.
Смотрите также:
Как начать работу с CloudKit
CloudKit — это потрясающая платформа, и Apple доказала, что это надежное и надежное решение для хранения данных в облаке. CloudKit поддерживает несколько флагманских приложений компании, включая iCloud Drive, Photos и Notes.
Несмотря на множество преимуществ, которые предоставляет фреймворк, я считаю, что CloudKit не так популярен, как мог бы. Основная причина в том, что фреймворк требует сложного обучения.API довольно сложен, и, что более важно, разработчики не могут придерживаться четкого пути. Есть много ингредиентов, но нет рецепта.
Начало работы
Начать работу с CloudKit легко благодаря тесной интеграции Xcode с веб-сайтом разработчиков Apple. Работать с CloudKit API немного сложнее. API не особенно сложен. Проблема заключается не столько в понимании структуры, сколько в том, чтобы разбить все на части. Вы, как разработчик, должны проделать тяжелую работу.
Позвольте мне прояснить одну вещь. CloudKit — это решение для передачи данных с серверов Apple iCloud и на них. Вот и все. Если вы хотите интегрировать CloudKit с Core Data, у вас есть над чем поработать. И нет рецепта, которому можно следовать. Отсутствие плана — вот что отпугивает многих разработчиков от фреймворка.
В ближайшие недели я планирую выпустить серию руководств по CloudKit, чтобы помочь разработчикам, интересующимся CloudKit, ближе познакомиться с фреймворком. Мы начнем с основ, включив CloudKit в проект, и рассмотрим несколько аспектов, типичных для большинства приложений CloudKit.В этой серии статей мы не будем создавать функциональные приложения, поскольку каждое приложение использует CloudKit немного по-своему. Основное внимание уделяется решению типичных проблем, с которыми вы сталкиваетесь при переходе на CloudKit.
Приступим
Чтобы начать эту серию, я хотел бы показать вам, как легко включить CloudKit в проекте, новом или существующем.
Создание проекта
Запустите Xcode, создайте новый проект на основе шаблона Single View App и назовите проект Scribbles .
Сообщите Xcode, где вы хотите сохранить проект, и нажмите Create .
Включение CloudKit
Чтобы включить CloudKit, нам сначала нужно щелкнуть переключатель iCloud на вкладке Capabilities цели Scribbles .
Раздел iCloud показывает нам, что хранилище ключ-значение включено по умолчанию. Установите флажок CloudKit , чтобы включить CloudKit для цели Scribbles .
Xcode немедленно переходит к действию за кулисами, выполняя несколько шагов, которые вы раньше делали вручную. Каждое приложение CloudKit имеет собственный контейнер iCloud. Контейнер iCloud похож на песочницу или контейнер на устройстве пользователя. Это гарантирует, что данные вашего приложения изолированы на серверах Apple iCloud.
Вы также можете предоставить своему приложению доступ к контейнеру iCloud другого приложения, связанного с вашей учетной записью разработчика.Это интересно, например, если вы разрабатываете мобильный клиент для настольного приложения.
Сделано для вас
Ранее я упоминал, что Xcode выполняет несколько шагов за кулисами. Он создает для вас App ID , включает iCloud для этого App ID и добавляет App ID в контейнер iCloud, созданный для вашего приложения.
Xcode также создал файл прав для цели Scribbles и добавил к нему права iCloud .Цель Scribbles также связана с платформой CloudKit.
Короче говоря, проект готов воспользоваться фреймворком и сервисами CloudKit. Давайте попробуем.
Разговор с серверами Apple
Позже в этой серии мы обсудим управление учетными записями более подробно. На данный момент убедитесь, что вы вошли в систему с действующим Apple ID на устройстве, которое используете для разработки.
Откройте ViewController.swift и добавьте вверху оператор импорта для инфраструктуры CloudKit .
импорт UIKit
импортировать CloudKit
class ViewController: UIViewController {
}
Чтобы показать вам, что CloudKit включен и правильно настроен, мы собираемся получить идентификатор записи пользователя, который вошел в систему на устройстве. Нам нужно запросить эту информацию у контейнера iCloud. Мы можем получить доступ к контейнеру iCloud, связанному с приложением, через класс CKContainer
.
Помните, что мы настроили CloudKit для использования контейнера по умолчанию, то есть контейнера, назначенного этому приложению.Мы можем получить ссылку на этот контейнер, вызвав метод default ()
.
импорт UIKit
импортировать CloudKit
class ViewController: UIViewController {
// MARK: - Просмотр жизненного цикла
переопределить функцию viewDidLoad () {
super.viewDidLoad ()
CKContainer.default ()
}
}
Чтобы получить идентификатор записи для текущего пользователя, вошедшего в систему, мы вызываем fetchUserRecordID (_ :)
в контейнере по умолчанию. Этот метод принимает один аргумент — закрытие.Это закрытие вызывается, когда асинхронный запрос завершается, успешно или безуспешно. Он принимает два аргумента: необязательный идентификатор записи и необязательную ошибку.
импорт UIKit
импортировать CloudKit
class ViewController: UIViewController {
// MARK: - Просмотр жизненного цикла
переопределить функцию viewDidLoad () {
super.viewDidLoad ()
CKContainer.default (). FetchUserRecordID {(идентификатор записи, ошибка) в
if let error = error {
печать (ошибка)
} иначе, если разрешить recordID = recordID {
печать (идентификатор записи)
}
}
}
}
Запустите приложение на своем устройстве и проверьте вывод в консоли.Вот как должен выглядеть результат. Позже в этой серии вы узнаете больше об именах записей и зонах записи. Пока не беспокойтесь об этих деталях.
Легкий старт
Надеюсь, вы согласны с тем, что приступить к работе с CloudKit легко. Xcode заботится о мельчайших деталях, и мы можем сосредоточиться на интеграции с инфраструктурой Apple iCloud.
Однако, как только вы начнете использовать CloudKit, вы заметите, что это самый простой шаг. CloudKit API несложно использовать или понять, но вам нужно принять во внимание множество деталей.
Чехол для CloudKit
Когда дело доходит до удаленных серверных служб для мобильных приложений, у вас есть варианты. Что в случае с CloudKit? Как решение Apple для удаленного хранения и обмена данными вписывается в набор инструментов Apple Developer?
Недавно я разместил в Твиттере опрос, просто чтобы понять, чем пользуются люди.
#iosdev, ребята: что вы используете, когда вашему приложению требуется серверная часть (например, удаленный сервер / удаленная база данных)?
Меня также интересует «почему» — ответьте твитом, если хотите поделиться! Спасибо! 🙌🏻
— Эндрю Бэнкрофт (@andrewcbancroft) 20 июля 2018 г.
Явным победителем стала Firebase, за которой последовали люди, реализующие свою собственную архитектуру внутреннего сервера. Последний мертвый? Связь между Realm и CloudKit.
«Хм…», — подумал я, «Что случилось? Почему нет любви к CloudKit? »
Из вариантов в моем собственном опросе у меня был опыт создания приложений с использованием трех из четырех: CloudKit, Firebase и «развертывание собственного бэкэнда» с приложением, созданным для моего работодателя.
Итак, я спросил себя: «Когда вы, , использовали бы CloudKit, Эндрю?»
Вот как я это разбиваю. Чем больше из них относится к вам и вашему опыту разработки приложений, тем более веские аргументы в пользу использования CloudKit (на мой взгляд):
Только платформы Apple? CloudKit.
Если вы разрабатываете исключительно для платформ Apple, CloudKit — естественный выбор.
Одна из других альтернатив CloudKit, упомянутых в опросе, может иметь преимущество, если вы также разрабатываете для Android, но если вы придерживаетесь экосистемы Apple, имеет смысл использовать их службу удаленного хранения и обмена данными ( т.е. CloudKit).
Выходите за пределы платформ Apple с помощью CloudKit JS
Даже если вы разрабатываете , а не исключительно для платформ Apple, CloudKit все равно может быть для вас жизнеспособным выбором!
Apple разработала CloudKit JS для создания сопутствующих веб-приложений для ваших приложений iOS и macOS. У вас должно быть существующее приложение CloudKit и разрешить веб-сервисам использовать CloudKit JS, но после настройки приложения Apple вы можете использовать CloudKit в веб-версии своего приложения, если она у вас есть.
Общаясь с людьми в Twitter по этому поводу, я узнал, что некоторые даже использовали CloudKit в приложении для Android, используя CloudKit JS в качестве моста в экосистему Apple.
Это значительно расширяет аудиторию для вашего приложения!
Встроенная проверка подлинности: CloudKit.
Если вы устали создавать экраны регистрации и входа в систему, обратите внимание на CloudKit.
Это особенно верно, если вы беспокоитесь только о разработке для платформ Apple.
Пользователи, выполнившие вход с помощью Apple ID, автоматически проходят аутентификацию, когда ваше приложение использует CloudKit.
Поцелуй на прощание экраны регистрации / входа в систему!
CloudKit дольше бесплатен
Все серверные части как поставщики услуг предлагают уровень бесплатного пользования или бесплатную пробную версию, и, хотя у меня нет личного опыта работы с порогами оплаты для этих сервисов, я считаю, что уровень бесплатного пользования CloudKit может «прослужить дольше». Что я имею в виду?
- Любые данные, хранящиеся в частной базе данных пользователя , не учитываются при выделении хранилища для вашего приложения … они учитываются в плане хранилища iCloud пользователя .Это увеличивает емкость хранилища вашего приложения .
- CloudKit различает хранилище для недвоичных данных и хранилище для носителей (двоичные данные, такие как изображения, видео, аудио и т. Д.). Вы начинаете со 100 МБ недвоичных данных, но 10 ГБ изображений / видео / аудио / и т. Д. место хранения. В отличие…
- По мере роста числа пользователей вашего приложения, ваше хранилище и лимиты передачи масштабируются вместе с пользовательской базой вашего приложения. У меня нет личного опыта здесь, но мне интересно … к тому времени, когда ваше приложение вырастет до 100 000 пользователей (переломный момент для Apple, чтобы увеличить объем вашего приложения), насколько вероятно, что вы уже будете платить за использование Firebase ?
Развертывает собственный бэкэнд? Рассмотрим CloudKit.
Люди, уже вложившие средства в Realm или Firebase, уже используют серверную часть в качестве платформы обслуживания. Я бы посоветовал рассмотреть CloudKit именно те, которые обслуживают инфраструктуру внутреннего сервера сами .
Я был на месте тех, кому нужно было развернуть свой собственный внутренний сервер и поддерживать эти виртуальные машины, базы данных, веб-серверы… все работает.
Часто это происходит из-за того, что необходимо подключить мобильное приложение к локальной / общедоступной облачной экосистеме организации, и я могу полностью посочувствовать этому.
CloudKit предлагает надежную альтернативу, которую следует рассмотреть организациям, разрабатывающим для платформ Apple. Абстрагировать серверную часть и позволить Apple обслуживать эти серверы и компоненты хранения — это огромная нагрузка. Данные, хранящиеся в общедоступной облачной базе данных , , доступны программно, поэтому ваша организация может получить данные, хранящиеся там, для включения в свою внутреннюю экосистему.
Требования компании: только сторонние структуры и услуги? CloudKit.
Это требование, скорее всего, будет встречаться в компаниях и организациях, которые не хотят вводить множество сторонних зависимостей.
Тем из нас, у кого есть работодатель или кто работает на клиентов, которые имеют право голоса в том, из чего может состоять наш набор инструментов для разработки приложений, возможно, потребуется рассмотреть варианты в экосистеме Apple.
CloudKit — надежный выбор для компаний, не склонных к риску.
Кстати о неприятии риска…
Неприятие личного риска третьей стороной… CloudKit.
Этот вариант подходит для меня близко к дому, потому что я испытал боль из-за того, что сторонний сервис, от которого я зависел, для нескольких приложений был прекращен. * Кашель * Разбор * Кашель *
Обычно меня устраивают сторонние библиотеки, но сторонние службы — это для меня совсем другая история.
Когда вы используете библиотеку, которая в конечном итоге исчезает, может потребоваться небольшая переделка, чтобы ваш код скомпилировался с альтернативой.
Но когда у вас есть данные, от которых зависит ваше приложение, живущее на удаленном сервере, принадлежащем третьей стороне, и эта служба прекращает свое существование… Ой! Поговорите о боли, разочаровании и хлопотах, связанных с необходимостью выполнять миграцию поверх кода перезаписи .
Требуется только удаленное хранилище данных? Будь проще. CloudKit.
В наши дни есть тонны , которую можно сделать на устройстве. Если все, что вам нужно, — это удаленное хранилище данных для облегчения обмена данными между устройствами или с другими пользователями и вам не нужны удаленные облачные функции, аналитика, удаленные службы машинного обучения и т. Д., Вы можете сделать это простым. и используйте CloudKit.
Зависит от того, от чего зависит Apple: CloudKit.
От чего зависит Apple при удаленном хранении данных и совместном использовании их собственных приложений?
Ответ: CloudKit.
Очевидно, что Apple находится в уникальной ситуации: Apple не нуждается и не заботится о разработке каких-либо своих приложений для Android. Естественно, они будут зависеть от той самой основы, которую они создали. Иначе мы могли бы возразить: «Что за кучка лицемеров!», Верно?
CloudKit обслуживает такие приложения, как Фотографии, iCloud Drive, Заметки, Новости и другие.
Это похоже на то, как если бы Apple разработала CloudKit для Apple и решила: «Эй, давайте откроем это нашим разработчикам и дадим им возможность узнать, что для нас отлично работает!»
Мне это нравится.Мне нравится безопасность, когда я знаю, что Apple зависит от платформы, которую они нам предоставляют.
Компромиссы
Честно говоря, я хотел бы указать на несколько компромиссов, если бы вы выбрали CloudKit.
Нет встроенной поддержки Android
Этот компромисс квалифицируется словами « не поддерживает встроенную поддержку Android» по какой-то причине. CloudKit JS может фактически открыть дверь для людей, разрабатывающих кроссплатформенные приложения, хотя это может потребовать некоторого взлома.
Ожидайте некоторых проблем со стороны Android, но не стоит полностью исключать CloudKit.
Хранение и совместное использование данных
только с CloudKitCloudKit предоставляет вам полнофункциональную службу удаленного хранения и обмена данными. Если вы хотите использовать некоторые расширенные функции, такие как «облачные функции», аналитика для своего приложения, аутентификация без использования Apple ID, удаленные службы машинного обучения и т. Д., Вы не найдете этих функций в CloudKit.
Это не означает, что Apple оставляет вас в покое, когда дело доходит до аутентификации, аналитики, машинного обучения и т. Д.
Не забывайте, что у вас будет встроенная аутентификация Apple ID, как я упоминал выше, а аналитика предоставляется через iTunes Connect.
Машинное обучение на устройстве с CoreML также может облегчить потребность в удаленных сервисах машинного обучения.
Если после рассмотрения компонентных решений Apple вы ищете альтернативы, Firebase предлагает все вышеперечисленное как своего рода универсальный пакет.
Как я уже упоминал выше… если все, что вам действительно нужно, — это удаленное хранилище данных для синхронизации данных между устройствами, не усложняйте ситуацию.CloudKit по-прежнему может служить вам хорошо.
Другой альтернативой является использование поставщика общедоступного облака, такого как Azure или AWS, для «облачных функций» или удаленного машинного обучения. Если вы предпочитаете компоновать эти виды услуг, это жизнеспособная альтернатива Firebase или собственным решениям Apple для подобных проблем.
Что вы используете? Почему?
Я прагматик. Я не всегда использую CloudKit. Но мне нравится CloudKit, когда я могу его использовать.
Что вы используете ? Почему? Какой у вас опыт работы с серверной частью в качестве поставщика услуг? Что еще вы бы рассмотрели в случае с CloudKit? Какие еще компромиссы вы понимаете?
Пожалуйста, включите JavaScript, чтобы просматривать комментарии от Disqus. (NSArray * результаты, NSError * ошибка) { }];Создать новую запись
CKRecord * запись = [[выделение CKRecord] initWithRecordType: @ "RecordType"]; запись [@ "key"] = @ "Некоторые данные"; [[CKContainer defaultContainer].(CKRecordID * recordID, NSError * error) { }];
Контакты
Ялантис
Следуйте за Yalantis в Twitter (@Yalantis) и [Facebook] (https://www.facebook.com/Yalantis?ref=ts&fref=ts)
Лицензия
Лицензия MIT (MIT)
Авторские права © 2015 Yalantis
Настоящим предоставляется бесплатное разрешение любому лицу, получившему копию этого программного обеспечения и связанных файлов документации («Программное обеспечение»), для работы с Программным обеспечением без ограничений, включая, помимо прочего, права на использование, копирование, изменение, объединение, публикацию, распространять, сублицензировать и / или продавать копии Программного обеспечения, а также разрешать лицам, которым предоставляется Программное обеспечение, делать это при соблюдении следующих условий:
Вышеупомянутое уведомление об авторских правах и это уведомление о разрешении должны быть включены во все копии или существенные части Программного обеспечения.ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ ПРЕДОСТАВЛЯЕТСЯ «КАК ЕСТЬ», БЕЗ КАКИХ-ЛИБО ГАРАНТИЙ, ЯВНЫХ ИЛИ ПОДРАЗУМЕВАЕМЫХ, ВКЛЮЧАЯ, НО НЕ ОГРАНИЧИВАЯСЬ, ГАРАНТИИ КОММЕРЧЕСКОЙ ЦЕННОСТИ,
ПРИГОДНОСТЬ ДЛЯ КОНКРЕТНОЙ ЦЕЛИ И ЗАЩИТА ОТ ПРАВ. НИ В КОЕМ СЛУЧАЕ
АВТОРЫ ИЛИ ДЕРЖАТЕЛИ АВТОРСКИХ ПРАВ НЕСУТ ОТВЕТСТВЕННОСТЬ ЗА ЛЮБЫЕ ПРЕТЕНЗИИ, УБЫТКИ ИЛИ ДРУГИЕ
ОТВЕТСТВЕННОСТЬ, ВЫЯВЛЯЮЩАЯСЯ ЛИ В РЕЗУЛЬТАТЕ ДОГОВОРА, ПРАКТИКИ ИЛИ ИНЫМ ОБРАЗОМ,
ВНЕЗАПНО ИЛИ В СВЯЗИ С ПРОГРАММНЫМ ОБЕСПЕЧЕНИЕМ, ИЛИ ИСПОЛЬЗОВАНИЕМ ИЛИ ДРУГИМИ ДЕЯМИ В
ПРОГРАММНОЕ ОБЕСПЕЧЕНИЕ.
ios — CloudKit — не удается получить данные из iCloud
Есть ли разница в отправке и получении данных из iCloud через CloudKit
, когда приложение (iOS9) работает через Xcode или когда оно загружается из App Store?
Моя проблема: приложение, запущенное из Xcode, успешно получает и отображает данные, то же приложение, опубликованное в App Store, не может отображать данные…
В чем может быть ошибка?
Спасибо.
func getRecordsFromCloud () {
// Получение данных с помощью Convenience API
let cloudContainer = CKContainer.defaultContainer ()
пусть publicDatabase = cloudContainer.publicCloudDatabase
let predicate = NSPredicate (значение: true)
let query = CKQuery (recordType: "Pivo", предикат: предикат)
query.sortDescriptors = [NSSortDescriptor (ключ: "creationDate", по возрастанию: false)]
// Создаем операцию запроса с запросом
пусть queryOperation = CKQueryOperation (запрос: запрос)
queryOperation.желаемыйKeys = ["имя", "тип", "местоположение"]
queryOperation.queuePriority = .VeryHigh
queryOperation.resultsLimit = 50
queryOperation.recordFetchedBlock = {(запись: CKRecord!) -> Пусто в
if let restaurantRecord = record {
self.restaurants.append (restaurantRecord)
}
}
queryOperation.queryCompletionBlock = {(курсор: CKQueryCursor ?, ошибка: NSError?) -> Пусто в
if (error! = nil) {
print ("Не удалось получить данные из iCloud - \ (ошибка !.localizedDescription) ")
возвращаться
}
print («Успешно получить данные из iCloud»)
self.refreshControl? .endRefreshing ()
NSOperationQueue.mainQueue (). AddOperationWithBlock () {
self.spinner.stopAnimating ()
self.tableView.reloadData ()
}
}
// Выполняем запрос
publicDatabase.addOperation (queryOperation)
}
SwiftUI2: интеграция входа с Apple в CloudKit (2020) | по МИНГ
5.Нажмите + Возможность
Применить Войти через Apple и;
API хранилища iCloud.
6. Чтобы использовать iCloud, включите CloudKit в разделе iCloud.
7. + Добавить новый контейнер для сервисов iCloud
8. Включите контейнеры iCloud.
9. Посетите панель управления CloudKit.
10. После входа в панель управления CloudKit выберите «Схема».
(Если вы не видите эту страницу, перезагрузите страницу или войдите снова.)
11. Добавьте новый Тип, назовем его «UsersData».
12.Добавьте поле (адрес электронной почты, firstName, lastName) и сделайте их доступными для запросов (не забудьте «recordName»).
13. Выйдите из схемы и перейдите в раздел «Данные (записи)», нажмите «Записи запроса».
Убедитесь, что нет сообщения об ошибке. Если данные не запрашиваются, вам необходимо повторить проверку на шаге 12.
14. Вернуться к Xcode
Войти с помощью Apple и общедоступной базы данных iCloud
SwiftUI предоставляет нам простой способ добавить кнопку «Войти с помощью Apple».
15. Перейдите в ContentView / я назвал его LoginView.
Откройте библиотеку или воспользуйтесь сочетанием клавиш «CMD» + «shift» + «L».
Примените Войти с помощью кнопки Apple .
16. Замените приведенным ниже кодом.
Для использования и объяснения, проверьте комментарий кода.
Совет: используйте общедоступную базу данных вместо частной, иначе вы не сможете просматривать сведения о пользователе. Тип пользователей по умолчанию может быть трудным для запроса, гораздо лучше добавить пользовательский тип, такой как «UsersData».
17. Добавить логин Состояние
@AppStorage («логин») частный var login = false
18.Пусть userID будет идентификатором пользователя, который ранее был сохранен в локальном.
пусть userID = UserDefaults.standard.object (forKey: «userID») как ? Строка
19. Используйте оператор if для определения представления пользователя
if (! Login && (userID == nil )) {
...
} else {
...
}
20. (Необязательно) Я также добавил интересную анимацию из LottieFile.
21. Круто! Попробуем сборку на iPhone.
Предварительный просмотр:
Молодец!
23. Вернитесь на панель управления CloudKit и снова нажмите «Запросы на записи».
Вот данные пользователя (ей):
запросов-cloudkit · PyPI
| image0 | | image1 || Статус CI | | Версия |
Этот проект предоставляет обработчик аутентификации для межсерверного API Apple CloudKit
для библиотеки Python запросов. На английском языке это
означает, что эта библиотека позволяет вам взаимодействовать с CloudKit с Python в серверной среде
.Он просто имеет две зависимости
— \ `запросы
и python-ecdsa — и позволяет пропустить все скучные и скучные шаги криптографической подписи
при аутентификации с помощью CloudKit на ваш
собственное. Хотя основной код довольно прост, в Интернете не было доступного правильного образца кода
, в котором описывалось бы, как это сделать, но теперь есть
!
Установка
————
requests-cloudkit доступен для загрузки через индекс пакета Python
(PyPi).Вы можете установить его прямо сейчас, используя pip или easy_install.
.. code :: bash
pip install requests-cloudkit
Использование
——
requests-cloudkit предоставляет обработчик аутентификации, который можно передать
непосредственно в библиотеку запросов для аутентификации запросов к
CloudKit API. Перед тем, как начать работу с межсерверным API CloudKit,
, вам сначала нужно будет следовать инструкциям Apple, чтобы сгенерировать сертификат
и межсерверный ключ (см. `Доступ к CloudKit с помощью межсерверного ключа
Когда у вас есть эти значения, просто вставьте их в объект CloudKitAuth,
, который вы можете использовать с запросами для взаимодействия с CloudKit. Например:
.. code :: pycon
>>> запросы на импорт
>>> from requests_cloudkit import CloudKitAuth
>>> auth = CloudKitAuth (key_id = YOUR_KEY_ID, key_file_name = YOUR_PRIVATE_KEY_PATH)
>>> requests.get («https://api.apple-cloudkit.com/database/[version provided/[container provided/[environment provided/public/zones/list», auth = auth)
Использование Requests-CloudKit с RestMapper
— —————————————
запросов также может использоваться с
` python-restmapper
для прямой интеграции с CloudKit API.
.. code :: pycon
>>> CloudKit = restmapper.RestMapper («https: //api.apple-cloudkit.com / database / [version] / [container] / [environment] / «)
Создайте экземпляр CloudKit, используя идентификатор межсерверного ключа CloudKit
и укажите путь к файлу закрытого ключа.
.. code :: pycon
>>> cloudkit = CloudKit (auth = CloudKitAuth (key_id = YOUR_KEY_ID, key_file_name = YOUR_KEY_FILE))
Теперь вы можете начать делать запросы к CloudKit API, используя красивый синтаксис атрибута
.
.. code :: pycon
>>> response = cloudkit.public.zones.list ()
Вышеупомянутое будет достигать
https://api.apple-cloudkit.com/database/[version ]/[containerpting/[environmentpting/public/zones/list).
Если вы хотите передать данные тела для POST, укажите единственный аргумент
для вызова API и укажите «POST» в качестве первого атрибута (примечание:
этот аргумент ожидает значение * str *, поэтому, если вы хотите передать JSON, используйте
json.dumps, чтобы закодировать его в строку). Т.е.
.. code :: pycon
>>> cloudkit.POST.my.request (данные)
Или:
.. code :: pycon
>>> cloudkit.POST.my.request (json.dumps (json_payload))
Полный список серверов CloudKit: to-Server API, справочник
`Документация разработчика Apple
Поддержка
——-
Если вам нравится эта библиотека или вам нужна помощь во внедрении, просто отправьте нам письмо
по электронной почте: hi @ lionheartsw.com.
.. _license-licenselicense-imagelicense-url:
Лицензия | Лицензия |
——————
Лицензия Apache, версия 2.0. Подробности смотрите в `LICENSE
.. raw :: html
.. | загрузки | image :: https://img.shields.io/pypi/dm/requests-cloudkit.svg?style=flat
.. _downloads: https://pypi.python.org/pypi/requests-cloudkit
->
.. | image0 | image :: meta / repo-banner.png
.. | image1 | image :: meta / репо-баннер-дно.png
: цель: https://github.com/lionheart/requests-cloudkit/blob/master/https://lionheartsw.com/
.. | Статус CI | image :: https://img.shields.io/travis/lionheart/requests-cloudkit.svg?style=flat
: цель: https://github.com/lionheart/requests-cloudkit/blob/master/https: //travis-ci.org/lionheart/requests-cloudkit.py
.. | Версия | image :: https://img.shields.io/pypi/v/requests-cloudkit.svg?style=flat
: цель: https://github.com/lionheart/requests-cloudkit/blob/master/https: // pypi.python.org/pypi/requests-cloudkit
.. | Лицензия | image :: http://img.shields.io/pypi/l/requests-cloudkit.svg?style=flat
: цель: https://github.com/lionheart/requests-cloudkit/blob/master/LICENSE
CloudKit Art — зачем и как использовать в своих проектах?
Cloudkit — это решение от Apple, позволяющее приложениям iOS и macOS использовать облако данных — iCloud. Он предлагает интерфейсы для перемещения данных между приложением и контейнерами, предоставляя пользователям доступ к данным, расположенным на всех их устройствах.Кроме того, CloudKit позволяет хранить данные в общедоступном месте, доступном всем пользователям приложения. Лимиты данных зависят от количества пользователей приложения. Решение было впервые представлено на конференции WWDC в 2014 году.
1. Типы базы данных
Apple предоставляет три типа баз данных, которые могут использоваться приложениями iOS: частные, общедоступные и общие.
Частная база данных
Доступно каждому пользователю, у которого есть учетная запись в iCloud. Лимит данных распределяется между всеми используемыми приложениями.Он используется для хранения конфиденциальных данных или данных, связанных только с конкретным пользователем. Это недоступно для разработчиков.
Публичная база данных
Доступно всем пользователям приложения, в том числе тем, у кого нет учетной записи iCloud. Лимит данных зависит от типа учетной записи разработчика и применяется ко всем экземплярам одного приложения. Чтение данных возможно для разработчиков через дашборд.
Общая база данных
База данных, используемая для обмена информацией между другими пользователями.Лимит данных зависит от учетной записи пользователя iPhone. Также необходимо иметь учетную запись iCloud.
2. Фреймворк CloudKit
Чтобы начать использовать CloudKit, сначала необходимо настроить идентификатор приложения при добавлении службы iCloud. Также необходимо настроить контейнер iCloud по умолчанию.
2.1. Контейнеры
Уровень iCloud, управляющий доступом к базам данных. У каждого приложения есть объект контейнера по умолчанию, который управляет своим собственным контентом. Все операции между сервером и приложением координируются экземпляром контейнера для проверки правильности конфигурации учетной записи iCloud или загрузки объектов базы данных (CKDatabase).Более того, приложение может ссылаться на другие контейнеры, отличные от контейнера по умолчанию, если оно может получить к ним свободный доступ, вводя их идентификатор во время инициализации. Это помогает создать набор приложений, которые могут иметь общую базу данных. Каждый контейнер диверсифицирует общедоступные и частные данные, связанные только с одной учетной записью iCloud.
2.2. Типы записей
Данные в базах CloudKit хранятся в виде записей в экземплярах класса CKRecord. Каждая запись может иметь любое количество полей указанного типа.В CloudKit мы можем хранить как простые типы (Integer, Double, String), так и более сложные, такие как Location (CLLocation) или активы. Активы — это объекты класса CKAsset для хранения большего количества данных, таких как изображения или фотографии. CloudKit ограничивает размер одной записи до 1 МБ. Благодаря тому, что активы хранятся отдельно, их лимиты значительно выше, уменьшенные выбранным планом iCloud. Более того, записи могут быть связаны со ссылками так же, как в реляционных базах данных. Для этого мы используем объекты класса CKReference.
В таблице представлены все возможные типы.
Тип поля | Класс | Описание |
Актив | CKAsset | Большой файл, связанный с записью, но хранящийся отдельно |
NS Оболочка для байтовых буферов, которая хранится с записью | ||
Дата | NSDate | Единый момент времени |
Двойной | NSNumber | Двойной |
Целое число | NSNumber (64) | Целое число |
Местоположение | CLLocation | Географические координаты и высота |
Ссылка | CKReference | Связь от одного объекта к другому |
String | ||
String | ||
String | String | |
Список | 906 38 NSArrayМассивы любого из указанных выше типов полей |
3.Панель управления Cloudkit
CloudKit Dashboard обеспечивает подробный предварительный просмотр активности приложения и пользователей и может использоваться для настройки и создания схем баз данных. Он разделен на 6 разделов:
- Доступ к API: создание и управление ключами и токенами для использования CloudKit другими приложениями, кроме iOS (например, веб).
- Данные: предварительный просмотр данных, сохраненных в базе с возможностью фильтрации, создания кверендов, добавления индексов и т. Д.
- Схема: раздел для управления схемами базы данных. Разработчик определяет, какие данные и в какой форме будет хранить приложение.
- Журналы: журналов сервера iCloud, которые содержат все успешные и ошибочные обращения к базе данных.
- Телеметры: использование и производительность сервера iCloud представлены в графическом виде (графики).
- Использование: статистические данные с информацией об активных пользователях, запросах в секунду и т. Д.
4. NSUbiquitousKeyValueStore
NSUbiquitousKeyValueStore не является частью платформы CloudKit, а представляет собой интересную альтернативу для хранения небольшого количества личных данных пользователя в iCloud. Программист может использовать его так же, как и локальный NSUserDefaults. Он позволяет сохранять такие значения, как Bool, NSNumber, NSString, NSDate, NSData и NSArray, или коллекции NSDictionary, состоящие из объектов упомянутых классов. Сначала данные сохраняются локально на определенном устройстве, а затем отправляются в облако, если пользователь вошел в службу iCloud.В противном случае NSUbiquitousKeyValueStore работает как локальный кеш — данные не синхронизируются между всеми устройствами, но доступны на устройствах, на которых они были сохранены. Тем не менее, следует учитывать тот факт, что лимит данных значительно снижен и составляет 1 МБ на одного пользователя.