French (Français) translation by Soleil (you can also view the original English article)
Dans le tutoriel précédent de cette série, nous avons ajouté la possibilité d’ajouter, de mettre à jour et de supprimer des listes de courses. Une liste de courses sans aucun élément n'est pas très utile. Dans ce tutoriel, nous ajouterons la possibilité d'ajouter, de mettre à jour et de supprimer des éléments d'une liste d'achats. Cela signifie que nous allons travailler avec des références et la classe CKReference
.
Nous allons également examiner de plus près le modèle de données de l'application de liste d'achats. Est-il facile d'apporter des modifications au modèle de données et comment l'application répond-elle aux modifications apportées dans le CloudKit Dashboard?
Conditions préalables
Rappelez-vous que je vais utiliser Xcode 7 et Swift 2. Si vous utilisez une ancienne version de Xcode, gardez à l'esprit que vous utilisez une version différente du langage de programmation Swift.
Dans ce tutoriel, nous continuerons là où nous l'avions laissé dans le deuxième tutoriel de cette série. Vous pouvez télécharger ou cloner le projet depuis GitHub.
1. Détails de la liste d'achats
Actuellement, l'utilisateur peut modifier le nom d'une liste de courses en appuyant sur l'indicateur de divulgation détaillée, mais l'utilisateur doit également pouvoir voir le contenu d'une liste de courses en tapant sur une dans le contrôleur de vue des listes. Pour que cela fonctionne, nous avons d'abord besoin d'une nouvelle sous-classe UIViewController
.
Étape 1: Création de ListViewController
La classe ListViewController
affiche le contenu d'une liste d'achats dans une vue de table. L'interface de la classe ListViewController
ressemble à celle de la classe ListsViewController
. Nous importons les frameworks CloudKit et SVProgressHUD et conformons la classe aux protocoles UITableViewDataSource
et UITableViewDelegate
. Comme nous utiliserons une vue de table, nous déclarons une constante, ItemCell
, qui servira d'identificateur de réutilisation de cellule.
import UIKit import CloudKit import SVProgressHUD class ListViewController: UIViewController, UITableViewDataSource, UITableViewDelegate { static let ItemCell = "ItemCell" @IBOutlet weak var messageLabel: UILabel! @IBOutlet weak var tableView: UITableView! @IBOutlet weak var activityIndicatorView: UIActivityIndicatorView! var list: CKRecord! var items = [CKRecord]() var selection: Int? ... }
Nous déclarons trois sorties, messageLabel
de type UILabel !
, tableView
de type UITableView
!, et activityIndicatorView
de type UIActivityIndicatorView
!. Le contrôleur de vue liste conserve une référence à la liste d'achats qu'il affiche dans la propriété de list
, qui est de type CKRecord
!. Les éléments de la liste d'achats sont stockés dans la propriété items
, qui est de type [CKRecord]
. Enfin, nous utilisons une variable d’aide, la selection
, pour savoir quel élément de la liste de courses a été sélectionné par l’utilisateur. Cela apparaîtra plus tard dans ce tutoriel.
Étape 2: Création de l'interface utilisateur
Ouvrez Main.storyboard, ajoutez un contrôleur de vue et définissez sa classe sur ListViewController
dans Identity Inspector. Sélectionnez la cellule prototype du contrôleur de vue de liste, appuyez sur la touche de contrôle et faites glisser la cellule prototype vers le contrôleur de vue de liste. Choisissez Show dans le menu qui apparaît et définissez l'identifiant sur List dans Attributes Inspector.
Ajoutez une vue de table, une étiquette et une vue d'indicateur d'activité à la vue du contrôleur de vue. N'oubliez pas de câbler les prises du contrôleur de vue ainsi que celles de la vue tableau.
Sélectionnez la vue de table et définissez Prototype Cells sur 1 dans Attributes Inspector. Sélectionnez la cellule prototype, définissez Style sur RightDetail, Identifier sur ItemCell et Accessory to Disclosure Indicator. C'est ce à quoi devrait ressembler le contrôleur de vue lorsque vous avez terminé.

Étape 3: Configuration de View Controller
Avant de revoir le framework CloudKit, nous devons préparer le contrôleur de vue pour les données qu'il va recevoir. Commencez par mettre à jour l'implémentation de viewDidLoad
. Nous définissons le titre du contrôleur de vue sur le nom de la liste d'achats et appelons deux méthodes d'assistance, setupView
et fetchItems
.
// MARK: - // MARK: View Life Cycle override func viewDidLoad() { super.viewDidLoad() // Set Title title = list.objectForKey("name") as? String setupView() fetchItems() }
La méthode setupView
est identique à celle implémentée dans la classe ListsViewController
.
// MARK: - // MARK: View Methods private func setupView() { tableView.hidden = true messageLabel.hidden = true activityIndicatorView.startAnimating() }
Pendant que nous y sommes, implémentons également une autre méthode d'assistance familière, updateView
. Dans updateView
, nous mettons à jour l'interface utilisateur du contrôleur de vue en fonction des éléments stockés dans la propriété items
.
private func updateView() { let hasRecords = items.count > 0 tableView.hidden = !hasRecords messageLabel.hidden = hasRecords activityIndicatorView.stopAnimating() }
Je vais laisser fetchItems
vide pour le moment. Nous reviendrons sur cette méthode une fois que nous aurons fini de configurer le contrôleur de vue liste.
// MARK: - // MARK: Helper Methods private func fetchItems() { }
Étape 4: Méthodes de source de données de vue de table
Nous sommes presque prêts à prendre l'application pour un autre test. Avant cela, nous devons implémenter le protocole UITableViewDataSource
. Si vous avez lu les versions précédentes de cette série, l'implémentation vous sera familière.
// MARK: - // MARK: Table View Data Source Methods func numberOfSectionsInTableView(tableView: UITableView) -> Int { return 1; } func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return items.count } func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { // Dequeue Reusable Cell let cell = tableView.dequeueReusableCellWithIdentifier(ListViewController.ItemCell, forIndexPath: indexPath) // Configure Cell cell.accessoryType = .DetailDisclosureButton // Fetch Record let item = items[indexPath.row] if let itemName = item.objectForKey("name") as? String { // Configure Cell cell.textLabel?.text = itemName } else { cell.textLabel?.text = "-" } return cell } func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool { return true }
Étape 5: Gestion de la sélection
Pour tout lier, nous devons revoir la classe ListsViewController
. Commencez par implémenter la méthode tableView(_:didSelectRowAtIndexPath:)
du protocole UITableViewDelegate
.
// MARK: - // MARK: Table View Delegate Methods func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { tableView.deselectRowAtIndexPath(indexPath, animated: true) }
Nous devons également mettre à jour prepareForSegue (segue:sender:)
pour gérer le seguer que nous avons créé il y a quelques instants. Cela signifie que nous devons ajouter un nouveau case
à l'instruction switch
.
// MARK: - // MARK: Segue Life Cycle override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { guard let identifier = segue.identifier else { return } switch identifier { case SegueList: // Fetch Destination View Controller let listViewController = segue.destinationViewController as! ListViewController // Fetch Selection let list = lists[tableView.indexPathForSelectedRow!.row] // Configure View Controller listViewController.list = list case SegueListDetail: ... default: break } }
Pour satisfaire le compilateur, nous devons également déclarer la constante SegueList
en haut de ListsViewController.swift.
import UIKit import CloudKit import SVProgressHUD let RecordTypeLists = "Lists" let SegueList = "List" let SegueListDetail = "ListDetail" class ListsViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, AddListViewControllerDelegate { ... }
Générez et exécutez l'application pour voir si tout est correctement connecté. Comme nous n'avons pas encore implémenté la méthode fetchItems
, aucun élément ne sera affiché. C'est quelque chose que nous devons corriger.
2. Récupération d'articles
Étape 1: Créer un type d'enregistrement
Avant de pouvoir récupérer des éléments du backend CloudKit, nous devons créer un nouveau type d'enregistrement dans le CloudKit Dashboard. Accédez au tableau de bord CloudKit, créez un nouveau type d'enregistrement et nommez-le Items. Chaque élément doit avoir un nom, créez un nouveau champ, définissez le nom du champ sur name et définissez le type de champ sur String.
Chaque article doit également savoir à quelle liste de courses il appartient. Cela signifie que chaque article nécessite une référence à sa liste d'achats. Créez un nouveau champ, définissez le nom du champ sur list et définissez le type de champ sur Reference. Le type de champ Reference a été conçu à cette fin, en gérant les relations.

