Is there any way to use storyboard and SwiftUI in same iOS Xcode project?

Mixing UIKit with SwiftUI

Both can be embedded and mixed SwiftUI in Storyboards and the other way around

UIViewControllerRepresentable used for embedding Storyboards in SwiftUI

Create the ViewController on a Storyboard and give it a Storyboard ID

import SwiftUI

struct ContentView: View {
    var body: some View {
        StoryboardViewController()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

struct StoryboardViewController: UIViewControllerRepresentable {
    func makeUIViewController(context: Context) -> some UIViewController {
        let storyboard = UIStoryboard(name: "Storyboard", bundle: Bundle.main)
        let controller = storyboard.instantiateViewController(identifier: "Main")
        return controller
    }
    
    func updateUIViewController(_ uiViewController: UIViewControllerType, context: Context) {
        
    }
}

Communication between UIKit and SwiftUI can be through @Binding variable. This will let SwiftUI view inject variable state and control the it, func updateUIViewController will be called to do the work when changed.

UIViewRepresentable used same way for views

import SwiftUI

struct ContentView: View {
    @State var color = UIColor.green
    
    var body: some View {
        SampleView(color: $color).frame(width: 100, height: 100
                                        , alignment: .center)
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}


struct SampleView: UIViewRepresentable {
    @Binding var color: UIColor
    
    func makeUIView(context: Context) -> some UIView {
        return UIView()
    }
    
    func updateUIView(_ uiView: UIViewType, context: Context) {
        uiView.backgroundColor = color
    }
}

UIHostingController used for embedding SwiftUI in UIKit

First is to be created programmatically

let childViewController = UIHostingController(rootView: SwiftUIContentView())
addChild(childViewController)
childViewController.view.frame = frame
view.addSubview(childViewController.view)
childViewController.didMove(toParent: self)

Second with Xcode 11 by adding Hosting View Controller to the storyboard and create a segue to it and then create an @IBSegueAction by Control-drag from the segue to your ViewController and then create an instance of the SwiftUI view and pass it to HostingViewController initializer

Third is by adding Hosting View Controller to the storyboard and then subclassing it as mentioned in the previous answer above https://stackoverflow.com/a/58250271/3033056


I just started to look at the SwiftUI. Sharing a small example.

  1. In the storyboard add Hosting View Controller
  2. Subclass the UIHostingController with your own class (ChildHostingController) enter image description here
  3. ChildHostingController should look something like that:

import UIKit
import SwiftUI

struct SecondView: View {
  var body: some View {
      VStack {
          Text("Second View").font(.system(size: 36))
          Text("Loaded by SecondView").font(.system(size: 14))
      }
  }
}

class ChildHostingController: UIHostingController<SecondView> {

    required init?(coder: NSCoder) {
        super.init(coder: coder,rootView: SecondView());
    }

    override func viewDidLoad() {
        super.viewDidLoad()
    }
}

For more details have a look at Custom UIHostingController
Apple Docs UIhostingController (Unfortunatelly it hasn't been documented yet)
Integrating SwiftUI Video


Yes you can do that! Here are the steps you can take to do so:

  1. Go to your current Xcode project -> Storyboard, click on the + sign (right upper corner) and search for Hosting Controller (just like you would for a button or label).

  2. Drag Hosting Controller to your Storyboard. Create a Segue connection from your UI element (I'm using a button) to that Hosting Controller and select Push. Create an outlet connection from that Segue to your View Controller (it's a new feature - just like you would create an outlet for a Label), and name it.

enter image description here

  1. Declare your view inside of this outlet connection (you can do that, don't have to use PrepareForSegue method), and return it.

For example: I created a SwiftUI view in my current project (in Xcode: File -> New -> File -> SwiftUI View) and called it DetailsView. My outlet connection would look like this:

import UIKit 
import SwiftUI

class ViewController: UIViewController {

    @IBSegueAction func showDetails(_ coder: NSCoder) -> UIViewController? {
        let detailsView = DetailsView()
        return UIHostingController(coder: coder, rootView: detailsView)
    }

    override func viewDidLoad() {
        super.viewDidLoad()

        // some code

    }
}

That's it! Now run it.