icono_twiter icono LinkedIn icono Facebook Icono Xing
Roberto Canales Mora

Creador y propietario de AdictosAlTrabajo.com, Director General de Autentia S.L., Ingeniero Técnico de Telecomunicaciones y Executive MBA por el Instituto de Empresa 2007.
Twitter:

Autor de los Libros: Planifica tu éxito: de aprendiz a empresario y Informática profesional, las reglas no escritas para triunfar en la empresa

Puedes consultar mi CV y alguna de mis primeras aplicaciones (de los 90) aquí

Ver todos los tutoriales del autor

Fecha de publicación del tutorial: 2012-11-12

Tutorial visitado 8.149 veces Descargar en PDF
Gestor Tablas

Manipulando Tablas de datos en IOS con StoryBoards usando UML y TDD

Cuando construimos cualquier aplicación IOS, tarde o temprano mostraremos datos en una ventana con una vista de tipo UITableView. Tal como comentaba en el último tutorial en mi juego me puede interesar tener una pantalla donde mostrar las puntuaciones y podría ser un buen modo de hacerlo.

A mi me gusta hacer una aplicación nueva desde el principio donde probar de un modo aislado lo que quiero hacer y después trasladarlo a la aplicación principal: de ese modo lo haces dos veces: la primera guarreando un poco y la segunda, en plan kata, tratando de mejorar el diseño. Os muestro las capturas de la primera aplicación ejemplo con el código imprescindible.

Creamos un proyecto de tipo vista simple en XCode 4.5.1.

Lo llamamos EjemploTablaDatos.

La aplicación se basará en una primera ventana donde introduciremos datos de puntuaciones. Esto realmente no existirá en la aplicación final sino que al terminar la partida se grabarán automáticamente. El código será el mismo pero ahora lo simulamos con un botón.

Después tendremos nuestra tabla de datos donde visualizaremos los datos introducidos.
Al pinchar la flecha del detalle volveremos a mostrar los datos.

Lo primero es ir a XCode y montar el StoryBoard completo.

Partimos de la configuración por defecto:

Tenemos un ViewControler pero vamos a querer navegar entre pantallas por los que necesitamos un Navigation View Controller.

Añadimos un Navigation View Controller que ya por defecto nos arrastra un Table View Controller.

Ahora, vamos a ordenar la navegación que está un pelín desordenada.
La flecha que indica cual es el primer controlador se la ponemos al Navigation Controller.
Rompemos el Segue que une el Navigation Controller con el Table View Controller.
Creamos un Segue entre el Navigation Controller y el View Controller (Con la tecla control pulsada pinchamos del primero al segundo).

Lo hacemos de tipo root view controller.

Pintamos los controles y un botón para guardar los datos y navegar a la vista de tabla.

Creamos un Segue de tipo Push.

Creamos un nuevo View Controler para mostrar el detalle:

Ahora vamos a ir a la celda que se define como prototipo de las que se pintarán y decir que tiene detalle a la derecha (no es custom como viene por defecto):

Y que tiene indicador de navegación:

Sobre el indicador de navegación de la celda vamos a crear un Segue a la nueva ventana:

Y lo especificamos como Push:

Ahora ya tenemos toda la navegación de nuestra aplicación ejemplo declarada.

Pulsamos Control + R para arrancar la aplicación y veremos que funcionan las primeras pantallas. Obviamente no podremos ver el detalle de la celda porque todavía no hemos añadido ninguna.

Ahora vienen una serie de dudas existenciales:

Si en un controlador voy a guardar las puntuaciones y en otros utilizarlas ¿dónde almaceno esos datos y cómo los gestiono?

Os invito a visitar este link para ver las distintas aproximaciones.

Os recomiendo buscar en Google: "appdelegate vs singleton" y encontraréis otros buenos artículos dando solución al problema.

Básicamente podemos: O bien usar un objeto global a toda la aplicación y añadirle la estructura de datos o bien utilizar una variable global. Bueno, hay una variación a esta última que es crear un Singleton para garantizar que hay una única instancia del objeto en memoria y que los accesos están sincronizados (más vale prevenir problemas de concurrencia).

Yo lo voy a hacer del modo más sencillo posible (que no el mejor) y voy a aprovechar el objeto que representa el delegado de la aplicación.
Desde cualquier parte de la aplicación se puede acceder a través de la linea:

Uno de los principales problemas que se puede percibir antes de empezar es que al AppDelegare le demos más responsabilidad del que le corresponde por lo que voy a crear una clase gestora (recordad las clases de análisis) que me permita interactuar con mi almacén de datos. De este modo, desde cualquier sitio accederé a la instancia de esta clase gestora y no contaminaré la clase que representa la aplicación.

Sería casi una buena idea modelar lo que vamos a hacer antes de hacerlo. Os recomiendo revisar un antiguo tutorial sobre el tema.
Respecto a UML me voy a bajar Visual Paradigm for UML versión 10.0 Community Edition para Mac y así aprovecho y lo pruebo.

La clase AppDelegare tendrá una propiedad de tipo Gestor (que es un interfaz marcador) que será inicializado a un objeto de la clase GestorPuntuaciones. GestorPuntuaciones representa una colección de objetos de tipo Puntuación y los métodos para manipularlas.

Nuestras vistas sólo usarán el AppDelegate para obtener el Gestor adecuado por lo que el nivel de acoplamiento entre el AppDelegate y el GestorEspecífico es bajo. De echo podría ser inexistente si se le inyectase o utilizase una factoría para conseguirlo. De momento no vamos a complicar más el problema porque sino me voy a ir del objetivo de la aplicación: lo que tampoco podemos hacer es obviar la complejidad en los diseños de cualquier tipo de aplicación, sobre todo cuando hagamos muchas para muchos clientes y queramos ir construyendo un "framework" de buenas soluciones.

Después de modelar vamos a construir el proyecto en base a TDD: os recomiendo el video de Alejandro Pérez García sobre el tema.

Con UML hemos rediseñado la solución pero, como se suele decir, en un documento vale todo. Con TDD vamos a ir diseñando y construyendo la solución real. Los test son la red de seguridad a la hora de modificar funcionalidad asegurándonos que lo que funcionaba sigue funcionando.

Os recomiendo que tengáis a mano la documentación de SentestCase.

Primero construimos el test que fallará porque no existirá la funcionalidad que invocamos: estará en rojo. Después pasará porque construiremos el código pero no es suficiente: está en naranja. Por último tendremos que asegurarnos de que el código es óptimo, refactorizando con el existente: sólo entonces estará en verde. Deberíamos medir el código duplicado automáticamente para obtener pistas de necesidad de refactorización.

Para no hacer más pesado el tutorial describiré sólo unos pocos test.

Empezamos por verificar que la clase gestora es del tipo adecuado.

Creo una método de prueba llamado testGestorCorrecto. Vemos que ni siquiera compila.

Por tanto ahora tendremos que crear el interfaz marcador Gestor (en objectiveC un protocolo), la clase derivada GestorPuntuaciones y en el AppDelegare un método que me retorne una objeto de tipo Gestor. Sin eso no funciona. Si alguien alguna vez toca la inicialización en el AppDelegate, este test fallará y sabrá porqué (si el mensaje es suficientemente descriptivo y el nombre del test).

Creamos nuestro protocolo (equivalente a interfaz en Java):

Y lo añadimos a los targets adecuados.

Sólo nos importa el fichero .h

Tiene poca chicha porque sólo es un agrupador, para evitar usar una clase base. Los patrones de diseño en OOP se aplican normalmente con protocolos.

Ahora creamos la clase GestorPuntuaciones:

Escribimos la primera versión del código:

Ahora tenemos que crear la propiedad en el AppDelegate e inicializar el gestor adecuado:

Si corremos la aplicación en modo Test, con Control+U ya tenemos el resultado de nuestro primer test.

Ahora vamos a construir el cuerpo de los métodos del gestor, para ello vamos a crear otra clase de test desde cero. La idea es tener conjuntos de test agrupados por funcionalidad.

Lo llamaremos PruebaGestorPuntuaciones.

En el fichero de cabecera creamos un gestor de puntuaciones.Alguien podría pensar que para qué, si ya tenemos en otro test otro... Porque los test tiene que ser lo más independientes entre ellos.

E implementamos el código (de nuevo sobreactuando). La gracia no es hacerlo tan gordo sino ir haciendo pruebas más pequeñ... Pero quedaría un tutorial inmenso.

De momento nos centramos en la primera porción del test donde se crea un objeto de tipo puntuación.

Hemos utilizado una nueva clase llamada Puntuacion que no existe, por lo tanto la tendremos que crear.

Insertamos el código.

Ahora ya tenemos el código pero el gestor de puntuaciones.

En el código del gestor podemos ver que he usado un NSMutableList para guardar las puntuaciones y luego ordenarlas por valor a través de un bloque de código.

Podríamos hacer un test para verificar que esta ordenación es correcta, pero ya os lo dejo a vosotros.

Si queréis el proyecto a este nivel os lo podéis descargar aquí.

Bueno, tenemos construidas las partes de código que no depende mucho del interfaz y tenemos test que nos garantizan que va a funcionar.

Ahora vamos a enlazar las ventanas en el código existe. Esto es lo más sencillo.

En el primer controlador, creamos los Outlets.

Y escribimos en código en el manejador del Segue.

No olvidar poner el mismo nombre en el Segue.

Ahora creamos un UITableViewController desde el fichero que asociaremos a la vista que ya tenemos.

Asociamos la clase al controlador.

Ahora hay que ponerle a la celda prototipo un nombre: celdaPrototipo elijo yo.

Y ahora sólo tenemos que poblar el controlador de la tabla:

Funciona de un modo sencillo. Cuando hay que pintar la tabla primero pregunta cuantas secciones hay.

Posteriormente, cuantas filas en cada sección.

Por último, por cada fila crea una celda en base al prototipo que definimos en el editor visual.

Nosotros tiramos de nuestro API ya probada:

Este es el aspecto que obtenemos. En la primera entrada aparece una imagen (podemos sustituir por una copa de campeón o similar).

Sólo queda la parte más sencilla que es conectar el detalle con la flecha...
Le ponemos nombre al Segue, creamos un nuevo controlador DetallePuntuacionViewController y en el evento del Segue de las Table View pasamos los datos.

Lo asignamos.

Damos nombre al Segue.

Creamos los outlets.

Creamos una propiedad de tipo puntuación a la que le enchufaremos el dato desde la tabla.

Y desde el controlador de la tabla sólo tenemos que interceptar el evento del Segue y pasar el objeto puntuación.

Y ya tenemos la navegación completa.

Bueno, podemos comprobar que no es complicado pero es algo trabajoso. Hay gente que suele confundir estas variables: mover y camión de tierra, pala a pala. No es difícil pero cuesta.

Os dejo aquí todo el código final por si os ayuda en algo.

De todos modos, no nos despistemos porque empieza a haber trampas entre lineas:

  • ¿UML y TDD son complementarios o incompatibles?
  • ¿Hemos hecho TDD correctamente?
  • El nombre de clases, propiedades, outlets y clases ¿tiene una nomenclatura unificada y auto-descriptiva?
  • El nivel de acoplamiento entre vistas, a través del uso que hemos dado a los segures ¿es correcto?
  • ¿Usar el AppDelegate para acceder a un gestor es correcto? ¿Qué impacto futuro puede tener? ¿Qué otras opciones hay?
  • La memoria la estamos gestionado bien aún usando ARC.
  • ¿Qué pasa con la integración continua, cobertura de pruebas, mockeado de objetos, calidad de código?

Hacer las cosas mejor que peor es siempre fácil porque se puede copiar y pegar código de cualquier sitio... Hacer las cosas con criterio requiere mucho conocimiento.
Yo como director general de Autentia hago esto como hobbie siendo consciente de que mis compañeros saben mucho más.

Si quieres resolver dudas a estas preguntas y muchas más, puedes llamarnos.

A continuación puedes evaluarlo:

Regístrate para evaluarlo

Por favor, vota +1 o compártelo si te pareció interesante

Share |
Anímate y coméntanos lo que pienses sobre este TUTORIAL: