Je suis développeur iOS avec une certaine expérience et cette question est vraiment intéressante pour moi. J'ai vu beaucoup de ressources et de documents différents sur ce sujet, mais néanmoins je suis toujours confus. Quelle est la meilleure architecture pour une application en réseau iOS? Je veux dire un cadre abstrait de base, des modèles, qui s'adapteront à toutes les applications de mise en réseau, que ce soit une petite application qui ne dispose que de quelques demandes de serveur ou un client REST complexe. Apple recommande d'utiliser MVC
une approche architecturale de base pour toutes les applications iOS, mais MVC
ni les MVVM
modèles plus modernes n'expliquent où placer le code logique du réseau et comment l'organiser en général.
Dois-je développer quelque chose comme MVCS
( S
pour Service
) et dans cette Service
couche mettre toutes les API
demandes et autres logiques de mise en réseau, qui en perspective peuvent être vraiment complexes? Après avoir fait quelques recherches, j'ai trouvé deux approches de base pour cela. Ici, il a été recommandé de créer une classe distincte pour chaque demande de réseau vers un service Web API
(comme une LoginRequest
classe ou une PostCommentRequest
classe, etc.) qui hérite tous de la classe abstraite de la demande de base AbstractBaseRequest
et en plus de créer un gestionnaire de réseau global qui encapsule le code de réseau commun et d'autres préférences (il peut s'agir de AFNetworking
personnalisation ouRestKit
tuning, si nous avons des mappages d'objets complexes et la persistance, ou même une propre implémentation de communication réseau avec une API standard). Mais cette approche me semble une surcharge. Une autre approche est d'avoir un singleton API
répartiteur ou une classe de gestionnaire comme dans la première approche, mais pas de créer des classes pour chaque demande et au lieu d'encapsuler chaque demande comme méthode publique d'instance de cette classe de gestionnaire comme: fetchContacts
, loginUser
méthodes, etc. Alors, que est le meilleur et le meilleur moyen? Y a-t-il d'autres approches intéressantes que je ne connais pas encore?
Et dois-je créer une autre couche pour tous ces trucs de réseautage comme Service
, ou une NetworkProvider
couche ou quoi que ce soit au-dessus de mon MVC
architecture, ou cette couche doit être intégrée (injectée) dans des MVC
couches existantes , par exemple Model
?
Je sais qu'il existe de belles approches, ou comment ces monstres mobiles comme le client Facebook ou le client LinkedIn gèrent-ils la complexité exponentiellement croissante de la logique de mise en réseau?
Je sais qu'il n'y a pas de réponse exacte et formelle au problème. Le but de cette question est de rassembler les approches les plus intéressantes de développeurs iOS expérimentés . La meilleure approche suggérée sera marquée comme acceptée et récompensée par une prime de réputation, d'autres seront votées. C'est surtout une question théorique et de recherche. Je veux comprendre l'approche architecturale de base, abstraite et correcte pour les applications de mise en réseau dans iOS. J'espère des explications détaillées de la part de développeurs expérimentés.
la source
Réponses:
I want to understand basic, abstract and correct architectural approach for networking applications in iOS
: il n'y a pas d' approche "la meilleure" ou "la plus correcte" pour construire une architecture d'application. C'est un travail très créatif. Vous devez toujours choisir l'architecture la plus simple et la plus extensible, qui sera claire pour tout développeur, qui commence à travailler sur votre projet ou pour d'autres développeurs de votre équipe, mais je suis d'accord, qu'il peut y avoir un "bon" et un "mauvais" " architecture.Vous avez dit:,
collect the most interesting approaches from experienced iOS developers
je ne pense pas que mon approche soit la plus intéressante ou la plus correcte, mais je l'ai utilisée dans plusieurs projets et j'en suis satisfaite. Il s'agit d'une approche hybride de celles que vous avez mentionnées ci-dessus, ainsi que des améliorations de mes propres efforts de recherche. Je m'intéresse aux problèmes de construction d'approches, qui combinent plusieurs modèles et idiomes bien connus. Je pense que beaucoup de modèles d'entreprise de Fowler peuvent être appliqués avec succès aux applications mobiles. Voici une liste des plus intéressantes, que nous pouvons appliquer pour créer une architecture d'application iOS ( à mon avis ): couche de service , unité de travail , façade distante , objet de transfert de données ,Passerelle , supertype de couche , cas spécial , modèle de domaine . Vous devez toujours concevoir correctement une couche de modèle et ne pas oublier la persistance (cela peut augmenter considérablement les performances de votre application). Vous pouvez utiliserCore Data
pour cela. Mais vous ne devez pas oublier, ceCore Data
n'est pas un ORM ou une base de données, mais un gestionnaire de graphes d'objets avec la persistance comme une bonne option. Ainsi, très souvent, celaCore Data
peut être trop lourd pour vos besoins et vous pouvez envisager de nouvelles solutions telles que Realm et Couchbase Lite , ou créer votre propre couche de mappage / persistance d'objets légers, basée sur SQLite brut ou LevelDB. Je vous conseille également de vous familiariser avec la conception pilotée par domaine et le CQRS .Au début, je pense que nous devrions créer une autre couche pour la mise en réseau, car nous ne voulons pas de gros contrôleurs ou de modèles lourds et dépassés. Je ne crois pas à ces
fat model, skinny controller
choses. Mais je crois en l'skinny everything
approche, car aucune classe ne devrait être grosse, jamais. Tout réseau peut être généralement résumé comme une logique métier, par conséquent, nous devrions avoir une autre couche, où nous pouvons la mettre. La couche de service est ce dont nous avons besoin:Dans notre
MVC
royaumeService Layer
est quelque chose comme un médiateur entre le modèle de domaine et les contrôleurs. Il existe une variante assez similaire de cette approche appelée MVCS où aStore
est en fait notreService
couche.Store
instances de modèle vends et gère le réseau, la mise en cache , etc. Je veux mentionner que vous ne devriez pas écrire tout votre réseau et la logique métier dans votre couche de service. Cela peut également être considéré comme une mauvaise conception. Pour plus d'informations, consultez les modèles de domaine anémique et riche . Certaines méthodes de service et logique métier peuvent être gérées dans le modèle, ce sera donc un modèle "riche" (avec comportement).J'utilise toujours largement deux bibliothèques: AFNetworking 2.0 et ReactiveCocoa . Je pense que c'est un must pour toute application moderne qui interagit avec le réseau et les services Web ou contient une logique d'interface utilisateur complexe.
ARCHITECTURE
Au début, je crée une
APIClient
classe générale , qui est une sous-classe de AFHTTPSessionManager . Il s'agit d'un cheval de bataille de tous les réseaux de l'application: toutes les classes de service lui délèguent des demandes REST réelles. Il contient toutes les personnalisations du client HTTP, dont j'ai besoin dans l'application particulière: épinglage SSL, traitement des erreurs et création d'NSError
objets simples avec des raisons d'échec détaillées et des descriptions de toutesAPI
et des erreurs de connexion (dans ce cas, le contrôleur sera en mesure d'afficher les messages corrects pour l'utilisateur), définir des sérialiseurs de demande et de réponse, des en-têtes http et d'autres éléments liés au réseau. Ensuite , je divise logiquement toutes les demandes de l' API dans subservices ou, plus correctement, microservices :UserSerivces
,CommonServices
,SecurityServices
,FriendsServices
et ainsi de suite, conformément à la logique métier qu'ils mettent en œuvre. Chacun de ces microservices est une classe distincte. Ils forment ensemble unService Layer
. Ces classes contiennent des méthodes pour chaque demande d'API, traitent les modèles de domaine et renvoient toujours unRACSignal
avec le modèle de réponse analysé ouNSError
à l'appelant.Je veux mentionner que si vous avez une logique de sérialisation de modèle complexe - alors créez une autre couche pour cela: quelque chose comme Data Mapper mais plus général, par exemple JSON / XML -> Model mapper. Si vous avez un cache: créez-le également en tant que couche / service séparé (vous ne devez pas mélanger la logique métier avec la mise en cache). Pourquoi? Parce que la couche de mise en cache correcte peut être assez complexe avec ses propres accrochages. Les gens mettent en œuvre une logique complexe pour obtenir une mise en cache valide et prévisible comme par exemple la mise en cache monoïdale avec des projections basées sur des profuncteurs. Vous pouvez lire sur cette belle bibliothèque appelée Carlos pour en savoir plus. Et n'oubliez pas que Core Data peut vraiment vous aider avec tous les problèmes de mise en cache et vous permettra d'écrire moins de logique. De plus, si vous avez une logique entre
NSManagedObjectContext
les modèles de demande de serveur et les modèles de serveur, vous pouvez utiliserModèle de référentiel , qui sépare la logique qui récupère les données et les mappe au modèle d'entité de la logique métier qui agit sur le modèle. Donc, je conseille d'utiliser le modèle de référentiel même lorsque vous avez une architecture basée sur les données de base. Référentiel peut des choses abstraites, commeNSFetchRequest
,NSEntityDescription
,NSPredicate
et ainsi de suite à des méthodes simples commeget
ouput
.Après toutes ces actions dans la couche Service, l'appelant (contrôleur de vue) peut effectuer des tâches asynchrones complexes avec la réponse: manipulations de signaux, chaînage, mappage, etc. à l'aide de
ReactiveCocoa
primitives, ou simplement vous y abonner et afficher les résultats dans la vue . J'injectent l' injection de dépendance dans toutes ces classes de service mesAPIClient
, ce qui se traduira par un appel de service particulier en correspondantGET
,POST
,PUT
,DELETE
, etc. demande au point de terminaison REST. Dans ce casAPIClient
est passé implicitement à tous les contrôleurs, vous pouvez le rendre explicite avec un paramétré surAPIClient
les classes de service. Cela peut avoir un sens si vous souhaitez utiliser différentes personnalisations duAPIClient
pour des classes de service particulières, mais si, pour certaines raisons, vous ne voulez pas de copies supplémentaires ou si vous êtes sûr que vous utiliserez toujours une instance particulière (sans personnalisation) deAPIClient
- faites-en un singleton, mais ne le faites pas, s'il vous plaît DON 'T faire des classes de service comme singletons.Ensuite, chaque contrôleur de vue avec le DI injecte la classe de service dont il a besoin, appelle les méthodes de service appropriées et compose leurs résultats avec la logique de l'interface utilisateur. Pour l'injection de dépendance, j'aime utiliser BloodMagic ou un framework Typhoon plus puissant . Je n'utilise jamais de singletons, de cours de Dieu
APIManagerWhatever
ou d'autres mauvaises choses. Parce que si vous appelez votre classeWhateverManager
, cela indique que vous ne connaissez pas son objectif et c'est un mauvais choix de conception . Les singletons sont également un anti-modèle, et dans la plupart des cas (sauf les rares), c'est une mauvaise solution. Le singleton ne devrait être envisagé que si les trois critères suivants sont satisfaits:Dans notre cas, la propriété de l'instance unique n'est pas un problème et nous n'avons pas non plus besoin d'un accès global après avoir divisé notre gestionnaire de dieux en services, car maintenant un ou plusieurs contrôleurs dédiés ont besoin d'un service particulier (par exemple,
UserProfile
les besoins des contrôleursUserServices
, etc.) .Nous devons toujours respecter le
S
principe dans SOLID et utiliser la séparation des préoccupations , alors ne mettez pas toutes vos méthodes de service et vos appels réseaux dans une seule classe, car c'est fou, surtout si vous développez une application de grande entreprise. C'est pourquoi nous devrions considérer l'injection de dépendance et l'approche des services. Je considère cette approche comme moderne et post-OO . Dans ce cas, nous avons divisé notre application en deux parties: la logique de contrôle (contrôleurs et événements) et les paramètres.Voici un workflow général de mon architecture par exemple. Supposons que nous ayons un
FriendsViewController
, qui affiche la liste des amis de l'utilisateur et que nous ayons une option à supprimer des amis. Je crée une méthode dans maFriendsServices
classe appelée:où
Friend
est un objet modèle / domaine (ou il peut être juste unUser
objet s'ils ont des attributs similaires). Underhood cette méthode ParsisFriend
àNSDictionary
des paramètres JSONfriend_id
,name
,surname
,friend_request_id
et ainsi de suite. J'utilise toujours la bibliothèque Mantle pour ce type de passe-partout et pour ma couche de modèle (analyse en avant et en avant, gestion des hiérarchies d'objets imbriquées dans JSON, etc.). Après l' analyse qu'il appelleAPIClient
DELETE
méthode pour faire une demande REST réelle et retourneResponse
enRACSignal
l'appelant (FriendsViewController
dans notre cas) pour afficher un message approprié pour l'utilisateur ou autre.Si notre application est très importante, nous devons séparer notre logique encore plus clairement. Par exemple, il n'est pas toujours bon de mélanger
Repository
ou de modéliser la logique avecService
une seule. Lorsque j'ai décrit mon approche, j'avais dit que laremoveFriend
méthode devrait être dans laService
couche, mais si nous allons être plus pédants, nous pouvons remarquer qu'elle appartient mieuxRepository
. Rappelons-nous ce qu'est le référentiel. Eric Evans en a donné une description précise dans son livre [DDD]:Ainsi, a
Repository
est essentiellement une façade qui utilise la sémantique de style Collection (Ajouter, Mettre à jour, Supprimer) pour fournir l'accès aux données / objets. Voilà pourquoi quand vous avez quelque chose comme:getFriendsList
,getUserGroups
,removeFriend
vous pouvez le placer dans leRepository
, parce que la collection comme la sémantique est assez clair. Et du code comme:est certainement une logique métier, car elle va au-delà des
CRUD
opérations de base et connecte deux objets de domaine (Friend
etRequest
), c'est pourquoi elle doit être placée dans laService
couche. Je veux aussi remarquer: ne créez pas d'abstractions inutiles . Utilisez judicieusement toutes ces approches. Parce que si vous submergez votre application d'abstractions, cela augmentera sa complexité accidentelle, et la complexité causera plus de problèmes dans les systèmes logiciels qu'autre choseJe vous décris un "ancien" exemple Objective-C mais cette approche peut être très facilement adaptée au langage Swift avec beaucoup plus d'améliorations, car elle a des fonctionnalités plus utiles et du sucre fonctionnel. Je recommande fortement d'utiliser cette bibliothèque: Moya . Il vous permet de créer une
APIClient
couche plus élégante (notre cheval de bataille comme vous vous en souvenez). Désormais, notreAPIClient
fournisseur sera un type de valeur (enum) avec des extensions conformes aux protocoles et exploitant la mise en correspondance des modèles de déstructuration. L'énumération rapide + la correspondance de motifs nous permet de créer des types de données algébriques comme dans la programmation fonctionnelle classique. Nos microservices utiliseront ceAPIClient
fournisseur amélioré comme dans l'approche Objective-C habituelle. Pour la couche modèle au lieu deMantle
vous pouvez utiliser la bibliothèque ObjectMapperou j'aime utiliser une bibliothèque Argo plus élégante et fonctionnelle .J'ai donc décrit mon approche architecturale générale, qui peut être adaptée à n'importe quelle application, je pense. Il peut y avoir bien sûr beaucoup plus d'améliorations. Je vous conseille d'apprendre la programmation fonctionnelle, car vous pouvez en bénéficier beaucoup, mais n'allez pas trop loin avec. L'élimination d'un état mutable global, excessif, partagé, la création d'un modèle de domaine immuable ou la création de fonctions pures sans effets secondaires externes est, en général, une bonne pratique, et un nouveau
Swift
langage encourage cela. Mais rappelez-vous toujours que surcharger votre code avec de lourds modèles fonctionnels purs, les approches théoriques par catégorie est une mauvaise idée, car d' autres développeurs liront et prendront en charge votre code, et ils peuvent être frustrés ou effrayants de laprismatic profunctors
et ce genre de choses dans votre modèle immuable. La même chose avec leReactiveCocoa
: ne faites pas tropRACify
votre code , car il peut devenir illisible très rapidement, surtout pour les débutants. Utilisez-le quand il peut vraiment simplifier vos objectifs et votre logique.Ainsi,
read a lot, mix, experiment, and try to pick up the best from different architectural approaches
. C'est le meilleur conseil que je puisse vous donner.la source
". I don't like singletons. I have an opinion that if you decided to use singletons in your project you should have at least three criteria why you do this (I edited my answer). So I inject them (lazy of course and not each time, but
fois ') dans chaque contrôleur.Selon l'objectif de cette question, je voudrais décrire notre approche architecturale.
Approche architecturale
L'architecture générale de notre application iOS repose sur les modèles suivants: couches de service , MVVM , liaison de données d'interface utilisateur , injection de dépendance ; et paradigme de programmation réactive fonctionnelle .
Nous pouvons découper une application standard destinée aux consommateurs en couches logiques suivantes:
La couche d'assemblage est un point d'amorçage de notre application. Il contient un conteneur d'Injection de dépendances et des déclarations d'objets d'application et de leurs dépendances. Cette couche peut également contenir la configuration de l'application (URL, clés de services tiers, etc.). Pour cela, nous utilisons la bibliothèque Typhoon .
La couche modèle contient des classes de modèles de domaine, des validations, des mappages. Nous utilisons la bibliothèque Mantle pour cartographier nos modèles: elle prend en charge la sérialisation / désérialisation en
JSON
format et enNSManagedObject
modèles. Pour la validation et la représentation sous forme de nos modèles, nous utilisons les bibliothèques FXForms et FXModelValidation .La couche Services déclare les services que nous utilisons pour interagir avec des systèmes externes afin d'envoyer ou de recevoir des données représentées dans notre modèle de domaine. Nous avons donc généralement des services de communication avec les API de serveur (par entité), des services de messagerie (comme PubNub ), des services de stockage (comme Amazon S3), etc. logique. Pour le réseautage général, nous utilisons la bibliothèque AFNetworking .
L'objectif de la couche de stockage est d'organiser le stockage local des données sur l'appareil. Pour cela, nous utilisons Core Data ou Realm (les deux ont des avantages et des inconvénients, la décision à utiliser est basée sur des spécifications concrètes). Pour la configuration de Core Data, nous utilisons la bibliothèque MDMCoreData et un tas de classes - stockages - (similaires aux services) qui fournissent un accès au stockage local pour chaque entité. Pour Realm, nous utilisons simplement des stockages similaires pour avoir accès au stockage local.
La couche Managers est un endroit où vivent nos abstractions / wrappers.
Dans un rôle de gestionnaire pourrait être:
Ainsi, dans le rôle de gestionnaire, tout objet implémentant la logique d'un aspect particulier ou d'une préoccupation doit être utilisé pour le fonctionnement de l'application.
Nous essayons d'éviter les singletons, mais cette couche est un endroit où ils vivent s'ils sont nécessaires.
La couche Coordinators fournit des objets qui dépendent des objets d'autres couches (Service, Storage, Model) afin de combiner leur logique en une séquence de travail nécessaire pour certains modules (fonctionnalité, écran, user story ou expérience utilisateur). Il enchaîne généralement les opérations asynchrones et sait comment réagir en cas de réussite ou d'échec. À titre d'exemple, vous pouvez imaginer une fonction de messagerie et un
MessagingCoordinator
objet correspondant . La gestion de l'opération d'envoi de messages peut ressembler à ceci:À chacune des étapes ci-dessus, une erreur est traitée en conséquence.
La couche d'interface utilisateur se compose des sous-couches suivantes:
Afin d'éviter les contrôleurs de vue massifs, nous utilisons le modèle MVVM et implémentons la logique nécessaire pour la présentation de l'interface utilisateur dans ViewModels. Un ViewModel a généralement des coordinateurs et des gestionnaires comme dépendances. ViewModels utilisés par ViewControllers et certains types de vues (par exemple, les cellules de vue de tableau). La colle entre ViewControllers et ViewModels est le modèle de liaison de données et de commande. Afin de permettre d'avoir cette colle, nous utilisons la bibliothèque ReactiveCocoa .
Nous utilisons également ReactiveCocoa et son
RACSignal
concept comme interface et type de valeur de retour de tous les coordinateurs, services, méthodes de stockage. Cela nous permet de chaîner des opérations, de les exécuter parallèlement ou en série, et bien d'autres choses utiles fournies par ReactiveCocoa.Nous essayons d'implémenter notre comportement d'interface utilisateur de manière déclarative. La liaison de données et la mise en page automatique aident beaucoup à atteindre cet objectif.
La couche infrastructure contient tous les assistants, extensions et utilitaires nécessaires au travail d'application.
Cette approche fonctionne bien pour nous et pour les types d'applications que nous créons habituellement. Mais vous devez comprendre que ce n'est qu'une approche subjective qui doit être adaptée / modifiée pour les besoins de l'équipe concrète.
J'espère que ceci vous aidera!
Vous pouvez également trouver plus d'informations sur le processus de développement iOS dans ce billet de blog Développement iOS en tant que service
la source
Parce que toutes les applications iOS sont différentes, je pense qu'il y a différentes approches à considérer, mais je procède généralement de cette façon:
Créez une classe de gestionnaire central (singleton) pour gérer toutes les demandes d'API (généralement nommées APICommunicator) et chaque méthode d'instance est un appel d'API . Et il existe une méthode centrale (non publique):
Pour mémoire, j'utilise 2 bibliothèques / frameworks majeurs, ReactiveCocoa et AFNetworking. ReactiveCocoa gère parfaitement les réponses de mise en réseau asynchrone, vous pouvez le faire (sendNext:, sendError :, etc.).
Cette méthode appelle l'API, obtient les résultats et les envoie via RAC au format «brut» (comme NSArray ce que retourne AFNetworking).
Ensuite, une méthode comme celle
getStuffList:
qui a appelé la méthode ci-dessus souscrit à son signal, analyse les données brutes en objets (avec quelque chose comme Motis) et envoie les objets un par un à l'appelant (getStuffList:
et des méthodes similaires renvoient également un signal auquel le contrôleur peut s'abonner) ).Le contrôleur abonné reçoit les objets par
subscribeNext:
bloc et les gère.J'ai essayé de nombreuses façons dans différentes applications, mais celle-ci fonctionnait le mieux, donc je l'ai utilisée dans quelques applications récemment, elle convient aux petits et grands projets et elle est facile à étendre et à maintenir si quelque chose doit être modifié.
J'espère que cela vous aidera, j'aimerais entendre les opinions des autres sur mon approche et peut-être comment les autres pensent que cela pourrait être amélioré.
la source
+ (void)getAllUsersWithSuccess:(void(^)(NSArray*))success failure:(void(^)(NSError*))failure;
et- (void)postWithSuccess:(void(^)(instancetype))success failure:(void(^)(NSError*))failure;
qui effectuent les préparatifs nécessaires, puis feront appel au gestionnaire d'API.Dans ma situation, j'utilise généralement la bibliothèque ResKit pour configurer la couche réseau. Il fournit une analyse syntaxique facile à utiliser. Cela réduit mes efforts sur la configuration du mappage pour différentes réponses et autres choses.
J'ajoute seulement du code pour configurer le mappage automatiquement. Je définis la classe de base pour mes modèles (pas de protocole en raison de beaucoup de code pour vérifier si une méthode est implémentée ou non, et moins de code dans les modèles lui-même):
MappableEntry.h
MappableEntry.m
Les relations sont des objets qui représentent des objets imbriqués en réponse:
RelationshipObject.h
RelationshipObject.m
Ensuite, je configure le mappage pour RestKit comme ceci:
ObjectMappingInitializer.h
ObjectMappingInitializer.m
Un exemple d'implémentation de MappableEntry:
User.h
User.m
Maintenant à propos de l'encapsulation des requêtes:
J'ai un fichier d'en-tête avec la définition des blocs, pour réduire la longueur de ligne dans toutes les classes APIRequest:
APICallbacks.h
Et Exemple de ma classe APIRequest que j'utilise:
LoginAPI.h
LoginAPI.m
Et tout ce que vous devez faire dans le code, initialisez simplement l'objet API et appelez-le quand vous en avez besoin:
SomeViewController.m
Mon code n'est pas parfait, mais il est facile à définir une fois et à utiliser pour différents projets. Si cela intéresse quelqu'un, mb je pourrais passer du temps et en faire une solution universelle quelque part sur GitHub et CocoaPods.
la source
À mon avis, toute architecture logicielle est dictée par les besoins. Si c'est à des fins d'apprentissage ou à des fins personnelles, alors décidez de l'objectif principal et laissez-le guider l'architecture. S'il s'agit d'un travail de location, le besoin commercial est primordial. L'astuce consiste à ne pas laisser les choses brillantes vous distraire des besoins réels. Je trouve cela difficile à faire. Il y a toujours de nouvelles choses brillantes qui apparaissent dans cette entreprise et beaucoup d'entre elles ne sont pas utiles, mais vous ne pouvez pas toujours le dire à l'avance. Concentrez-vous sur le besoin et soyez prêt à abandonner les mauvais choix si vous le pouvez.
Par exemple, j'ai récemment réalisé un prototype rapide d'application de partage de photos pour une entreprise locale. Comme l'entreprise avait besoin de faire quelque chose de rapide et de sale, l'architecture a fini par être un code iOS pour faire apparaître une caméra et un code réseau attaché à un bouton d'envoi qui a téléchargé l'image dans un magasin S3 et écrit dans un domaine SimpleDB. Le code était trivial et le coût minime et le client dispose d'une collection de photos évolutive accessible sur le Web avec des appels REST. Bon marché et stupide, l'application avait de nombreux défauts et verrouillerait l'interface utilisateur à l'occasion, mais ce serait un gaspillage d'en faire plus pour un prototype et cela leur permet de se déployer auprès de leur personnel et de générer facilement des milliers d'images de test sans performances ni évolutivité. préoccupations. Une architecture merdique, mais elle répond parfaitement aux besoins et coûte parfaitement.
Un autre projet consistait à implémenter une base de données sécurisée locale qui se synchronise avec le système de l'entreprise en arrière-plan lorsque le réseau est disponible. J'ai créé un synchroniseur d'arrière-plan qui a utilisé RestKit car il semblait avoir tout ce dont j'avais besoin. Mais j'ai dû écrire tellement de code personnalisé pour RestKit pour gérer le JSON idiosyncratique que j'aurais pu le faire plus rapidement en écrivant mon propre JSON dans les transformations CoreData. Cependant, le client voulait apporter cette application en interne et je pensais que RestKit serait similaire aux frameworks qu'ils utilisaient sur d'autres plateformes. J'attends de voir si c'était une bonne décision.
Encore une fois, le problème pour moi est de se concentrer sur le besoin et de laisser cela déterminer l'architecture. J'essaie comme un diable d'éviter d'utiliser des packages tiers car ils entraînent des coûts qui n'apparaissent qu'après un certain temps sur le terrain. J'essaie d'éviter de faire des hiérarchies de classes car elles sont rarement payantes. Si je peux écrire quelque chose dans un délai raisonnable au lieu d'adopter un package qui ne convient pas parfaitement, je le fais. Mon code est bien structuré pour le débogage et commenté de manière appropriée, mais les packages tiers le sont rarement. Cela dit, je trouve AF Networking trop utile pour être ignoré et bien structuré, bien commenté et maintenu et je l'utilise beaucoup! RestKit couvre un grand nombre de cas courants, mais j'ai l'impression que je me suis battu lorsque je l'utilise, et la plupart des sources de données que je rencontre sont pleines de bizarreries et de problèmes qui sont mieux gérés avec du code personnalisé. Dans mes dernières applications, j'utilise simplement les convertisseurs JSON intégrés et j'écris quelques méthodes utilitaires.
Un modèle que j'utilise toujours est de retirer les appels réseau du thread principal. Les 4 à 5 dernières applications que j'ai effectuées ont configuré une tâche de minuterie d'arrière-plan à l'aide de dispatch_source_create qui se réveille de temps en temps et effectue des tâches réseau selon les besoins. Vous devez effectuer un travail de sécurité sur les threads et vous assurer que le code de modification de l'interface utilisateur est envoyé au thread principal. Cela aide également à faire votre intégration / initialisation de manière à ce que l'utilisateur ne se sente pas surchargé ou retardé. Jusqu'à présent, cela a plutôt bien fonctionné. Je suggère d'examiner ces choses.
Enfin, je pense qu'au fur et à mesure que nous travaillons et que le système d'exploitation évolue, nous avons tendance à développer de meilleures solutions. Il m'a fallu des années pour surmonter ma conviction que je dois suivre des modèles et des conceptions qui, selon d'autres, sont obligatoires. Si je travaille dans un contexte où cela fait partie de la religion locale, ahem, je veux dire les meilleures pratiques d'ingénierie du département, alors je respecte les coutumes à la lettre, c'est pour cela qu'ils me paient. Mais je trouve rarement que suivre des conceptions et des modèles plus anciens est la solution optimale. J'essaie toujours de regarder la solution à travers le prisme des besoins de l'entreprise et de construire l'architecture pour l'adapter et garder les choses aussi simples que possible. Quand j'ai l'impression qu'il n'y en a pas assez, mais que tout fonctionne correctement, alors je suis sur la bonne voie.
la source
J'utilise l'approche que j'ai obtenue d'ici: https://github.com/Constantine-Fry/Foursquare-API-v2 . J'ai réécrit cette bibliothèque dans Swift et vous pouvez voir l'approche architecturale de ces parties du code:
Fondamentalement, il existe une sous-classe NSOperation qui effectue la NSURLRequest, analyse la réponse JSON et ajoute le bloc de rappel avec le résultat à la file d'attente. La classe API principale construit NSURLRequest, initialise cette sous-classe NSOperation et l'ajoute à la file d'attente.
la source
Nous utilisons quelques approches en fonction de la situation. Pour la plupart des choses, AFNetworking est l'approche la plus simple et la plus robuste en ce que vous pouvez définir des en-têtes, télécharger des données en plusieurs parties, utiliser GET, POST, PUT & DELETE et il existe un tas de catégories supplémentaires pour UIKit qui vous permettent par exemple de définir une image à partir de une URL. Dans une application complexe avec beaucoup d'appels, nous résumons parfois cela à une méthode pratique qui serait quelque chose comme:
Il existe quelques situations où AFNetworking n'est pas approprié, par exemple lorsque vous créez un framework ou un autre composant de bibliothèque car AFNetworking peut déjà être dans une autre base de code. Dans cette situation, vous utiliseriez une NSMutableURLRequest soit en ligne si vous effectuez un seul appel, soit résumée dans une classe de demande / réponse.
la source
J'évite les singletons lors de la conception de mes applications. Ils sont typiques pour beaucoup de gens, mais je pense que vous pouvez trouver des solutions plus élégantes ailleurs. En général, ce que je fais est de construire mes entités dans CoreData, puis de mettre mon code REST dans une catégorie NSManagedObject. Si, par exemple, je voulais créer et poster un nouvel utilisateur, je ferais ceci:
J'utilise RESTKit pour le mappage d'objet et l'initialise au démarrage. Je trouve que le routage de tous vos appels via un singleton est une perte de temps et ajoute beaucoup de passe-partout qui ne sont pas nécessaires.
Dans NSManagedObject + Extensions.m:
Dans NSManagedObject + Networking.m:
Pourquoi ajouter des classes d'assistance supplémentaires lorsque vous pouvez étendre les fonctionnalités d'une classe de base commune à travers des catégories?
Si vous êtes intéressé par des informations plus détaillées sur ma solution, faites-le moi savoir. Je suis heureux de partager.
la source
Essayez https://github.com/kevin0571/STNetTaskQueue
Créez des demandes d'API dans des classes séparées.
STNetTaskQueue traitera du filetage et du délégué / rappel.
Extensible pour différents protocoles.
la source
Du point de vue de la conception purement de classe, vous aurez généralement quelque chose comme ceci:
Classe de modèle de données - Cela dépend vraiment du nombre d'entités distinctes réelles avec lesquelles vous traitez et de la façon dont elles sont liées.
Par exemple, si vous avez un tableau d'éléments à afficher dans quatre représentations différentes (liste, graphique, graphique, etc.), vous aurez une classe de modèle de données pour la liste des éléments, une de plus pour un élément. La liste des classes d'objets sera partagée par quatre contrôleurs de vue - tous les enfants d'un contrôleur de barre d'onglets ou d'un contrôleur de navigation.
Les classes de modèles de données seront utiles non seulement pour afficher des données, mais aussi pour les sérialiser, chacune d'elles pouvant exposer son propre format de sérialisation via des méthodes d'exportation JSON / XML / CSV (ou autre).
Il est important de comprendre que vous avez également besoin de classes de générateur de demande d'API qui mappent directement avec vos points de terminaison d'API REST. Supposons que vous disposez d'une API qui connecte l'utilisateur - de sorte que votre classe de générateur d'API de connexion créera une charge utile POST JSON pour l'API de connexion. Dans un autre exemple, une classe de générateur de demande d'API pour la liste des éléments de catalogue API créera une chaîne de requête GET pour l'api correspondante et déclenchera la requête REST GET.
Ces classes de générateur de demande d'API reçoivent généralement des données des contrôleurs de vue et retransmettent également les mêmes données aux contrôleurs de vue pour la mise à jour de l'interface utilisateur / d'autres opérations. Les contrôleurs de vue décideront alors comment mettre à jour les objets du modèle de données avec ces données.
Finalement, le cœur du client REST - classe de récupérateur de données API qui est inconscient de toutes sortes de demandes d'API que votre application fait. Cette classe sera plus probablement un singleton, mais comme d'autres l'ont souligné, il n'est pas nécessaire que ce soit un singleton.
Notez que le lien est juste une implémentation typique et ne prend pas en considération des scénarios comme la session, les cookies, etc., mais il suffit de vous lancer sans utiliser de frameworks tiers.
la source
Cette question a déjà beaucoup de réponses excellentes et détaillées, mais je pense que je dois la mentionner car personne d'autre ne l'a fait.
Alamofire pour Swift. https://github.com/Alamofire/Alamofire
Il est créé par les mêmes personnes que AFNetworking, mais est plus directement conçu avec Swift à l'esprit.
la source
Je pense que pour le moment le projet moyen utilise l'architecture MVVM et le grand projet utilise l'architecture VIPER et essaie d'atteindre
Et approches architecturales pour la création d'applications de mise en réseau iOS (clients REST)
Le souci de séparation pour un code propre et lisible évite la duplication:
inversion de dépendance
Responsable principal:
Vous trouverez ici l' architecture GitHub MVVM avec reste API Swift Project
la source
En génie logiciel mobile, les plus utilisés sont les modèles Clean Architecture + MVVM et Redux.
Clean Architecture + MVVM se compose de 3 couches: domaine, présentation, couches de données. Où la couche de présentation et la couche de référentiels de données dépendent de la couche de domaine:
Et la couche de présentation se compose de ViewModels and Views (MVVM):
Dans cet article, il existe une description plus détaillée de Clean Architecture + MVVM https://tech.olx.com/clean-architecture-and-mvvm-on-ios-c9d167d9f5b3
la source