Soy un desarrollador de iOS con algo de experiencia y esta pregunta es realmente interesante para mí. Vi muchos recursos y materiales diferentes sobre este tema, pero aún así estoy confundido. ¿Cuál es la mejor arquitectura para una aplicación en red iOS? Me refiero a un marco abstracto básico, patrones, que se ajustarán a todas las aplicaciones de red, ya sea una aplicación pequeña que solo tenga unas pocas solicitudes de servidor o un cliente REST complejo. Apple recomienda usarMVC
como un enfoque arquitectónico básico para todas las aplicaciones de iOS, pero MVC
ni los MVVM
patrones más modernos explican dónde colocar el código lógico de la red y cómo organizarlo en general.
¿Necesito desarrollar algo como MVCS
( S
para Service
) y en esta Service
capa poner todas las API
solicitudes y otra lógica de red, que en perspectiva puede ser realmente compleja? Después de investigar un poco, encontré dos enfoques básicos para esto. Aquí se recomendó crear una clase separada para cada solicitud de red al servicio web API
(como LoginRequest
clase o PostCommentRequest
clase, etc.) que hereda de la clase abstracta de solicitud base AbstractBaseRequest
y además de crear un administrador de red global que encapsule el código de red común y otras preferencias (puede ser AFNetworking
personalización oRestKit
ajuste, si tenemos asignaciones complejas de objetos y persistencia, o incluso una implementación de comunicación de red propia con API estándar). Pero este enfoque me parece una sobrecarga. Otro enfoque es tener algún API
despachador singleton o clase de administrador como en el primer enfoque, pero no para crear clases para cada solicitud y en su lugar para encapsular cada petición como un método de instancia pública de esta clase como gerente: fetchContacts
, loginUser
métodos, etc Por lo tanto, lo Cuál es la mejor y correcta forma? ¿Hay otros enfoques interesantes que aún no conozco?
¿Y debería crear otra capa para todas estas cosas de redes como Service
, o NetworkProvider
capa o lo que sea sobre mi MVC
arquitectura, o esta capa debería integrarse (inyectarse) en las MVC
capas existentes, por ejemplo Model
?
Sé que existen enfoques hermosos, o ¿cómo entonces tales monstruos móviles como el cliente de Facebook o el cliente de LinkedIn lidian con la complejidad exponencialmente creciente de la lógica de redes?
Sé que no hay una respuesta exacta y formal al problema. El objetivo de esta pregunta es recopilar los enfoques más interesantes de desarrolladores experimentados de iOS . El mejor enfoque sugerido será marcado como aceptado y premiado con una recompensa de reputación, otros serán votados. Es sobre todo una pregunta teórica y de investigación. Quiero comprender el enfoque arquitectónico básico, abstracto y correcto para las aplicaciones de red en iOS. Espero una explicación detallada de desarrolladores experimentados.
fuente
Respuestas:
I want to understand basic, abstract and correct architectural approach for networking applications in iOS
: no existe un enfoque "el mejor" o "el más correcto" para crear una arquitectura de aplicación. Es un muy trabajo creativo. Siempre debe elegir la arquitectura más sencilla y extensible, que será clara para cualquier desarrollador, que comience a trabajar en su proyecto o para otros desarrolladores de su equipo, pero estoy de acuerdo, que puede haber un "bien" y un "mal" "arquitectura.Usted dijo:
collect the most interesting approaches from experienced iOS developers
no creo que mi enfoque sea el más interesante o correcto, pero lo he usado en varios proyectos y estoy satisfecho con él. Es un enfoque híbrido de los que ha mencionado anteriormente, y también con mejoras de mis propios esfuerzos de investigación. Me interesan los problemas de los enfoques de construcción, que combinan varios patrones y modismos conocidos. Creo que muchos de los patrones empresariales de Fowler se pueden aplicar con éxito a las aplicaciones móviles. Aquí hay una lista de los más interesantes, que podemos aplicar para crear una arquitectura de aplicación iOS ( en mi opinión ): Capa de servicio , Unidad de trabajo , Fachada remota , Objeto de transferencia de datos ,Puerta de enlace , Supertipo de capa , Caso especial , Modelo de dominio . Siempre debe diseñar correctamente una capa de modelo y no olvidarse nunca de la persistencia (puede aumentar significativamente el rendimiento de su aplicación). Puedes usarCore Data
para esto. Pero no debe olvidar queCore Data
no es un ORM o una base de datos, sino un administrador de gráficos de objetos con persistencia como una buena opción. Por lo tanto, muy a menudoCore Data
puede ser demasiado pesado para sus necesidades y puede buscar nuevas soluciones como Realm y Couchbase Lite , o crear su propia capa ligera de mapeo / persistencia de objetos, basada en SQLite sin procesar o LevelDB. También le aconsejo que se familiarice con el diseño impulsado por dominio y CQRS .Al principio, creo, deberíamos crear otra capa para redes, porque no queremos controladores gordos o modelos pesados y abrumados. No creo en esas
fat model, skinny controller
cosas. Pero sí creo en elskinny everything
enfoque, porque ninguna clase debería ser gorda, nunca. En general, todas las redes pueden abstraerse como lógica de negocios, por lo tanto, deberíamos tener otra capa, donde podamos ponerla. La capa de servicio es lo que necesitamos:En nuestro
MVC
ámbitoService Layer
es algo así como un mediador entre el modelo de dominio y los controladores. Hay una variación bastante similar de este enfoque llamada MVCS donde aStore
es en realidad nuestraService
capa.Store
vende instancias de modelos y maneja las redes, el almacenamiento en caché, etc. Quiero mencionar que no debe escribir toda su lógica de negocios y redes en su capa de servicio. Esto también puede considerarse como un mal diseño. Para obtener más información, consulte los modelos de dominio Anemic y Rich . Algunos métodos de servicio y lógica de negocios pueden manejarse en el modelo, por lo que será un modelo "rico" (con comportamiento).Siempre uso ampliamente dos bibliotecas: AFNetworking 2.0 y ReactiveCocoa . Creo que es imprescindible para cualquier aplicación moderna que interactúe con la red y los servicios web o que contenga una lógica de interfaz de usuario compleja.
ARQUITECTURA
Al principio creo una
APIClient
clase general , que es una subclase de AFHTTPSessionManager . Este es un caballo de batalla de todas las redes en la aplicación: todas las clases de servicio le delegan solicitudes REST reales. Contiene todas las personalizaciones del cliente HTTP, que necesito en la aplicación en particular: fijación SSL, procesamiento de errores y creación deNSError
objetos directos con razones detalladas de fallas y descripciones de todosAPI
y errores de conexión (en tal caso, el controlador podrá mostrar mensajes correctos para el usuario), configurando serializadores de solicitud y respuesta, encabezados http y otras cosas relacionadas con la red. Entonces lógicamente divido todas las solicitudes de la API en subservicios o, más correctamente, microservicios :UserSerivces
,CommonServices
,SecurityServices
,FriendsServices
y así sucesivamente, de acuerdo con la lógica empresarial que implementan. Cada uno de estos microservicios es una clase separada. Ellos, juntos, forman aService Layer
. Estas clases contienen métodos para cada solicitud de API, procesan modelos de dominio y siempre devuelven unRACSignal
con el modelo de respuesta analizado oNSError
al llamante.Quiero mencionar que si tiene una lógica de serialización de modelo compleja, cree otra capa para ella: algo como Data Mapper pero más general, por ejemplo, JSON / XML -> Model mapper. Si tiene caché: créelo también como una capa / servicio separado (no debe mezclar la lógica empresarial con el almacenamiento en caché). ¿Por qué? Porque la capa de almacenamiento en caché correcta puede ser bastante compleja con sus propios problemas. Las personas implementan una lógica compleja para obtener un almacenamiento en caché válido y predecible como, por ejemplo, almacenamiento en caché monoidal con proyecciones basadas en profunctores. Puedes leer sobre esta hermosa biblioteca llamada Carlos para entender más. Y no olvide que Core Data realmente puede ayudarlo con todos los problemas de almacenamiento en caché y le permitirá escribir menos lógica. Además, si tiene alguna lógica entre el repositorio
NSManagedObjectContext
modelos de solicitud del servidor, puede usarPatrón de , que separa la lógica que recupera los datos y los asigna al modelo de entidad de la lógica de negocios que actúa sobre el modelo. Por lo tanto, le recomiendo usar el patrón de repositorio incluso cuando tenga una arquitectura basada en Core Data. Repositorio puede cosas abstractas, comoNSFetchRequest
,NSEntityDescription
,NSPredicate
y así sucesivamente con los métodos de civil comoget
oput
.Después de todas estas acciones en la capa de Servicio, la persona que llama (controlador de vista) puede hacer algunas cosas complejas asincrónicas con la respuesta: manipulaciones de señal, encadenamiento, mapeo, etc. con la ayuda de
ReactiveCocoa
primitivas, o simplemente suscribirse y mostrar resultados en la vista . Me inyecto con la inyección de dependencia en todas estas clases de servicios misAPIClient
, lo que se traducirá una llamada de servicio particular, en los correspondientesGET
,POST
,PUT
,DELETE
, etc. solicitud al extremo REST. En este casoAPIClient
se pasa implícitamente a todos los controladores, puede hacer esto explícito con una parametrizada sobreAPIClient
clases de servicio. Esto puede tener sentido si desea utilizar diferentes personalizaciones deAPIClient
para clases de servicio particulares, pero si, por alguna razón, no desea copias adicionales o está seguro de que siempre usará una instancia en particular (sin personalizaciones) deAPIClient
- conviértalo en un singleton, pero NO, por favor NO HAGA Haga clases de servicio como singletons.Luego, cada controlador de vista nuevamente con el DI inyecta la clase de servicio que necesita, llama a los métodos de servicio apropiados y compone sus resultados con la lógica de la interfaz de usuario. Para la inyección de dependencia, me gusta usar BloodMagic o un framework más potente Typhoon . Nunca uso singletons,
APIManagerWhatever
clase de Dios u otras cosas incorrectas. Porque si llamas a tu claseWhateverManager
, esto indica que no conoces su propósito y es una mala elección de diseño . Singletons también es un antipatrón, y en la mayoría de los casos (excepto los raros) es una solución incorrecta . Singleton debe considerarse solo si se cumplen los tres criterios siguientes:En nuestro caso, la propiedad de la instancia única no es un problema y tampoco necesitamos acceso global después de dividir a nuestro God Manager en servicios, porque ahora solo uno o varios controladores dedicados necesitan un servicio en particular (por ejemplo,
UserProfile
necesidades de controladores,UserServices
etc.) .Siempre debemos respetar los
S
principios en SOLID y usar la separación de preocupaciones , así que no coloque todos sus métodos de servicio y llamadas de red en una clase, porque es una locura, especialmente si desarrolla una aplicación de gran empresa. Es por eso que debemos considerar la inyección de dependencia y el enfoque de servicios. Considero este enfoque como moderno y post-OO . En este caso, dividimos nuestra aplicación en dos partes: lógica de control (controladores y eventos) y parámetros.Aquí hay un flujo de trabajo general de mi arquitectura, por ejemplo. Supongamos que tenemos un
FriendsViewController
, que muestra la lista de amigos del usuario y tenemos una opción para eliminar de amigos. Creo un método en miFriendsServices
clase llamado:donde
Friend
es un objeto modelo / dominio (o puede ser solo unUser
objeto si tienen atributos similares). Bajo el cofre este método análisis sintácticosFriend
aNSDictionary
los parámetros JSONfriend_id
,name
,surname
,friend_request_id
y así sucesivamente. Siempre uso la biblioteca Mantle para este tipo de repetitivo y para mi capa de modelo (análisis hacia adelante y hacia atrás, gestión de jerarquías de objetos anidados en JSON, etc.). Después de analizar que llamaAPIClient
DELETE
método para hacer una solicitud de un descanso efectivo y regresaResponse
enRACSignal
que la persona que llama (FriendsViewController
en nuestro caso) para mostrar el mensaje adecuado para el usuario o lo que sea.Si nuestra aplicación es muy grande, tenemos que separar nuestra lógica aún más claramente. Por ejemplo, no siempre es bueno mezclar
Repository
o modelar la lógica conService
uno. Cuando describí mi enfoque, dije que elremoveFriend
método debería estar en laService
capa, pero si vamos a ser más pedantes, podemos notar que pertenece mejorRepository
. Recordemos qué es el repositorio. Eric Evans le dio una descripción precisa en su libro [DDD]:Entonces, a
Repository
es esencialmente una fachada que usa semántica de estilo Colección (Agregar, Actualizar, Eliminar) para proporcionar acceso a datos / objetos. Es por eso que cuando se tiene algo como:getFriendsList
,getUserGroups
,removeFriend
se puede colocar en elRepository
, porque la recolección como la semántica está bastante claro aquí. Y código como:definitivamente es una lógica de negocios, porque está más allá de las
CRUD
operaciones básicas y conecta dos objetos de dominio (Friend
yRequest
), es por eso que debe colocarse en laService
capa. También quiero notar: no cree abstracciones innecesarias . Use todos estos enfoques sabiamente. Porque si abruma su aplicación con abstracciones, esto aumentará su complejidad accidental, y la complejidad causa más problemas en los sistemas de software que cualquier otra cosaLe describo un "viejo" ejemplo de Objective-C, pero este enfoque puede adaptarse muy fácilmente para el lenguaje Swift con muchas más mejoras, ya que tiene características más útiles y azúcar funcional. Recomiendo utilizar esta biblioteca: Moya . Le permite crear una
APIClient
capa más elegante (nuestro caballo de batalla como recordará). Ahora nuestroAPIClient
proveedor será un tipo de valor (enumeración) con extensiones que se ajustan a los protocolos y que aprovechan la coincidencia de patrones de desestructuración. Las combinaciones rápidas de enumeraciones + patrones nos permiten crear tipos de datos algebraicos como en la programación funcional clásica. Nuestros microservicios utilizarán esteAPIClient
proveedor mejorado como en el enfoque habitual de Objective-C. Para la capa de modelo en lugar deMantle
puede usar la biblioteca ObjectMappero me gusta usar una biblioteca Argo más elegante y funcional .Entonces, describí mi enfoque arquitectónico general, que creo que se puede adaptar a cualquier aplicación. Puede haber muchas más mejoras, por supuesto. Te aconsejo que aprendas programación funcional, porque puedes beneficiarte mucho de ella, pero no vayas demasiado lejos también. Eliminar, en general, un estado mutable global excesivo, compartido, crear un modelo de dominio inmutable o crear funciones puras sin efectos secundarios externos es, en general, una buena práctica, y un nuevo
Swift
lenguaje fomenta esto. Pero recuerde siempre que sobrecargar su código con patrones funcionales puros y pesados, los enfoques teóricos de categoría es una mala idea, porque hay otras desarrolladores leerán y apoyarán su código, y pueden sentirse frustrados o asustados por elprismatic profunctors
y ese tipo de cosas en tu modelo inmutable. Lo mismo con elReactiveCocoa
: noRACify
codifique demasiado , porque puede volverse ilegible muy rápido, especialmente para los novatos. Úselo cuando realmente puede simplificar sus objetivos y lógica.Por lo tanto,
read a lot, mix, experiment, and try to pick up the best from different architectural approaches
. Es el mejor consejo que puedo darte.fuente
". 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
vez `) en cada controlador.De acuerdo con el objetivo de esta pregunta, me gustaría describir nuestro enfoque de arquitectura.
Enfoque de la arquitectura
La arquitectura general de nuestra aplicación iOS se basa en los siguientes patrones: capas de servicio , MVVM , enlace de datos de la interfaz de usuario , inyección de dependencias ; y paradigma de programación reactiva funcional .
Podemos dividir una aplicación típica orientada al consumidor en las siguientes capas lógicas:
La capa de ensamblaje es un punto de arranque de nuestra aplicación. Contiene un contenedor de Inyección de dependencias y declaraciones de los objetos de la aplicación y sus dependencias. Esta capa también puede contener la configuración de la aplicación (URL, claves de servicios de terceros, etc.). Para este propósito utilizamos la biblioteca Typhoon .
La capa de modelo contiene clases de modelos de dominio, validaciones, asignaciones. Utilizamos la biblioteca Mantle para mapear nuestros modelos: admite la serialización / deserialización en
JSON
formato yNSManagedObject
modelos. Para la validación y la representación de formularios de nuestros modelos, utilizamos las bibliotecas FXForms y FXModelValidation .La capa de servicios declara los servicios que usamos para interactuar con sistemas externos para enviar o recibir datos que están representados en nuestro modelo de dominio. Por lo general, tenemos servicios para la comunicación con las API del servidor (por entidad), servicios de mensajería (como PubNub ), servicios de almacenamiento (como Amazon S3), etc. Básicamente, los servicios envuelven objetos proporcionados por SDK (por ejemplo, PubNub SDK) o implementan su propia comunicación lógica. Para redes generales, utilizamos la biblioteca AFNetworking .
El objetivo de la capa de almacenamiento es organizar el almacenamiento local de datos en el dispositivo. Usamos Core Data o Realm para esto (ambos tienen pros y contras, la decisión de qué usar se basa en especificaciones concretas). Para la configuración de Core Data utilizamos la biblioteca MDMCoreData y un montón de clases (almacenamientos) (similares a los servicios) que proporcionan acceso al almacenamiento local para cada entidad. Para Realm solo usamos almacenamientos similares para tener acceso al almacenamiento local.
La capa de gerentes es un lugar donde viven nuestras abstracciones / envoltorios.
En un rol de gerente podría ser:
Por lo tanto, en el rol de administrador podría haber cualquier objeto que implemente la lógica de un aspecto particular o preocupación necesaria para el funcionamiento de la aplicación.
Intentamos evitar Singletons, pero esta capa es un lugar donde viven si es necesario.
La capa de coordinadores proporciona objetos que dependen de objetos de otras capas (Servicio, Almacenamiento, Modelo) para combinar su lógica en una secuencia de trabajo necesaria para cierto módulo (característica, pantalla, historia de usuario o experiencia de usuario). Por lo general, encadena operaciones asincrónicas y sabe cómo reaccionar ante sus casos de éxito y fracaso. Como ejemplo, puede imaginar una función de mensajería y el
MessagingCoordinator
objeto correspondiente . La operación de manejo de mensajes de envío podría verse así:En cada uno de los pasos anteriores se maneja un error correspondientemente.
La capa de IU consta de las siguientes subcapas:
Para evitar los Controladores de vista masiva, usamos el patrón MVVM e implementamos la lógica necesaria para la presentación de la interfaz de usuario en ViewModels. Un ViewModel generalmente tiene coordinadores y gerentes como dependencias. ViewModels utilizados por ViewControllers y algunos tipos de Vistas (por ejemplo, celdas de vista de tabla). El pegamento entre ViewControllers y ViewModels es el enlace de datos y el patrón de comando. Para que sea posible tener ese pegamento, usamos la biblioteca ReactiveCocoa .
También utilizamos ReactiveCocoa y su
RACSignal
concepto como interfaz y tipo de valor de retorno de todos los coordinadores, servicios, métodos de almacenamiento. Esto nos permite encadenar operaciones, ejecutarlas en paralelo o en serie, y muchas otras cosas útiles proporcionadas por ReactiveCocoa.Intentamos implementar nuestro comportamiento de UI de manera declarativa. El enlace de datos y el diseño automático ayudan mucho a lograr este objetivo.
La capa de infraestructura contiene todos los ayudantes, extensiones y utilidades necesarias para el trabajo de la aplicación.
Este enfoque funciona bien para nosotros y para ese tipo de aplicaciones que generalmente creamos. Pero debe comprender que este es solo un enfoque subjetivo que debe adaptarse / cambiarse para el propósito del equipo concreto.
¡Espero que esto te ayudará!
También puede encontrar más información sobre el proceso de desarrollo de iOS en esta publicación de blog Desarrollo de iOS como servicio
fuente
Debido a que todas las aplicaciones de iOS son diferentes, creo que hay diferentes enfoques a tener en cuenta aquí, pero generalmente voy de esta manera:
creo una clase de administrador central (singleton) para manejar todas las solicitudes de API (generalmente llamadas APICommunicator) y cada método de instancia es una llamada de API . Y hay un método central (no público):
Para el registro, uso 2 bibliotecas / marcos principales, ReactiveCocoa y AFNetworking. ReactiveCocoa maneja las respuestas de red asíncronas perfectamente, puede hacerlo (sendNext :, sendError :, etc.).
Este método llama a la API, obtiene los resultados y los envía a través de RAC en formato 'sin procesar' (como NSArray lo que devuelve AFNetworking).
Luego, un método como
getStuffList:
el llamado método anterior se suscribe a su señal, analiza los datos sin procesar en objetos (con algo como Motis) y envía los objetos uno por uno a la persona que llama (getStuffList:
y métodos similares también devuelven una señal a la que el controlador puede suscribirse )El controlador suscrito recibe los objetos por
subscribeNext:
bloque y los maneja.Probé de muchas maneras en diferentes aplicaciones, pero esta funcionó de la mejor manera posible, así que he estado usando esto en algunas aplicaciones recientemente, se adapta a proyectos pequeños y grandes y es fácil de extender y mantener si algo necesita ser modificado.
Espero que esto ayude, me gustaría escuchar las opiniones de los demás sobre mi enfoque y tal vez cómo otros piensan que esto podría mejorarse.
fuente
+ (void)getAllUsersWithSuccess:(void(^)(NSArray*))success failure:(void(^)(NSError*))failure;
y- (void)postWithSuccess:(void(^)(instancetype))success failure:(void(^)(NSError*))failure;
que hacen los preparativos necesarios y luego llaman al administrador de API.En mi situación, generalmente uso la biblioteca ResKit para configurar la capa de red. Proporciona un análisis fácil de usar. Reduce mi esfuerzo en configurar el mapeo para diferentes respuestas y otras cosas.
Solo agrego algo de código para configurar el mapeo automáticamente. Defino la clase base para mis modelos (no protocolo debido a la gran cantidad de código para verificar si algún método está implementado o no, y menos código en los modelos):
MappableEntry.h
MappableEntry.m
Las relaciones son objetos que representan objetos anidados en respuesta:
RelationshipObject.h
RelationshipObject.m
Luego estoy configurando la asignación para RestKit de esta manera:
ObjectMappingInitializer.h
ObjectMappingInitializer.m
Algunos ejemplos de implementación de MappableEntry:
Usuario.h
Usuario.m
Ahora sobre el ajuste de solicitudes:
Tengo un archivo de encabezado con definición de bloques, para reducir la longitud de línea en todas las clases APIRequest:
APICallbacks.h
Y Ejemplo de mi clase APIRequest que estoy usando:
LoginAPI.h
LoginAPI.m
Y todo lo que necesita hacer en código, simplemente inicialice el objeto API y llámelo siempre que lo necesite:
SomeViewController.m
Mi código no es perfecto, pero es fácil de configurar una vez y usar para diferentes proyectos. Si es interesante para alguien, podría pasar algo de tiempo y encontrar una solución universal en algún lugar de GitHub y CocoaPods.
fuente
En mi opinión, toda la arquitectura de software está impulsada por la necesidad. Si esto es para fines de aprendizaje o personales, entonces decida el objetivo principal y haga que eso impulse la arquitectura. Si este es un trabajo por contrato, entonces la necesidad del negocio es primordial. El truco es no dejar que las cosas brillantes te distraigan de las necesidades reales. Encuentro esto difícil de hacer. Siempre hay cosas nuevas y brillantes que aparecen en este negocio y muchas de ellas no son útiles, pero no siempre se puede decir por adelantado. Concéntrese en la necesidad y esté dispuesto a abandonar las malas elecciones si puede.
Por ejemplo, recientemente hice un prototipo rápido de una aplicación para compartir fotos para un negocio local. Dado que la necesidad comercial era hacer algo rápido y sucio, la arquitectura terminó siendo un código de iOS para abrir una cámara y un código de red adjunto a un botón de envío que cargó la imagen en una tienda S3 y escribió en un dominio SimpleDB. El código era trivial y el costo mínimo, y el cliente tiene una colección de fotos escalable accesible a través de la web con llamadas REST. Barato y tonto, la aplicación tenía muchos defectos y en ocasiones bloqueaba la interfaz de usuario, pero sería un desperdicio hacer más por un prototipo y les permite desplegarse en su personal y generar miles de imágenes de prueba fácilmente sin rendimiento o escalabilidad preocupaciones Arquitectura mala, pero se ajusta a la necesidad y cuesta perfectamente.
Otro proyecto consistió en implementar una base de datos segura local que se sincroniza con el sistema de la empresa en segundo plano cuando la red está disponible. Creé un sincronizador de fondo que usaba RestKit, ya que parecía tener todo lo que necesitaba. Pero tuve que escribir tanto código personalizado para RestKit para tratar con JSON idiosincrásico que podría haberlo hecho todo más rápido escribiendo mi propio JSON en las transformaciones de CoreData. Sin embargo, el cliente quería traer esta aplicación internamente y sentí que RestKit sería similar a los marcos que usaban en otras plataformas. Espero a ver si esa fue una buena decisión.
Nuevamente, el problema para mí es centrarme en la necesidad y dejar que eso determine la arquitectura. Trato de evitar el uso de paquetes de terceros, ya que traen costos que solo aparecen después de que la aplicación ha estado en el campo por un tiempo. Intento evitar hacer jerarquías de clase, ya que rara vez dan sus frutos. Si puedo escribir algo en un período de tiempo razonable en lugar de adoptar un paquete que no encaja perfectamente, entonces lo hago. Mi código está bien estructurado para la depuración y está debidamente comentado, pero los paquetes de terceros rara vez lo están. Dicho esto, considero que las redes AF son demasiado útiles para ignorarlas y están bien estructuradas, bien comentadas y mantenidas, ¡y las uso mucho! RestKit cubre muchos casos comunes, pero siento que he estado en una pelea cuando lo uso, y la mayoría de las fuentes de datos que encuentro están llenas de peculiaridades y problemas que se manejan mejor con código personalizado. En mis últimas aplicaciones, solo uso los convertidores JSON integrados y escribo algunos métodos de utilidad.
Un patrón que siempre uso es sacar las llamadas de red del hilo principal. Las últimas 4-5 aplicaciones que he realizado configuraron una tarea de temporizador en segundo plano utilizando dispatch_source_create que se activa cada cierto tiempo y realiza tareas de red según sea necesario. Debe hacer un trabajo de seguridad de subprocesos y asegurarse de que el código de modificación de la interfaz de usuario se envíe al subproceso principal. También ayuda a hacer su incorporación / inicialización de tal manera que el usuario no se sienta abrumado o retrasado. Hasta ahora esto ha funcionado bastante bien. Sugiero buscar en estas cosas.
Finalmente, creo que a medida que trabajamos más y a medida que el sistema operativo evoluciona, tendemos a desarrollar mejores soluciones. Me ha llevado años superar mi creencia de que tengo que seguir patrones y diseños que otras personas afirman que son obligatorios. Si estoy trabajando en un contexto donde eso es parte de la religión local, ejem, me refiero a las mejores prácticas de ingeniería departamentales, entonces sigo las costumbres al pie de la letra, eso es por lo que me están pagando. Pero rara vez encuentro que seguir diseños y patrones más antiguos es la solución óptima. Siempre trato de ver la solución a través del prisma de las necesidades del negocio y construir la arquitectura para que coincida y mantener las cosas lo más simples posible. Cuando siento que no hay suficiente allí, pero todo funciona correctamente, entonces estoy en el camino correcto.
fuente
Utilizo el enfoque que obtuve de aquí: https://github.com/Constantine-Fry/Foursquare-API-v2 . He reescrito esa biblioteca en Swift y puedes ver el enfoque arquitectónico de estas partes del código:
Básicamente, hay una subclase de NSOperation que hace NSURLRequest, analiza la respuesta JSON y agrega el bloque de devolución de llamada con el resultado a la cola. La clase API principal construye NSURLRequest, inicializa esa subclase NSOperation y la agrega a la cola.
fuente
Utilizamos algunos enfoques dependiendo de la situación. Para la mayoría de las cosas, AFNetworking es el enfoque más simple y robusto, ya que puede configurar encabezados, cargar datos de varias partes, usar GET, POST, PUT & DELETE y hay un montón de categorías adicionales para UIKit que le permiten, por ejemplo, establecer una imagen desde una url En una aplicación compleja con muchas llamadas, a veces resumimos esto en un método de conveniencia propio que sería algo como:
Sin embargo, hay algunas situaciones en las que AFNetworking no es apropiado, como cuando está creando un marco u otro componente de la biblioteca, ya que AFNetworking ya puede estar en otra base de código. En esta situación, utilizaría una NSMutableURLRequest en línea si realiza una sola llamada o se abstrae en una clase de solicitud / respuesta.
fuente
Evito los singletons al diseñar mis aplicaciones. Son una opción típica para muchas personas, pero creo que puedes encontrar soluciones más elegantes en otros lugares. Por lo general, lo que hago es construir mis entidades en CoreData y luego poner mi código REST en una categoría NSManagedObject. Si, por ejemplo, quisiera crear y PUBLICAR un nuevo usuario, haría esto:
Uso RESTKit para el mapeo de objetos y lo inicializo al inicio. Considero que enrutar todas sus llamadas a través de un singleton es una pérdida de tiempo y agrega muchas repeticiones que no son necesarias.
En NSManagedObject + Extensions.m:
En NSManagedObject + Networking.m:
¿Por qué agregar clases auxiliares adicionales cuando puede ampliar la funcionalidad de una clase base común a través de categorías?
Si está interesado en información más detallada sobre mi solución, hágamelo saber. Estoy feliz de compartir
fuente
Pruebe https://github.com/kevin0571/STNetTaskQueue
Crear solicitudes de API en clases separadas.
STNetTaskQueue se ocupará de subprocesos y delegado / devolución de llamada.
Extensible para diferentes protocolos.
fuente
Desde una perspectiva de diseño puramente de clase, generalmente tendrá algo como esto:
Clase de modelo de datos : realmente depende de cuántas entidades distintas reales esté tratando y cómo estén relacionadas.
Por ejemplo, si tiene una matriz de elementos para mostrar en cuatro representaciones diferentes (lista, gráfico, gráfico, etc.), tendrá una clase de modelo de datos para la lista de elementos, uno más para un elemento. La lista de clases de elementos será compartida por cuatro controladores de vista: todos los hijos de un controlador de barra de pestañas o un controlador de navegación.
Las clases de modelos de datos serán útiles no solo para mostrar datos, sino también para serializarlos en los que cada uno de ellos puede exponer su propio formato de serialización a través de métodos de exportación JSON / XML / CSV (o cualquier otra cosa).
Es importante comprender que también necesita clases de creador de solicitudes de API que se asignen directamente con sus puntos finales de API REST. Supongamos que tiene una API que inicia sesión en el usuario, por lo que su clase de generador de API de inicio de sesión creará la carga útil POST JSON para la API de inicio de sesión. En otro ejemplo, una clase de generador de solicitud de API para la lista de elementos de catálogo API creará una cadena de consulta GET para la API correspondiente y activará la consulta REST GET.
Estas clases de generador de solicitudes de API generalmente recibirán datos de los controladores de vista y también pasarán los mismos datos a los controladores de vista para la actualización de la interfaz de usuario / otras operaciones. Los controladores de vista decidirán cómo actualizar los objetos del Modelo de datos con esos datos.
Finalmente, el corazón del cliente REST : la clase de captador de datos API que no tiene en cuenta todo tipo de solicitudes API que realiza su aplicación. Es probable que esta clase sea un singleton, pero como otros señalaron, no tiene que ser un singleton.
Tenga en cuenta que el enlace es solo una implementación típica y no tiene en cuenta escenarios como sesión, cookies, etc., pero es suficiente para que pueda continuar sin usar marcos de terceros.
fuente
Esta pregunta ya tiene muchas respuestas excelentes y extensas, pero siento que debo mencionarla ya que nadie más la tiene.
Alamofire para Swift. https://github.com/Alamofire/Alamofire
Fue creado por las mismas personas que AFNetworking, pero está diseñado más directamente con Swift en mente.
fuente
Creo que por ahora el proyecto mediano usa la arquitectura MVVM y el proyecto grande usa la arquitectura VIPER y trata de lograr
Y enfoques arquitectónicos para crear aplicaciones de red iOS (clientes REST)
La preocupación por la separación del código limpio y legible evita la duplicación:
inversión de dependencia
Responsable principal:
Encontrarás aquí la arquitectura GitHub MVVM con el resto API Swift Project
fuente
En ingeniería de software móvil, los más utilizados son los patrones Clean Architecture + MVVM y Redux.
Clean Architecture + MVVM consta de 3 capas: Dominio, Presentación, capas de datos. Donde la capa de presentación y la capa de repositorios de datos dependen de la capa de dominio:
Y la capa de presentación consta de modelos de vista y vistas (MVVM):
En este artículo, hay una descripción más detallada de Clean Architecture + MVVM https://tech.olx.com/clean-architecture-and-mvvm-on-ios-c9d167d9f5b3
fuente