Previews with UIKit(created programatically without storyboard or nib file)

Cover Image for Previews with UIKit(created programatically without storyboard or nib file)

You might wondering how to use the PreviewProvider with UIKit objects.

First, in order to be able to that, we need the SwiftUI to be imported to access the PreviewProvider. This is straightforward if your project is targetting from iOS 13, but if you're still supporting an older version of iOS, like for example 11 or 12, you have to inform the compiler that the SwiftUI is -weak_framework. To do that, go to Targets > your_app_target > Build Settings > Linking > Other Linker Flags and then add -weak_framework SwiftUI.

and the let's create a SwiftUI x UIKit Wrapper.

#if canImport(SwiftUI) && DEBUG
import Foundation
import SwiftUI
import UIKit

@available(iOS 13.0, *)
struct UIViewSwiftUIWrapper<T>: UIViewRepresentable where T: UIView {
  private var setup: (T) -> Void
  
  init(_ setup: @escaping ((T) -> Void)) {
    self.setup = setup
  }
  
  func makeUIView(context _: Context) -> T {
    let view = T()
    setup(view)
    return view
  }
  
  func updateUIView(_: T, context _: Context) {}
}
#endif

and then to use this wrapper:

import UIKit
#if canImport(SwiftUI) && DEBUG
import SwiftUI

@available(iOS 13.0, *)
struct ContentView_Previews: PreviewProvider {
  static var previews: some View {
    UIViewSwiftUIWrapper<UILabel> {
      $0.text = "hello world"
    }
  }
}
#endif

Please take note that if you're supporting ios version that is less than 13, you have to do wrap inside #if canImport(SwiftUI). And && DEBUG so that block of code will only be included during development.

Simmilar approach with UIViewController with this function instead. and that is it.

@available(iOS 13.0, *)
struct UIViewControllerSwiftUIWrapper<T>: UIViewControllerRepresentable where T: UIViewController {

  private var setup: (T) -> Void

  init(_ setup: @escaping ((T) -> Void)) {
    self.setup = setup
  }

  func makeUIViewController(context _: Context) -> T {
    let viewController = T()
    setup(viewController)
    return viewController
  }

  func updateUIViewController(_: T, context _: Context) {}
}

UPDATE!

With the swift-macros, you can now simply achieve this through #Preview.

#Preview {
  UILabel()
}