Vaccine
描述
Vaccine 是一个旨在使您的应用程序对所有重编译疾病免疫的框架。Vaccine 提供了一种简单的方法来使您的应用程序准备好进行代码注入,也称为热重载。它为应用代理、NSObject 和视图控制器提供扩展。
在您继续之前,请确保您已经安装了 InjectionIII 并且理解了代码注入的核心概念及其限制。有关 InjectionIII 的更多信息,请访问 https://github.com/johnno1962/InjectionIII。
Vaccine 并不会消除重编译的需求,但它为更快的迭代和立即查看应用程序更改打开了一种可能。将会有一些场景,您需要重新编译应用程序才能看到更改。值得注意的是,代码注入仅适用于模拟器,在实际设备上运行时没有任何效果。
有关如何将注入融合到您的工作流程中的更多信息,请查阅以下文章:
使用方法
以下示例并不旨在成为最佳实践或代码注入的规范做法。示例基于在使用 InjectionIII 的项目中的个人经验。
示例项目
尝试Vaccine with InjectionIII最简单的方法是运行示例项目。
按照以下步骤操作
- 从Mac App Store安装InjectionIII
git clone [email protected]:zenangst/Vaccine.git
- 在
Example/VaccineDemo/
中运行pod install
- 打开并运行
VaccineDemo.xcworkspace
- 当InjectionIII要求您选择文件夹时,选择示例项目。
- 开始享受乐趣 🤩
一般提示
为了最大程度地发挥效果,您应该用依赖注入实现视图控制器,这样您就可以提供与您当前上下文相关的模拟材料。当您想要测试用户界面的不同状态时,它工作得很好。
加载注入包
为了让InjectionIII工作,您需要加载应用程序包内位于的应用程序包中的包。您希望尽早这样做,最好是在应用程序完成启动后尽快进行。
// Loads the injection bundle and registers
// for injection notifications using `injected` selector.
Injection.load(then: applicationDidLoad)
.add(observer: self, with: #selector(injected(_:)))
应用程序委托
为了充分利用代码注入,您需要能够为您应用程序提供一个您要注入的类的实例。在应用程序委托级别重新初始化您的应用程序是一个很好的注入代码的切入点。这样会增加通过使用新注入的代码重新创建根对象来获得所需代码注入效果的可能性。这也为修改的目标视图控制器提供了一个切入点。所以,在实践中这意味着您可以从您的应用程序委托中直接推送或呈现相关的视图控制器,无需手动创建视图控制器堆栈,手动导航到您正在编辑的视图控制器。使用InjectionIII的方式与playground-driven非常相似,无需等待playground加载或重新编译您的应用程序作为框架。
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
Injection.load(then: applicationDidLoad).add(observer: self,
with: #selector(injected(_:)))
return true
}
@objc open func injected(_ notification: Notification) {
applicationDidLoad()
// Add your view hierarchy creation here.
}
private func applicationDidLoad() {
let window = UIWindow(frame: UIScreen.main.bounds)
window.rootViewController = ViewController()
window.makeKeyAndVisible()
self.window = window
}
}
当代码被注入时,会调用 applicationDidLoad
。它会通过创建新窗口来清理并重新创建整个视图层次。
视图控制器
注入视图控制器是InjectionIII最擅长的领域。Vaccine提供了扩展来简化设置和维护。当收到注入通知时,Vaccine会筛选出不符合重载条件的视图控制器。它会检查当前视图控制器是否属于子视图控制器,如果是,则会重新加载父视图控制器,以确保所有必要的控制器都被通知到更改。
注意 Vaccine还支持通过视图、视图控制器以及表格和集合视图数据源的swizzling进行注入。这个功能默认启用,但可以在加载包时将其设置为false
以禁用。
Injection.load(then: ..., swizzling: false)
注入视图控制器时,以下事情会发生
- 移除当前注入观察者
- 移除视图和图层
- 调用
viewDidLoad
以正确设置视图控制器 - 在控制器的视图的所有可用子视图中调用布局相关的方法。
- 对尚未收到大小的所有视图调用
sizeToFit
在你的视图控制器中,你需要监听传入的通知,并在需要回收时取消注册。注册应在viewDidLoad
中完成,因为观察者在注入期间将暂时被移除。
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
Injection.add(observer: self, with: #selector(injected(_:)))
// Implementation goes here.
}
}
当一个视图控制器被注入时,它将调用viewDidLoad
中的所有内容,因此您对控制器所做的任何更改都应该在屏幕上呈现。
视图
注入视图类似于视图控制器,但它们没有覆盖来构建自定义实现的常规方法。通常,您会在初始化器内完成所有操作。为了使您的视图易于注入,您应该将实现从初始化器移动到单独的方法,您可以在视图的类被注入时随时调用该方法。
class CustomView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
addInjection(with: #selector(injected(_:)))
loadView()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
private func loadView() {
// Your code goes here.
}
@objc open func injected(_ notification: Notification) {
loadView()
}
}
如果您在加载注入包时启用swizzling,则所有视图的初始化器将切换,以评估您的视图是否符合注入条件。这是通过检查视图是否响应loadView
选择器来完成的。这消除了在您的视图中手动添加与注入相关的代码的需要。请注意,为了使注入能够正确找到并调用方法,loadView
需要@objc
。不响应该选择器的视图将被忽略。
class CustomView: UIView {
override init(frame: CGRect) {
super.init(frame: frame)
loadView()
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
@objc private func loadView() {
// Your code goes here.
}
}
如果您觉得为创建的所有视图编写这么多代码太多,我建议您创建一个Xcode模板来创建视图。
自动布局约束
增加额外的约束可能会使你的布局约束变得模糊不清。解决这个问题的方法之一是将所有视图约束收集到一个数组中,然后在设置方法的最顶部,将这些约束设置为不激活。这样,你可以通过继续注入来添加额外的约束,只有最新的配对才会处于激活和被使用状态。
当使用交换
时,框架会尝试从你的视图中解析出布局约束
并使其不激活,以避免与你在loadView()
方法中可能应用的新约束发生冲突。这意味着你可以省略调用NSLayoutConstraint
来不激活当前约束。
注意:使用布局约束
是可选的,如果你的视图没有使用存储的约束,那么在视图被注入时,Vaccine将递归地不激活所有子视图上的所有约束。
class CustomView: UIView {
private var layoutConstraints = [NSLayoutConstraint]()
private func loadView() {
NSLayoutConstraint.deactivate(layoutConstraints)
// Your code goes here.
}
}
安装
Vaccine 可通过 CocoaPods 获取。要安装它,只需在 Podfile 中添加以下行
pod 'Vaccine'
Vaccine 也可通过 Carthage 获取。要安装,只需在 Cartfile 中写入
github "zenangst/Vaccine"
Vaccine 也可以手动安装。只需下载并将在项目的 Sources
文件夹中。
作者
Christoffer Winterkvist,[email protected]
致谢
- Vadym Markov 为交换功能的灵感。 [Source]
- John Holdsworth 使运行时代码注入成为可能。
贡献力量
我们非常欢迎您为疫苗贡献力量,请查看CONTRIBUTING文件以获取更多信息。
许可协议
疫苗可在MIT许可下使用。更多信息请查看LICENSE文件。