Readme de langue chinoise: 中文集成指南
L'injection de code vous permet de mettre à jour l'implémentation des fonctions et toute méthode de classe, de structure ou d'énumération progressivement dans le simulateur iOS sans avoir à effectuer une reconstruction complète ou de redémarrer votre application. Cela permet au développeur une quantité importante de code de peaufinage de temps ou d'itréter une conception. En effet, il change Xcode, en étant un "éditeur de source" à un "éditeur de programme" où les changements de source ne sont pas seulement enregistrés sur le disque, mais dans votre programme de course directement.
La configuration de vos projets pour utiliser l'injection est désormais aussi simple que de télécharger l'une des versions GitHub de l'application ou de l'App Store Mac et d'ajouter le code ci-dessous quelque part dans votre application à exécuter au démarrage (il n'est plus nécessaire d'exécuter l'application elle-même).
#if DEBUG
Bundle ( path : " /Applications/InjectionIII.app/Contents/Resources/iOSInjection.bundle " ) ? . load ( )
//for tvOS:
Bundle ( path : " /Applications/InjectionIII.app/Contents/Resources/tvOSInjection.bundle " ) ? . load ( )
//Or for macOS:
Bundle ( path : " /Applications/InjectionIII.app/Contents/Resources/macOSInjection.bundle " ) ? . load ( )
#endif Il est également important d'ajouter les options -Xlinker et -interposable (sans devis doubles et sur des lignes séparées) aux "autres drapeaux de liaison" des cibles de votre projet (pour la configuration Debug uniquement) pour permettre "l'interposition" (voir l'explication ci-dessous).

