Home » Cursos » Swift 2 Lección 4: Funciones
Curso Swift Lección 4

Swift 2 Lección 4: Funciones

Compra "Aprendiendo Swift 3"

La presente lección está probada y certificada en su funcionamiento con la versión 7 o superior de Xcode y corresponde a la especificación 2 y 2.1 del lenguaje Swift.
En la última lección del curso vimos algo tan esencial como las clases, elemento esencial del que se compone cualquier programa o aplicación en Swift. Pero las clases se componen a su vez de otros elementos igual de importantes: las funciones.

Funciones en Swift

Una función (método o procedimiento, como prefiramos llamarlo) es un trozo de código con entidad propia que recibe o puede recibir unos valores y que devolverá o puede devolver un resultado. Su cometido es simple: permitir ejecutar una serie de instrucciones en nuestro código, de manera repetida, sin tener que poner el mismo código cada vez.

Las funciones se declaran siempre con la palabra clave func seguida del nombre que queramos darle. Luego, imprescindible, paréntesis. Si la función no recibe parámetros de entrada (valores con los que trabajará) ponemos unos de apertura y cierre seguidos () y si no, separados por coma ponemos los parámetros y el tipo de los mismos. Las funciones no tienen inferencia de tipos y siempre hemos de indicar cuál es el de cada variable de entrada.

Tras los parámetros de entrada, si no vamos a tener parámetros de salida que devolveremos con la función, directamente abrimos corchete y empezamos a escribir el cuerpo de la misma. En caso de querer usar de salida, usamos los símbolos -> e indicamos el tipo o tipos de variables que vamos a devolver. Podemos no usar nombre en cuyo caso se podrá acceder a ellas por índice o ponerles nombre para que sean más fáciles de identificar.

Veamos algunos ejemplos de cabeceras de funciones:

Todas las funciones han de tener siempre parámetros de entrada y salida, obligatoriamente. Pero Swift nos permite inferir los de salida, de forma que no es necesario ponerlos si no vamos a usarlo. No obstante, la primera función (por ejemplo) bien escrita se debería ver así:

Podemos usar tantos parámetros de entrada como queramos. A la hora de invocarlos, no obstante, hemos de recordar que (salvo cambio por nuestra parte) el nombre del parámetro siempre se infiere (no hay que indicar, solo el valor) y a partir del segundo siempre habrá que indicarlo. Esto se debe a que el lenguaje que el primer parámetro tiene que tener su significado contextualizado en el nombre de la propia función.

Si queremos cambiar esto y que el nombre del primer parámetro también deba ser usado obligatoriamente, usamos los nombres de parámetros externos como vemos en el ejemplo de func tipoDispositivoVoid(size size:Int). De esta manera forzamos que al llamar a la función indiquemos el nombre del primer parámetro, porque de otra forma la llamada sería simplemente con tipoDispositivo(3) y no tipoDispositivo(size:3).

Pero es importante notar que () no es más que un alias que usa el sistema para la palabra clave Void. Por lo tanto podríamos usar -> Void en vez de -> () de forma indistinta. Notar que si la función devuelve más de un valor, hemos de hacerlo con una tupla (conjunción de valores contenido entre paréntesis).

Luego no tenemos más que invocar la función o, en caso que esta devuelva valores, asignar dicha función a una variable previamente declarada (o una constante, lo que nos pueda interesar).

Es muy importante recordar que el compilador en tiempo real de Swift nos prevendrá de usar funciones con parámetros de salida donde no usemos la palabra clave return seguido de lo que vamos a devolver. Si el código de la función, en alguno de sus flujos, no finaliza con un return el compilador nos dará error.

Ejemplo de función

Por ejemplo, vamos a crear una función que, a partir del tamaño de la pantalla del dispositivo iOS con que trabajemos, nos diga cuál es. En este caso, al trabajar con la altura (height) solo funcionará si nuestro dispositivo está en modo portrait (vertical).

