Il y a 5 mois · 4 minutes · Mobile

iOS : Une application modulaire, avec les sous-projets de Xcode

Lorsque nous créons une application et que nous travaillons en équipe, nous avons souvent dû faire face aux problèmes de conflits de fusionnement qui se manifestent quand plusieurs personnes ajoutent ou effacent des ressources en même temps.

Pour résoudre ce problème, nous avons décidé de mettre en place un projet qui se compose de plusieurs sous-projets. Chaque projet aura ses dépendances, gérées à l’aide de CocoaPods. 

L’approche que l’on décrit au cours de cet article présente plusieurs avantages :

  • la réutilisation du composant à l’intérieur d’une autre application
  • une re-distribution facilitée sous forme de Pod
  • une "separation of concerns" plus robuste
  • une meilleure maintenabilité et lisibilité du code et des tests
  • une durée des temps des builds incrémentales réduite (mais, en contrepartie, une compilation de zéro plus longue)  

Nous voulons créer un projet (MainProject) et un composant (MyComponent) référencé par le premier.

Comme nous nous servons de bibliothèques dynamiques, cette approche fonctionnera seulement pour les versions de iOS supérieures ou égales à 8.0. 

C’est parti !

À l’aide de Xcode, créons un projet iOS vide, de nom "MainProject.xcodeproj".

Dans un autre dossier, par exemple "SubProjects", toujours à l’aide de Xcode créons un autre projet, "MyComponent.xcodeproj", de type Cocoa Touch Framework.

Les Pods

Comme souvent, nous allons avoir besoin d’ajouter certaines dépendances à notre projet principal et à notre composant. Dans notre exemple, nous allons ajouter, par exemple RxCocoa+RxSwift au targets principaux de MyComponent et Quick+Nimble aux targets de test de tous nos projets. Voici le Podfile qu’il faudra écrire :

workspace 'Project.xcworkspace'

def test_pods
  pod 'Quick'
  pod 'Nimble'
end
 
def my_component_pods
  pod 'RxSwift'
  pod 'RxCocoa'
end
 
target 'MyComponent' do
  project 'SubProjects/MyComponent/MyComponent.xcodeproj'
  use_frameworks!
  my_component_pods
  target 'MyComponentTests' do
    inherit! :search_paths
    test_pods
  end
end
 
target 'MainProject' do
  project 'MainProject.xcodeproj'
  use_frameworks!
  my_component_pods
  target 'MainProjectTests' do
    inherit! :search_paths
    test_pods
  end
end

Nous allons ensuite lancer la commande habituelle

pod install

Quelques explications

Qu’avons-nous fait ici ? Nous devons d’abord déclarer le workspace sur lequel on veut travailler. C’est pour cela que nous avons ajouté 

workspace 'Project.xcworkspace'

Ensuite, avec la syntaxe typique de ruby, nous avons écrit une méthode my_component_pods qui contient les dépendances utilisées dans notre Component

def my_component_pods
  pod 'RxSwift'
  pod 'RxCocoa'
end

Une fois my_component_pods déclaré, nous l’avons employé à l’intérieur de la target MyComponent, mais aussi à l’intérieur de la target MainProject. Cette étape est nécessaire afin de permettre à CocoaPods de copier automatiquement les frameworks à l’intérieur du bundle de notre application, lors du packaging ; elle est exécutée lors de la build phase [CP] Embed Pods Frameworks que CocoaPods crée automatiquement pour vous lors du pod install.

Ensuite, nous avons ajoutée une méthode test_pods, contenant les dépendances nécessaires à lancer nos tests :

def test_pods
  pod 'Quick'
  pod 'Nimble'
end

Cette dernière est utilisée à l’intérieur de toutes les targets de test de nos projets, en appellant simplement test_pods.

Enfin, pour les projets MainProject et MyComponent, nous avons défini une target, suivie par la commande project et le chemin du fichier de projet :

target 'MainProject' do
  project 'MainProject.xcodeproj'
  use_frameworks!

...
 
target 'MyComponent' do
  project 'SubProjects/MyComponent/MyComponent.xcodeproj'

Comme d’habitude, nous pouvons dorénavant utiliser le workspace Project.xcworkspace pour ouvrir le workspace contenant tous nos projets.

Intégration

Bien sûr, nous devons maintenant intégrer MyComponent à l’intérieur de MainProject. Pour se faire, nous allons chercher le ficher "MyComponent.framework" dans le groupe "Products" du framework et le déposer à l’intérieur de "Embedded Binaries" de notre projet principal.

 intégrer MyComponent à l'intérieur de MainProject
intégrer MyComponent à l’intérieur de MainProject

Aussi, cette procédure ajoute automatiquement MyComponent dans la liste "Linked Framework and Libraries".

Et avec Carthage ?

Si vous êtes utilisateurs de Carthage, il faudra juste ne pas oublier de déposer manuellement, dans "Embedded Binaries" tous les frameworks utilisés dans vos sous projets et de suivre les autres étapes détaillés dans le README du projet.

Et c’est tout !

Ou pas. Il peut rester, bien sûr, quelques problèmes à régler. Par exemple, en cas d’erreurs de linking, après le pod install, il pourrait être nécessaire de quitter le projet Xcode, effacer le dossier DerivedData et re-ouvrir le projet. 
Aussi, si on développe en Swift, il ne faudra pas oublier de se servir du modificateur d’accès public à l’intérieur des composants, afin de rendre l’API visible depuis le projet principal.
Pour conclure, la procédure n’a rien de vraiment complexe, et la plus-value, en particulier pour des projets employant un bon nombre de développeurs, est énorme : les conflits de fusionnement seront réduits drastiquement et l’application sera globalement beaucoup plus modulaire.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *