Статьи

Создание приложения для списка покупок с помощью CloudKit: совместное использование покупок

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

В этой серии вы работали с частными базами данных, а также изучали публичные базы данных. Однако до WWDC 2016 года, когда Apple представила CKShare , у приложений не было надлежащего способа обмена данными. Частные базы данных доступны только пользователям, вошедшим в систему, тогда как публичные базы данных предназначены для публичного контента и позволяют любому просматривать записи. Однако при использовании некоторых собственных приложений Apple iCloud, таких как Pages, Keynote или Notes, вы, возможно, заметили, нажав кнопку «Поделиться», — возможность пригласить других пользователей для доступа к вашим данным.

В этом посте я покажу вам, как делиться контентом.

Приложение Apple Notes

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

Помните, что я буду использовать Xcode 9 и Swift 3. Если вы используете более старую версию Xcode, имейте в виду, что вы используете другую версию языка программирования Swift.

В этом уроке мы продолжим с того места, на котором остановились в третьем уроке этой серии. Вы можете скачать или клонировать проект с GitHub .

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

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

Общая база данных — это окно общей базы данных Apple в вашу личную базу данных, которое предоставляет пользователям доступ для чтения или записи через CKShare .

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

Различные типы баз данных

Сначала вы реализуете UICloudSharingController и связанный с ним UICloudSharingControllerDelegate . Безусловно, самый простой подход к совместному использованию — это использование UICloudSharingController , который заботится о предоставлении и представлении экранов для обмена вашей записью, приглашения пользователей и установки разрешений для вашей записи, и все это с помощью нескольких строк кода. Согласно SDK от Apple, UICloudSharingController позволяет вам:

  • приглашать людей для просмотра или совместной работы над общей записью
  • установить права доступа, определяющие, кто может получить доступ к общей записи (только приглашенные пользователи или лица, имеющие ссылку на общий доступ)
  • установить общие или индивидуальные разрешения (только чтение или чтение / запись)
  • удалить доступ от одного или нескольких участников
  • прекратить участие (если пользователь является участником)
  • прекратить совместное использование со всеми участниками (если пользователь является владельцем общей записи)

В своем файле ListViewController.swift UICloudSharingControllerDelegate делегат UICloudSharingControllerDelegate :

1
2
class ListViewController: UIViewController, UITableViewDelegate, UITableViewDataSource,
UICloudSharingControllerDelegate{

Затем добавьте метод didSelectRowAt: так, чтобы, когда пользователь выбирает ячейку, он вызывал лист совместного доступа.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
       let item = items[indexPath.row]
        
       let share = CKShare(rootRecord: item)
        
 
       if let itemName = item.object(forKey: «name») as?
           self.itemName = item.object(forKey: «name») as?
           share[CKShareTitleKey] = «Sharing \(itemName)» as CKRecordValue?
 
       } else {
           share[CKShareTitleKey] = «» as CKRecordValue?
           self.itemName = «item»
       }
        
       share[CKShareTypeKey] = «com.doronkatz.List» as CKRecordValue
       prepareToShare(share: share, record: item)
   }

В приведенном выше методе вы создаете CKShare , связывая item CKRecord в качестве корневой записи. Затем метод устанавливает свойства share[CKShareTitleKey] и share[CKShareTypeKey] при подготовке к отображению общей записи. Наконец, метод вызывает prepareToShare , передавая исходную запись, а также только что созданную общую запись, которую вы будете реализовывать следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
private func prepareToShare(share: CKShare, record: CKRecord){
        
       let sharingViewController = UICloudSharingController(preparationHandler: {(UICloudSharingController, handler: @escaping (CKShare?, CKContainer?, Error?) -> Void) in
   
           let modRecordsList = CKModifyRecordsOperation(recordsToSave: [record, share], recordIDsToDelete: nil)
            
           modRecordsList.modifyRecordsCompletionBlock = {
               (record, recordID, error) in
                
               handler(share, CKContainer.default(), error)
           }
           CKContainer.default().privateCloudDatabase.add(modRecordsList)
       })
        
       sharingViewController.delegate = self
 
       sharingViewController.availablePermissions = [.allowReadWrite,
                                                 .allowPrivate]
       self.navigationController?.present(sharingViewController, animated:true, completion:nil)
   }

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

