Le problème est plus important pour les programmes existants écrits en Objective-C.
Est-il compliqué d'avoir l'UI réécrite en SwiftUI en conservant tout le code autre que l'UI en Objective-C ?
@Eric P. a dit :
Le problème est plus important pour les programmes existants écrits en Objective-C.
Est-il compliqué d'avoir l'UI réécrite en SwiftUI en conservant tout le code autre que l'UI en Objective-C ?
Oui et non. SwiftUI n'utilise plus vraiment le pattern MVC ce qui fait que tout le code de tes contrôleurs va devoir être extrait et réécrit. Tu vas peut-être pouvoir sauver la couche model quitte à la écrire quelques adapters de-ci, de-là.
@Eric P. a dit :
Est-il compliqué d'avoir l'UI réécrite en SwiftUI en conservant tout le code autre que l'UI en Objective-C ?
SwiftUI est conçu pour fonctionner main dans la main avec Combine, et il faut utiliser @State, @Binding, etc. pour connecter le modèle et l'IHM.
Ça me parait faisable dans une architecture MVVM, avec les ViewModels écrits en Swift, mais ce n'est pas forcément simple. Tu vas devoir annoter toutes les propriétés d'entrée avec @objc, et à la sortie, déclarer les propriétés en @State, @Binding, etc.
Mais surtout ce qui représente beaucoup de travail, c'est le passage à l'architecture MVVM, sachant que tu ne peux pas utiliser Combine avec ObjC.
Merci Pyroh et Céroce.
Déjà je ne suis pas certain que j'étais parfaitement en MVC donc de là à passer en MVVM...??
Je vais chercher des exemples de code mélangeant Objective-C et Swift.
@Eric P. a dit
Je vais chercher des exemples de code mélangeant Objective-C et Swift.
Tu pourras me demander, j'ai une grosse expérience dans le domaine. C'est ainsi quand on récupère une base de code qui date de 2013! J'ai réécrit ~40% du code en Swift, et tous les tests automatiques.
Swift peut utiliser tous les types de données d'Objective-C, mais Objective-C ne peut pas utiliser les structs et la plupart des enums. Aussi, tu ne pourras pas réécrire ton modèle en Swift et l'utiliser en ObjC à moins de te limiter ou faire des travaux d'interfaçage trop importants.
Je te conseillerais donc d'attaquer le problème par le haut: réécrit d'abord tes View Controllers en Swift, la conversion étant assez directe (quitte à écrire du code pas très swifty). Au passage, tu auras annoté ton code Objective-C avec des nullable, nonnull et autres NS_ENUM. Tu verras, Apple a bien travaillé.
@Eric P. a dit :
Aurais-tu un exemple ou un extrait pour comparer l'ancien code 100% Objective-C et celui mixé avec du Swift ?
Non, désolé rien en source ouvert, mais c'est assez facile.
Dans le sens Objective-C -> Swift
Ce qui pose problème ce sont les méthodes qui renvoient des objets. Le compilateur ne sachant pas si l'objet peut être nil, il va le typer en MonObjet!, et on vas se retrouver avec plein d'optionals à gérer dans le code Swift.
Alors on va annoter avec nonnull si le paramètre ne peut pas être nil et nullable s'il peut l'être. Alors, le compilateur va typer le paramètre en MonObjet ou MonObjet?.
Dès qu'on ajoute une seule annotation au fichier .h, le compilateur se plaint que les autres lui manquent. Aussi, il faut annoter entièrement un .h quand on commence.
Les paramètres étant généralement nonnull, il existe les macros NS_ASSUME_NONNULL_BEGIN et NS_ASSUME_NONNULL_END qui permettent de définir une zone comme tout nonnil sauf si on annote avec nullable.
Enfin, il existe _Nonnul et _Nullable qu'il faudra utiliser pour les types C (notamment les définitions de blocs/closures)
Tu verras que ce genre de méthodes est traduit ainsi:
func doSomething() throws
Les énumérations ObjC peuvent être déclarées avec les macros NS_ENUM et NS_CLOSED_ENUM. Ça permet d'avoir des énumérations bien propres en Swift.
Enfin, il y a un fichier qui s'appelle MaCible-Bridging-header.h. Il doit contenir les #import de tous les headers ObjC qui doivent être visibles par le code Swift. Xcode propose de le créer dès qu'on ajoute le premier fichier Swift au projet.
Dans le sens Swift -> Objective-C
Dans l'autre sens, en général, il faut tout précéder de @objc pour que le symbole soit vu par le code Objective-C. Quand je dis tout, ça veut dire: classes (qui doivent hériter de NSObject), protocoles, méthodes (y compris d'init), variables, constantes. Si le type n'est pas utilisable en ObjC, le compilateur gueulera. En général, quelle que soit l'erreur, commence par ajouter des @objc partout!
Pour utiliser les objets Swift dans le code ObjC, il faut un #import "MaCible-Swift.h";. Le compilateur est un peu paresseux pour mettre ce fichier à jour, alors parfois, il faut le forcer en lançant un build.
Un autre soucis de Swift vers Objective-C, car cela peut parfois être utile quand tu commences à traduire l'inverse, mais que ça touche 2 fichiers et que tu dois faire un truc entre les deux. Ça m'est arrivé plus d'une fois.
Les optionnels avec des primitives. En bref, il te faut passer par des NSNumber si tu veux, ou des defaults values, ce qui rend la traduction un peu étrange parfois...
Et je pars du principe, si on ne fait pas de SwiftUI par exemple, que ce qui marche n'a pas forcément besoin d'être traduit, ne pas traduire par le plaisir, car il y a les tests, et du legacy non-écrit (ah tiens, mais pourquoi c'était comme ça? Ça serait plus simple ainsi, non ? Ah, y'a un cas qui arrive 1/100 qui fait foirer, c'est ça ?) Etc.
Réponses
Le problème est plus important pour les programmes existants écrits en Objective-C.
Est-il compliqué d'avoir l'UI réécrite en SwiftUI en conservant tout le code autre que l'UI en Objective-C ?
Oui et non. SwiftUI n'utilise plus vraiment le pattern MVC ce qui fait que tout le code de tes contrôleurs va devoir être extrait et réécrit. Tu vas peut-être pouvoir sauver la couche model quitte à la écrire quelques adapters de-ci, de-là.
SwiftUI est conçu pour fonctionner main dans la main avec Combine, et il faut utiliser @State, @Binding, etc. pour connecter le modèle et l'IHM.
Ça me parait faisable dans une architecture MVVM, avec les ViewModels écrits en Swift, mais ce n'est pas forcément simple. Tu vas devoir annoter toutes les propriétés d'entrée avec @objc, et à la sortie, déclarer les propriétés en @State, @Binding, etc.
Mais surtout ce qui représente beaucoup de travail, c'est le passage à l'architecture MVVM, sachant que tu ne peux pas utiliser Combine avec ObjC.
Merci Pyroh et Céroce.
Déjà je ne suis pas certain que j'étais parfaitement en MVC donc de là à passer en MVVM...??
Je vais chercher des exemples de code mélangeant Objective-C et Swift.
Tu pourras me demander, j'ai une grosse expérience dans le domaine. C'est ainsi quand on récupère une base de code qui date de 2013! J'ai réécrit ~40% du code en Swift, et tous les tests automatiques.
Swift peut utiliser tous les types de données d'Objective-C, mais Objective-C ne peut pas utiliser les structs et la plupart des enums. Aussi, tu ne pourras pas réécrire ton modèle en Swift et l'utiliser en ObjC à moins de te limiter ou faire des travaux d'interfaçage trop importants.
Je te conseillerais donc d'attaquer le problème par le haut: réécrit d'abord tes View Controllers en Swift, la conversion étant assez directe (quitte à écrire du code pas très swifty). Au passage, tu auras annoté ton code Objective-C avec des
nullable
,nonnull
et autresNS_ENUM
. Tu verras, Apple a bien travaillé.Merci Céroce !
Aurais-tu un exemple ou un extrait pour comparer l'ancien code 100% Objective-C et celui mixé avec du Swift ?
Non, désolé rien en source ouvert, mais c'est assez facile.
Dans le sens Objective-C -> Swift
Ce qui pose problème ce sont les méthodes qui renvoient des objets. Le compilateur ne sachant pas si l'objet peut être
nil
, il va le typer enMonObjet!
, et on vas se retrouver avec plein d'optionals à gérer dans le code Swift.Alors on va annoter avec
nonnull
si le paramètre ne peut pas être nil etnullable
s'il peut l'être. Alors, le compilateur va typer le paramètre enMonObjet
ouMonObjet?
.Dès qu'on ajoute une seule annotation au fichier .h, le compilateur se plaint que les autres lui manquent. Aussi, il faut annoter entièrement un .h quand on commence.
Les paramètres étant généralement nonnull, il existe les macros
NS_ASSUME_NONNULL_BEGIN
etNS_ASSUME_NONNULL_END
qui permettent de définir une zone comme tout nonnil sauf si on annote avecnullable
.Enfin, il existe
_Nonnul
et_Nullable
qu'il faudra utiliser pour les types C (notamment les définitions de blocs/closures)Un cas particulier, les méthodes du type:
Tu verras que ce genre de méthodes est traduit ainsi:
Les énumérations ObjC peuvent être déclarées avec les macros
NS_ENUM
etNS_CLOSED_ENUM
. Ça permet d'avoir des énumérations bien propres en Swift.Enfin, il y a un fichier qui s'appelle
MaCible-Bridging-header.h
. Il doit contenir les#import
de tous les headers ObjC qui doivent être visibles par le code Swift. Xcode propose de le créer dès qu'on ajoute le premier fichier Swift au projet.Dans le sens Swift -> Objective-C
Dans l'autre sens, en général, il faut tout précéder de
@objc
pour que le symbole soit vu par le code Objective-C. Quand je dis tout, ça veut dire: classes (qui doivent hériter de NSObject), protocoles, méthodes (y compris d'init), variables, constantes. Si le type n'est pas utilisable en ObjC, le compilateur gueulera. En général, quelle que soit l'erreur, commence par ajouter des@objc
partout!Pour utiliser les objets Swift dans le code ObjC, il faut un
#import "MaCible-Swift.h";
. Le compilateur est un peu paresseux pour mettre ce fichier à jour, alors parfois, il faut le forcer en lançant un build.Merci Céroce !
Le sujet m'intéresse aussi et je vais conserver tes conseils précieusement
J'avais commencé à rédiger une doc sur le sujet. Si vraiment ça intéresse plus d'une personne, je la rendrai publique 8-)
Un autre soucis de Swift vers Objective-C, car cela peut parfois être utile quand tu commences à traduire l'inverse, mais que ça touche 2 fichiers et que tu dois faire un truc entre les deux. Ça m'est arrivé plus d'une fois.
Les optionnels avec des primitives. En bref, il te faut passer par des NSNumber si tu veux, ou des defaults values, ce qui rend la traduction un peu étrange parfois...
Et je pars du principe, si on ne fait pas de SwiftUI par exemple, que ce qui marche n'a pas forcément besoin d'être traduit, ne pas traduire par le plaisir, car il y a les tests, et du legacy non-écrit (ah tiens, mais pourquoi c'était comme ça? Ça serait plus simple ainsi, non ? Ah, y'a un cas qui arrive 1/100 qui fait foirer, c'est ça ?) Etc.
Merci Céroce, je vais regarder ça de près !