Usamos los valores de dos propiedades de la clase UIScreen del sistema, que nos permite saber valores en tiempo de ejecución sobre la pantalla. En este caso UIScreen.mainScreen().bounds.size que nos devuelve un tipo CGSize (una tupla con valores width/ancho y height/alto). A su vez, también vamos a preguntar por la escala de la pantalla con UIScreen.mainScreen().scale. Este valor flotante nos dice el valor de escala de la pantalla, que en el caso que la altura sea 1024 (valor exclusivo de un iPad) si la escala es 2.0 nos indica que el iPad en cuestión es con pantalla Retina.

Pero antes vamos a detenernos otro momento e introducir un práctico elemento: las enumeraciones.

Con esta sencilla instrucción en la cabecera de nuestra clase, tendremos la posibilidad de crear una variable de tipo deviceType a la que luego podremos asignar cualquiera de los valores que hemos indicado tras la palabra case.

Ahora sí, vamos por la función. Declaramos un parámetro de salida pero ninguno de entrada, ya que no nos interesa recibir nada “del exterior” (nuestra función no lo necesita):

Como es fácil intuir, si cada vez que queremos saber qué dispositivo estamos usando tuviéramos que escribir todo este código, sería eterno. Ahora multipliquen este ejemplo hasta el infinito, llévenlo al fin de la eternidad y aun así solo tendrán un pequeño atisbo de hasta donde puede facilitar y ser práctico este concepto que acompaña a los lenguajes de programación casi desde su nacimiento.

Polimorfoseando

Vamos a probar el polimorfismo. Esto no es más que la capacidad de una función de ser definida de diferentes formas y tener diferentes comportamientos en función de cómo es invocada.

Vamos a imaginar que tenemos una función que suma dos coordenadas de tipo CGPoint. En el curso de SpriteKit ya vimos que este tipo se usa para representar la posición x e y (horizontal y vertical) de un elemento en pantalla.

Pero, ¿y si queremos poder usar también los valores de posición? Pues creamos otra definición con el mismo nombre y otros parámetros.

De esta forma podemos usar uno u otro indistintamente y cuando escribamos el asistente de código nos mostrará ambas definiciones para que elijamos aquella que nos resulte más práctica en ese momento.

Valores por defecto

También podemos usar valores por defecto en las funciones para pre-asignar parámetros en caso que no se indique un valor para estos en la llamada. De esta forma, podremos indicar o no (según el caso) un parámetro en función de nuestro interés.

Vamos a suponer que queremos unir cadenas separadas por un espacio pero, opcionalmente, puede que queramos usar otros caracteres como un guión o un hashtag. De igual forma, puede que queramos que queramos unir dos cadenas, o puede que queramos dos o cuatro o diez. Vamos a crear entonces una función que reciba un array de cadenas y un valor opcional de separador:

Con esta función, si indicamos el separador usará el de nuestra elección, pero si no, usará un espacio. Ahora solo tenemos que llamar a nuestra función de una de estas dos maneras:

Claramente vemos que si no incluimos el valor para separador, no importa pues el sistema cogerá el que hemos asignado por defecto. Eso sí, para evitar errores tenemos que indicar que el parámetro que estamos enviando es cadenas y no separador, que además, al ser el primer parámetro, va intrínseco.

Las buenas prácticas de codificación animan a usar los parámetros con valores por defecto como últimos parámetros y no como los primeros. Funciona de igual manera, pero es más fácil para un desarrollador por ese orden. En nuestro ejemplo lo hemos hecho “mal” adrede para que veáis como funciona.

Para finalizar, vamos a optimizar nuestra función de unir cadenas usando una forma diferente de enumerar valores de un array. En vez de usar solo for in como aprendimos en la segunda lección del curso, vamos a usar la instrucción enumerate. Esta nos permite que en vez de hacer el for solo a un valor, lo hagamos a dos: el índice actual y el valor correspondiente.

En este caso hemos hecho dos cosas: la primera invertir los parámetros y poner el valor por defecto al final, como recomiendan las buenas prácticas. Y en segundo, hemos usado como nombre externo para separador el placeholder _ de forma que ahora, en la llamada, no hará falta indicar explícitamente el nombre de dicho parámetro, aunque sea el segundo que como hemos dicho, a partir del mismo es obligatorio incluirlo.

¿Qué conseguimos con esto? Que el separador no se incluya tras la última cadena de nuestro array. Antes de añadir el mismo, preguntamos si el índice por el que vamos enumerando a cadenas, es el último (que se corresponde con el total menos 1). Si no lo es, ponemos el separador.

Resumiendo

En esta nueva lección hemos visto como usar las funciones, declararlas, pasarles parámetros, recibir uno o varios de vuelta, poner valores por defecto e incluso hemos jugado con el concepto del polimorfismo. Quedan aun más cosas por saber como parámetros de entrada y salida, otras formas de invocarlas, usar funciones como variables… pero todo eso lo veremos más adelante.

Por ahora, esperemos que os sea útil, practicar mucho y si tenéis cualquier duda, ya sabéis dónde estamos. Hasta la próxima y Good Apple Coding.

Compra "Aprendiendo Swift 3"

Acerca de Julio César Fernández

Analista, consultor y periodista tecnológico, desarrollador, empresario, productor audiovisual, actor de doblaje e ingeniero de vídeo y audio.

Otras recomendaciones

Lecciones Prototipos (I): UITableView

Lecciones por prototipos (I): Vistas de tabla (UITableView)

Primera lección por prototipos, un nuevo e innovador contenido. A veces, lo normal es que nos perdamos sin terminar de entender qué es o cómo funcionan los componentes que forman parte de una app o un juego. Para este caso hemos creado las lecciones por prototipos. Una exploración básica de conceptos esenciales a través de prototipos en Playground que podemos probar con Swift Playgrounds en el iPad o con Xcode 8. En esta primera lección abordamos las UITableView (vistas de tabla). Un elemento esencial en la mayoría de apps de iOS que muchas veces no es entendido desde su base y por lo tanto, provoca un mal uso de las mismas.

  • Pingback: Swift Lección 5, Tuplas | Apple Coding()

  • .[Roberto Hevens]

    Julio:

    Esta lección me fue màs fácil que la anterior.

    Un saludo.

  • Luis Duarte

    Excelente tu curso, tu libro es compra obligada. Cuando esté actualizado a la versión 2.0 lo compraré a pesar de que aquí en Colombia el euro esta por las nubes.
    En la sección de polimorfismo, ¿realmente no estas hablando de sobrecarga?

  • Vid Brenes

    Tengo una duda, probando funciones me topé con este asunto:
    Cuando se incluye más de un parámetro en la declaración de una función, al llamarla me pide que envíe cada parámetro, a partir del segundo, con el mismo nombre que tiene en la declaración. Esto es normal o algo estoy haciendo mal?
    EJEMPLO:

    func datosPersona(nombre:String, pais:String) ->(){

    print(“La persona es (nombre) de (pais).”)

    }

    datosPersona(“David”, “San José”) //No funciona
    datosPersona(“David”, pais:”San José”) //Si funciona

    • Vid Brenes

      Ok, parece que es normal. Documentación oficial de Apple.

      When calling a function with more than one parameter, any argument after the first is labeled according to its corresponding parameter name. Function parameter naming is described in more detail in Function Parameter Names.

      • Hola Vid,
        Gracias por el comentario. Para aclarar ese importante punto, he actualizado el texto del artículo para dejarlo más claro. Como comentas, el primer parámetro se infiere y se pone siempre a partir del segundo, salvo que uses nombres de parámetros externos. Es un cambio que se hizo en Swift 2.
        Un saludo y gracias.

        • Vid Brenes

          Buenísimo Julio César, estoy aprendiendo Swift, gracias por la ayuda.
          También leí en la documentación la manera para que no se necesiten estos identificadores de parámetros en las llamadas a las funciones.
          Sería de la siguiente manera, utilizando un underscore y un espacio antes de la declaración del parámetros:

          func datosPersona(nombre:String, _ pais:String) ->(){
          print(“La persona es (nombre) de (pais).”)
          }

          datosPersona(“David”, “Costa Rica”)

          • El underscore _ es lo que se denomina un placeholder o hueco para el código. Puedes usarlo en un for in para que ignore el valor del índice como for _ in 1…10 o como nombre de parámetro externo para que así no haya que indicar el nombre del parámetro a partir del segundo parámetro.

            Un saludo y espero que te sea muy provechoso el aprendizaje.