L'année passée, Apple a présenté son nouveau langage de programmation pour iOS et OSX : Swift. Ce langage est plus facile à utiliser (et à apprendre) que l'Objective-C et sa syntaxe est radicalement différente. Pour assurer une transition en douceur à ce nouveau langage, il est possible de créer des applications "hybrides" qui ont une partie écrite en Swift et une partie écrite en Objective-C. En théorie, notre librairie Objective-C devrait donc être utilisable telle quellle dans un projet Swift. Dans la pratique, c'est un peu plus compliqué...
Avant d'essayer d'intégrer la librairie Yoctopuce à un projet Swift, il faut télécharger la dernière version de notre librairie Objective-C. En effet, les libraires antérieures à la version 19716 ne compileront pas si votre projet Xcode contient des fichiers Swift. Xcode n'utilise pas les mêmes options de compilation si votre projet est un projet "Swift" ou "Objective-C". Plus précisément, le compilateur LLVM est beaucoup plus strict sur la gestion des pointeurs ARC et génère une erreur si certaines assignations de variables ne sont pas typées. Si vous utilisez une vieille version de notre librairie, la mise à jour de la librairie se fait sans risque car nous maintenons toujours la compatibilité descendante avec la librairie et avec les modules.
Comment compiler la librairie Objective-C dans un projet Swift
Pour inclure des fichiers Objective-C à un projet Swift, il faut ajouter les fichiers .m et .h au projet XCode et créer un "bridging header" qui sera inclus à la compilation des fichiers .swift. Apple a écrit une page (en anglais) qui explique en détails toutes les possibilités. Dans le cas de notre librairie, le plus simple est de glisser-déposer tous les fichiers du répertoire Sources de la librairie Objective-C directement dans le Navigateur de projet Xcode. Xcode lance un assistant qui ajoute les fichiers au projet et qui crée le "bridging header" qui sera utilisé par les fichiers .swift.
Selectionner Yes quand Xcode vous propose de créer le bridging header sinon vos fichiers seront compilés mais inutilisables dans votre fichier Swift.
Quelques petites remarques :
- N'oubliez pas de sélectionner votre application dans le champ "add to target" de cet assistant, sinon Xcode ajoute les fichiers au projet mais ne les compile pas.
- Sélectionnez "Yes" quand Xcode vous propose de créer le "Bridging header" sinon les fichiers sont compilés mais inutilisables dans votre fichier Swift.
- Sélectionnez tous les fichiers du répertoire Source et non le répertoire Source, sinon Xcode ne propose pas de créer le "Bridging header".
La dernière étape est d'inclure les headers des fichiers Objective-C nécessaires dans le bridging header. Par exemple, si l'on veut utiliser un Yocto-Meteo dans un programme Swift, il faut inclure les fichiers yocto_api.h, yocto_humidity.h, yocto_temperature.h et yocto_pressure.h.
// Use this file to import your target's public headers that
// you would like to expose to Swift.
//
#import "yocto_api.h"
#import "yocto_humidity.h"
#import "yocto_temperature.h"
#import "yocto_pressure.h"
Si tout s'est bien passé, Xcode compile sans erreur tous les fichiers Swift, Objective-C et les quelques fichiers C.
Utiliser la libraire Yoctopuce en Swift
Maintenant que les fichiers Objective-C sont compilés, il faut écrire le code Swift qui utilise la librairie Yoctopuce. Nous allons traduire l'exemple "Doc-Inventory" en Swift. Les explications sur ce que fait cet exemple sont détaillés dans la documentation de notre librairie Objective-C et dans la documentation de tous les modules Yoctopuce.
L'exemple original "Doc-Inventory" écrit en Objective-C.
#import "yocto_api.h"
int main (int argc, const char * argv[])
{
NSError *error;
@autoreleasepool {
// Setup the API to use local USB devices
if([YAPI RegisterHub:@"usb" :&error] != YAPI_SUCCESS) {
NSLog(@"RegisterHub error: %@\n", [error localizedDescription]);
return 1;
}
NSLog(@"Device list:\n");
YModule *module = [YModule FirstModule];
while (module != nil) {
NSLog(@"%@ %@",[module get_serialNumber],
[module get_productName]µ);
module = [module nextModule];
}
}
return 0;
}
Et voici le même exemple écrit en Swift.
var error: NSError?
// Sets up the API to use local USB devices
let res = YAPI.RegisterHub("usb", &error)
if res.rawValue != YAPI_SUCCESS.rawValue {
println("Error: \(error?.localizedDescription)")
exit(1)
}
println("Device list:")
var module = YModule.FirstModule()
while module != nil {
println("\(module.get_serialNumber()) \(module.get_productName())")
module = module.nextModule()
}
Comme on peut le voir, les mêmes méthodes sont appelées et le code garde la même structure. Il y a cependant quelques petites subtilités qui méritent d'être mentionnées.
Il n'est plus nécessaire d'inclure les fichiers .h dans le code Swift car nous les avons déjà ajoutés dans le "bridging header".
La syntaxe Swift pour passer un pointeur sur l'NSError à une méthode est assez déroutante. Lors de la déclaration de variable, il faut spécifier le type et ajouter un "?" pour que le compilateur sache qu'il s’agit d'un pointeur sur un objet. Le passage par référence se fait en ajoutant un "&" (tout comme en Objective-C), mais lors de son utilisation la même variable nécessite d'être suivie d'un "?".
La dernière particularité concerne l'utilisation des énumérations. Beaucoup de méthodes de notre libraire utilisent des énumérations (comme valeur de retour ou paramètres). Pour des raisons historiques, ces énumérations sont définies avec la syntaxe C. Malheureusement, Swift n'est pas capable de convertir automatiquement ces énumérations. Pour utiliser une énumération dans un test il faut ajouter ".value" à la variable et à la valeur de l'énumération sinon le compilateur génère une erreur.
Exemple d'utilisation de l'attribut beacon qui utilise une énumération "Y_BEACON_enum" (Y_BEACON_ON,Y_BEACON_OFF,Y_BEACON_ON,Y_BEACON_INVALID).
if beaconState.value == Y_BEACON_ON.value {
println("beacon is on")
}
module.set_beacon(Y_BEACON_OFF)
Conclusion
La dernière version de notre librairie Objective-C peut aussi être utilisée dans un projet Xcode Swift. Pour l’instant, nous ne pensons pas créer de librairie écrite en Swift. La principale raison est qu'elle n'apporterait rien de plus car il resterait toujours une partie écrite en pur C à intégrer dans le projet. La seconde est que jusqu'à présent, nos clients n'ont pas montré beaucoup d'intéret pour une librairie Yoctopuce spécifique à Swift.