Home » Swift » Diario de una migración a Swift 3
Swift Diario

Diario de una migración a Swift 3

Un paso que muchos de nosotros tendremos que dar: migrar nuestros proyectos a Swift 3. Un proceso que el asistente de migración nos facilitará en gran medida, aunque otra parte tendremos que hacerla aceptando sugerencias de cambio en el código tras la migración y una última parte supondrán cambios manuales en el código para adaptar a las nuevas especificaciones.

Como ya comentamos en pasados artículos, Swift 3 ha sufrido importantes cambios en la especificación de casi todos los componentes de Cocoa, incluyendo SpriteKit y el resto de librerías. Cambios sutiles, pero que nos obligan a re-escribir parte del código y a entender una forma más “swiftificada” de plantear nuestro código.

Aprovechando la necesario migración de nuestro juego en SpriteKit, Virtual Judo (que es mitad juego, mitad app, ya que mezcla componentes tanto de SpriteKit como de UIKit) vamos a escribir qué pasos, tanto automáticos, como semiautomáticos o manuales hemos tenido que dar hasta conseguir la compilación con 0 errores y 0 warnings (el soñado objetivo de todo programador).

Asistente de migración

El primer y necesario paso, el asistente de migración. Nada más abrir nuestro proyecto Xcode 8 detecta que nuestro proyecto está hecho en una versión anterior y nos pedirá si queremos migrarlo. En mi caso, Virtual Judo está perfectamente codificado en Swift 2.2 con 0 errores y 0 warnings, testado en la versión 7.3.1 de Xcode. Al ver el cuadro de diálogo aceptamos, le decimos que queremos migrar a la versión 3.0 y elegimos el único target del proyecto (en mi caso, Virtual Judo). Ahora le damos a siguiente y empieza el proceso automático. Al terminar, nos encontramos una versión que nos resume el cambio en gran parte de los ficheros, la mayoría propios pero algunos librerías de terceros, como la que se encarga de gestionar la conexión a la base de datos SQLite3.

