Hot Reloading a SceneDelegate in Swift

Hot Reloading a SceneDelegate in Swift

I told you about Hot Reloading a SwiftUI App in my previous blog post.

In this blog post, I will tell you how to inject code that is not bound to a UI representation. Example: you need to change code in your scene delegate when testing the handling of custom URLs in a UIKit-based app.

You need the same tooling:

  • Inject, a Swift Package created by Krzysztof Zabłocki
  • InjectionIII, a macOS application for the heavy lifting (i.e. watching for modified source code and interposing the new function as if it had been compiled into the SwiftUI app)

I assume you made the necessary configurations in the Build Settings of your App (-Xlinker -interposable) and that you run the InjectIII.app on your Mac.

The critical difference is that we don't use Inject.ViewControllerHost(...) or Inject.ViewHost(...). Instead we call the lazy property Inject.load in the AppDelegate so that InjectIII.app will watch for file changes right from the application start.

import UIKit
import Inject

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        Inject.load // connects running iOS simulator with InjectIII.app
        return true
    }
    // ...
}

That's it !

In my video, I also prove that changes to SceneDelegate.swift will be injected and eventually executed when called by the iOS lifecycle handling.

For my video I registered a URL scheme in the Info.plist.

I added the following function to the SceneDelegate class before running the app.

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
    for context in URLContexts {
        print("url: \(context.url.absoluteURL)")
        print("scheme: \(String(describing: context.url.scheme))")
        print("host: \(String(describing: context.url.host))")
        print("path: \(context.url.path)")
        print("components: \(context.url.pathComponents)")
    }
}

Once the app starts running in the iOS simulator, I change the implementation of scene(_:openURLContexts:)

Using the terminal command xcrun simctl openurl booted <myCustomURLScheme>, I invoke a custom URL in the running iOS simulator, and I witness that the injected code gets executed.

Did you find this article valuable?

Support Swifty Tech by Marco Eidinger by becoming a sponsor. Any amount is appreciated!