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

Personnalisation d’une application Swift forceios

Le modèle utilisé par forceios pour créer des applications Swift affiche une liste de noms de comptes provenant d’une organisation Salesforce. Cette liste vous permet de consulter des informations détaillées au sujet d’un compte sélectionné, ainsi que la liste des contacts de ce dernier. Vous pouvez ensuite sélectionner un contact pour afficher ses informations. Les clients ne peuvent pas utiliser l’application pour interagir avec les données, mais seulement pour les consulter. Nous allons agrémenter une application Swift forceios d’une nouvelle fonctionnalité en ajoutant à la vue Détails du contact un bouton permettant à l’utilisateur de supprimer l’enregistrement du contact concerné. Si l’utilisateur touche ce bouton, votre code répond comme suit :
  1. Il envoie une requête REST pour supprimer l’enregistrement de contact associé.
  2. 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.

  1. À partir de l’explorateur de projets Xcode, ouvrez Classes > SwiftUI > ContactDetailsView.swift.
  2. 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 {
  3. Dans la définition de var body: some View, placez la déclaration List dans un conteneur VStack. Supprimez le mot-clé return avant List.
  4. 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)
            }
        }
    }
  5. 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)
            }
        }
    }
Si vous exécutiez l’application maintenant, le bouton apparaîtrait en bas de la vue, mais n’aurait pas beaucoup d’utilité. Le fait de le toucher n’afficherait qu’un message d’information destiné au développeur, c’est-à-dire vous-même, dans Xcode.

Les clients courent le risque de toucher involontairement le bouton Supprimer le contact, par exemple s’ils utilisent votre application lors d’un trajet mouvementé dans les transports en commun. Pour protéger les données du client, il est judicieux de demander une confirmation avant de supprimer un contact. Ajoutons une feuille d’actions pour rappeler aux clients qu’ils suppriment l’enregistrement de contact de leur organisation Salesforce. Une feuille d’actions s’apparente à une alerte où figurent plusieurs boutons d’action. Dans notre cas, deux boutons suffisent : OK et Annuler. Si le client touche le bouton Annuler, l’application abandonne la requête de suppression.
  1. Au début de la définition ContactDetailView, ajoutez une variable d’état privée nommée deleteWarning et définissez-la sur false. Cette variable permet de gérer la présentation de notre feuille d’actions.
    struct ContactDetailView: View {
        @State private var deleteWarning = false
  2. Dans l’action Button que vous avez configurée, définissez deleteWarning sur true. Cette attribution s’exécute lorsque le client touche le bouton. 
    Button(action:{
            self.deleteWarning = true
            print("Delete Contact button tapped.")})
  3. 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! 
                }
            ]
        )
    }
}
Vous y avez ajouté beaucoup de code, testons donc la configuration.
  1. 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.
  2. Connectez-vous à votre organisation Developer et autorisez l’accès aux données.
  3. Dans la vue Comptes, cliquez sur n’importe quel nom de compte pour afficher sa liste de contacts.
  4. Cliquez sur le nom d’un contact pour voir les détails le concernant. Vous vous trouvez maintenant dans la vue ContactDetails.
  5. Cliquez sur le bouton Supprimer le contact. Si ce bouton n’est pas présent, vérifiez votre code.
  6. Pour vous assurer que le bouton est correctement configuré, recherchez une ligne indiquant « Delete Contact button tapped » dans la console de débogage Xcode.
  7. Vous n’aimez pas les couleurs utilisées actuellement ? Essayez de modifier les propriétés foregroundColor et background de votre bouton. Pour afficher les modifications, arrêtez et redémarrez l’application.
Remarque

Remarque

Lors de la création et de l’exécution de votre application, il est possible que l’avertissement suivant (avec des ID différents) s’affiche dans Xcode. Si tel est le cas, vous pouvez l’ignorer. 

[LayoutConstraints] Unable to simultaneously satisfy constraints.
                              Probably at least one of the constraints in the following list is one you don't want. 
                              Try this: 
                                  (1) look at each constraint and try to figure out which you don't expect; 
                                  (2) find the code that added the unwanted constraint or constraints and fix it. 
                                  (
                                      "<NSLayoutConstraint:0x600003c4ecb0 UIView:0x7fca9dd27330.width == - 16   (active)>"
                                  )
                                  Will attempt to recover by breaking constraint 
                                      <NSLayoutConstraint:0x600003c4ecb0 UIView:0x7fca9dd27330.width == - 16   (active)>

Pour en savoir plus, consultez cette discussion sur Stack Overflow (en anglais).

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. 

  1. À partir de l’explorateur de projets Xcode, ouvrez Classes > SwiftUI > ContactDetailsModel.swift.
  2. Sous le code existant de la classe ContactDetailModel, définissez un nouvel élément func vide nommé deleteContact. Cette nouvelle méthode traite un seul argument, contact, de type Contact, et renvoie void.
  3. class ContactDetailModel: ObservableObject{
        @Published var contact: Contact
        init(with contact: Contact){
            self.contact = contact
        }
        func deleteContact(contact: Contact) -> Void {
        }
    }
  4. En haut de votre nouvelle fonction, appelez la méthode de l’API REST pour définir request, à savoir votre objet RestRequest.
    func deleteContact(contact: Contact) -> Void {
        let request = RestClient.shared.requestForDelete(withObjectType: "Contact", 
                                                               objectId: contact.Id, 
                                                             apiVersion: nil)
    }
  5. 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
        }
    }
  6. 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.
  7. 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.")
            }
        }
    }
La configuration de la classe de modèle est terminée ! Vous avez codé les fonctionnalités du kit de développement mobile afin de supprimer un enregistrement Salesforce. Il ne vous reste plus qu’une chose à faire : appeler votre nouvelle méthode deleteContact(_:). Maintenant, il reste à savoir où exécuter cet appel. Vous souvenez-vous de la feuille d’actions ?
  1. Ouvrez le fichier ContactDetailsView.swift.
  2. Faites défiler l’écran jusqu’à la définition .actionSheet. Dans le tableau buttons de la feuille d’actions, le bouton .default (c’est-à-dire le bouton « OK ») dispose d’une closure vide.
  3. Ajoutez vos appels dans la closure .default. Commencez par créer une instance de ContactDetailModel transmettant la variable contact locale, puis appelez deleteContact(_:) sur votre instance de modèle.
  4. .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

Formez-vous gratuitement !
Créez un compte pour continuer.
Qu’est-ce que vous y gagnez ?
  • Obtenez des recommandations personnalisées pour vos objectifs de carrière
  • Mettez en pratique vos compétences grâce à des défis pratiques et à des questionnaires
  • Suivez et partagez vos progrès avec des employeurs
  • Découvrez des opportunités de mentorat et de carrière