Hagamos un repaso general de los cambios que realiza en el código:

  • La función didMoveToView(view:SKView) que se usa para iniciar cualquier objeto de tipo SKScene de SpriteKit, ahora ha cambiado y nos hará el cambio pertinente. Ahora se llama como didMove(to view: SKView). Este es uno de los cambios más importantes de especificación que hemos de tener en cuenta: las funciones demasiado gramaticales del Swift (heredadas de Objective-C) ahora se hacen más cortas y los parámetros más claros.
  • Cuando hacemos referencia a un elemento de una escena a través del método childNodeWithName, nos ha hecho algo parecido. Ha dividido la especificación y ahora queda como childNode(withName:" ").
  • En algunos casos (aunque no era buena práctica) hacíamos uso del método de construcción de puntos CGPoint denominado CGPointMake. Swift 3 ha cambiado dichos constructores por el propio inicializador por defecto del struct. De forma que ahora funciona como CGPoint(x: y:).
  • Las acciones también han cambiado y la muy usada acción SKAction.waitForDuration ahora se ha convertido en SKAction.wait(forDuration:). Como vemos, es una constante el cambio de especificación a métodos de nombres más cortos y específicos y que los parámetros dejen claro por sí solos el contenido. Es decir, por un lado tenemos la acción ( wait) y por otro cuánto hemos de esperar (el parámetro forDuration), no como antes que usábamos ambos conceptos gramaticales en el nombre del método dejando el valor del parámetro solo. Igualmente SKAction.moveTo pasa a ser SKAction.move(to:). Y ojo, el cambio más importante aunque sutil: self.runAction ahora es self.run. Otros como los fundidos pasan de SKAction.fadeOutWithDuration a SKAction.fadeOut(withDuration:).
  • En el famoso AppDelegate.swift también tenemos cambios importantes. La cabecera pasa de: func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool a func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool donde ahora el parámetro launchOptions recoge sus valores de un diccionario de tipos UIApplicationLaunchOptionsKey en vez de usar un genérico NSObject como en la versión anterior.
  • Aquí, en este fichero, hacemos uso de NSFileManager.defaultManager() para comprobar la ubicación de la base de datos y si está o no copiada de inicio. En Swift 3, esto cambia. Ahora este singleton se llama simplemente FileManager.default. Nada más. De igual forma NSSearchPathDirectory.DocumentDirectory se convierte en FileManager.SearchPathDirectory.documentDirectory y NSSearchPathDomainMask.UserDomainMask en FileManager.SearchPathDomainMask.userDomainMask. Como vemos, son propiedades dentro de FileManager y luego propiedades de estas. Y de hecho, siguiendo con los ficheros, el famoso NSBundle.mainBundle().URLForResource ahora se convierte en Bundle.main.url(forResource:). De nuevo vemos cómo un singleton cambia radicalmente su especificación de NSBundle.mainBundle() a Bundle.main donde pasamos de un método a una propiedad calculada, más práctico y rápido.
  • También hacemos uso aquí del método de los String que permite añadir una componente de ruta de archivos: stringByAppendingPathComponent que se convierte en appendingPathComponent. De igual forma, el método de copia se convierte siguiente las directrices de construcción de APIs de Swift 3 que ya hemos comentado. Pasamos de copyItemAtPath(from, toPath:pathCopy) a copyItem(atPath: from, toPath:pathCopy). Vemos nuevamente cómo separa la acción de los componentes de dicha acción: unos al nombre del método y otros a los nombres de los parámetros.
  • Entramos también con el cambio de colores: ahora UIColor.blackColor() pasa a ser UIColor.black. Pasamos de un método a una propiedad calculada, lo cual es mucho más eficiente.
  • Otro detalle importante es como las propiedades pasan a empezar todas con minúsculas. De esta forma CGRectZero pasa a ser CGRect.zero, pasando de una método genérico a un componente del propio tipo CGRect y, muy importante, con el nombre de dicha propiedad zero en minúscula. Todas las enumeraciones que usemos también empezarán por minúscula, como usar el .Justified en la propiedad textAlignment de un UITextView, que ahora se llama .justified cambiando la J mayúscula por una minúscula. De igual forma, las enumeraciones .Left o .Top que también nos ayudan a justificar, eliminan mayúscula: .left o .top.
  • Las propiedades que pueden ser true o false, como la que nos dice si un UIScrollView debe tener scroll o no, pasando a añadir un is que antecede a la misma, con el propósito de dejar más claro que guarda un sí o un no. Por lo que .scrollEnabled ahora es isScrollEnabled. De igual forma, la famosa propiedad hidden de igual forma ha pasado a ser isHidden.
  • La función que nos permite controlar el control de toques touchesBegan cambia en su especificación igualmente: de override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) a override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?). El parámetro withEvent event pasa a ser with event. De hecho, cuando recuperamos el valor first del conjunto touches de tipo Set<UITouch> que nos permite acceder al primer dedo que ha tocado la pantalla (al toque del mismo), la propiedad a la que ahora accedemos para preguntar por la posición pasa de ser touch.locationInNode(self) a touch.location(in: self).
  • Leves cambios, como el método reverse() de una secuencia que ahora es reversed(). Y de hecho, la otra propiedad más usada de las secuencias enumerate() también añadido el participio con enumerated(). Y más cambios en los String, métodos como stringByReplacingOccurrencesOfString ahora es replacingOccurrences(of: "", with: ""). Y otro muy usado: componentsSeparatedByString ahora es components(separatedBy: ",").
  • Más cambios sutiles de mayúsculas y minúsculas que nos pueden volver un poco locos: el método scaleMode de las SKScene pasa de ser .AspectFill (por ejemplo) a .aspectFill, cambiando esa primera a en minúscula. Hemos de recordar que la primera letra de cualquier valor en una enumeración (según las nuevas directrices de construcción de APIs en Swift 3) han de ser siempre minúsculas y no mayúsculas como hasta ahora.
  • Otro cambio importante: el centro de notificaciones. El muy famoso NSNotificationCenter.defaultCenter(), pasa de singleton a propiedad calculada con NotificationCenter.default, perdiendo como otros muchos el NS por el camino. Y de singletons tenemos unos cuantos porque, por ejemplo, el acceso a las propiedades de la pantalla con UIScreen.mainScreen() pasa a ser UIScreen.main. De igual forma UIDevice.currentDevice() pasa a ser UIDevice.current.
  • Otro sospechoso habitual: NSUserDefaults.standardUserDefaults() pasa a ser UserDefaults.standard. Más simple. Y de hecho, una cosa buena que heredan los métodos set es la inferencia. Antes, para poner en valor entero en NSUserDefaults llamábamos al método setInteger. Ahora no. Simplemente llamamos a set y según el tipo el lenguaje ya sabe qué dato hay que poner. Por lo tanto NSUserDefaults.standardUserDefaults().setInteger(4, forKey: "test") pasa ahora a ser UserDefaults.standard.set(4, forKey: "test").

Y hasta ahí, no he tenido que hacer mucho más en cuanto al código. Eso sí, el wrapper que usaba de SQLite he tenido que descartarlo porque este NO se ha transformado correctamente y daba demasiados errores. Principalmente porque trabaja mucho con tipos NSString distinguiéndolos de String o incluso tipos cString. Y, lo más peligroso, usaba muchos tipos de valor UnsafePointer<Int8> para recuperar valores de la base de datos. Conclusión: que al ver que es poco eficiente, he decidido buscar otra solución para esta gestión.

Pero el resto de cosas las ha convertido y no he tenido que hacer nada más. Cuando sustituya el wrapper de base de datos os contaré cómo ha ido el tema, pero en principio, casi el 99% del trabajo lo ha realizado el asistente de migración de Swift 3. Los único problemas que podéis tener es si habéis metido mucho el dedo con tipos como los UnsafePointer o tal vez vuestro código no está muy actualizado y usáis métodos o clases deprecadas. ¿Cómo ha sido vuestra experiencia de migración? Contádnoslo y si tenéis alguna duda no dudéis en acudir a nosotros. Un saludo, buena migración y Good Apple Coding.

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

Swift 3.1 Hoja de Ruta

Swift 3.1, hoja de ruta hasta primavera de 2017

Swift 3.1 marca su hoja de ruta de aquí a primavera de 2017 con dos grandes retos: la compatibilidad de código con la versión 3.0 y potenciar el gestor de paquetes y la integración con servidor para conseguir aun más rendimiento en soluciones como Kitura, Vapor o Perfect (entre otros). Descubre los detalles en nuestra noticia y qué nos depara esta nueva versión menor del lenguaje.