Readme de idioma chino: 中文集成指南
La inyección de código le permite actualizar la implementación de funciones y cualquier método de una clase, estructura o enum de forma incremental en el simulador iOS sin tener que realizar una reconstrucción completa o reiniciar su aplicación. Esto le ahorra al desarrollador una cantidad significativa de tiempo de ajuste de tiempo o iterando sobre un diseño. Efectivamente, cambia de XCode de ser un "editor de origen" a ser un "editor de programas" donde los cambios de origen no solo se guardan en el disco sino en su programa en ejecución directamente.
Configurar sus proyectos para usar la inyección ahora es tan simple como descargar una de las versiones de GitHub de la aplicación o desde la App Store de Mac y agregar el código a continuación en algún lugar de su aplicación que se ejecutará en el inicio (ya no es necesario ejecutar la aplicación en sí).
#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 También es importante agregar las opciones -Xlinker y -interposable (sin citas dobles y en líneas separadas) a los "otros banderas de enlace" de los objetivos en su proyecto (solo para la configuración Debug ) para habilitar "interponerse" (consulte la explicación a continuación).

Después de eso, cuando ejecuta su aplicación en el simulador, debería ver un mensaje que dice que un observador de archivos ha comenzado para su directorio de inicio y, cada vez que guarde un archivo fuente en el proyecto actual, debe informar que se ha inyectado. Esto significa que todos los lugares que anteriormente se llamaron la antigua implementación se habrán actualizado para llamar a la última versión de su código.
No es tan simple como eso como para ver los resultados en la pantalla inmediatamente, el nuevo código debe haber sido llamado realmente. Por ejemplo, si inyecta un controlador de vista, necesita forzar una redislay. Para resolver este problema, las clases pueden implementar un método @objc func injected() que se llamará después de que se haya inyectado la clase para realizar cualquier actualización de la pantalla. Una técnica que puede usar es incluir el siguiente código en algún lugar de su programa:
#if DEBUG
extension UIViewController {
@ objc func injected ( ) {
viewDidLoad ( )
}
}
#endifOtra solución a este problema es "alojarse" utilizando el paquete inyectado Swift introducido por esta publicación de blog.
No puede inyectar cambios en cómo se establecen los datos en la memoria, es decir, no puede agregar, eliminar o reordenar las propiedades con el almacenamiento. Para las clases no final, esto también se aplica a la adición o eliminación de métodos ya que el vtable utilizado para el despacho es en sí mismo una estructura de datos que no debe cambiar sobre la inyección. La inyección tampoco puede calcular qué piezas de código deben reecuperarse para actualizar la pantalla como se discutió anteriormente. Además, no te dejes llevar por el control de acceso. Las propiedades y métodos private no se pueden inyectar directamente, particularmente en extensiones, ya que no son un símbolo global interposible. Generalmente inyectan indirectamente, ya que solo pueden estar acenados dentro del archivo que se inyecta, pero esto puede causar confusión. Finalmente, la inyección no hace frente bien a los archivos de origen que se agregan/renombran/se eliminan durante la inyección. Es posible que deba construir y relanzar su aplicación o incluso cerrar y volver a abrir su proyecto para borrar los viejos registros de compilación de Xcode.
Swiftui es, en todo caso, más adecuado para la inyección que UIKIT, ya que tiene mecanismos específicos para actualizar la pantalla, pero debe hacer un par de cambios en cada estructura View que desee inyectar. Para forzar la forma más simple es agregar una propiedad que observa cuándo se ha producido una inyección:
@ ObserveInjection var forceRedraw Este envoltorio de propiedades está disponible en el paquete HotSwiftui o inyectado Swift. Esencialmente contiene un entero @Published que sus puntos de vista observan que se incrementan con cada inyección. Puede usar uno de los siguientes para hacer que uno de estos paquetes esté disponible a lo largo de su proyecto:
@ _exported import HotSwiftUI
or
@ _exported import Inject El segundo cambio que debe hacer para una inyección confiable de Swiftui es "borrar el tipo de retorno" de la propiedad del cuerpo envolviéndola en AnyView utilizando el método .enableInjection() que extiende View en estos paquetes. Esto se debe a que, a medida que agrega o elimina los elementos swiftui, puede cambiar el tipo de retorno concreto de la propiedad del cuerpo que equivale a un cambio de diseño de memoria que puede bloquearse. En resumen, el extremo de la cola de cada cuerpo siempre debe verse así:
var body : some View {
VStack or whatever {
// Your SwiftUI code...
}
. enableInjection ( )
}
@ ObserveInjection var redraw Puede dejar estas modificaciones en su código de producción, ya que, para una compilación Release , optimizan a una OPS NO-OP.
Nuevo en xcode 16 es SWIFT_ENABLE_OPAQUE_TYPE_ERASURE Configuración de compilación. Esta configuración se enciende de forma predeterminada y no necesita borrar el cuerpo de vista explícitamente. Todavía necesitará @ObserveInjection para forzar redibujados.
Para obtener más información, consulte Xcode 16.2 Notas de versión.
Esto puede funcionar, pero deberá ejecutar una de las versiones de GitHub 4.8.0+ de la inyectioniii.app, establecer un usuario predeterminado en opción y reiniciar la aplicación.
$ defaults write com.johnholdsworth.InjectionIII deviceUnlock any
Luego, en lugar de cargar los paquetes de inyección, ejecute este script en una "fase de compilación": (también necesitará desactivar la configuración del proyecto "Script Script Sandboxing")
RESOURCES=/Applications/InjectionIII.app/Contents/Resources
if [ -f "$RESOURCES/copy_bundle.sh" ]; then
"$RESOURCES/copy_bundle.sh"
fi
Y, en su aplicación, ejecute el siguiente código al inicio:
#if DEBUG
if let path = Bundle . main . path ( forResource :
" iOSInjection " , ofType : " bundle " ) ??
Bundle . main . path ( forResource :
" macOSInjection " , ofType : " bundle " ) {
Bundle ( path : path ) ! . load ( )
}
#endifUna vez que haya cambiado a esta configuración, también funcionará cuando use el simulador. Consulte el readme del proyecto HotReloading para obtener detalles sobre cómo depurar tener que su programa se conecte a la inyectioniii.app sobre Wi-Fi. También deberá seleccionar el directorio del proyecto para el observador de archivos manualmente desde el menú de desplazamiento.
Funciona, pero debe desactivar temporalmente el "App Sandbox" y la "Validación de la biblioteca" en el "tiempo de ejecución endurecido" durante el desarrollo para que pueda cargar dinámicamente el código. Para evitar los problemas de codificación de códigos, use el nuevo script copy_bundle.sh como se detalla en las instrucciones de inyección en dispositivos reales anteriores.
La inyección ha funcionado de varias maneras a lo largo de los años, comenzando utilizando las API "Swizzling" para Objective-C, pero ahora se basa en gran medida en una característica del enlazador de Apple llamado "interposición" que proporciona una solución para cualquier método Swift o propiedad calculada de cualquier tipo.
Cuando su código llama a una función en Swift, generalmente se "envía estáticamente", es decir, vinculado usando el "símbolo destrozado" de la función que se llama. Sin embargo, siempre que vincule su aplicación con la opción "-interposible", se agrega un nivel adicional de indirección donde encuentra la dirección de todas las funciones que se llama a través de una sección de memoria de escritura. El uso de la capacidad del sistema operativo para cargar el código ejecutable y la biblioteca Fishhook para "reembolsar" la llamada, por lo tanto, es posible "interponer" nuevas implementaciones de cualquier función y coserlas efectivamente en el resto de su programa en tiempo de ejecución. A partir de ese momento, funcionará como si el nuevo código hubiera sido integrado en el programa.
La inyección utiliza la API FSEventSteam para observar cuándo se ha cambiado un archivo fuente y escanea el último registro de compilación de Xcode sobre cómo recompilarlo y vincula una biblioteca dinámica que se puede cargar en su programa. El soporte de tiempo de ejecución para la inyección luego carga la biblioteca dinámica y la escanea para las definiciones de función que contiene las cuales "interpone" en el resto del programa. Esta no es la historia completa, ya que el envío de métodos de clase no final utiliza un "VTable" (piense en métodos virtuales C ++) que también debe actualizarse, pero el proyecto se ocupa de eso junto con cualquier objeto heredado "" Swizzling ".
Si está interesado en saber más sobre cómo funciona la inyección, la mejor fuente es mi libro Swift Secrets o la nueva implementación de referencia de inicio en el paquete Swift inyectionLite. Para obtener más información sobre la "interposición", consulte esta publicación de blog o el readme del proyecto Fishhook. Para obtener más información sobre la organización de la aplicación en sí, consulte Roadmap.md.
Obtener inyección al trabajo tiene tres componentes. Un FileWatcher, el código para recompilar cualquier archivo cambiado y construir una biblioteca dinámica que se pueda cargar y el código de inyección en sí mismo que cose las nuevas versiones de su código en la aplicación mientras se está ejecutando. La forma en que se combinan estos tres componentes da lugar a la cantidad de formas en que se puede usar la inyección.
"Inyection Classic" es donde descargas uno de los lanzamientos binarios de GitHub y ejecuta la inyectioniii.app. Luego carga uno de los paquetes dentro de esa aplicación en su programa como se muestra arriba en el simulador. En esta configuración, el observador de archivos y la recompina de fuente se realizan dentro de la aplicación y el paquete se conecta a la aplicación usando un socket para saber cuándo está lista una nueva biblioteca dinámica.
"Inyección de App Store" Esta versión de la aplicación está en arena y mientras el observador de archivos todavía se ejecuta dentro de la aplicación, la recompilación y la carga se delegan para realizar dentro del simulador. Esto puede crear problemas con los archivos de encabezado C, ya que el simulador utiliza un sistema de archivos sensible a mayúscula para ser una simulación fiel de un dispositivo real.
La "inyección de HotReloading" fue donde está ejecutando su aplicación en un dispositivo y, debido a que no puede cargar un paquete del sistema de archivos de su Mac en un teléfono real, agregue el paquete Swift de HotReloading a su proyecto (¡solo durante el desarrollo!), Que contiene todo el código que normalmente estaría en el paquete para realizar la carga dinámica. Esto requiere que use una de las versiones binarias sin arandelas. También ha sido reemplazado por el script copy_bundle.sh descrito anteriormente.
"Inyección independiente". Esta fue la evolución más reciente del proyecto en la que ya no ejecuta la aplicación en sí, sino que simplemente carga uno de los paquetes de inyección y el observador de archivos, la compilación y la inyección se realizan dentro del simulador. Por defecto, esto observa los cambios en cualquier archivo Swift dentro de su directorio de inicio, aunque puede cambiar esto utilizando la variable de entorno INJECTION_DIRECTORIES .
InyectionLite es una implementación mínima de inicio de la inyección independiente para referencia. Simplemente agregue este paquete Swift y debería poder inyectar en el simulador.
InyectionNext es una versión actualmente experimental de inyección que debería ser más rápida y más confiable para grandes proyectos. Se integra en un indicador de depuración de Xcode para descubrir cómo recompilar archivos para evitar analizar registros de compilación y reutiliza la implementación del cliente de inyección de InjectionLite . Para usar con editores externos como Cursor , InyectionNext también puede usar un observador de archivos para detectar ediciones y retroceder para construir el código de análisis de registro de registro.
Todas estas variaciones requieren que agregue los indicadores de enlazador "-xlinker -interposble" para una compilación de depuración o solo podrá inyectar métodos de clases no final y todos se pueden usar junto con el inyección de nivel superior o HotSwiftui.
Consulte el readme antiguo que si algo contenía simplemente "demasiada información", incluidas las diversas variables de entorno que puede usar para la personalización. Algunos ejemplos:
| Entorno var. | Objetivo |
|---|---|
| Inyection_detail | Salida detallada de todas las acciones realizadas |
| Inyección_trace | Llamadas de registro a funciones inyectadas (v4.6.6+) |
| Inyección_host | Dirección IP de Mac para inyección en el dispositivo |
Con una variable de entorno inyection_trace , inyectar cualquier archivo agregará el registro de todas las llamadas a las funciones y métodos en el archivo junto con los valores de sus argumentos como una ayuda para la depuración.
Una característica poco conocida de inyectionIII es que siempre que haya ejecutado las pruebas para su aplicación en algún momento, puede inyectar una clase XCTEST individual y tener si se ejecuta de inmediato, informando si ha fallado cada vez que la modifique.
Este proyecto incluye código de Rentzsch/Mach_Inject, Erwanb/MachinjectSample, Davedelong/Ddhotkey y ACJ/TimeLapseBuilder-Swift bajo sus respectivas licencias.
La funcionalidad de rastreo de aplicaciones utiliza la implementación del trampolín OliverLeter/IMP_ImplementationForwardingTosElector a través del proyecto SwiftTrace bajo una licencia MIT.
SwiftTrace utiliza el muy práctico https://github.com/facebook/fishhook. Consulte la fuente del proyecto y el archivo de encabezado incluidos en el paquete de aplicaciones para obtener detalles de licencias.
Esta versión incluye una versión muy ligeramente modificada de la excelente biblioteca Canviz para representar archivos "DOT" en un lienzo HTML que está sujeto a una licencia MIT. Los cambios son pasar a través de la ID del nodo al etiqueta de la etiqueta del nodo (línea 212), para revertir la representación de nodos y las líneas que los vinculan (línea 406) y para almacenar rutas de borde para que puedan colorear (línea 66 y 303) en "Canviz-0.1/Canviz.js".
También incluye el editor JavaScript de CodeMirror para el código que se evaluará utilizando la inyección bajo una licencia MIT.
El fabuloso ícono de la aplicación es gracias a Katya de Pixel-mixer.com.