Après cela, lorsque vous exécutez votre application dans le simulateur, vous devriez voir un message disant qu'un observateur de fichiers a commencé pour votre répertoire domestique et, chaque fois que vous enregistrez un fichier source dans le projet actuel, il devrait le signaler, il a été injecté. Cela signifie que tous les endroits que l'ancienne implémentation auront été mis à jour pour appeler la dernière version de votre code.
Ce n'est pas aussi simple que de voir les résultats à l'écran immédiatement, le nouveau code doit avoir été appelé. Par exemple, si vous injectez un contrôleur de vue, il doit forcer un redéplayage. Pour résoudre ce problème, les classes peuvent implémenter une méthode @objc func injected() qui sera appelée après l'injection de la classe pour effectuer toute mise à jour de l'affichage. Une technique que vous pouvez utiliser consiste à inclure le code suivant quelque part dans votre programme:
#if DEBUG
extension UIViewController {
@ objc func injected ( ) {
viewDidLoad ( )
}
}
#endifUne autre solution à ce problème consiste à «l'hébergement» en utilisant le package Inject Swift introduit par cet article de blog.
Vous ne pouvez pas injecter des modifications dans la façon dont les données sont présentées en mémoire, c'est-à-dire que vous ne pouvez pas ajouter, supprimer ou réorganiser les propriétés avec le stockage. Pour les classes non finales, cela s'applique également à l'ajout ou à la suppression des méthodes, car le vtable utilisé pour la répartition est lui-même une structure de données qui ne doit pas changer par injection. L'injection ne peut pas non plus déterminer quels pièces de code doivent être réexécutées pour mettre à jour l'affichage comme indiqué ci-dessus. De plus, ne vous laissez pas emporter par le contrôle d'accès. Les propriétés et les méthodes private ne peuvent pas être injectées directement, en particulier dans les extensions car elles ne sont pas un symbole global interposable. Ils injectent généralement indirectement car ils ne peuvent être accessoires que dans le fichier injecté, mais cela peut provoquer une confusion. Enfin, l'injection ne fait pas bien face aux fichiers source ajoutés / renommés / supprimés lors de l'injection. Vous devrez peut-être créer et relancer votre application ou même fermer et rouvrir votre projet pour effacer les anciens journaux de construction Xcode.
Swiftui est, si quoi que ce soit, mieux adapté à l'injection que Uikit car il a des mécanismes spécifiques pour mettre à jour l'affichage, mais vous devez apporter quelques modifications à chaque structure View que vous souhaitez injecter. Force Rederser le moyen le plus simple, c'est ajouter une propriété qui observe lorsqu'une injection s'est produite:
@ ObserveInjection var forceRedraw Ce wrapper de propriété est disponible dans le package Swift Hotswiftui ou Inject. Il contient essentiellement un entier @Published vos vues observent que les incréments avec chaque injection. Vous pouvez utiliser l'un des éléments suivants pour rendre l'un de ces packages disponible tout au long de votre projet:
@ _exported import HotSwiftUI
or
@ _exported import Inject Le deuxième changement que vous devez effectuer pour l'injection de Swiftui fiable consiste à "effacer le type de retour" de la propriété corporelle en l'emballage dans AnyView en utilisant la View étendue de la méthode .enableInjection() dans ces packages. En effet, à mesure que vous ajoutez ou supprimez les éléments Swiftui, il peut modifier le type de retour en béton de la propriété corporelle qui équivaut à un changement de mise en page qui peut s'écraser. En résumé, la fin de chaque corps doit toujours ressembler à ceci:
var body : some View {
VStack or whatever {
// Your SwiftUI code...
}
. enableInjection ( )
}
@ ObserveInjection var redraw Vous pouvez laisser ces modifications dans votre code de production car, pour une version Release ils optimisent en un sans-opération.
Nouveau dans Xcode 16 est SWIFT_ENABLE_OPAQUE_TYPE_ERASURE BUILD. Ce paramètre est activé par défaut et vous n'avez pas besoin d'effacer le corps de vue explicitement. Vous devrez toujours @ObserveInjection pour forcer des refonds.
Pour plus d'informations, voir Xcode 16.2 Notes de version.
Cela peut fonctionner, mais vous devrez réellement exécuter l'une des versions GitHub 4.8.0+ de l'injectioniii.App, définissez une valeur par défaut de l'utilisateur pour opter-in et redémarrer l'application.
$ defaults write com.johnholdsworth.InjectionIII deviceUnlock any
Ensuite, au lieu de charger les bundles d'injection, exécutez ce script dans une "phase de construction": (vous devrez également désactiver le paramètre de construction du projet "Script utilisateur Sable de sable")
RESOURCES=/Applications/InjectionIII.app/Contents/Resources
if [ -f "$RESOURCES/copy_bundle.sh" ]; then
"$RESOURCES/copy_bundle.sh"
fi
et, dans votre application, exécutez le code suivant au démarrage:
#if DEBUG
if let path = Bundle . main . path ( forResource :
" iOSInjection " , ofType : " bundle " ) ??
Bundle . main . path ( forResource :
" macOSInjection " , ofType : " bundle " ) {
Bundle ( path : path ) ! . load ( )
}
#endifUne fois que vous avez basculé à cette configuration, cela fonctionnera également lors de l'utilisation du simulateur. Consultez la lecture du projet HotReloading pour plus de détails sur la façon de déboguer pour que votre programme se connecte à l'injectioniii.app sur le Wi-Fi. Vous devrez également sélectionner manuellement le répertoire de projet pour l'observateur de fichiers dans le menu pop-bas.
Cela fonctionne, mais vous devez éteindre temporairement le "APP Sandbox" et la "Validation de la bibliothèque" dans le cadre du "Runtime durci" pendant le développement afin qu'il puisse charger dynamiquement le code. Afin d'éviter les problèmes de codésignage, utilisez le nouveau script copy_bundle.sh comme détaillé dans les instructions d'injection sur les appareils réels ci-dessus.
L'injection a fonctionné de différentes manières au fil des ans, commençant à utiliser les API "Swizzling" pour Objective-C, mais est désormais largement construite autour d'une fonctionnalité de l'éditeur de liaison d'Apple appelée "interposition" qui fournit une solution pour toute méthode rapide ou propriété calculée de tout type.
Lorsque votre code appelle une fonction dans Swift, il est généralement "dépêché statiquement", c'est-à-dire lié à l'aide du "symbole mutilé" de la fonction appelée. Cependant, chaque fois que vous liez votre application avec l'option "-interposable", un niveau supplémentaire d'indirection est ajouté lorsqu'il trouve l'adresse de toutes les fonctions appelée via une section de mémoire écrite. En utilisant la capacité du système d'exploitation à charger le code exécutable et la bibliothèque Fishhook pour "rebinding" l'appel, il est donc possible de "interposer" de nouvelles implémentations de n'importe quelle fonction et de les coudre efficacement dans le reste de votre programme au moment de l'exécution. À partir de ce moment, il se produira comme si le nouveau code avait été intégré au programme.
L'injection utilise l'API FSEventSteam pour surveiller lorsqu'un fichier source a été modifié et scanne le dernier journal de build Xcode pour le recompiler et relier une bibliothèque dynamique qui peut être chargée dans votre programme. La prise en charge de l'exécution pour l'injection charge ensuite la bibliothèque dynamique et le scanne pour les définitions de fonction qu'il contient qu'il "interpose" dans le reste du programme. Ce n'est pas l'histoire complète car l'envoi de méthodes de classe non finale utilise un "VTable" (pensez aux méthodes virtuelles C ++) qui doivent également être mises à jour, mais le projet s'occupe de cela avec tout "swizzling" hérité-objectif-C.
Si vous êtes intéressé à savoir comment l'injection fonctionne, la meilleure source est soit mon livre Swift Secrets ou la nouvelle implémentation de référence de démarrage dans le package InjectionLite Swift. Pour plus d'informations sur «l'interposition», consultez ce billet de blog ou le Readme of the Fishhook Project. Pour plus d'informations sur l'organisation de l'application elle-même, consultez RoadMap.md.
Faire fonctionner l'injection a trois composants. Un fichierwatcher, le code pour recompiler tous les fichiers modifiés et créer une bibliothèque dynamique qui peut être chargée et le code d'injection lui-même qui couvre les nouvelles versions de votre code dans l'application pendant son exécution. La façon dont ces trois composants sont combinés donne lieu au nombre de façons dont l'injection peut être utilisée.
"Injection Classic" est l'endroit où vous téléchargez l'une des sorties binaires de GitHub et exécutez le injectioniii.app. Vous chargez ensuite l'un des paquets à l'intérieur de cette application dans votre programme comme indiqué ci-dessus dans le simulateur. Dans cette configuration, l'observateur de fichiers et la recompilation de source se font à l'intérieur de l'application et le bundle se connecte à l'application à l'aide d'une prise pour savoir quand une nouvelle bibliothèque dynamique est prête à être chargée.
"App Store Injection" Cette version de l'application est sandbox et bien que l'observateur de fichiers s'exécute toujours à l'intérieur de l'application, le recompilation et le chargement sont délégués pour être effectués à l'intérieur du simulateur. Cela peut créer des problèmes avec les fichiers d'en-tête C car le simulateur utilise un système de fichiers sensible à la casse pour être une simulation fidèle d'un réel appareil.
"Injection HotReloading" était l'endroit où vous exécutez votre application sur un appareil et parce que vous ne pouvez pas charger un paquet sur le système de fichiers de votre Mac sur un vrai téléphone, vous ajoutez le package Swift HotReloading à votre projet (pendant le développement uniquement!) Qui contient tout le code qui serait normalement dans le bundle pour effectuer le chargement dynamique. Cela nécessite que vous utilisiez l'une des versions binaires non sandboxes. Il a également été remplacé par le script copy_bundle.sh décrit ci-dessus.
"Injection autonome". Ce fut l'évolution la plus récente du projet où vous n'exécutez plus l'application elle-même, mais il suffit de charger l'un des faisceaux d'injection et le gardien de fichier, la recompilation et l'injection sont tous effectués à l'intérieur du simulateur. Par défaut, cela regarde les modifications de n'importe quel fichier Swift dans votre répertoire domestique, mais vous pouvez modifier cela à l'aide de la variable d'environnement INJECTION_DIRECTORIES .
Injectionlite est une mise en œuvre minimale de démarrage de l'injection autonome pour référence. Ajoutez simplement ce package rapide et vous devriez pouvoir injecter dans le simulateur.
InjectionNext est une version actuellement expérimentale de l'injection qui devrait être plus rapide et plus fiable pour les grands projets. Il s'intègre dans un drapeau de débogage de Xcode pour savoir comment recompiler les fichiers pour éviter d'analyser les journaux de construction et réutiliser la mise en œuvre du client de l'injection à partir InjectionLite . Pour utiliser avec des éditeurs externes tels que Cursor , InjectionNext peut également utiliser un observateur de fichiers pour détecter les modifications et retomber pour créer du code d'analyse de journal.
Toutes ces variantes vous obligent à ajouter les drapeaux de liaison "-xlinker -interposble" pour une version de débogage ou vous ne pourrez qu'injecter des méthodes de classes non finales et tous peuvent être utilisés en conjonction avec l'un des niveaux de niveau supérieur ou Hotswiftui.
Consultez l'ancien ReadMe qui, si quelque chose, contenait simplement «trop d'informations», y compris les différentes variables d'environnement que vous pouvez utiliser pour la personnalisation. Quelques exemples:
| Environnement var. | But |
|---|---|
| Injection_detail | Sortie verbeux de toutes les actions effectuées |
| Injection_trace | Les appels du journal vers des fonctions injectées (v4.6.6 +) |
| Injection_host | Adresse IP de Mac pour l'injection sur les appareils |
Avec une variable d'environnement Injection_Trace , l'injection de tout fichier ajoutera la journalisation de tous les appels aux fonctions et méthodes dans le fichier ainsi que leurs valeurs d'argument comme aide au débogage.
Une caractéristique peu connue de l'injectioniii est que, à condition que vous ayez exécuté les tests de votre application à un moment donné, vous pouvez injecter une classe XCTEST individuelle et avoir exécuté immédiatement - signaler s'il a échoué chaque fois que vous le modifiez.
Ce projet comprend le code de Rentzsch / Mach_Inject, Erwanb / Machinjectsample, Davedelong / Ddhotkey et ACJ / TimelapseBuilder-Swift sous leurs licences respectives.
La fonctionnalité de traçage des applications utilise la mise en œuvre du trampoline OliverPlerer / Imp_ImplementationForwardingToselector via le projet SwiftTrace sous une licence MIT.
SwiftTrace utilise le https://github.com/facebook/fishhook très pratique. Voir la source de projet et le fichier d'en-tête inclus dans le bundle de l'application pour les détails de licence.
Cette version comprend une version très légèrement modifiée de l'excellente bibliothèque Canviz pour rendre des fichiers "dot" dans une toile HTML qui est soumise à une licence MIT. Les modifications sont de passer par l'ID du nœud à la balise d'étiquette de nœud (ligne 212), pour inverser le rendu des nœuds et les lignes les reliant (ligne 406) et pour stocker les chemins de bord afin qu'ils puissent être colorés (ligne 66 et 303) dans "Canviz-0.1 / Canviz.js".
Il inclut également l'éditeur JavaScript CodeMirror pour le code à évaluer à l'aide d'injection sous une licence MIT.
La fabuleuse icône de l'application est grâce à Katya de pixel-mixer.com.