Revenez à Xcode, ouvrez ListsViewController.swift et déclarez une nouvelle constante en haut pour le type d'enregistrement Items.
import UIKit import CloudKit import SVProgressHUD let RecordTypeLists = "Lists" let RecordTypeItems = "Items" let SegueList = "List" let SegueListDetail = "ListDetail" class ListsViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, AddListViewControllerDelegate { ... }
Étape 2: Récupération d'éléments
Ouvrez ListViewController.swift et accédez à la méthode fetchItems
. L'implémentation est similaire à la méthode fetchLists
de la classe ListsViewController
. Il y a cependant une différence importante.
private func fetchItems() { // Fetch Private Database let privateDatabase = CKContainer.defaultContainer().privateCloudDatabase // Initialize Query let reference = CKReference(recordID: list.recordID, action: .DeleteSelf) let query = CKQuery(recordType: RecordTypeItems, predicate: NSPredicate(format: "list == %@", reference)) // Configure Query query.sortDescriptors = [NSSortDescriptor(key: "name", ascending: true)] // Perform Query privateDatabase.performQuery(query, inZoneWithID: nil) { (records, error) -> Void in dispatch_async(dispatch_get_main_queue(), { () -> Void in // Process Response on Main Thread self.processResponseForQuery(records, error: error) }) } }
La différence entre fetchItems
et fetchLists
est le prédicat que nous transmettons à l'initialiseur CKQuery
. Nous ne sommes pas intéressés par chaque élément de la base de données privée de l'utilisateur. Nous nous intéressons uniquement aux éléments associés à une liste de courses particulière. Cela se reflète dans le prédicat de l'instance CKQuery
.
Nous créons le prédicat en transmettant une instance de CKReference
, que nous créons en invoquant init(recordID:action:)
. Cette méthode accepte deux arguments, une instance CKRecordID
qui référence l'enregistrement de liste d'achats et une instance CKReferenceAction
qui détermine ce qui se produit lorsque la liste d'achats est supprimée.
Les actions de référence sont très similaires aux règles de suppression des données de base. Si l'objet référencé, la liste d'achats de cet exemple, est supprimé, l'infrastructure CloudKit inspecte l'action de référence pour déterminer ce qui doit arriver aux enregistrements contenant une référence à l'enregistrement supprimé. L'énumération CKReferenceAction
a deux valeurs de membre:
-
None
: Si la référence est supprimée, rien ne se produit dans les enregistrements faisant référence à l'enregistrement supprimé. -
DeleteSelf
: Si la référence est supprimée, chaque enregistrement faisant référence à l'enregistrement supprimé est également supprimé.
Comme aucun élément ne doit exister sans une liste d'achats, nous définissons l'action de référence sur DeleteSelf
.
La méthode processResponseForQuery(records: error:)
ne contient rien de nouveau. Nous traitons la réponse de la requête et mettons à jour l'interface utilisateur en conséquence.
private func processResponseForQuery(records: [CKRecord]?, error: NSError?) { var message = "" if let error = error { print(error) message = "Error Fetching Items for List" } else if let records = records { items = records if items.count == 0 { message = "No Items Found" } } else { message = "No Items Found" } if message.isEmpty { tableView.reloadData() } else { messageLabel.text = message } updateView() }
Créez et exécutez l'application. Vous ne verrez aucun élément pour le moment, mais l'interface utilisateur devrait mettre à jour que la liste d'achats est vide.
3. Ajouter des articles
Étape 1: Création de AddItemViewControler
Il est temps d'implémenter la possibilité d'ajouter des éléments à une liste d'achats. Commencez par créer une nouvelle sous-classe UIViewController
, AddItemViewController
. L'interface du contrôleur de vue est similaire à celle de la classe AddListViewController
.
En haut, nous importons les frameworks CloudKit et SVProgressHUD. Nous déclarons le protocole AddItemViewControllerDelegate
, qui servira le même objectif que le protocole AddListViewControllerDelegate
. Le protocole définit deux méthodes, l'une pour l'ajout d'éléments et l'autre pour la mise à jour des éléments.
import UIKit import CloudKit import SVProgressHUD protocol AddItemViewControllerDelegate { func controller(controller: AddItemViewController, didAddItem item: CKRecord) func controller(controller: AddItemViewController, didUpdateItem item: CKRecord) } class AddItemViewController: UIViewController { @IBOutlet weak var nameTextField: UITextField! @IBOutlet weak var saveButton: UIBarButtonItem! var delegate: AddItemViewControllerDelegate? var newItem: Bool = true var list: CKRecord! var item: CKRecord? ... }
Nous déclarons deux points de vente, un champ de texte et un élément de bouton de barre. Nous déclarons également une propriété pour le délégué et une variable d'assistance, newItem
, qui nous aide à déterminer si nous créons un nouvel élément ou mettons à jour un élément existant. Enfin, nous déclarons une propriété list
pour référencer la liste d'achats à laquelle l'élément sera ajouté et un élément de propriété pour l'item
que nous créons ou mettons à jour.
Avant de créer l'interface utilisateur, implémentons deux actions dont nous aurons besoin dans le storyboard, cancel(_:)
etsave(_:)
. Nous mettrons à jour l'implémentation de l'action save(_:)
plus tard dans ce tutoriel.
// MARK: - // MARK: Actions @IBAction func cancel(sender: AnyObject) { navigationController?.popViewControllerAnimated(true) } @IBAction func save(sender: AnyObject) { navigationController?.popViewControllerAnimated(true) }
Étape 2: Création de l'interface utilisateur
Ouvrez Main.storyboard, ajoutez un élément de bouton de barre à la barre de navigation du contrôleur de vue de liste et définissez System Item sur Add dans Attributes Inspector. Faites glisser un contrôleur de vue depuis Object Library et définissez sa classe sur AddItemViewController. Créez un point entre l'élément de bouton de la barre que nous venons de créer et le contrôleur de vue d'élément ajouté. Choisissez Show dans le menu qui apparaît et définissez l'identificateur du lieu sur ItemDetail.
Ajoutez deux éléments de bouton de barre à la barre de navigation du contrôleur de vue d'ajout d'élément, un bouton d'annulation à gauche et un bouton de sauvegarde à droite. Connectez chaque élément du bouton de barre à l'action correspondante. Ajoutez un champ de texte à la vue du contrôleur de vue et n'oubliez pas de connecter les prises du contrôleur de vue. Voici à quoi devrait ressembler le contrôleur d'affichage des éléments lorsque vous avez terminé.

Étape 3: Configuration de View Controller
L'implémentation du contrôleur de vue d'élément ajouté ne contient rien que nous n'avons pas encore couvert. Il y a une exception, cependant, dont nous discuterons dans un instant. Commençons par configurer le contrôleur de vue dans viewDidLoad
.
Nous appelons setupView
, une méthode d'assistance, et mettons à jour la valeur de newItem
. Si la propriété item
est égale à nil
, newItem
est égal à true
. Cela nous aide à déterminer si nous créons ou mettons à jour un élément de liste d'achats.
Nous ajoutons également le contrôleur de vue en tant qu'observateur pour les notifications de type UITextFieldTextDidChangeNotification
. Cela signifie que le contrôleur de vue est averti lorsque le contenu de nameTextField
change.
// MARK: - // MARK: View Life Cycle override func viewDidLoad() { super.viewDidLoad() setupView() // Update Helper newItem = item == nil // Add Observer let notificationCenter = NSNotificationCenter.defaultCenter() notificationCenter.addObserver(self, selector: "textFieldTextDidChange:", name: UITextFieldTextDidChangeNotification, object: nameTextField) }
Dans viewDidAppear (animé :)
, nous affichons le clavier en appelant la méthode becomeFirstResponder
sur nameTextField
.
override func viewDidAppear(animated: Bool) { nameTextField.becomeFirstResponder() }
La méthode setupView
appelle deux méthodes d'assistance, updateNameTextField
et updateSaveButton
. La mise en œuvre de ces méthodes d'assistance est simple. Dans updateNameTextField
, nous remplissons le champ de texte. Dans updateSaveButton
, nous activons ou désactivons le bouton Enregistrer en fonction du contenu du champ de texte.
// MARK: - // MARK: View Methods private func setupView() { updateNameTextField() updateSaveButton() } // MARK: - private func updateNameTextField() { if let name = item?.objectForKey("name") as? String { nameTextField.text = name } } // MARK: - private func updateSaveButton() { let text = nameTextField.text if let name = text { saveButton.enabled = !name.isEmpty } else { saveButton.enabled = false } }
Avant de regarder l'implémentation mise à jour de la méthode save(_:)
, nous devons implémenter la méthode textFieldDidChange(_:)
. Tout ce que nous faisons est d'appeler updateSaveButton
pour activer ou désactiver le bouton Enregistrer.
// MARK: - // MARK: Notification Handling func textFieldTextDidChange(notification: NSNotification) { updateSaveButton() }
Étape 4: Enregistrement d'éléments
La méthode save(_:)
est la méthode la plus intéressante de la classe AddItemViewController
, car elle nous montre comment utiliser les références CloudKit. Jetez un oeil à l'implémentation de la méthode save(_:)
ci-dessous.
La plupart de son implémentation devrait sembler familière puisque nous avons couvert l'enregistrement des enregistrements dans la classe AddListViewController
. Ce qui nous intéresse le plus, c'est la façon dont l'article garde une référence à sa liste de courses. Nous créons d'abord une instance de CKReference
en appelant l'initialiseur désigné, init(recordID:action :)
. Nous avons couvert les détails de la création d'une instance de CKReference
il y a quelques minutes lorsque nous avons créé la requête pour récupérer des éléments de liste d'achats.
Dire l'objet de cette référence est facile. Nous appelons setObjec(_:forKey:)
sur la propriété item
, en passant l'instance CKReference
comme valeur et "list"
comme clé. La clé correspond au nom du champ que nous avons attribué dans le tableau de bord CloudKit. L'enregistrement de l'élément sur iCloud est identique à ce que nous avons déjà couvert. C'est facile de travailler avec des références CloudKit.
@IBAction func save(sender: AnyObject) { // Helpers let name = nameTextField.text // Fetch Private Database let privateDatabase = CKContainer.defaultContainer().privateCloudDatabase if item == nil { // Create Record item = CKRecord(recordType: RecordTypeItems) // Initialize Reference let listReference = CKReference(recordID: list.recordID, action: .DeleteSelf) // Configure Record item?.setObject(listReference, forKey: "list") } // Configure Record item?.setObject(name, forKey: "name") // Show Progress HUD SVProgressHUD.show() // Save Record privateDatabase.saveRecord(item!) { (record, error) -> Void in dispatch_async(dispatch_get_main_queue(), { () -> Void in // Dismiss Progress HUD SVProgressHUD.dismiss() // Process Response self.processResponse(record, error: error) }) } }
L'implémentation de processResponse(record:error:)
ne contient rien de nouveau. Nous vérifions si des erreurs sont apparues et, si aucune erreur n'a été commise, nous en avisons le délégué.
// MARK: - // MARK: Helper Methods private func processResponse(record: CKRecord?, error: NSError?) { var message = "" if let error = error { print(error) message = "We were not able to save your item." } else if record == nil { message = "We were not able to save your item." } if !message.isEmpty { // Initialize Alert Controller let alertController = UIAlertController(title: "Error", message: message, preferredStyle: .Alert) // Present Alert Controller presentViewController(alertController, animated: true, completion: nil) } else { // Notify Delegate if newItem { delegate?.controller(self, didAddItem: item!) } else { delegate?.controller(self, didUpdateItem: item!) } // Pop View Controller navigationController?.popViewControllerAnimated(true) } }
Étape 5: mise à jour de ListViewController
Nous avons encore du travail à faire dans la classe ListViewController
. Commencez par conformer la classe ListViewController
au protocole AddItemViewControllerDelegate
. C'est aussi un bon moment pour déclarer une constante pour le segue avec l'identifiant ItemDetail.
import UIKit import CloudKit import SVProgressHUD let SegueItemDetail = "ItemDetail" class ListViewController: UIViewController, UITableViewDataSource, UITableViewDelegate, AddItemViewControllerDelegate { ... }
Implémenter le protocole AddItemViewControllerDelegate
est trivial. Dans controller(_:didAddItem:)
, nous ajoutons le nouvel item
à items
, trions les éléments, rechargez la vue de la table et appelons updateView
.
// MARK: - // MARK: Add Item View Controller Delegate Methods func controller(controller: AddItemViewController, didAddItem item: CKRecord) { // Add Item to Items items.append(item) // Sort Items sortItems() // Update Table View tableView.reloadData() // Update View updateView() }
L'implémentation du controller(_:didUpdateItem:)
est encore plus facile. Nous trions les items
et rechargeons la vue tableau.
func controller(controller: AddItemViewController, didUpdateItem item: CKRecord) { // Sort Items sortItems() // Update Table View tableView.reloadData() }
Dans sortItems
, nous trions le tableau des instances de CKRecord
par nom en utilisant la fonction sortInPlace
, une méthode du protocole MutableCollectionType
.
private func sortItems() { items.sortInPlace { var result = false let name0 = $0.objectForKey("name") as? String let name1 = $1.objectForKey("name") as? String if let itemName0 = name0, itemName1 = name1 { result = itemName0.localizedCaseInsensitiveCompare(itemName1) == .OrderedAscending } return result } }
Il y a deux autres fonctionnalités dont nous avons besoin pour implémenter, mettre à jour et supprimer des éléments de liste d'achats.
Étape 6: Suppression d'éléments
Pour supprimer des éléments, nous devons implémenter tableView(_:commitEditingStyle:forRowAtIndexPath:)
du protocole UITableViewDataSource
. Nous récupérons l'élément de la liste de courses à supprimer et le passons à la méthode deleteRecord(_:)
.
func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath) { guard editingStyle == .Delete else { return } // Fetch Record let item = items[indexPath.row] // Delete Record deleteRecord(item) }
L'implémentation de deleteRecord(_ :)
ne contient rien de nouveau. Nous appelons deleteRecordWithID(_:completionHandler:)
sur la base de données privée et traitons la réponse dans le gestionnaire d'achèvement.
private func deleteRecord(item: CKRecord) { // Fetch Private Database let privateDatabase = CKContainer.defaultContainer().privateCloudDatabase // Show Progress HUD SVProgressHUD.show() // Delete List privateDatabase.deleteRecordWithID(item.recordID) { (recordID, error) -> Void in dispatch_async(dispatch_get_main_queue(), { () -> Void in // Dismiss Progress HUD SVProgressHUD.dismiss() // Process Response self.processResponseForDeleteRequest(item, recordID: recordID, error: error) }) } }
DansprocessResponseForDeleteRequest(enregistrement:recordID:erreur:)
, nous mettons à jour la propriété items
et l'interface utilisateur. Si quelque chose ne va pas, nous avertissons l'utilisateur en affichant une alerte.
private func processResponseForDeleteRequest(record: CKRecord, recordID: CKRecordID?, error: NSError?) { var message = "" if let error = error { print(error) message = "We are unable to delete the item." } else if recordID == nil { message = "We are unable to delete the item." } if message.isEmpty { // Calculate Row Index let index = items.indexOf(record) if let index = index { // Update Data Source items.removeAtIndex(index) if items.count > 0 { // Update Table View tableView.deleteRowsAtIndexPaths([NSIndexPath(forRow: index, inSection: 0)], withRowAnimation: .Right) } else { // Update Message Label messageLabel.text = "No Items Found" // Update View updateView() } } } else { // Initialize Alert Controller let alertController = UIAlertController(title: "Error", message: message, preferredStyle: .Alert) // Present Alert Controller presentViewController(alertController, animated: true, completion: nil) } }
Étape 7: mise à jour des éléments
L'utilisateur peut mettre à jour un élément en appuyant sur l'indicateur de divulgation détaillée. Cela signifie que nous devons implémenter la méthode déléguée tableView(_:accessoireButtonTappedForRowWithIndexPath:)
. Dans cette méthode, nous stockons la sélection de l'utilisateur et effectuons manuellement la recherche ListDetail. Notez que rien ne se produit dans la méthode tableView(_:didSelectRowAtIndexPath:)
. Tout ce que nous faisons est de désélectionner la ligne sur laquelle l'utilisateur a appuyé.
// MARK: - // MARK: Table View Delegate Methods func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) { tableView.deselectRowAtIndexPath(indexPath, animated: true) } func tableView(tableView: UITableView, accessoryButtonTappedForRowWithIndexPath indexPath: NSIndexPath) { tableView.deselectRowAtIndexPath(indexPath, animated: true) // Save Selection selection = indexPath.row // Perform Segue performSegueWithIdentifier(SegueItemDetail, sender: self) }
Dans prepareForSegue(_:sender:)
, nous récupérons l'élément de la liste d'achats en utilisant la valeur de la propriété selection
et configurons le contrôleur de vue de destination, une instance de la classe AddItemViewController
.
// MARK: - // MARK: Segue Life Cycle override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) { guard let identifier = segue.identifier else { return } switch identifier { case SegueItemDetail: // Fetch Destination View Controller let addItemViewController = segue.destinationViewController as! AddItemViewController // Configure View Controller addItemViewController.list = list addItemViewController.delegate = self if let selection = selection { // Fetch Item let item = items[selection] // Configure View Controller addItemViewController.item = item } default: break } }
C'est tout ce que nous devons faire pour supprimer et mettre à jour les éléments de la liste d'achats. Dans la section suivante, nous explorons la facilité de mise à jour du modèle de données dans le tableau de bord CloudKit.
4. Mise à jour du modèle de données
Si vous avez déjà travaillé avec Core Data, vous savez que la mise à jour du modèle de données doit être effectuée avec prudence. Vous devez vous assurer de ne rien casser ou de ne corrompre aucun des magasins persistants de l'application. CloudKit est un peu plus flexible.
Le type d'enregistrement Items comporte actuellement deux champs, name et list. Je veux vous montrer ce que cela implique de mettre à jour le modèle de données en ajoutant un nouveau champ. Ouvrez le tableau de bord CloudKit et ajoutez un nouveau champ à l'enregistrement Items. Définissez le nom du champ sur number et définissez le type de champ sur Int (64). N'oubliez pas de sauvegarder vos modifications.

Ajoutons maintenant la possibilité de modifier le numéro d'un article. Ouvrez AddItemViewController.swift et déclarez deux sorties, une étiquette et un stepper.
import UIKit import CloudKit import SVProgressHUD protocol AddItemViewControllerDelegate { func controller(controller: AddItemViewController, didAddItem item: CKRecord) func controller(controller: AddItemViewController, didUpdateItem item: CKRecord) } class AddItemViewController: UIViewController { @IBOutlet weak var numberLabel: UILabel! @IBOutlet weak var numberStepper: UIStepper! @IBOutlet weak var nameTextField: UITextField! @IBOutlet weak var saveButton: UIBarButtonItem! var delegate: AddItemViewControllerDelegate? var newItem: Bool = true var list: CKRecord! var item: CKRecord? ... }
Nous devons également ajouter une action déclenchée lorsque la valeur du stepper change. Dans numberDidChange(_:)
, nous mettons à jour le contenu de numberLabel
.
@IBAction func numberDidChange(sender: UIStepper) { let number = Int(sender.value) // Update Number Label numberLabel.text = "\(number)" }
Ouvrez Main.storyboard et ajoutez une étiquette et un stepper au contrôleur de vue d'élément ajouté. Connectez les prises du contrôleur de vue aux éléments d'interface utilisateur correspondants et connectez l'action numberDidChange(_:)
au moteur pas à pas pour l'événement Value Changed.

L'action save(_:)
de la classe AddItemViewController
change également légèrement. Voyons ce que cela ressemble.
Il suffit d'ajouter deux lignes de code. En haut, nous stockons la valeur du stepper dans une constante, number
. Lorsque nous configurons item
, nous définissons number
comme valeur pour la clé "number"
et c'est tout.
@IBAction func save(sender: AnyObject) { // Helpers let name = nameTextField.text let number = Int(numberStepper.value) // Fetch Private Database let privateDatabase = CKContainer.defaultContainer().privateCloudDatabase if item == nil { // Create Record item = CKRecord(recordType: RecordTypeItems) // Initialize Reference let listReference = CKReference(recordID: list.recordID, action: .DeleteSelf) // Configure Record item?.setObject(listReference, forKey: "list") } // Configure Record item?.setObject(name, forKey: "name") item?.setObject(number, forKey: "number") // Show Progress HUD SVProgressHUD.show() print(item?.recordType) // Save Record privateDatabase.saveRecord(item!) { (record, error) -> Void in dispatch_async(dispatch_get_main_queue(), { () -> Void in // Dismiss Progress HUD SVProgressHUD.dismiss() // Process Response self.processResponse(record, error: error) }) } }
Nous devons également implémenter une méthode d'assistance pour mettre à jour l'interface utilisateur du contrôleur de vue d'élément ajouté. La méthode updateNumberStepper
vérifie si l'enregistrement contient un champ nommé number et met à jour le stepper si c'est le cas.
private func updateNumberStepper() { if let number = item?.objectForKey("number") as? Double { numberStepper.value = number } }
Nous appelons updateNumberStepper
dans la méthode setupView
de la classe AddItemViewController
.
private func setupView() { updateNameTextField() updateNumberStepper() updateSaveButton() }
Pour visualiser le nombre de chaque élément, nous devons apporter une modification au ListViewController
. Dans tableView(_:cellForRowAtIndexPath:)
, nous définissons le contenu de l'étiquette de droite de la cellule sur la valeur du champ de numéro de l'élément.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell { // Dequeue Reusable Cell let cell = tableView.dequeueReusableCellWithIdentifier(ListViewController.ItemCell, forIndexPath: indexPath) // Configure Cell cell.accessoryType = .DetailDisclosureButton // Fetch Record let item = items[indexPath.row] if let itemName = item.objectForKey("name") as? String { // Configure Cell cell.textLabel?.text = itemName } else { cell.textLabel?.text = "-" } if let itemNumber = item.objectForKey("number") as? Int { // Configure Cell cell.detailTextLabel?.text = "\(itemNumber)" } else { cell.detailTextLabel?.text = "1" } return cell }
C'est tout ce que nous devons faire pour mettre en œuvre les modifications apportées au modèle de données. Il n'y a pas besoin d'effectuer une migration ou quelque chose comme ça. CloudKit prend soin des détails les plus concrets.
Conclusion
Vous devriez maintenant avoir une base appropriée du framework CloudKit. J'espère que vous êtes d'accord que Apple a fait un excellent travail avec le framework et le CloudKit Dashboard. Il y a beaucoup de choses que nous n'avons pas couvertes dans cette série, mais cela devrait vous donner assez pour démarrer avec CloudKit dans vos propres projets.
Si vous avez des questions ou des commentaires, n'hésitez pas à les laisser dans les commentaires ci-dessous ou à me contacter sur Twitter.
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Update me weeklyEnvato Tuts+ tutorials are translated into other languages by our community members—you can be involved too!
Translate this post