Deepak Sharma
Deepak Sharma

Reputation: 6487

Adapting SwiftUI like flow in UIKit/Storyboard based app

I have the following flow in a sample SwiftUI example which is very handy. It basically checks for a flag in UserDefaults whether an intro view has been shown before or not, and based on it loads the ContentView or IntroView. Once IntroView has been shown and the user taps the IntroView, it automatically switches back to ContentView. This is really a good feature in SwiftUI where the App struct observes the bool and recomputes WindowGroup.

Problem: I have an existing UIKit based app based on Storyboard and I am slowly injecting SwiftUI views into it using UIHostingController. But I don't really get SwiftUI app lifecycle benefits like above in it and it becomes cumbersome. For instance, after showing IntroView, I might need to show another view such as PermissionsView where I ask for camera and photo library permissions which the user can alter anytime in iOS Settings. In SwiftUI, the code such as below automatically watches any changes and pops the IntroView or PermissionsView. How do I do this in my UIKit based app? Is it possible to migrate the UIApplicationDelegate & SceneDelegate code to SwiftUI App while still having view controllers load from Storyboard?

@main
struct TestSwiftUIIntroViewsApp: App {

   @AppStorage("showedIntroView") var showedIntroView: Bool = false

   var body: some Scene {
      WindowGroup {
         if showedIntroView {
             ContentView()
         } else {
             IntroView()
         }
     }
   }
 }

struct ContentView: View {
   var body: some View {
       VStack {
           Text("Content View")
       }
       .padding()
   }
 }

struct IntroView: View {

    @AppStorage("showedIntroView") var showedIntroView: Bool = false

    var body: some View {
      VStack(alignment: .center, content: {
         Text("Intro View")
      })
     .onTapGesture {
        showedIntroView = true
      }
    }

}

Upvotes: 0

Views: 185

Answers (1)

MatBuompy
MatBuompy

Reputation: 2093

I don't know if I'm getting this right, but yes, you can load storyboard views or any UIViewController view doing something like this:

struct MyView: UIViewControllerRepresentable {
    func makeUIViewController(context: Context) -> MyViewController {
        // For Storyboard views
        let storyboard = UIStoryboard(name: "Main", bundle: nil)
        let viewController = storyboard.instantiateViewController(withIdentifier: "MyViewController") as! MyViewController
        // For manually created UIViewControllers
        /*
        let vc = MyAwesomeViewController()
        return vc
        */
        return viewController
    }

    func updateUIViewController(_ uiViewController: MyViewController, context: Context) {
        // Update the view controller here
    }
}

Then you can use the above code like it was a SwiftUI view:

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

Also, I don't know how old is your app but it seems pretty recent to me, but it was common practice to use AppDelegate (and even before SceneDelegate) in SwiftUI apps. If you want to use an AppDelegate or your existing one into your SwiftUI app you can by declaring it into the struct that conforms to App in your SwiftUI project like this:

@UIApplicationDelegateAdaptor(MyAppDelegate.self) var appDelegate

And it should work like it was in a UIKit this way. I hope I was able to at least help you partially. Have a nice coding day!

Upvotes: 1

Related Questions