Вам также необходимо реализовать два обязательных метода делегата, itemTitleForCloudSharingController и failedToSaveShareWithError . Есть и другие необязательные методы делегата, которые вы также можете выбрать для реализации.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
func cloudSharingControllerDidSaveShare(_ csc: UICloudSharingController) {
       print(«saved successfully»)
   }
    
   func cloudSharingController(_ csc: UICloudSharingController, failedToSaveShareWithError error: Error) {
       print(«failed to save: \(error.localizedDescription)»)
   }
    
   func itemThumbnailData(for csc: UICloudSharingController) -> Data?
       return nil //You can set a hero image in your share sheet.
   }
    
   func itemTitle(for csc: UICloudSharingController) -> String?
       return self.itemName
   }

Метод itemTitleForCloudSharingController соответствует свойству CKShareTitleKey вы установили ранее, запрашивая заголовок для отображения в списке приглашений. failedToSaveShareWithError уведомляет делегата для обработки неудачных запросов на совместное использование. Соберите и запустите ваше приложение, создайте запись, а затем выберите отдельную запись, чтобы активировать лист обмена.

Поделиться своей записью

Это выглядит хорошо, но ваша работа еще не завершена! Вам необходимо определить, как другие пользователи могут получать и отображать вашу общую запись.

Наконец, вам нужно настроить приложение, чтобы иметь возможность получать и отображать общие записи. Откройте файл AppDelegate.swift и добавьте userDidAcceptCloudKitShareWith делегата userDidAcceptCloudKitShareWith :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
func application(_ application: UIApplication, userDidAcceptCloudKitShareWith cloudKitShareMetadata: CKShareMetadata) {
        
       let acceptSharing: CKAcceptSharesOperation = CKAcceptSharesOperation(shareMetadatas: [cloudKitShareMetadata])
        
       acceptSharing.qualityOfService = .userInteractive
       acceptSharing.perShareCompletionBlock = {meta, share, error in
           print(“successfully shared”)
       }
       acceptSharing.acceptSharesCompletionBlock = {
           error in
           guard (error == nil) else{
               print(“Error \(error?.localizedDescription ?? “”)”)
               return
           }
            
           let viewController: AddItemViewController =
               self.window?.rootViewController as!
           viewController.fetchShare(cloudKitShareMetadata)
            
       }
       CKContainer(identifier: cloudKitShareMetadata.containerIdentifier).add(acceptSharing)
   }

UserDidAcceptCloudKitShareWith: уведомляет делегата о том, что ваше приложение имеет доступ к общей информации, давая вам возможность обрабатывать новый контент. Он уведомляет вас, когда ваше приложение успешно предоставило общий CKAcceptSharesOperation к элементу, а также уведомляет вас о проблемах в процессе обмена, используя CKAcceptSharesOperation .

В этом методе вы создаете новый экземпляр AddItemViewController , вызывая новую fetchShare вами функцию fetchShare вместе с интересующими метаданными. Наконец, метод добавляет CKAcceptSharesOperation в ваш контейнер. Чтобы отобразить запись, откройте файл AddItemViewController.swift и fetchShare метод fetchShare :

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
func fetchShare(_ cloudKitShareMetadata: CKShareMetadata){
       let op = CKFetchRecordsOperation(
           recordIDs: [cloudKitShareMetadata.rootRecordID])
        
       op.perRecordCompletionBlock = { record, _, error in
           guard error == nil, record != nil else{
               print(“error \(error?.localizedDescription ?? “”)”)
               return
           }
           DispatchQueue.main.async {
               self.item = record
           }
       }
       op.fetchRecordsCompletionBlock = { _, error in
           guard error != nil else{
               print(“error \(error?.localizedDescription ?? “”)”)
               return
           }
       }
       CKContainer.default().sharedCloudDatabase.add(op)
   }

В этом последнем методе вы извлекаете общую запись и присваиваете ее своей глобальной переменной self.item , прежде чем окончательно self.item операцию в собственной sharedCloudDatabase данных sharedCloudDatabase вашего пользователя.

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

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

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