Migrar de Objective-C a Swift; acceder a clases Objective-C desde Swift


Xcode y Swift

En este artículo voy a explicar cómo configurar un proyecto con Xcode para poder utilizar código en Objective-C y Swift al mismo tiempo y poder acceder a clases de Objective-C desde Swift y desde Swift a Objective-C. De esta forma podremos ir migrando nuestro proyecto existente en Objective-C a Swift.

Migrando una app Objective-C a Swift

Mi primera app para iOS fue Ability Connect, desarrollada por completo en Objective-C. En aquél momento Swift aún era un lenguaje poco estable y en constante evolución, por lo que aún no era la apuesta para un proyecto estable. Han pasado unos años y ahora Swift es el futuro en el desarrollo de apps para los dispositivos de Apple, por lo que sería maravilloso poder aprovecharnos de las novedades que trae el nuevo lenguaje.

En una nueva app que estoy creando estoy utilizando Swift desde cero y evidentemente es mayor la velocidad de codificación, el código resultante es más amigable y mucho más fácil de mantener. Por lo que he decidido migrar Ability Connect a Swift. Este proceso ha realizarse poco a poco y con mucho cuidado. En el caso de Ability Connect utiliza BluetoothLE y WebSockets, por lo que hay partes del código críticas que no se pueden probar con el simulador y por tanto hay que hacer pruebas exhaustivas.

Por este motivo la estrategia que estoy siguiendo para la migración no es una migración progresiva, haciendo coexistir los dos lenguajes para beneficiarme de las ventajas de Swift en las nuevas funcionalidades y reescribiendo clases y controladores Objective-C a Swift a medida que lo voy necesitando. Con esta aproximación, en varias semanas llevo una cuarta parte del código en Swift y todo funcionando con normalidad.

Acceder a clases Objective-C desde Swift

Lo primero que necesitaremos es acceder a las clases Objective-C que tenemos en nuestro proyecto actual desde nuestras nuevas clases escritas en Swift, para ello, tenemos que crear un ‘bridging header’ o fichero puente para especificar qué clases Objective-C estarán disponibles para utilizarse desde código Swift.

Para crear este fichero puente, abrimos nuestro proyecto en Xcode y vamos a File -> New -> File… y seleccionamos el tipo de fichero Swift File.

Ventana de la aplicación Xcode donde se selecciona crear un fichero de tipo Swift

Ahora escribimos un nombre para nuestra clase, por ejemplo Utilidades.swfit y pulsamos en Create.

Ventana de la aplicación Xcode donde se le da nombre Utilidades.swift a un fichero de Swift

Inmediatamente después de pulsar en el botón se muestra un cuadro de dialogo que nos pregunta si queremos crear el fichero puente para incluir las clases de Objective-C accesibles por Swift. Pulsamos en Create bridging Header.

Cuadro de dialogo de la aplicación Xcode que pregunta si crear un Bridging Header

Y listo, vemos que en el explorador de ficheros se ha creado un nuevo fichero llamado: NombreTarget-Bridging-Header.h. En mi caso, como el target del proyecto es AppMixta, el fichero se llama AppMixta-Bridging-Header.h.

Explorador ficheros proyecto Xcode donde se ve bridging header

Si abrimos este nuevo fichero, veremos que solo contiene comentarios. Vamos a a incluir una línea para importar una clase llamada ViewController que tenemos en nuestro proyecto, para ello escribiríamos:

#import “ViewController.h”

Y listo, ya podemos acceder a esta clase desde Swift, para probarlo, abrimos nuestra clase Utilidades.swift y escribimos:

import Foundationc
class Utilidades {
  func accesoControlador(){
    let vc = ViewController()
    print(“Controlador Objective-C \(vc)”)
  }
}

Y fin. ¡Ya podemos inizializar nuestra clase ViewController de Objective-C desde una clase Swift!

Acceder a clases Swift desde Objective-C

Ahora vamos a hacer el proceso contrario, vamos a acceder a una clase Swift desde nuestro código en Objective-C. Abrimos nuestro fichero Utilidades.swift y modificamos lo siguiente:

  1. Hacemos que nuestra clase Utilidades herede del objeto NSObject. Este paso es obligatorio si queremos que nuestra clase Swift pueda ser instancia desde Objective-C. Entonces, nuestras clases deberían heredar siempre de un objeto NSObject u otro que herede de esta, como pueden ser las clases de Cocoa Touch: ViewController, TableViewController, etc.
  2. Añadimos la anotación @objc a nuestra clase para indicar a Xcode que nuestra clase puede inicializarse desde Objective-C. Esta anotación es obligatoria, de lo contrario no podremos acceder a ella.
  3. Creamos la función obtenerNumeroAleatorio(máximo).

Con estas modificaciones, nuestra clase fichero Utilidades.swift quedaría así:

import Foundation
@objc class Utilidades : NSObject {
  func accesoControlador(){
    let vc = ViewController()
    print(“Controlador Objective-C \(vc)”)
  }
  func obtenerNumeroAleatorio( maximo : Int ) -> Int {
    let numeroAleatorio = Int(arc4random_uniform( UInt32(maximo) ))
    return numeroAleatorio;
  }
}

Después de hacer estos cambios deberíamos compilar el proyecto para que Xcode genere las interfaces necesarias por nosotros, para ello pulsamos la combinación de teclas comando + B.

Ahora solo nos queda ir a una clase Objective-C e instanciar a la clase Utilidades e invocar a la función que genera números aleatorios. Para ello abrimos cualquier clase Objective-C, por ejemplo un VierController.m y hacemos lo siguiente:

  1. Debemos importar el fichero de interfaces Swift que Xcode genera automáticamente con todas las clases que marcamos con la anotación @objc. Este import no se puede auto completar. La línea a importar sería del tipo: #import “TargetName-Swift.h”. En mi caso, como el target de mi proyecto se llama AppMixta tendría que incluir la línea: #import “AppMixta-Swift.h”.
  2. En el método viewDidLoad de nuestro controlador creamos una instancia de la clase Utilidades y creamos un bucle que llame a la función de números aleatorios e imprima por el Log el resultado.

El fichero ViewController.m resultante quedaría así:

#import “ViewController.h”
#import “AppMixta-Swift.h”
@interface ViewController ()
@end
@implementation ViewController
– (void)viewDidLoad {
  [super viewDidLoad];
  Utilidades *utilidades = [[Utilidades alloc] init];
  for(int i=0;i<3;i++){
    NSInteger numero = [utilidades obtenerNumeroAleatorioWithMaximo:100];
    NSLog(@”Número aleatorio: %ld”,(long)numero);
  }
}
– (void)didReceiveMemoryWarning {
  [super didReceiveMemoryWarning];
}
@end

Finalmente, si ejecutamos la app en un simulador, nos saldría por consola tres números aleatorios:

AppMixta[24419:3959186] Número aleatorio: 76
AppMixta[24419:3959186] Número aleatorio: 41
AppMixta[24419:3959186] Número aleatorio: 59

Conclusiones

Como se puede comprobar en el artículo es muy sencillo dejar preparado un proyecto Xcode para compartir código Objective-C y Swift, con todas las ventajas que esto implica. ¡Ya podéis comenzar con la migración progresiva de vuestra app!

Si tenéis cualquier duda podéis utilizar los comentarios. Saludos! 🙂

Etiquetas: , , ,


Comentarios

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *