Modification de l’application native Forceios
Objectifs de formation
Une fois cette unité terminée, vous pourrez :
- Ajouter un bouton qui permet aux utilisateurs de supprimer un contact Salesforce
- Ajouter une nouvelle requête REST à l’application modèle iOS native
- Traiter la réponse REST
Vidéo de démonstration Trail Together
Vous souhaitez être guidé pas à pas par un expert pendant que vous travaillez sur cette étape ? Regardez cette vidéo qui fait partie de la série Trail Together.
(Ce clip commence à 18 min 11 s, au cas où vous voudriez revenir en arrière et regarder à nouveau le début de l’étape.)
Personnalisation d’une application Swift forceios
- Il envoie une requête REST pour supprimer l’enregistrement de contact associé.
- Il demande au client de confirmer la suppression.
Pour faire cet exercice, vous pouvez utiliser l’espace de travail du kit de développement mobile que vous avez créé dans l’unité de découverte d’iOS natif.
Vues et modèles
Lorsque vous consultez le dossier Classes de votre projet Xcode, deux sous-dossiers s’affichent, SwiftUI et Models. Comme leur nom l’indique, ces dossiers représentent l’architecture modèle-vue des applications SwiftUI.
- Le dossier SwiftUI contient des définitions de vue (c’est-à-dire les éléments relatifs à l’interface utilisateur). Pour l’essentiel, ces fichiers contiennent des configurations de mise en page SwiftUI et leurs attributs visuels. Les vues SwiftUI sont généralement définies comme des structures appelées « struct ».
- Le dossier Models contient les fonctionnalités relatives aux données alimentant les vues SwiftUI. Les vues font appel aux modèles pour effectuer des tâches liées aux données, telles que l’obtention de noms de comptes Salesforce ou la suppression d’un enregistrement de contact. Les modèles incluent généralement une définition de classe centrale qui définit les fonctionnalités, et parfois des éléments struct servant à organiser les données.
Chaque modèle de cette application modèle partage un préfixe de nom de fichier avec sa vue associée. Par exemple, ContactDetailsModel.swift fournit le modèle de ContactDetailsView.swift.
Ajout du bouton Supprimer
Commençons par le début : implémentons tout d’abord l’aspect « cosmétique », c’est-à-dire le visuel. Nous pouvons concevoir, coder et tester le bouton Supprimer le contact avant de le rendre pleinement fonctionnel.
L’élément struct ContactDetailView
du modèle est une liste agrégée d’éléments définis ailleurs dans ce fichier. Pour que le bouton et cet élément List
préexistant restent indépendants l’un de l’autre, vous devez les englober à l’aide d’un conteneur VStack
. Ce conteneur indique à iOS d’aligner ses éléments verticalement, tels qu’ils sont disposés dans le code. Pour faire apparaître le bouton Supprimer au bas de la scène, par exemple, placez-le après l’élément List
dans le conteneur VStack.
Dans SwiftUI, les constructeurs de boutons requièrent un argument action
. Cette action s’exécute lorsque le client touche le bouton. Pour l’instant, nous allons simplement envoyer à la console Xcode un message confirmant que le client a bien touché le bouton.
- À partir de l’explorateur de projets Xcode, ouvrez Classes > SwiftUI > ContactDetailsView.swift.
- Faites défiler l’écran jusqu’à l’élément struct
ContactDetailView
. Elle débute par la ligne contenant la chaîne suivante :struct ContactDetailView: View {
- Dans la définition de
var body: some View
, placez la déclarationList
dans un conteneurVStack
. Supprimez le mot-cléreturn
avantList
. -
var body: some View { VStack(alignment: .center, spacing: 3) { List { FieldView(label: "First Name", value: contact.FirstName) FieldView(label: "Last Name", value: contact.LastName) FieldView(label: "Email", value: contact.Email) FieldView(label: "Phone Number", value: contact.PhoneNumber) AddressView(contact: contact) } } }
- Sous le bloc List, juste avant le crochet fermant du conteneur VStack, ajoutez le bloc
Button
.var body: some View { VStack(alignment: .center, spacing: 3) { List { FieldView(label: "First Name", value: contact.FirstName) FieldView(label: "Last Name", value: contact.LastName) FieldView(label: "Email", value: contact.Email) FieldView(label: "Phone Number", value: contact.PhoneNumber) AddressView(contact: contact) } Button(action:{ print("Delete Contact button tapped.")}) { Text("Delete Contact") .bold() .font(.title) .padding() .foregroundColor(Color.white) .background(Color.gray) } } }
- Au début de la définition
ContactDetailView
, ajoutez une variable d’état privée nomméedeleteWarning
et définissez-la surfalse
. Cette variable permet de gérer la présentation de notre feuille d’actions.struct ContactDetailView: View { @State private var deleteWarning = false
- Dans l’action Button que vous avez configurée, définissez
deleteWarning
surtrue
. Cette attribution s’exécute lorsque le client touche le bouton.Button(action:{ self.deleteWarning = true print("Delete Contact button tapped.")})
- Après le crochet fermant du bloc
VStack
, ajoutez la définition de la feuille d’actions..actionSheet(isPresented: $deleteWarning) { ActionSheet(title: Text("Deleting Contact"), message: Text("This action deletes this contact in your org."), buttons: [ .cancel {}, .default(Text("OK")) { // TO DO } ] ) }
Voici à quoi ressemble votre définition de vue à ce stade.
var body: some View { VStack(alignment: .center, spacing: 3) { List { FieldView(label: "First Name", value: contact.FirstName) FieldView(label: "Last Name", value: contact.LastName) FieldView(label: "Email", value: contact.Email) FieldView(label: "Phone Number", value: contact.PhoneNumber) AddressView(contact: contact) } Button(action:{ self.deleteWarning = true print("Delete Contact button tapped.")}) { Text("Delete Contact") .bold() .font(.title) .padding() .foregroundColor(Color.white) .background(Color.gray) } } .actionSheet(isPresented: $deleteWarning) { ActionSheet(title: Text("Deleting Contact"), message: Text("This action deletes this contact in your org."), buttons: [ .cancel {}, .default(Text("OK")) { // TODO! } ] ) } }
- Cliquez sur Exécuter pour détecter les éventuelles erreurs de votre application. Si le travail que vous avez effectué jusqu’à présent ne comporte aucune erreur, un simulateur d’iPhone se lance et, après quelques secondes, affiche la page de connexion à Salesforce.
- Connectez-vous à votre organisation Developer et autorisez l’accès aux données.
- Dans la vue Comptes, cliquez sur n’importe quel nom de compte pour afficher sa liste de contacts.
- Cliquez sur le nom d’un contact pour voir les détails le concernant. Vous vous trouvez maintenant dans la vue ContactDetails.
- Cliquez sur le bouton Supprimer le contact. Si ce bouton n’est pas présent, vérifiez votre code.
- Pour vous assurer que le bouton est correctement configuré, recherchez une ligne indiquant « Delete Contact button tapped » dans la console de débogage Xcode.
- Vous n’aimez pas les couleurs utilisées actuellement ? Essayez de modifier les propriétés
foregroundColor
etbackground
de votre bouton. Pour afficher les modifications, arrêtez et redémarrez l’application.
Maintenant, faisons en sorte que ce bouton fasse son travail : supprimer l’enregistrement de contact.
Envoi de la requête de suppression à Salesforce
Pour supprimer un enregistrement de contact, vous devez ajouter du code au fichier source du modèle. Si vous parcourez le fichier Classes/Models/ContactDetailModel.swift, vous pouvez observer que la classe ContactDetailModel
est de taille minime.
class ContactDetailModel: ObservableObject{ @Published var contact: Contact init(with contact: Contact){ self.contact = contact } }
Vous pouvez utiliser le membre Id
de la propriété self.contact
pour identifier l’enregistrement actuellement consulté.
Pour créer la requête REST, vous devez appeler la méthode RestClient.shared.requestForDelete(withObjectType:objectId:apiVersion:)
. Cette requête d’API Salesforce est inhabituelle, car, si elle aboutit, elle ne renvoie rien d’important à l’appelant. Étant donné que votre application ne recevra pas de package de données à analyser et à déployer, vous pouvez traiter la réponse REST dans une simple closure d’achèvement.
- À partir de l’explorateur de projets Xcode, ouvrez Classes > SwiftUI > ContactDetailsModel.swift.
- Sous le code existant de la classe
ContactDetailModel
, définissez un nouvel élémentfunc
vide nommédeleteContact
. Cette nouvelle méthode traite un seul argument,contact
, de typeContact
, et renvoievoid
. -
class ContactDetailModel: ObservableObject{ @Published var contact: Contact init(with contact: Contact){ self.contact = contact } func deleteContact(contact: Contact) -> Void { } }
- En haut de votre nouvelle fonction, appelez la méthode de l’API REST pour définir
request
, à savoir votre objetRestRequest
.func deleteContact(contact: Contact) -> Void { let request = RestClient.shared.requestForDelete(withObjectType: "Contact", objectId: contact.Id, apiVersion: nil) }
- Pour envoyer la requête à Salesforce, appelez la fonction
RestClient.shared.send(request:_:)
. Le premier argument correspond à la requête prédéfinie que vous avez créée. Pour le deuxième argument, insérez une closure d’achèvement.func deleteContact(contact: Contact) -> Void { let request = RestClient.shared.requestForDelete(withObjectType: "Contact", objectId: contact.Id, apiVersion: nil) RestClient.shared.send(request: request) {result in } }
- Remplissez la closure d’achèvement avec un bloc
switch
qui traite deux requêtes :.success(_)
et.failure(_)
. Dans le cadre de cet exercice, configurez simplement l’affichage d’un message d’état pour chaque résultat dans la console de débogage Xcode. -
func deleteContact(contact: Contact) -> Void { let request = RestClient.shared.requestForDelete(withObjectType: "Contact", objectId: contact.Id, apiVersion: nil) RestClient.shared.send(request: request) {result in switch result { case .success(_): print("Contact deleted.") case .failure(_): print("Your attempt to delete this contact could not be completed. This is possibly because it is associated with cases or other dependencies.") } } }
deleteContact(_:)
. Maintenant, il reste à savoir où exécuter cet appel. Vous souvenez-vous de la feuille d’actions ?- Ouvrez le fichier ContactDetailsView.swift.
- Faites défiler l’écran jusqu’à la définition
.actionSheet
. Dans le tableaubuttons
de la feuille d’actions, le bouton.default
(c’est-à-dire le bouton « OK ») dispose d’une closure vide. - Ajoutez vos appels dans la closure
.default
. Commencez par créer une instance deContactDetailModel
transmettant la variablecontact
locale, puis appelezdeleteContact(_:)
sur votre instance de modèle. -
.actionSheet(isPresented: $deleteWarning) { ActionSheet(title: Text("Deleting Contact"), message: Text("This action deletes this contact in your org."), buttons: [ .cancel {}, .default(Text("OK")) { let model = ContactDetailModel(with: contact) model.deleteContact(contact: contact) } ] ) }
Bien que son élaboration soit terminée, cette solution n’est pas d’une qualité suffisante pour être mise en production. Les clients ne pourront savoir si leur suppression a effectivement abouti qu’en revenant à la vue de liste des contacts et en constatant que le contact n’est plus répertorié. Dans le cas contraire, s’ils se rendent compte que le contact se trouve toujours dans la vue, ils ne pourront pas identifier l’origine de l’erreur. Pour combler cette lacune, vous pourriez remplacer l’instruction RestClient.shared.send(request:_:)
par un éditeur Combine
et publier le résultat de la requête de suppression. Il suffirait ensuite de présenter le résultat au client dans l’interface utilisateur de la vue ou dans un message d’alerte dans ContactDetailView
.
Exercez-vous !
Vous pouvez créer et exécuter votre code dans le simulateur iOS. Notez que vous recevez une réponse d’erreur lorsque vous essayez de supprimer un contact par défaut dans la base de données Developer Edition. Ces erreurs se produisent car chaque contact pré-empaqueté dans une organisation Developer Edition est le parent d’autres enregistrements. Pour préparer le test, connectez-vous à votre organisation Developer Edition, puis créez un ou plusieurs contacts test qui n’ont pas d’autres enregistrements.
Ressources
- Guides pour développeurs : Traitement des réponses REST
- Guides pour développeurs : Modèle Swift natif
- Guides pour développeurs : Gestion des données de modèle dans votre application (bibliothèque du développeur iOS)
- Lien externe : Closures (The Swift Programming Language)