How does the hot-reloading work in XCode11?

I'm interested in the hot-reloading demonstrated for SwiftUI. How is this implemented? Could something similar be achieved for a desktop application using the swift toolchain independently of XCode?

1 Like

It uses Dynamic method replacement.

1 Like

Do you know what the status of that feature is? I.e. will it be public in Swift 5.1? I can't seem to find any documentation.

Not sure, you can use it in Swift 5.1 (if you have a development toolchain or Xcode 11), but it's not meant for public use yet. @Arnold is probably the best person who can answer this as he wrote the feature.

Before anything I have to preface this with saying that @_dynamicReplacement(for:) is an unsupported feature that might go away anytime (It has not gone through Swift evolution and there have been suggestions of how to make this more general. Expect that syntax or functionality changes.)

Starting with Swift 5.0 the compiler and runtime have support for dynamic replacements as outlined in the thread dynamic-method-replacement.

What that means is that if you mark a function (or variable declaration) as dynamic you can provide an alternate implementation with @_dynamicReplacement(for:).

Dynamic replacement works by compiling those replacements into a dynamic library that can be loaded at runtime (e.g via dlopen). When a library with @_dynamicReplacement(for:) declarations is loaded the replacement will take place.

Consider this small example:

% cat Original.swift
public struct Something {
  public init() {}
  public dynamic func original() {
    print("original")
  }
}
% xcrun swiftc -emit-library -emit-module Original.swift
% cat Replacement.swift
import Original

extension Something {
  @_dynamicReplacement(for: original())
  func replacement() {
    print("replacement")
  }
}
% xcrun swiftc -emit-library -emit-module Replacement.swift -I. -L. -lOriginal
% cat main.swift
import Original
import Darwin

Something().original()
_ = dlopen("libReplacement.dylib", 0)
Something().original()
% xcrun swiftc -I. -L. -lOriginal main.swift      
% ./main
original
replacement

One could imagine that you could build code replacement on top of this by tracking which methods change and whether the changes allow replacement this way.

21 Likes

Thank you, this is extremely helpful!